text: #ifndef _C4_YML_EMIT_DEF_HPP_
  #define _C4_YML_EMIT_DEF_HPP_
  
  #ifndef _C4_YML_EMIT_HPP_
  #include "c4/yml/emit.hpp"
  #endif
  
  namespace c4 {
  namespace yml {
  
  template<class Writer>
  substr Emitter<Writer>::emit(EmitType_e type, Tree const& t, size_t id, bool error_on_excess)
  {
      if(t.empty())
      {
          _RYML_CB_ASSERT(t.callbacks(), id == NONE);
          return {};
      }
      _RYML_CB_CHECK(t.callbacks(), id < t.size());
      m_tree = &t;
      if(type == EMIT_YAML)
          _emit_yaml(id);
      else if(type == EMIT_JSON)
          _do_visit_json(id);
      else
          _RYML_CB_ERR(m_tree->callbacks(), "unknown emit type");
      return this->Writer::_get(error_on_excess);
  }
  
  template<class Writer>
  substr Emitter<Writer>::emit(EmitType_e type, Tree const& t, bool error_on_excess)
  {
      if(t.empty())
          return {};
      return emit(type, t, t.root_id(), error_on_excess);
  }
  
  template<class Writer>
  substr Emitter<Writer>::emit(EmitType_e type, NodeRef const& n, bool error_on_excess)
  {
      _RYML_CB_CHECK(n.tree()->callbacks(), n.valid());
      return emit(type, *n.tree(), n.id(), error_on_excess);
  }
  
  
  //-----------------------------------------------------------------------------
  
  template<class Writer>
  void Emitter<Writer>::_emit_yaml(size_t id)
  {
      // save branches in the visitor by doing the initial stream/doc
      // logic here, sparing the need to check stream/val/keyval inside
      // the visitor functions
      auto dispatch = [this](size_t node){
          NodeType ty = m_tree->type(node);
          if(ty.marked_flow_sl())
              _do_visit_flow_sl(node, 0);
          else if(ty.marked_flow_ml())
              _do_visit_flow_ml(node, 0);
          else
          {
              _do_visit_block(node, 0);
          }
      };
      if(!m_tree->is_root(id))
      {
          if(m_tree->is_container(id) && !m_tree->type(id).marked_flow())
          {
              size_t ilevel = 0;
              if(m_tree->has_key(id))
              {
                  this->Writer::_do_write(m_tree->key(id));
                  this->Writer::_do_write(":\n");
                  ++ilevel;
              }
              _do_visit_block_container(id, ilevel, ilevel);
              return;
          }
      }
  
      if(m_tree->is_stream(id))
      {
          for(size_t child = m_tree->first_child(id); child != NONE; child = m_tree->next_sibling(child))
              dispatch(child);
      }
      else if(m_tree->is_container(id))
      {
          dispatch(id);
      }
      else if(m_tree->is_doc(id))
      {
          _RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->is_container(id)); // checked above
          _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_val(id)); // so it must be a val
          _write_doc(id);
      }
      else if(m_tree->is_keyval(id))
      {
          _writek(id, 0);
          this->Writer::_do_write(": ");
          _writev(id, 0);
          if(!m_tree->type(id).marked_flow())
              this->Writer::_do_write('\n');
      }
      else if(m_tree->is_val(id))
      {
          //this->Writer::_do_write("- ");
          _writev(id, 0);
          if(!m_tree->type(id).marked_flow())
              this->Writer::_do_write('\n');
      }
      else if(m_tree->type(id) == NOTYPE)
      {
          ;
      }
      else
      {
          _RYML_CB_ERR(m_tree->callbacks(), "unknown type");
      }
  }
  
  template<class Writer>
  void Emitter<Writer>::_write_doc(size_t id)
  {
      RYML_ASSERT(m_tree->is_doc(id));
      if(!m_tree->is_root(id))
      {
          RYML_ASSERT(m_tree->is_stream(m_tree->parent(id)));
          this->Writer::_do_write("---");
      }
      if(!m_tree->has_val(id)) // this is more frequent
      {
          if(m_tree->has_val_tag(id))
          {
              if(!m_tree->is_root(id))
                  this->Writer::_do_write(' ');
              _write_tag(m_tree->val_tag(id));
          }
          if(m_tree->has_val_anchor(id))
          {
              if(!m_tree->is_root(id))
                  this->Writer::_do_write(' ');
              this->Writer::_do_write('&');
              this->Writer::_do_write(m_tree->val_anchor(id));
          }
      }
      else // docval
      {
          RYML_ASSERT(m_tree->has_val(id));
          RYML_ASSERT(!m_tree->has_key(id));
          if(!m_tree->is_root(id))
              this->Writer::_do_write(' ');
          _writev(id, 0);
      }
      this->Writer::_do_write('\n');
  }
  
  template<class Writer>
  void Emitter<Writer>::_do_visit_flow_sl(size_t node, size_t ilevel)
  {
      RYML_ASSERT(!m_tree->is_stream(node));
      RYML_ASSERT(m_tree->is_container(node) || m_tree->is_doc(node));
      RYML_ASSERT(m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)));
  
      if(m_tree->is_doc(node))
      {
          _write_doc(node);
          if(!m_tree->has_children(node))
              return;
      }
      else if(m_tree->is_container(node))
      {
          RYML_ASSERT(m_tree->is_map(node) || m_tree->is_seq(node));
  
          bool spc = false; // write a space
  
          if(m_tree->has_key(node))
          {
              _writek(node, ilevel);
              this->Writer::_do_write(':');
              spc = true;
          }
  
          if(m_tree->has_val_tag(node))
          {
              if(spc)
                  this->Writer::_do_write(' ');
              _write_tag(m_tree->val_tag(node));
              spc = true;
          }
  
          if(m_tree->has_val_anchor(node))
          {
              if(spc)
                  this->Writer::_do_write(' ');
              this->Writer::_do_write('&');
              this->Writer::_do_write(m_tree->val_anchor(node));
              spc = true;
          }
  
          if(spc)
              this->Writer::_do_write(' ');
  
          if(m_tree->is_map(node))
          {
              this->Writer::_do_write('{');
          }
          else
          {
              _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_seq(node));
              this->Writer::_do_write('[');
          }
      } // container
  
      for(size_t child = m_tree->first_child(node), count = 0; child != NONE; child = m_tree->next_sibling(child))
      {
          if(count++)
              this->Writer::_do_write(',');
          if(m_tree->is_keyval(child))
          {
              _writek(child, ilevel);
              this->Writer::_do_write(": ");
              _writev(child, ilevel);
          }
          else if(m_tree->is_val(child))
          {
              _writev(child, ilevel);
          }
          else
          {
              // with single-line flow, we can never go back to block
              _do_visit_flow_sl(child, ilevel + 1);
          }
      }
  
      if(m_tree->is_map(node))
      {
          this->Writer::_do_write('}');
      }
      else if(m_tree->is_seq(node))
      {
          this->Writer::_do_write(']');
      }
  }
  
  template<class Writer>
  void Emitter<Writer>::_do_visit_flow_ml(size_t id, size_t ilevel, size_t do_indent)
  {
      C4_UNUSED(id);
      C4_UNUSED(ilevel);
      C4_UNUSED(do_indent);
      RYML_CHECK(false/*not implemented*/);
  }
  
  template<class Writer>
  void Emitter<Writer>::_do_visit_block_container(size_t node, size_t next_level, size_t do_indent)
  {
      RepC ind = indent_to(do_indent * next_level);
  
      if(m_tree->is_seq(node))
      {
          for(size_t child = m_tree->first_child(node); child != NONE; child = m_tree->next_sibling(child))
          {
              _RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->has_key(child));
              if(m_tree->is_val(child))
              {
                  this->Writer::_do_write(ind);
                  this->Writer::_do_write("- ");
                  _writev(child, next_level);
                  this->Writer::_do_write('\n');
              }
              else
              {
                  _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(child));
                  NodeType ty = m_tree->type(child);
                  if(ty.marked_flow_sl())
                  {
                      this->Writer::_do_write(ind);
                      this->Writer::_do_write("- ");
                      _do_visit_flow_sl(child, 0u);
                      this->Writer::_do_write('\n');
                  }
                  else if(ty.marked_flow_ml())
                  {
                      this->Writer::_do_write(ind);
                      this->Writer::_do_write("- ");
                      _do_visit_flow_ml(child, next_level, do_indent);
                      this->Writer::_do_write('\n');
                  }
                  else
                  {
                      _do_visit_block(child, next_level, do_indent);
                  }
              }
              do_indent = true;
              ind = indent_to(do_indent * next_level);
          }
      }
      else // map
      {
          _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_map(node));
          for(size_t ich = m_tree->first_child(node); ich != NONE; ich = m_tree->next_sibling(ich))
          {
              _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->has_key(ich));
              if(m_tree->is_keyval(ich))
              {
                  this->Writer::_do_write(ind);
                  _writek(ich, next_level);
                  this->Writer::_do_write(": ");
                  _writev(ich, next_level);
                  this->Writer::_do_write('\n');
              }
              else
              {
                  _RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(ich));
                  NodeType ty = m_tree->type(ich);
                  if(ty.marked_flow_sl())
                  {
                      this->Writer::_do_write(ind);
                      _do_visit_flow_sl(ich, 0u);
                      this->Writer::_do_write('\n');
                  }
                  else if(ty.marked_flow_ml())
                  {
                      this->Writer::_do_write(ind);
                      _do_visit_flow_ml(ich, 0u);
                      this->Writer::_do_write('\n');
                  }
                  else
                  {
                      _do_visit_block(ich, next_level, do_indent);
                  }
              }
              do_indent = true;
              ind = indent_to(do_indent * next_level);
          }
      }
  }
  
  template<class Writer>
  void Emitter<Writer>::_do_visit_block(size_t node, size_t ilevel, size_t do_indent)
  {
      RYML_ASSERT(!m_tree->is_stream(node));
      RYML_ASSERT(m_tree->is_container(node) || m_tree->is_doc(node));
      RYML_ASSERT(m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)));
      RepC ind = indent_to(do_indent * ilevel);
  
      if(m_tree->is_doc(node))
      {
          _write_doc(node);
          if(!m_tree->has_children(node))
              return;
      }
      else if(m_tree->is_container(node))
      {
          RYML_ASSERT(m_tree->is_map(node) || m_tree->is_seq(node));
  
          bool spc = false; // write a space
          bool nl = false;  // write a newline
  
          if(m_tree->has_key(node))
          {
              this->Writer::_do_write(ind);
              _writek(node, ilevel);
              this->Writer::_do_write(':');
              spc = true;
          }
          else if(!m_tree->is_root(node))
          {
              this->Writer::_do_write(ind);
              this->Writer::_do_write('-');
              spc = true;
          }
  
          if(m_tree->has_val_tag(node))
          {
              if(spc)
                  this->Writer::_do_write(' ');
              _write_tag(m_tree->val_tag(node));
              spc = true;
              nl = true;
          }
  
          if(m_tree->has_val_anchor(node))
          {
              if(spc)
                  this->Writer::_do_write(' ');
              this->Writer::_do_write('&');
              this->Writer::_do_write(m_tree->val_anchor(node));
              spc = true;
              nl = true;
          }
  
          if(m_tree->has_children(node))
          {
              if(m_tree->has_key(node))
                  nl = true;
              else
                  if(!m_tree->is_root(node) && !nl)
                      spc = true;
          }
          else
          {
              if(m_tree->is_seq(node))
                  this->Writer::_do_write(" []\n");
              else if(m_tree->is_map(node))
                  this->Writer::_do_write(" {}\n");
              return;
          }
  
          if(spc && !nl)
              this->Writer::_do_write(' ');
  
          do_indent = 0;
          if(nl)
          {
              this->Writer::_do_write('\n');
              do_indent = 1;
          }
      } // container
  
      size_t next_level = ilevel + 1;
      if(m_tree->is_root(node) || m_tree->is_doc(node))
          next_level = ilevel; // do not indent at top level
  
      _do_visit_block_container(node, next_level, do_indent);
  }
  
  template<class Writer>
  void Emitter<Writer>::_do_visit_json(size_t id)
  {
      _RYML_CB_CHECK(m_tree->callbacks(), !m_tree->is_stream(id)); // JSON does not have streams
      if(m_tree->is_keyval(id))
      {
          _writek_json(id);
          this->Writer::_do_write(": ");
          _writev_json(id);
      }
      else if(m_tree->is_val(id))
      {
          _writev_json(id);
      }
      else if(m_tree->is_container(id))
      {
          if(m_tree->has_key(id))
          {
              _writek_json(id);
              this->Writer::_do_write(": ");
          }
          if(m_tree->is_seq(id))
              this->Writer::_do_write('[');
          else if(m_tree->is_map(id))
              this->Writer::_do_write('{');
      }  // container
  
      for(size_t ich = m_tree->first_child(id); ich != NONE; ich = m_tree->next_sibling(ich))
      {
          if(ich != m_tree->first_child(id))
              this->Writer::_do_write(',');
          _do_visit_json(ich);
      }
  
      if(m_tree->is_seq(id))
          this->Writer::_do_write(']');
      else if(m_tree->is_map(id))
          this->Writer::_do_write('}');
  }
  
  template<class Writer>
  void Emitter<Writer>::_write(NodeScalar const& C4_RESTRICT sc, NodeType flags, size_t ilevel)
  {
      if( ! sc.tag.empty())
      {
          _write_tag(sc.tag);
          this->Writer::_do_write(' ');
      }
      if(flags.has_anchor())
      {
          RYML_ASSERT(flags.is_ref() != flags.has_anchor());
          RYML_ASSERT( ! sc.anchor.empty());
          this->Writer::_do_write('&');
          this->Writer::_do_write(sc.anchor);
          this->Writer::_do_write(' ');
      }
      else if(flags.is_ref())
      {
          if(sc.anchor != "<<")
              this->Writer::_do_write('*');
          this->Writer::_do_write(sc.anchor);
          return;
      }
  
      // ensure the style flags only have one of KEY or VAL
      _RYML_CB_ASSERT(m_tree->callbacks(), ((flags & (_WIP_KEY_STYLE|_WIP_VAL_STYLE)) == 0) || (((flags&_WIP_KEY_STYLE) == 0) != ((flags&_WIP_VAL_STYLE) == 0)));
  
      auto style_marks = flags & (_WIP_KEY_STYLE|_WIP_VAL_STYLE);
      if(style_marks & (_WIP_KEY_LITERAL|_WIP_VAL_LITERAL))
      {
          _write_scalar_literal(sc.scalar, ilevel, flags.has_key());
      }
      else if(style_marks & (_WIP_KEY_FOLDED|_WIP_VAL_FOLDED))
      {
          _write_scalar_folded(sc.scalar, ilevel, flags.has_key());
      }
      else if(style_marks & (_WIP_KEY_SQUO|_WIP_VAL_SQUO))
      {
          _write_scalar_squo(sc.scalar, ilevel);
      }
      else if(style_marks & (_WIP_KEY_DQUO|_WIP_VAL_DQUO))
      {
          _write_scalar_dquo(sc.scalar, ilevel);
      }
      else if(style_marks & (_WIP_KEY_PLAIN|_WIP_VAL_PLAIN))
      {
          _write_scalar_plain(sc.scalar, ilevel);
      }
      else if(!style_marks)
      {
          size_t first_non_nl = sc.scalar.first_not_of('\n');
          bool all_newlines = first_non_nl == npos;
          bool has_leading_ws = (!all_newlines) && sc.scalar.sub(first_non_nl).begins_with_any(" \t");
          bool do_literal = ((!sc.scalar.empty() && all_newlines) || (has_leading_ws && !sc.scalar.trim(' ').empty()));
          if(do_literal)
          {
              _write_scalar_literal(sc.scalar, ilevel, flags.has_key(), /*explicit_indentation*/has_leading_ws);
          }
          else
          {
              for(size_t i = 0; i < sc.scalar.len; ++i)
              {
                  if(sc.scalar.str[i] == '\n')
                  {
                      _write_scalar_literal(sc.scalar, ilevel, flags.has_key(), /*explicit_indentation*/has_leading_ws);
                      goto wrote_special;
                  }
                  // todo: check for escaped characters requiring double quotes
              }
              _write_scalar(sc.scalar, flags.is_quoted());
          wrote_special:
              ;
          }
      }
      else
      {
          _RYML_CB_ERR(m_tree->callbacks(), "not implemented");
      }
  }
  template<class Writer>
  void Emitter<Writer>::_write_json(NodeScalar const& C4_RESTRICT sc, NodeType flags)
  {
      if(C4_UNLIKELY( ! sc.tag.empty()))
          _RYML_CB_ERR(m_tree->callbacks(), "JSON does not have tags");
      if(C4_UNLIKELY(flags.has_anchor()))
          _RYML_CB_ERR(m_tree->callbacks(), "JSON does not have anchors");
      _write_scalar_json(sc.scalar, flags.has_key(), flags.is_quoted());
  }
  
  #define _rymlindent_nextline() for(size_t lv = 0; lv < ilevel+1; ++lv) { this->Writer::_do_write(' '); this->Writer::_do_write(' '); }
  
  template<class Writer>
  void Emitter<Writer>::_write_scalar_literal(csubstr s, size_t ilevel, bool explicit_key, bool explicit_indentation)
  {
      if(explicit_key)
          this->Writer::_do_write("? ");
      csubstr trimmed = s.trimr("\n\r");
      size_t numnewlines_at_end = s.len - trimmed.len - s.sub(trimmed.len).count('\r');
      //
      if(!explicit_indentation)
          this->Writer::_do_write('|');
      else
          this->Writer::_do_write("|2");
      //
      if(numnewlines_at_end > 1 || (trimmed.len == 0 && s.len > 0)/*only newlines*/)
          this->Writer::_do_write("+\n");
      else if(numnewlines_at_end == 1)
          this->Writer::_do_write('\n');
      else
          this->Writer::_do_write("-\n");
      //
      if(trimmed.len)
      {
          size_t pos = 0; // tracks the last character that was already written
          for(size_t i = 0; i < trimmed.len; ++i)
          {
              if(trimmed[i] != '\n')
                  continue;
              // write everything up to this point
              csubstr since_pos = trimmed.range(pos, i+1); // include the newline
              _rymlindent_nextline()
              this->Writer::_do_write(since_pos);
              pos = i+1; // already written
          }
          if(pos < trimmed.len)
          {
              _rymlindent_nextline()
              this->Writer::_do_write(trimmed.sub(pos));
          }
          if(numnewlines_at_end)
          {
              this->Writer::_do_write('\n');
              --numnewlines_at_end;
          }
      }
      for(size_t i = 0; i < numnewlines_at_end; ++i)
      {
          _rymlindent_nextline()
          if(i+1 < numnewlines_at_end || explicit_key)
              this->Writer::_do_write('\n');
      }
      if(explicit_key && !numnewlines_at_end)
          this->Writer::_do_write('\n');
  }
  
  template<class Writer>
  void Emitter<Writer>::_write_scalar_folded(csubstr s, size_t ilevel, bool explicit_key)
  {
      if(explicit_key)
      {
          this->Writer::_do_write("? ");
      }
      RYML_ASSERT(s.find("\r") == csubstr::npos);
      csubstr trimmed = s.trimr('\n');
      size_t numnewlines_at_end = s.len - trimmed.len;
      if(numnewlines_at_end == 0)
      {
          this->Writer::_do_write(">-\n");
      }
      else if(numnewlines_at_end == 1)
      {
          this->Writer::_do_write(">\n");
      }
      else if(numnewlines_at_end > 1)
      {
          this->Writer::_do_write(">+\n");
      }
      if(trimmed.len)
      {
          size_t pos = 0; // tracks the last character that was already written
          for(size_t i = 0; i < trimmed.len; ++i)
          {
              if(trimmed[i] != '\n')
                  continue;
              // write everything up to this point
              csubstr since_pos = trimmed.range(pos, i+1); // include the newline
              pos = i+1; // because of the newline
              _rymlindent_nextline()
              this->Writer::_do_write(since_pos);
              this->Writer::_do_write('\n'); // write the newline twice
          }
          if(pos < trimmed.len)
          {
              _rymlindent_nextline()
              this->Writer::_do_write(trimmed.sub(pos));
          }
          if(numnewlines_at_end)
          {
              this->Writer::_do_write('\n');
              --numnewlines_at_end;
          }
      }
      for(size_t i = 0; i < numnewlines_at_end; ++i)
      {
          _rymlindent_nextline()
          if(i+1 < numnewlines_at_end || explicit_key)
              this->Writer::_do_write('\n');
      }
      if(explicit_key && !numnewlines_at_end)
          this->Writer::_do_write('\n');
  }
  
  template<class Writer>
  void Emitter<Writer>::_write_scalar_squo(csubstr s, size_t ilevel)
  {
      size_t pos = 0; // tracks the last character that was already written
      this->Writer::_do_write('\'');
      for(size_t i = 0; i < s.len; ++i)
      {
          if(s[i] == '\n')
          {
              csubstr sub = s.range(pos, i+1);
              this->Writer::_do_write(sub);  // write everything up to (including) this char
              this->Writer::_do_write('\n'); // write the character again
              if(i + 1 < s.len)
                  _rymlindent_nextline()     // indent the next line
              pos = i+1;
          }
          else if(s[i] == '\'')
          {
              csubstr sub = s.range(pos, i+1);
              this->Writer::_do_write(sub); // write everything up to (including) this char
              this->Writer::_do_write('\''); // write the character again
              pos = i+1;
          }
      }
      // write missing characters at the end of the string
      if(pos < s.len)
          this->Writer::_do_write(s.sub(pos));
      this->Writer::_do_write('\'');
  }
  
  template<class Writer>
  void Emitter<Writer>::_write_scalar_dquo(csubstr s, size_t ilevel)
  {
      size_t pos = 0; // tracks the last character that was already written
      this->Writer::_do_write('"');
      for(size_t i = 0; i < s.len; ++i)
      {
          const char curr = s.str[i];
          if(curr == '"' || curr == '\\')
          {
              csubstr sub = s.range(pos, i);
              this->Writer::_do_write(sub);  // write everything up to (excluding) this char
              this->Writer::_do_write('\\'); // write the escape
              this->Writer::_do_write(curr); // write the char
              pos = i+1;
          }
          else if(s[i] == '\n')
          {
              csubstr sub = s.range(pos, i+1);
              this->Writer::_do_write(sub);  // write everything up to (including) this newline
              this->Writer::_do_write('\n'); // write the newline again
              if(i + 1 < s.len)
                  _rymlindent_nextline()     // indent the next line
              pos = i+1;
              if(i+1 < s.len) // escape leading whitespace after the newline
              {
                  const char next = s.str[i+1];
                  if(next == ' ' || next == '\t')
                      this->Writer::_do_write('\\');
              }
          }
          else if(curr == ' ' || curr == '\t')
          {
              // escape trailing whitespace before a newline
              size_t next = s.first_not_of(" \t\r", i);
              if(next != npos && s[next] == '\n')
              {
                  csubstr sub = s.range(pos, i);
                  this->Writer::_do_write(sub);  // write everything up to (excluding) this char
                  this->Writer::_do_write('\\'); // escape the whitespace
                  pos = i;
              }
          }
      }
      // write missing characters at the end of the string
      if(pos < s.len)
      {
          csubstr sub = s.sub(pos);
          this->Writer::_do_write(sub);
      }
      this->Writer::_do_write('"');
  }
  
  template<class Writer>
  void Emitter<Writer>::_write_scalar_plain(csubstr s, size_t ilevel)
  {
      size_t pos = 0; // tracks the last character that was already written
      for(size_t i = 0; i < s.len; ++i)
      {
          const char curr = s.str[i];
          if(curr == '\n')
          {
              csubstr sub = s.range(pos, i+1);
              this->Writer::_do_write(sub);  // write everything up to (including) this newline
              this->Writer::_do_write('\n'); // write the newline again
              if(i + 1 < s.len)
                  _rymlindent_nextline()     // indent the next line
              pos = i+1;
          }
      }
      // write missing characters at the end of the string
      if(pos < s.len)
      {
          csubstr sub = s.sub(pos);
          this->Writer::_do_write(sub);
      }
  }
  
  #undef _rymlindent_nextline
  
  template<class Writer>
  void Emitter<Writer>::_write_scalar(csubstr s, bool was_quoted)
  {
      // this block of code needed to be moved to before the needs_quotes
      // assignment to work around a g++ optimizer bug where (s.str != nullptr)
      // was evaluated as true even if s.str was actually a nullptr (!!!)
      if(s.len == size_t(0))
      {
          if(was_quoted)
              this->Writer::_do_write("''");
          return;
      }
  
      const bool needs_quotes = (
          was_quoted
          ||
          (
              ( ! s.is_number())
              &&
              (
                  // has leading whitespace
                  s.begins_with_any(" \n\t\r")
                  ||
                  // looks like reference or anchor
                  s.begins_with_any("*&")
                  ||
                  s.begins_with("<<")
                  ||
                  // has trailing whitespace
                  s.ends_with_any(" \n\t\r")
                  ||
                  // has special chars
                  (s.first_of("#:-?,\n{}[]'\"") != npos)
              )
          )
      );
  
      if( ! needs_quotes)
      {
          this->Writer::_do_write(s);
      }
      else
      {
          const bool has_dquotes = s.first_of( '"') != npos;
          const bool has_squotes = s.first_of('\'') != npos;
          if(!has_squotes && has_dquotes)
          {
              this->Writer::_do_write('\'');
              this->Writer::_do_write(s);
              this->Writer::_do_write('\'');
          }
          else if(has_squotes && !has_dquotes)
          {
              RYML_ASSERT(s.count('\n') == 0);
              this->Writer::_do_write('"');
              this->Writer::_do_write(s);
              this->Writer::_do_write('"');
          }
          else
          {
              _write_scalar_squo(s, /*FIXME FIXME FIXME*/0);
          }
      }
  }
  template<class Writer>
  void Emitter<Writer>::_write_scalar_json(csubstr s, bool as_key, bool was_quoted)
  {
      if(was_quoted)
      {
          this->Writer::_do_write('"');
          this->Writer::_do_write(s);
          this->Writer::_do_write('"');
      }
      // json only allows strings as keys
      else if(!as_key && (s.is_number() || s == "true" || s == "null" || s == "false"))
      {
          this->Writer::_do_write(s);
      }
      else
      {
          size_t pos = 0;
          this->Writer::_do_write('"');
          for(size_t i = 0; i < s.len; ++i)
          {
              if(s[i] == '"')
              {
                  if(i > 0)
                  {
                      csubstr sub = s.range(pos, i);
                      this->Writer::_do_write(sub);
                  }
                  pos = i + 1;
                  this->Writer::_do_write("\\\"");
              }
          }
          if(pos < s.len)
          {
              csubstr sub = s.sub(pos);
              this->Writer::_do_write(sub);
          }
          this->Writer::_do_write('"');
      }
  }
  
  } // namespace yml
  } // namespace c4
  
  #endif /* _C4_YML_EMIT_DEF_HPP_ */
  