text: /*
   * Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
   *
   * Permission to use, copy, modify, and/or distribute this software for any
   * purpose with or without fee is hereby granted, provided that the above
   * copyright notice and this permission notice appear in all copies.
   *
   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   */
  
  #include "yajl/yajl_gen.h"
  #include "yajl_buf.h"
  #include "yajl_encode.h"
  
  #include <stdlib.h>
  #include <string.h>
  #include <stdio.h>
  #include <math.h>
  #include <stdarg.h>
  
  typedef enum {
      yajl_gen_start,
      yajl_gen_map_start,
      yajl_gen_map_key,
      yajl_gen_map_val,
      yajl_gen_array_start,
      yajl_gen_in_array,
      yajl_gen_complete,
      yajl_gen_error
  } yajl_gen_state;
  
  struct yajl_gen_t
  {
      unsigned int flags;
      unsigned int depth;
      const char * indentString;
      yajl_gen_state state[YAJL_MAX_DEPTH];
      yajl_print_t print;
      void * ctx; /* yajl_buf */
      /* memory allocation routines */
      yajl_alloc_funcs alloc;
  };
  
  int
  yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...)
  {
      int rv = 1;
      va_list ap;
      va_start(ap, opt);
  
      switch(opt) {
          case yajl_gen_beautify:
          case yajl_gen_validate_utf8:
          case yajl_gen_escape_solidus:
              if (va_arg(ap, int)) g->flags |= opt;
              else g->flags &= ~opt;
              break;
          case yajl_gen_indent_string: {
              const char *indent = va_arg(ap, const char *);
              g->indentString = indent;
              for (; *indent; indent++) {
                  if (*indent != '\n'
                      && *indent != '\v'
                      && *indent != '\f'
                      && *indent != '\t'
                      && *indent != '\r'
                      && *indent != ' ')
                  {
                      g->indentString = NULL;
                      rv = 0;
                  }
              }
              break;
          }
          case yajl_gen_print_callback:
              yajl_buf_free(g->ctx);
              g->print = va_arg(ap, const yajl_print_t);
              g->ctx = va_arg(ap, void *);
              break;
          default:
              rv = 0;
      }
  
      va_end(ap);
  
      return rv;
  }
  
  
  
  yajl_gen
  yajl_gen_alloc(const yajl_alloc_funcs * afs)
  {
      yajl_gen g = NULL;
      yajl_alloc_funcs afsBuffer;
  
      /* first order of business is to set up memory allocation routines */
      if (afs != NULL) {
          if (afs->_malloc == NULL || afs->_realloc == NULL || afs->_free == NULL)
          {
              return NULL;
          }
      } else {
          yajl_set_default_alloc_funcs(&afsBuffer);
          afs = &afsBuffer;
      }
  
      g = (yajl_gen) YA_MALLOC(afs, sizeof(struct yajl_gen_t));
      if (!g) return NULL;
  
      memset((void *) g, 0, sizeof(struct yajl_gen_t));
      /* copy in pointers to allocation routines */
      memcpy((void *) &(g->alloc), (void *) afs, sizeof(yajl_alloc_funcs));
  
      g->print = (yajl_print_t)&yajl_buf_append;
      g->ctx = yajl_buf_alloc(&(g->alloc));
      g->indentString = "    ";
  
      return g;
  }
  
  void
  yajl_gen_reset(yajl_gen g, const char * sep)
  {
      g->depth = 0;
      memset((void *) &(g->state), 0, sizeof(g->state));
      if (sep != NULL) g->print(g->ctx, sep, strlen(sep));
  }
  
  void
  yajl_gen_free(yajl_gen g)
  {
      if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_free((yajl_buf)g->ctx);
      YA_FREE(&(g->alloc), g);
  }
  
  #define INSERT_SEP \
      if (g->state[g->depth] == yajl_gen_map_key ||                       \
          g->state[g->depth] == yajl_gen_in_array) {                      \
          g->print(g->ctx, ",", 1);                                       \
          if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);  \
      } else if (g->state[g->depth] == yajl_gen_map_val) {                \
          g->print(g->ctx, ":", 1);                                       \
          if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, " ", 1);   \
     }
  
  #define INSERT_WHITESPACE                                               \
      if ((g->flags & yajl_gen_beautify)) {                               \
          if (g->state[g->depth] != yajl_gen_map_val) {                   \
              unsigned int _i;                                            \
              for (_i=0;_i<g->depth;_i++)                                 \
                  g->print(g->ctx,                                        \
                           g->indentString,                               \
                           (unsigned int)strlen(g->indentString));        \
          }                                                               \
      }
  
  #define ENSURE_NOT_KEY \
      if (g->state[g->depth] == yajl_gen_map_key ||       \
          g->state[g->depth] == yajl_gen_map_start)  {    \
          return yajl_gen_keys_must_be_strings;           \
      }                                                   \
  
  /* check that we're not complete, or in error state.  in a valid state
   * to be generating */
  #define ENSURE_VALID_STATE \
      if (g->state[g->depth] == yajl_gen_error) {             \
          return yajl_gen_in_error_state;                     \
      } else if (g->state[g->depth] == yajl_gen_complete) {   \
          return yajl_gen_generation_complete;                \
      }
  
  #define INCREMENT_DEPTH \
      if (++(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded;
  
  #define DECREMENT_DEPTH \
    if (--(g->depth) >= YAJL_MAX_DEPTH) return yajl_gen_generation_complete;
  
  #define APPENDED_ATOM \
      switch (g->state[g->depth]) {                   \
          case yajl_gen_start:                        \
              g->state[g->depth] = yajl_gen_complete; \
              break;                                  \
          case yajl_gen_map_start:                    \
          case yajl_gen_map_key:                      \
              g->state[g->depth] = yajl_gen_map_val;  \
              break;                                  \
          case yajl_gen_array_start:                  \
              g->state[g->depth] = yajl_gen_in_array; \
              break;                                  \
          case yajl_gen_map_val:                      \
              g->state[g->depth] = yajl_gen_map_key;  \
              break;                                  \
          default:                                    \
              break;                                  \
      }                                               \
  
  #define FINAL_NEWLINE                                                               \
      if ((g->flags & yajl_gen_beautify) && g->state[g->depth] == yajl_gen_complete)  \
          g->print(g->ctx, "\n", 1);
  
  yajl_gen_status
  yajl_gen_integer(yajl_gen g, long long int number)
  {
      char i[32];
      ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
      sprintf(i, "%lld", number);
      g->print(g->ctx, i, (unsigned int)strlen(i));
      APPENDED_ATOM;
      FINAL_NEWLINE;
      return yajl_gen_status_ok;
  }
  
  #if defined(_WIN32) || defined(WIN32)
  #include <float.h>
  #define isnan _isnan
  //#define isinf !_finite
  #endif
  
  yajl_gen_status
  yajl_gen_double(yajl_gen g, double number)
  {
      char i[32];
      ENSURE_VALID_STATE; ENSURE_NOT_KEY;
      if (isnan(number) || isinf(number)) return yajl_gen_invalid_number;
      INSERT_SEP; INSERT_WHITESPACE;
      sprintf(i, "%.20g", number);
      if (strspn(i, "0123456789-") == strlen(i)) {
          strcat(i, ".0");
      }
      g->print(g->ctx, i, (unsigned int)strlen(i));
      APPENDED_ATOM;
      FINAL_NEWLINE;
      return yajl_gen_status_ok;
  }
  
  yajl_gen_status
  yajl_gen_number(yajl_gen g, const char * s, size_t l)
  {
      ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
      g->print(g->ctx, s, l);
      APPENDED_ATOM;
      FINAL_NEWLINE;
      return yajl_gen_status_ok;
  }
  
  yajl_gen_status
  yajl_gen_string(yajl_gen g, const unsigned char * str,
                  size_t len)
  {
      // if validation is enabled, check that the string is valid utf8
      // XXX: This checking could be done a little faster, in the same pass as
      // the string encoding
      if (g->flags & yajl_gen_validate_utf8) {
          if (!yajl_string_validate_utf8(str, len)) {
              return yajl_gen_invalid_string;
          }
      }
      ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE;
      g->print(g->ctx, "\"", 1);
      yajl_string_encode(g->print, g->ctx, str, len, g->flags & yajl_gen_escape_solidus);
      g->print(g->ctx, "\"", 1);
      APPENDED_ATOM;
      FINAL_NEWLINE;
      return yajl_gen_status_ok;
  }
  
  yajl_gen_status
  yajl_gen_null(yajl_gen g)
  {
      ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
      g->print(g->ctx, "null", strlen("null"));
      APPENDED_ATOM;
      FINAL_NEWLINE;
      return yajl_gen_status_ok;
  }
  
  yajl_gen_status
  yajl_gen_bool(yajl_gen g, int boolean)
  {
      const char * val = boolean ? "true" : "false";
  
  	ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
      g->print(g->ctx, val, (unsigned int)strlen(val));
      APPENDED_ATOM;
      FINAL_NEWLINE;
      return yajl_gen_status_ok;
  }
  
  yajl_gen_status
  yajl_gen_map_open(yajl_gen g)
  {
      ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
      INCREMENT_DEPTH;
  
      g->state[g->depth] = yajl_gen_map_start;
      g->print(g->ctx, "{", 1);
      if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
      FINAL_NEWLINE;
      return yajl_gen_status_ok;
  }
  
  yajl_gen_status
  yajl_gen_map_close(yajl_gen g)
  {
      ENSURE_VALID_STATE;
      DECREMENT_DEPTH;
  
      if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
      APPENDED_ATOM;
      INSERT_WHITESPACE;
      g->print(g->ctx, "}", 1);
      FINAL_NEWLINE;
      return yajl_gen_status_ok;
  }
  
  yajl_gen_status
  yajl_gen_array_open(yajl_gen g)
  {
      ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
      INCREMENT_DEPTH;
      g->state[g->depth] = yajl_gen_array_start;
      g->print(g->ctx, "[", 1);
      if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
      FINAL_NEWLINE;
      return yajl_gen_status_ok;
  }
  
  yajl_gen_status
  yajl_gen_array_close(yajl_gen g)
  {
      ENSURE_VALID_STATE;
      DECREMENT_DEPTH;
      if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
      APPENDED_ATOM;
      INSERT_WHITESPACE;
      g->print(g->ctx, "]", 1);
      FINAL_NEWLINE;
      return yajl_gen_status_ok;
  }
  
  yajl_gen_status
  yajl_gen_get_buf(yajl_gen g, const unsigned char ** buf,
                   size_t * len)
  {
      if (g->print != (yajl_print_t)&yajl_buf_append) return yajl_gen_no_buf;
      *buf = yajl_buf_data((yajl_buf)g->ctx);
      *len = yajl_buf_len((yajl_buf)g->ctx);
      return yajl_gen_status_ok;
  }
  
  void
  yajl_gen_clear(yajl_gen g)
  {
      if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_clear((yajl_buf)g->ctx);
  }
  