text: #include <assert.h>
  #include <stdlib.h>
  
  #include "config.h"
  #include "node.h"
  #include "cmark.h"
  #include "iterator.h"
  
  static const int S_leaf_mask =
      (1 << CMARK_NODE_HTML_BLOCK) | (1 << CMARK_NODE_THEMATIC_BREAK) |
      (1 << CMARK_NODE_CODE_BLOCK) | (1 << CMARK_NODE_TEXT) |
      (1 << CMARK_NODE_SOFTBREAK) | (1 << CMARK_NODE_LINEBREAK) |
      (1 << CMARK_NODE_CODE) | (1 << CMARK_NODE_HTML_INLINE);
  
  cmark_iter *cmark_iter_new(cmark_node *root) {
    if (root == NULL) {
      return NULL;
    }
    cmark_iter *iter = (cmark_iter *)malloc(sizeof(cmark_iter));
    if (iter == NULL) {
      return NULL;
    }
    iter->root = root;
    iter->cur.ev_type = CMARK_EVENT_NONE;
    iter->cur.node = NULL;
    iter->next.ev_type = CMARK_EVENT_ENTER;
    iter->next.node = root;
    return iter;
  }
  
  void cmark_iter_free(cmark_iter *iter) { free(iter); }
  
  static bool S_is_leaf(cmark_node *node) {
    return ((1 << node->type) & S_leaf_mask) != 0;
  }
  
  cmark_event_type cmark_iter_next(cmark_iter *iter) {
    cmark_event_type ev_type = iter->next.ev_type;
    cmark_node *node = iter->next.node;
  
    iter->cur.ev_type = ev_type;
    iter->cur.node = node;
  
    if (ev_type == CMARK_EVENT_DONE) {
      return ev_type;
    }
  
    /* roll forward to next item, setting both fields */
    if (ev_type == CMARK_EVENT_ENTER && !S_is_leaf(node)) {
      if (node->first_child == NULL) {
        /* stay on this node but exit */
        iter->next.ev_type = CMARK_EVENT_EXIT;
      } else {
        iter->next.ev_type = CMARK_EVENT_ENTER;
        iter->next.node = node->first_child;
      }
    } else if (node == iter->root) {
      /* don't move past root */
      iter->next.ev_type = CMARK_EVENT_DONE;
      iter->next.node = NULL;
    } else if (node->next) {
      iter->next.ev_type = CMARK_EVENT_ENTER;
      iter->next.node = node->next;
    } else if (node->parent) {
      iter->next.ev_type = CMARK_EVENT_EXIT;
      iter->next.node = node->parent;
    } else {
      assert(false);
      iter->next.ev_type = CMARK_EVENT_DONE;
      iter->next.node = NULL;
    }
  
    return ev_type;
  }
  
  void cmark_iter_reset(cmark_iter *iter, cmark_node *current,
                        cmark_event_type event_type) {
    iter->next.ev_type = event_type;
    iter->next.node = current;
    cmark_iter_next(iter);
  }
  
  cmark_node *cmark_iter_get_node(cmark_iter *iter) { return iter->cur.node; }
  
  cmark_event_type cmark_iter_get_event_type(cmark_iter *iter) {
    return iter->cur.ev_type;
  }
  
  cmark_node *cmark_iter_get_root(cmark_iter *iter) { return iter->root; }
  
  void cmark_consolidate_text_nodes(cmark_node *root) {
    if (root == NULL) {
      return;
    }
    cmark_iter *iter = cmark_iter_new(root);
    cmark_strbuf buf = GH_BUF_INIT;
    cmark_event_type ev_type;
    cmark_node *cur, *tmp, *next;
  
    while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
      cur = cmark_iter_get_node(iter);
      if (ev_type == CMARK_EVENT_ENTER && cur->type == CMARK_NODE_TEXT &&
          cur->next && cur->next->type == CMARK_NODE_TEXT) {
        cmark_strbuf_clear(&buf);
        cmark_strbuf_put(&buf, cur->as.literal.data, cur->as.literal.len);
        tmp = cur->next;
        while (tmp && tmp->type == CMARK_NODE_TEXT) {
          cmark_iter_next(iter); // advance pointer
          cmark_strbuf_put(&buf, tmp->as.literal.data, tmp->as.literal.len);
          next = tmp->next;
          cmark_node_free(tmp);
          tmp = next;
        }
        cmark_chunk_free(&cur->as.literal);
        cur->as.literal = cmark_chunk_buf_detach(&buf);
      }
    }
  
    cmark_strbuf_free(&buf);
    cmark_iter_free(iter);
  }
  