/// Jansson の json_t を Tcl_Obj に置き換えてみた
/// Copyright (C) 2018, Mocchi

// 以下、Jansson オリジナルの Copyright
/*
 * Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
 *
 * Jansson is free software; you can redistribute it and/or modify
 * it under the terms of the MIT license. See LICENSE for details.
 */

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#define JSON_INTEGER_IS_LONG_LONG 1

#define JSON_REJECT_DUPLICATES  0x1
#define JSON_DISABLE_EOF_CHECK  0x2
#define JSON_DECODE_ANY         0x4
#define JSON_DECODE_INT_AS_REAL 0x8
#define JSON_ALLOW_NUL          0x10


//#include "jansson_private.h"
/* Windows compatibility */
#if defined(_WIN32) || defined(WIN32)
#  if defined(_MSC_VER)  /* MS compiller */
#    if (_MSC_VER < 1900) && !defined(snprintf)  /* snprintf not defined yet & not introduced */
#      define snprintf _snprintf
#    endif
#    if (_MSC_VER < 1500) && !defined(vsnprintf)  /* vsnprintf not defined yet & not introduced */
#      define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a)
#    endif
#  else  /* Other Windows compiller, old definition */
#    define snprintf _snprintf
#    define vsnprintf _vsnprintf
#  endif
#endif

#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <numeric>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

//#include "jansson.h"
//#include "strbuffer.h"
//#include "utf.h"
#include "TTW.h"
#include "stdint.h"

#ifdef JSON_INTEGER_IS_LONG_LONG
#define JSON_INTEGER_FORMAT TCL_LL_MODIFIER "d"
typedef TCL_WIDE_INT_TYPE json_int_t;
#else
#define JSON_INTEGER_FORMAT "ld"
typedef long json_int_t;
#endif /* JSON_INTEGER_IS_LONG_LONG */



#define container_of(ptr_, type_, member_)  \
    ((type_ *)((char *)ptr_ - offsetof(type_, member_)))

#define JSON_HAVE_LOCALECONV 1

#define JSON_PARSER_MAX_DEPTH 2048

namespace {

#pragma region utf
#if TCL_UTF_MAX <= 4
int utf8_encode(int32_t codepoint, char *buffer, size_t *size)
{
    if(codepoint < 0)
        return -1;
    else if(codepoint < 0x80)
    {
        buffer[0] = (char)codepoint;
        *size = 1;
    }
    else if(codepoint < 0x800)
    {
        buffer[0] = 0xC0 + ((codepoint & 0x7C0) >> 6);
        buffer[1] = 0x80 + ((codepoint & 0x03F));
        *size = 2;
    }
    else if(codepoint < 0x10000)
    {
        buffer[0] = 0xE0 + ((codepoint & 0xF000) >> 12);
        buffer[1] = 0x80 + ((codepoint & 0x0FC0) >> 6);
        buffer[2] = 0x80 + ((codepoint & 0x003F));
        *size = 3;
    }
    else if(codepoint <= 0x10FFFF)
    {
        buffer[0] = 0xF0 + ((codepoint & 0x1C0000) >> 18);
        buffer[1] = 0x80 + ((codepoint & 0x03F000) >> 12);
        buffer[2] = 0x80 + ((codepoint & 0x000FC0) >> 6);
        buffer[3] = 0x80 + ((codepoint & 0x00003F));
        *size = 4;
    }
    else
        return -1;

    return 0;
}
#else
int utf8_encode(int32_t codepoint, char *buffer, size_t *size)
{
	*side = Tcl_UniCharToUtf(codepoint, buffer);
	return 0;
}
#endif

size_t utf8_check_first(char byte)
{
    unsigned char u = (unsigned char)byte;

    if(u < 0x80)
        return 1;

    if(0x80 <= u && u <= 0xBF) {
        /* second, third or fourth byte of a multi-byte
           sequence, i.e. a "continuation byte" */
        return 0;
    }
    else if(u == 0xC0 || u == 0xC1) {
        /* overlong encoding of an ASCII byte */
        return 0;
    }
    else if(0xC2 <= u && u <= 0xDF) {
        /* 2-byte sequence */
        return 2;
    }

    else if(0xE0 <= u && u <= 0xEF) {
        /* 3-byte sequence */
        return 3;
    }
    else if(0xF0 <= u && u <= 0xF4) {
        /* 4-byte sequence */
        return 4;
    }
    else { /* u >= 0xF5 */
        /* Restricted (start of 4-, 5- or 6-byte sequence) or invalid
           UTF-8 */
        return 0;
    }
}

size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint)
{
    size_t i;
    int32_t value = 0;
    unsigned char u = (unsigned char)buffer[0];

    if(size == 2)
    {
        value = u & 0x1F;
    }
    else if(size == 3)
    {
        value = u & 0xF;
    }
    else if(size == 4)
    {
        value = u & 0x7;
    }
    else
        return 0;

    for(i = 1; i < size; i++)
    {
        u = (unsigned char)buffer[i];

        if(u < 0x80 || u > 0xBF) {
            /* not a continuation byte */
            return 0;
        }

        value = (value << 6) + (u & 0x3F);
    }

    if(value > 0x10FFFF) {
        /* not in Unicode range */
        return 0;
    }

    else if(0xD800 <= value && value <= 0xDFFF) {
        /* invalid code point (UTF-16 surrogate halves) */
        return 0;
    }

    else if((size == 2 && value < 0x80) ||
            (size == 3 && value < 0x800) ||
            (size == 4 && value < 0x10000)) {
        /* overlong encoding */
        return 0;
    }

    if(codepoint)
        *codepoint = value;

    return 1;
}

const char *utf8_iterate(const char *buffer, size_t bufsize, int32_t *codepoint)
{
    size_t count;
    int32_t value;

    if(!bufsize)
        return buffer;

    count = utf8_check_first(buffer[0]);
    if(count <= 0)
        return NULL;

    if(count == 1)
        value = (unsigned char)buffer[0];
    else
    {
        if(count > bufsize || !utf8_check_full(buffer, count, &value))
            return NULL;
    }

    if(codepoint)
        *codepoint = value;

    return buffer + count;
}

int utf8_check_string(const char *string, size_t length)
{
    size_t i;

    for(i = 0; i < length; i++)
    {
        size_t count = utf8_check_first(string[i]);
        if(count == 0)
            return 0;
        else if(count > 1)
        {
            if(count > length - i)
                return 0;

            if(!utf8_check_full(&string[i], count, NULL))
                return 0;

            i += count - 1;
        }
    }

    return 1;
}
#pragma endregion // utf

#pragma region Tcl_Obj_to_json_t
typedef Tcl_Obj json_t;

struct Tcl_TFN{
	Tcl_Obj *true_obj, *false_obj;
	Tcl_TFN(){
		true_obj = ::Tcl_NewStringObj("true", -1);
		false_obj = ::Tcl_NewStringObj("false", -1);
		int boolean;
		::Tcl_GetBooleanFromObj(0, true_obj, &boolean);
		::Tcl_GetBooleanFromObj(0, false_obj, &boolean);
	}
}tcl_tfn;

inline json_t *json_create_null(){
	return ::Tcl_NewObj();
}
inline json_t *json_create_true(){
	return ::Tcl_DuplicateObj(tcl_tfn.true_obj);
}
inline json_t *json_create_false(){
	return ::Tcl_DuplicateObj(tcl_tfn.false_obj);
}

inline bool json_is_null(const json_t *json){
	return (json && json->typePtr == 0 && json->length == 0);
}
inline bool json_is_object(const json_t *json){
	return (json && json->typePtr == TTW_GetDictType());
}
inline bool json_is_array(const json_t *json){
	return (json && json->typePtr == TTW_GetListType());
}
/*
inline bool json_is_string(const json_t *json){
	return (json && ((json->typePtr == 0 && json != json_null()) || json->typePtr == str_type));
}
*/
inline bool json_is_integer(const json_t *json){
	return (json && (json->typePtr == TTW_GetIntType() || json->typePtr == TTW_GetWideIntType()));
}
inline bool json_is_real(const json_t *json){
	return (json && json->typePtr == TTW_GetDoubleType());
}
inline bool json_is_number(const json_t *json){
	return (json_is_integer(json) || json_is_real(json));
}
inline bool json_is_boolean(const json_t *json){
	return (json && (json->typePtr == TTW_GetBooleanType()));
}
inline bool json_is_true(const json_t *json){
	if (!json_is_boolean(json)) return false;
	return TTW_GetBoolean(const_cast<Tcl_Obj *>(json), false);
}
inline bool json_is_false(const json_t *json){
	if (!json_is_boolean(json)) return false;
	return !TTW_GetBoolean(const_cast<Tcl_Obj *>(json), false);
}

inline void json_decref(json_t *json){
	Tcl_DecrRefCount(json);
}

inline json_t *json_object(){
	return ::Tcl_NewDictObj();
}

inline size_t json_object_size(const json_t *json){
    if(!json_is_object(json))
        return 0;

	int sz;
	if (::Tcl_DictObjSize(0, const_cast<Tcl_Obj *>(json), &sz) != TCL_OK) return 0;
	return sz;
}

inline json_t *json_object_get(const json_t *json, const char *key){
	Tcl_Obj *val = 0;

	if(!key || !json_is_object(json))
        return NULL;

	Tcl_Obj *key_obj = ::Tcl_NewStringObj(key, -1);
	if (::Tcl_DictObjGet(0, const_cast<Tcl_Obj *>(json), key_obj, &val) != TCL_OK) val = 0;
	Tcl_DecrRefCount(key_obj);
	return val;
}

inline int json_object_set_new_nocheck(json_t *json, Tcl_Obj *key_obj, json_t *value){
    if(!value)
        return -1;

    if(!key_obj || !json_is_object(json) || json == value)
    {
        json_decref(value);
        return -1;
    }

	int rc = 0;
	if (::Tcl_DictObjPut(0, json, key_obj, value) != TCL_OK) rc = -1;
	return rc;
}

inline int json_object_set_new(json_t *json, Tcl_Obj *key_obj, json_t *value){
	int len;
	const char *key = ::Tcl_GetStringFromObj(key_obj, &len);
    if(!key || !utf8_check_string(key, len)){
        json_decref(value);
        return -1;
    }
	return json_object_set_new_nocheck(json, key_obj, value);
}

inline int json_object_clear(json_t *json){
    if(!json_is_object(json))
        return -1;

	::Tcl_SetListObj(json, 0, 0);
	::Tcl_ConvertToType(0, json, TTW_GetDictType());
	return 0;
}

inline json_t *json_array(void){
	Tcl_Obj *listObj = ::Tcl_NewListObj(0, 0);
	::Tcl_ConvertToType(0, listObj, TTW_GetListType());
	Tcl_IncrRefCount(listObj);
	return listObj;
}

inline int json_array_append_new(json_t *json, json_t *value){
    if(!value)
        return -1;

    if(!json_is_array(json) || json == value)
    {
        json_decref(value);
        return -1;
    }

	Tcl_ListObjAppendElement(0, json, value);
	return 0;
}

#ifdef JSON_INTEGER_IS_LONG_LONG
#define json_integer_value TTW_GetWideInt
#else
#define json_integer_value TTW_GetLong
#endif

#pragma endregion // Tcl_Obj_to_json_t

#pragma region memory
typedef void *(*json_malloc_t)(size_t);
typedef void (*json_free_t)(void *);

void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn);
void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn);

char* jsonp_malloc(size_t size);
void jsonp_free(char *ptr);
char *jsonp_strndup(const char *str, size_t length);
char *jsonp_strdup(const char *str);
char *jsonp_strndup(const char *str, size_t len);

/* C89 allows these to be macros */
#undef malloc
#undef free

/* memory function pointers */
static json_malloc_t do_malloc = malloc;
static json_free_t do_free = free;

char *jsonp_malloc(size_t size)
{
    if(!size)
        return NULL;

    return static_cast<char *>((*do_malloc)(size));
}

void jsonp_free(char *ptr)
{
    if(!ptr)
        return;

    (*do_free)(ptr);
}

char *jsonp_strdup(const char *str)
{
    return jsonp_strndup(str, strlen(str));
}

char *jsonp_strndup(const char *str, size_t len)
{
    char *new_str;

	new_str = jsonp_malloc(len + 1);
    if(!new_str)
        return NULL;

    memcpy(new_str, str, len);
    new_str[len] = '\0';
    return new_str;
}

void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn)
{
    do_malloc = malloc_fn;
    do_free = free_fn;
}

void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn)
{
    if (malloc_fn)
        *malloc_fn = do_malloc;
    if (free_fn)
        *free_fn = do_free;
}
#pragma endregion // memory

#pragma region error
#define JSON_ERROR_TEXT_LENGTH    160
#define JSON_ERROR_SOURCE_LENGTH   80
typedef const char * json_error_code;
json_error_code json_error_unknown                = "unknown";
json_error_code json_error_out_of_memory          = "out of memory";
json_error_code json_error_stack_overflow         = "stack overflow";
json_error_code json_error_cannot_open_file       = "cannot open file";
json_error_code json_error_invalid_argument       = "invalid argument";
json_error_code json_error_invalid_utf8           = "invalid utf8";
json_error_code json_error_premature_end_of_input = "premature end of input";
json_error_code json_error_end_of_input_expected  = "end of input expected";
json_error_code json_error_invalid_syntax         = "invalid syntax";
json_error_code json_error_invalid_format         = "invalid format";
json_error_code json_error_wrong_type             = "wrong type";
json_error_code json_error_null_character         = "null character";
json_error_code json_error_null_value             = "null value";
json_error_code json_error_null_byte_in_key       = "null byte in key";
json_error_code json_error_duplicate_key          = "duplicate key";
json_error_code json_error_numeric_overflow       = "numeric overflow";
json_error_code json_error_item_not_found         = "item not found";
json_error_code json_error_index_out_of_range     = "index out of range";
typedef Tcl_Obj json_error_t;

void jsonp_error_init(json_error_t *error, const char *source)
{
    if(error)
    {
		::Tcl_DictObjPut(0, error, ::Tcl_NewStringObj("line", -1),    ::Tcl_NewIntObj(-1));
		::Tcl_DictObjPut(0, error, ::Tcl_NewStringObj("col", -1),     ::Tcl_NewIntObj(-1));
		::Tcl_DictObjPut(0, error, ::Tcl_NewStringObj("pos", -1),     ::Tcl_NewIntObj(0));
		::Tcl_DictObjPut(0, error, ::Tcl_NewStringObj("source", -1),  ::Tcl_NewStringObj(source ? source : "", -1));
    }
}

void jsonp_error_set_source(json_error_t *error, const char *source)
{
    size_t length;

    if(!error || !source)
        return;

    length = strlen(source);
    if(length < JSON_ERROR_SOURCE_LENGTH)
		::Tcl_DictObjPut(0, error, ::Tcl_NewStringObj("source", -1),  ::Tcl_NewStringObj(source, length));
    else {
        size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4;
		Tcl_Obj *err_str = ::Tcl_NewStringObj("...", 3);
		::Tcl_AppendToObj(err_str, source, length - extra + 1);
		::Tcl_DictObjPut(0, error, ::Tcl_NewStringObj("source", -1), err_str);
    }
}

#pragma endregion // error

#pragma region strbuffer
#if 0
typedef struct {
    char *value;
    size_t length;   /* bytes used */
    size_t size;     /* bytes allocated */
} strbuffer_t;

int strbuffer_init(strbuffer_t *strbuff);
void strbuffer_close(strbuffer_t *strbuff);

void strbuffer_clear(strbuffer_t *strbuff);

const char *strbuffer_value(const strbuffer_t *strbuff);

int strbuffer_append_byte(strbuffer_t *strbuff, char byte);
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size);

char strbuffer_pop(strbuffer_t *strbuff);

#define STRBUFFER_MIN_SIZE  16
#define STRBUFFER_FACTOR    2
#define STRBUFFER_SIZE_MAX  ((size_t)-1)

int strbuffer_init(strbuffer_t *strbuff)
{
    strbuff->size = STRBUFFER_MIN_SIZE;
    strbuff->length = 0;

    strbuff->value = jsonp_malloc(strbuff->size);
    if(!strbuff->value)
        return -1;

    /* initialize to empty */
    strbuff->value[0] = '\0';
    return 0;
}

void strbuffer_close(strbuffer_t *strbuff)
{
    if(strbuff->value)
        jsonp_free(strbuff->value);

    strbuff->size = 0;
    strbuff->length = 0;
    strbuff->value = NULL;
}

void strbuffer_clear(strbuffer_t *strbuff)
{
    strbuff->length = 0;
    strbuff->value[0] = '\0';
}

const char *strbuffer_value(const strbuffer_t *strbuff)
{
    return strbuff->value;
}

int strbuffer_append_byte(strbuffer_t *strbuff, char byte)
{
    return strbuffer_append_bytes(strbuff, &byte, 1);
}

int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size)
{
    if(size >= strbuff->size - strbuff->length)
    {
        size_t new_size;
        char *new_value;

        /* avoid integer overflow */
        if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR
            || size > STRBUFFER_SIZE_MAX - 1
            || strbuff->length > STRBUFFER_SIZE_MAX - 1 - size)
            return -1;

		new_size = std::max(strbuff->size * STRBUFFER_FACTOR,
                       strbuff->length + size + 1);

        new_value = jsonp_malloc(new_size);
        if(!new_value)
            return -1;

        memcpy(new_value, strbuff->value, strbuff->length);

        jsonp_free(strbuff->value);
        strbuff->value = new_value;
        strbuff->size = new_size;
    }

    memcpy(strbuff->value + strbuff->length, data, size);
    strbuff->length += size;
    strbuff->value[strbuff->length] = '\0';

    return 0;
}

char strbuffer_pop(strbuffer_t *strbuff)
{
    if(strbuff->length > 0) {
        char c = strbuff->value[--strbuff->length];
        strbuff->value[strbuff->length] = '\0';
        return c;
    }
    else
        return '\0';
}
#else
typedef Tcl_DString strbuffer_t;
int strbuffer_init(strbuffer_t *strbuff){
	::Tcl_DStringInit(strbuff);
	return 0;
}

void strbuffer_close(strbuffer_t *strbuff){
	::Tcl_DStringFree(strbuff);
}

void strbuffer_clear(strbuffer_t *strbuff){
	::Tcl_DStringSetLength(strbuff, 0);
}

const char *strbuffer_value(const strbuffer_t *strbuff){
	return Tcl_DStringValue(strbuff);
}

int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size){
	::Tcl_DStringAppend(strbuff, data, size);
	return 0;
}

int strbuffer_append_byte(strbuffer_t *strbuff, char byte){
    return strbuffer_append_bytes(strbuff, &byte, 1);
}

char strbuffer_pop(strbuffer_t *strbuff){
	int len = Tcl_DStringLength(strbuff);
    if(len > 0) {
		char *p = Tcl_DStringValue(strbuff);
		--len;
		char c = p[len];
		::Tcl_DStringSetLength(strbuff, len);
        return c;
    }
    else
        return '\0';
}

#endif
#pragma endregion // strbuffer

#pragma region strconv
int jsonp_strtod(strbuffer_t *strbuffer, double *out)
{
    double value;
	::Tcl_GetDouble(0, strbuffer_value(strbuffer), &value);

    if((value == HUGE_VAL || value == -HUGE_VAL) && errno == ERANGE) {
        /* Overflow */
        return -1;
    }

    *out = value;
    return 0;
}
#pragma endregion // strconv

#pragma region load
typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data);
#define JSON_REJECT_DUPLICATES  0x1
#define JSON_DISABLE_EOF_CHECK  0x2
#define JSON_DECODE_ANY         0x4
#define JSON_DECODE_INT_AS_REAL 0x8
#define JSON_ALLOW_NUL          0x10


#define STREAM_STATE_OK        0
#define STREAM_STATE_EOF      -1
#define STREAM_STATE_ERROR    -2

#define TOKEN_INVALID         -1
#define TOKEN_EOF              0
#define TOKEN_STRING         256
#define TOKEN_INTEGER        257
#define TOKEN_REAL           258
#define TOKEN_TRUE           259
#define TOKEN_FALSE          260
#define TOKEN_NULL           261

/* Locale independent versions of isxxx() functions */
#define l_isupper(c)  ('A' <= (c) && (c) <= 'Z')
#define l_islower(c)  ('a' <= (c) && (c) <= 'z')
#define l_isalpha(c)  (l_isupper(c) || l_islower(c))
#define l_isdigit(c)  ('0' <= (c) && (c) <= '9')
#define l_isxdigit(c) \
    (l_isdigit(c) || ('A' <= (c) && (c) <= 'F') || ('a' <= (c) && (c) <= 'f'))

/* Read one byte from stream, convert to unsigned char, then int, and
   return. return EOF on end of file. This corresponds to the
   behaviour of fgetc(). */
typedef int (*get_func)(void *data);

struct stream_t{
    get_func get;
    void *data;
    char buffer[5];
    size_t buffer_pos;
    int state;
    int line;
    int column, last_column;
    size_t position;
};

struct lex_t{
    stream_t stream;
    strbuffer_t saved_text;
    size_t flags;
    size_t depth;
    int token;
	Tcl_Obj *value;
};

#define stream_to_lex(stream) container_of(stream, lex_t, stream)


/*** error reporting ***/

static void error_set(json_error_t *error, const lex_t *lex,
                      json_error_code code,
                      const char *msg, ...)
{
    va_list ap;
    char msg_text[JSON_ERROR_TEXT_LENGTH];
    char msg_with_context[JSON_ERROR_TEXT_LENGTH];

    int line = -1, col = -1;
    size_t pos = 0;
    const char *result = msg_text;

    if(!error)
        return;

    va_start(ap, msg);
    vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap);
    msg_text[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
    va_end(ap);

    if(lex)
    {
        const char *saved_text = strbuffer_value(&lex->saved_text);

        line = lex->stream.line;
        col = lex->stream.column;
        pos = lex->stream.position;

        if(saved_text && saved_text[0])
        {
            if(lex->saved_text.length <= 20) {
                snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
                         "%s near '%s'", msg_text, saved_text);
                msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
                result = msg_with_context;
            }
        }
        else
        {
            if(code == json_error_invalid_syntax) {
                /* More specific error code for premature end of file. */
                code = json_error_premature_end_of_input;
            }
            if(lex->stream.state == STREAM_STATE_ERROR) {
                /* No context for UTF-8 decoding errors */
                result = msg_text;
            }
            else {
                snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
                         "%s near end of file", msg_text);
                msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
                result = msg_with_context;
            }
        }
    }

	::Tcl_DictObjPut(0, error, ::Tcl_NewStringObj("line", -1),    ::Tcl_NewIntObj(line));
	::Tcl_DictObjPut(0, error, ::Tcl_NewStringObj("col", -1),     ::Tcl_NewIntObj(col));
	::Tcl_DictObjPut(0, error, ::Tcl_NewStringObj("pos", -1),     ::Tcl_NewIntObj(pos));
	::Tcl_DictObjPut(0, error, ::Tcl_NewStringObj("errcode", -1), ::Tcl_NewStringObj(code, -1));
	::Tcl_DictObjPut(0, error, ::Tcl_NewStringObj("msg", -1),     ::Tcl_NewStringObj(msg_with_context, -1));
}

static void error_set_pos(json_error_t *error, int pos)
{
	::Tcl_DictObjPut(0, error, ::Tcl_NewStringObj("pos", -1), ::Tcl_NewIntObj(pos));
}

/*** lexical analyzer ***/

static void
stream_init(stream_t *stream, get_func get, void *data)
{
    stream->get = get;
    stream->data = data;
    stream->buffer[0] = '\0';
    stream->buffer_pos = 0;

    stream->state = STREAM_STATE_OK;
    stream->line = 1;
    stream->column = 0;
    stream->position = 0;
}

static int stream_get(stream_t *stream, json_error_t *error)
{
    int c;

    if(stream->state != STREAM_STATE_OK)
        return stream->state;

    if(!stream->buffer[stream->buffer_pos])
    {
        c = stream->get(stream->data);
        if(c == EOF) {
            stream->state = STREAM_STATE_EOF;
            return STREAM_STATE_EOF;
        }

        stream->buffer[0] = c;
        stream->buffer_pos = 0;

        if(0x80 <= c && c <= 0xFF)
        {
            /* multi-byte UTF-8 sequence */
            size_t i, count;

            count = utf8_check_first(c);
            if(!count)
                goto out;

            assert(count >= 2);

            for(i = 1; i < count; i++)
                stream->buffer[i] = stream->get(stream->data);

            if(!utf8_check_full(stream->buffer, count, NULL))
                goto out;

            stream->buffer[count] = '\0';
        }
        else
            stream->buffer[1] = '\0';
    }

    c = stream->buffer[stream->buffer_pos++];

    stream->position++;
    if(c == '\n') {
        stream->line++;
        stream->last_column = stream->column;
        stream->column = 0;
    }
    else if(utf8_check_first(c)) {
        /* track the Unicode character column, so increment only if
           this is the first character of a UTF-8 sequence */
        stream->column++;
    }

    return c;

out:
    stream->state = STREAM_STATE_ERROR;
    error_set(error, stream_to_lex(stream), json_error_invalid_utf8, "unable to decode byte 0x%x", c);
    return STREAM_STATE_ERROR;
}

static void stream_unget(stream_t *stream, int c)
{
    if(c == STREAM_STATE_EOF || c == STREAM_STATE_ERROR)
        return;

    stream->position--;
    if(c == '\n') {
        stream->line--;
        stream->column = stream->last_column;
    }
    else if(utf8_check_first(c))
        stream->column--;

    assert(stream->buffer_pos > 0);
    stream->buffer_pos--;
    assert(stream->buffer[stream->buffer_pos] == c);
}


static int lex_get(lex_t *lex, json_error_t *error)
{
    return stream_get(&lex->stream, error);
}

static void lex_save(lex_t *lex, int c)
{
    strbuffer_append_byte(&lex->saved_text, c);
}

static int lex_get_save(lex_t *lex, json_error_t *error)
{
    int c = stream_get(&lex->stream, error);
    if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR)
        lex_save(lex, c);
    return c;
}

static void lex_unget(lex_t *lex, int c)
{
    stream_unget(&lex->stream, c);
}

static void lex_unget_unsave(lex_t *lex, int c)
{
    if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) {
        /* Since we treat warnings as errors, when assertions are turned
         * off the "d" variable would be set but never used. Which is
         * treated as an error by GCC.
         */
        #ifndef NDEBUG
        char d;
        #endif
        stream_unget(&lex->stream, c);
        #ifndef NDEBUG
        d =
        #endif
            strbuffer_pop(&lex->saved_text);
        assert(c == d);
    }
}

static void lex_save_cached(lex_t *lex)
{
    while(lex->stream.buffer[lex->stream.buffer_pos] != '\0')
    {
        lex_save(lex, lex->stream.buffer[lex->stream.buffer_pos]);
        lex->stream.buffer_pos++;
        lex->stream.position++;
    }
}

static void lex_free_string(lex_t *lex)
{
	if (lex->value) Tcl_DecrRefCount(lex->value);
	lex->value = 0;
}

/* assumes that str points to 'u' plus at least 4 valid hex digits */
static int32_t decode_unicode_escape(const char *str)
{
    int i;
    int32_t value = 0;

    assert(str[0] == 'u');

    for(i = 1; i <= 4; i++) {
        char c = str[i];
        value <<= 4;
        if(l_isdigit(c))
            value += c - '0';
        else if(l_islower(c))
            value += c - 'a' + 10;
        else if(l_isupper(c))
            value += c - 'A' + 10;
        else
            return -1;
    }

    return value;
}

static void lex_scan_string(lex_t *lex, json_error_t *error)
{
    int c;
    const char *p;
    int i;

    lex_free_string(lex);
    lex->token = TOKEN_INVALID;

    c = lex_get_save(lex, error);

    while(c != '"') {
        if(c == STREAM_STATE_ERROR)
            goto out;

        else if(c == STREAM_STATE_EOF) {
            error_set(error, lex, json_error_premature_end_of_input, "premature end of input");
            goto out;
        }

        else if(0 <= c && c <= 0x1F) {
            /* control character */
            lex_unget_unsave(lex, c);
            if(c == '\n')
                error_set(error, lex, json_error_invalid_syntax, "unexpected newline");
            else
                error_set(error, lex, json_error_invalid_syntax, "control character 0x%x", c);
            goto out;
        }

        else if(c == '\\') {
            c = lex_get_save(lex, error);
            if(c == 'u') {
                c = lex_get_save(lex, error);
                for(i = 0; i < 4; i++) {
                    if(!l_isxdigit(c)) {
                        error_set(error, lex, json_error_invalid_syntax, "invalid escape");
                        goto out;
                    }
                    c = lex_get_save(lex, error);
                }
            }
            else if(c == '"' || c == '\\' || c == '/' || c == 'b' ||
                    c == 'f' || c == 'n' || c == 'r' || c == 't')
                c = lex_get_save(lex, error);
            else {
                error_set(error, lex, json_error_invalid_syntax, "invalid escape");
                goto out;
            }
        }
        else
            c = lex_get_save(lex, error);
    }

    /* the actual value is at most of the same length as the source
       string, because:
         - shortcut escapes (e.g. "\t") (length 2) are converted to 1 byte
         - a single \uXXXX escape (length 6) is converted to at most 3 bytes
         - two \uXXXX escapes (length 12) forming an UTF-16 surrogate pair
           are converted to 4 bytes
    */
	char utf8_buf[8];
	Tcl_Obj *t = ::Tcl_NewStringObj(0, 0);
    if(!t) {
        /* this is not very nice, since TOKEN_INVALID is returned */
        goto out;
    }
	Tcl_IncrRefCount(t);
    lex->value = t;

    /* + 1 to skip the " */
    p = strbuffer_value(&lex->saved_text) + 1;

    while(*p != '"') {
        if(*p == '\\') {
            p++;
            if(*p == 'u') {
                size_t length;
                int32_t value;

                value = decode_unicode_escape(p);
                if(value < 0) {
                    error_set(error, lex, json_error_invalid_syntax, "invalid Unicode escape '%.6s'", p - 1);
                    goto out;
                }
                p += 5;

                if(0xD800 <= value && value <= 0xDBFF) {
                    /* surrogate pair */
                    if(*p == '\\' && *(p + 1) == 'u') {
                        int32_t value2 = decode_unicode_escape(++p);
                        if(value2 < 0) {
                            error_set(error, lex, json_error_invalid_syntax, "invalid Unicode escape '%.6s'", p - 1);
                            goto out;
                        }
                        p += 5;

                        if(0xDC00 <= value2 && value2 <= 0xDFFF) {
                            /* valid second surrogate */
                            value =
                                ((value - 0xD800) << 10) +
                                (value2 - 0xDC00) +
                                0x10000;
                        }
                        else {
                            /* invalid second surrogate */
                            error_set(error, lex,
                                      json_error_invalid_syntax,
                                      "invalid Unicode '\\u%04X\\u%04X'",
                                      value, value2);
                            goto out;
                        }
                    }
                    else {
                        /* no second surrogate */
                        error_set(error, lex, json_error_invalid_syntax, "invalid Unicode '\\u%04X'",
                                  value);
                        goto out;
                    }
                }
                else if(0xDC00 <= value && value <= 0xDFFF) {
                    error_set(error, lex, json_error_invalid_syntax, "invalid Unicode '\\u%04X'", value);
                    goto out;
                }

                if(utf8_encode(value, utf8_buf, &length))
                    assert(0);
				::Tcl_AppendToObj(t, utf8_buf, length);
            }
            else {
				char ch;
                switch(*p) {
                    case '"': case '\\': case '/':
                        ch = *p; break;
                    case 'b': ch = '\b'; break;
                    case 'f': ch = '\f'; break;
                    case 'n': ch = '\n'; break;
                    case 'r': ch = '\r'; break;
                    case 't': ch = '\t'; break;
                    default: assert(0);
                }
				::Tcl_AppendToObj(t, &ch, 1);
                p++;
            }
        }
        else
			::Tcl_AppendToObj(t, p++, 1);
    }
    lex->token = TOKEN_STRING;
    return;

out:
    lex_free_string(lex);
}

#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */
#if JSON_INTEGER_IS_LONG_LONG
#ifdef _MSC_VER  /* Microsoft Visual Studio */
#define json_strtoint     _strtoi64
#else
#define json_strtoint     strtoll
#endif
#else
#define json_strtoint     strtol
#endif
#endif

static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
{
    const char *saved_text;
    char *end;
    double doubleval;

    lex->token = TOKEN_INVALID;

    if(c == '-')
        c = lex_get_save(lex, error);

    if(c == '0') {
        c = lex_get_save(lex, error);
        if(l_isdigit(c)) {
            lex_unget_unsave(lex, c);
            goto out;
        }
    }
    else if(l_isdigit(c)) {
        do
            c = lex_get_save(lex, error);
        while(l_isdigit(c));
    }
    else {
        lex_unget_unsave(lex, c);
        goto out;
    }

    if(!(lex->flags & JSON_DECODE_INT_AS_REAL) &&
       c != '.' && c != 'E' && c != 'e')
    {
        json_int_t intval;

        lex_unget_unsave(lex, c);

        saved_text = strbuffer_value(&lex->saved_text);

        errno = 0;
        intval = json_strtoint(saved_text, &end, 10);
        if(errno == ERANGE) {
            if(intval < 0)
                error_set(error, lex, json_error_numeric_overflow, "too big negative integer");
            else
                error_set(error, lex, json_error_numeric_overflow, "too big integer");
            goto out;
        }

        assert(end == saved_text + lex->saved_text.length);

        lex->token = TOKEN_INTEGER;
		if (LONG_MIN <= intval  && intval <= LONG_MAX){
			lex->value = ::Tcl_NewLongObj(intval);
		}else{
			lex->value = ::Tcl_NewWideIntObj(intval);
		}
        return 0;
    }

    if(c == '.') {
        c = lex_get(lex, error);
        if(!l_isdigit(c)) {
            lex_unget(lex, c);
            goto out;
        }
        lex_save(lex, c);

        do
            c = lex_get_save(lex, error);
        while(l_isdigit(c));
    }

    if(c == 'E' || c == 'e') {
        c = lex_get_save(lex, error);
        if(c == '+' || c == '-')
            c = lex_get_save(lex, error);

        if(!l_isdigit(c)) {
            lex_unget_unsave(lex, c);
            goto out;
        }

        do
            c = lex_get_save(lex, error);
        while(l_isdigit(c));
    }

    lex_unget_unsave(lex, c);

    if(jsonp_strtod(&lex->saved_text, &doubleval)) {
        error_set(error, lex, json_error_numeric_overflow, "real number overflow");
        goto out;
    }

    lex->token = TOKEN_REAL;
	lex->value = ::Tcl_NewDoubleObj(doubleval);
    return 0;

out:
    return -1;
}

static int lex_scan(lex_t *lex, json_error_t *error)
{
    int c;

    strbuffer_clear(&lex->saved_text);

    if(lex->token == TOKEN_STRING)
        lex_free_string(lex);

    do
        c = lex_get(lex, error);
    while(c == ' ' || c == '\t' || c == '\n' || c == '\r');

    if(c == STREAM_STATE_EOF) {
        lex->token = TOKEN_EOF;
        goto out;
    }

    if(c == STREAM_STATE_ERROR) {
        lex->token = TOKEN_INVALID;
        goto out;
    }

    lex_save(lex, c);

    if(c == '{' || c == '}' || c == '[' || c == ']' || c == ':' || c == ',')
        lex->token = c;

    else if(c == '"')
        lex_scan_string(lex, error);

    else if(l_isdigit(c) || c == '-') {
        if(lex_scan_number(lex, c, error))
            goto out;
    }

    else if(l_isalpha(c)) {
        /* eat up the whole identifier for clearer error messages */
        const char *saved_text;

        do
            c = lex_get_save(lex, error);
        while(l_isalpha(c));
        lex_unget_unsave(lex, c);

        saved_text = strbuffer_value(&lex->saved_text);

        if(strcmp(saved_text, "true") == 0)
            lex->token = TOKEN_TRUE;
        else if(strcmp(saved_text, "false") == 0)
            lex->token = TOKEN_FALSE;
        else if(strcmp(saved_text, "null") == 0)
            lex->token = TOKEN_NULL;
        else
            lex->token = TOKEN_INVALID;
    }

    else {
        /* save the rest of the input UTF-8 sequence to get an error
           message of valid UTF-8 */
        lex_save_cached(lex);
        lex->token = TOKEN_INVALID;
    }

out:
    return lex->token;
}

static int lex_init(lex_t *lex, get_func get, size_t flags, void *data)
{
    stream_init(&lex->stream, get, data);
    if(strbuffer_init(&lex->saved_text))
        return -1;

    lex->flags = flags;
    lex->token = TOKEN_INVALID;
	lex->value = 0;
    return 0;
}

static void lex_close(lex_t *lex)
{
    if(lex->token == TOKEN_STRING)
        lex_free_string(lex);
    strbuffer_close(&lex->saved_text);
}


/*** parser ***/

static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error);

static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
{
	json_t *object = json_object();
    if(!object)
        return NULL;

    lex_scan(lex, error);
    if(lex->token == '}')
        return object;

    while(1) {
        char *key;
		json_t *key_obj = 0;
        int len;
        json_t *value;

        if(lex->token != TOKEN_STRING) {
            error_set(error, lex, json_error_invalid_syntax, "string or '}' expected");
            goto error;
        }

		key_obj = lex->value; lex->value = 0;
		key = ::Tcl_GetStringFromObj(key_obj, &len);
        if(!key)
            return NULL;
        if (memchr(key, '\0', len)) {
			Tcl_DecrRefCount(key_obj);
            error_set(error, lex, json_error_null_byte_in_key, "NUL byte in object key not supported");
            goto error;
        }

        if(flags & JSON_REJECT_DUPLICATES) {
            if(json_object_get(object, key)) {
				Tcl_DecrRefCount(key_obj);
                error_set(error, lex, json_error_duplicate_key, "duplicate object key");
                goto error;
            }
        }

        lex_scan(lex, error);
        if(lex->token != ':') {
			Tcl_DecrRefCount(key_obj);
            error_set(error, lex, json_error_invalid_syntax, "':' expected");
            goto error;
        }

        lex_scan(lex, error);
        value = parse_value(lex, flags, error);
        if(!value) {
			Tcl_DecrRefCount(key_obj); lex->value = 0;
            goto error;
        }

		if(json_object_set_new_nocheck(object, key_obj, value)) {
			Tcl_DecrRefCount(key_obj); lex->value = 0;
			goto error;
		}

		Tcl_DecrRefCount(key_obj);

        lex_scan(lex, error);
        if(lex->token != ',')
            break;

        lex_scan(lex, error);
    }

    if(lex->token != '}') {
        error_set(error, lex, json_error_invalid_syntax, "'}' expected");
        goto error;
    }


    return object;

error:
    json_decref(object);
    return NULL;
}

static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error)
{
    json_t *array = json_array();
    if(!array)
        return NULL;

    lex_scan(lex, error);
    if(lex->token == ']')
        return array;

    while(lex->token) {
        json_t *elem = parse_value(lex, flags, error);
        if(!elem)
            goto error;

        if(json_array_append_new(array, elem)) {
            goto error;
        }

        lex_scan(lex, error);
        if(lex->token != ',')
            break;

        lex_scan(lex, error);
    }

    if(lex->token != ']') {
        error_set(error, lex, json_error_invalid_syntax, "']' expected");
        goto error;
    }

    return array;

error:
    json_decref(array);
    return NULL;
}

static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
{
    json_t *json;

    lex->depth++;
    if(lex->depth > JSON_PARSER_MAX_DEPTH) {
        error_set(error, lex, json_error_stack_overflow, "maximum parsing depth reached");
        return NULL;
    }

    switch(lex->token) {
        case TOKEN_STRING: {
			int len;
			const char *value = ::Tcl_GetStringFromObj(lex->value, &len);
            if(!(flags & JSON_ALLOW_NUL)) {
                if(memchr(value, '\0', len)) {
                    error_set(error, lex, json_error_null_character, "\\u0000 is not allowed without JSON_ALLOW_NUL");
                    return NULL;
                }
            }

            json = lex->value;
			lex->value = 0;
			if (len == 0){
				// 文字長 0 のときは、内部表現の type を強制的に"string"にすることで、"null"型の要素と区別する。
				::Tcl_GetCharLength(json);
			}
            break;
        }

        case TOKEN_INTEGER: {
            json = lex->value;
			lex->value = 0;
            break;
        }

        case TOKEN_REAL: {
            json = lex->value;
			lex->value = 0;
            break;
        }

        case TOKEN_TRUE:
            json = json_create_true();
            break;

        case TOKEN_FALSE:
            json = json_create_false();
            break;

        case TOKEN_NULL:
            json = json_create_null();
            break;

        case '{':
            json = parse_object(lex, flags, error);
            break;

        case '[':
            json = parse_array(lex, flags, error);
            break;

        case TOKEN_INVALID:
            error_set(error, lex, json_error_invalid_syntax, "invalid token");
            return NULL;

        default:
            error_set(error, lex, json_error_invalid_syntax, "unexpected token");
            return NULL;
    }

    if(!json)
        return NULL;

    lex->depth--;
    return json;
}

static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
{
    json_t *result;

    lex->depth = 0;

    lex_scan(lex, error);
    if(!(flags & JSON_DECODE_ANY)) {
        if(lex->token != '[' && lex->token != '{') {
            error_set(error, lex, json_error_invalid_syntax, "'[' or '{' expected");
            return NULL;
        }
    }

    result = parse_value(lex, flags, error);
    if(!result)
        return NULL;

    if(!(flags & JSON_DISABLE_EOF_CHECK)) {
        lex_scan(lex, error);
        if(lex->token != TOKEN_EOF) {
            error_set(error, lex, json_error_end_of_input_expected, "end of file expected");
            json_decref(result);
            return NULL;
        }
    }

    if(error) {
        /* Save the position even though there was no error */
		error_set_pos(error, (int)lex->stream.position);
    }

    return result;
}

typedef struct
{
    const char *data;
    size_t pos;
} string_data_t;

static int string_get(void *data)
{
    char c;
    string_data_t *stream = (string_data_t *)data;
    c = stream->data[stream->pos];
    if(c == '\0')
        return EOF;
    else
    {
        stream->pos++;
        return (unsigned char)c;
    }
}

json_t *json_loads(const char *string, size_t flags, json_error_t *error)
{
    lex_t lex;
    json_t *result;
    string_data_t stream_data;

    jsonp_error_init(error, "<string>");

    if (string == NULL) {
        error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
        return NULL;
    }

    stream_data.data = string;
    stream_data.pos = 0;

    if(lex_init(&lex, string_get, flags, (void *)&stream_data))
        return NULL;

    result = parse_json(&lex, flags, error);

    lex_close(&lex);
    return result;
}

typedef struct
{
    const char *data;
    size_t len;
    size_t pos;
} buffer_data_t;

static int buffer_get(void *data)
{
    char c;
    buffer_data_t *stream = static_cast<buffer_data_t *>(data);
    if(stream->pos >= stream->len)
      return EOF;

    c = stream->data[stream->pos];
    stream->pos++;
    return (unsigned char)c;
}

json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error)
{
    lex_t lex;
    json_t *result;
    buffer_data_t stream_data;

    jsonp_error_init(error, "<buffer>");

    if (buffer == NULL) {
        error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
        return NULL;
    }

    stream_data.data = buffer;
    stream_data.pos = 0;
    stream_data.len = buflen;

    if(lex_init(&lex, buffer_get, flags, (void *)&stream_data))
        return NULL;

    result = parse_json(&lex, flags, error);

    lex_close(&lex);
    return result;
}

json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
{
    lex_t lex;
    const char *source;
    json_t *result;

    if(input == stdin)
        source = "<stdin>";
    else
        source = "<stream>";

    jsonp_error_init(error, source);

    if (input == NULL) {
        error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
        return NULL;
    }

    if(lex_init(&lex, (get_func)fgetc, flags, input))
        return NULL;

    result = parse_json(&lex, flags, error);

    lex_close(&lex);
    return result;
}

static int fd_get_func(int *fd)
{
    uint8_t c;
#ifdef HAVE_UNISTD_H
    if (read(*fd, &c, 1) == 1)
        return c;
#endif
    return EOF;
}
#if 0
json_t *json_loadfd(int input, size_t flags, json_error_t *error)
{
    lex_t lex;
    const char *source;
    json_t *result;

#ifdef HAVE_UNISTD_H
    if(input == STDIN_FILENO)
        source = "<stdin>";
    else
#endif
        source = "<stream>";

    jsonp_error_init(error, source);

    if (input < 0) {
        error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
        return NULL;
    }

    if(lex_init(&lex, (get_func)fd_get_func, flags, &input))
        return NULL;

    result = parse_json(&lex, flags, error);

    lex_close(&lex);
    return result;
}
#endif

json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
{
    json_t *result;
    FILE *fp;

    jsonp_error_init(error, path);

    if (path == NULL) {
        error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
        return NULL;
    }

    fp = fopen(path, "rb");
    if(!fp)
    {
        error_set(error, NULL, json_error_cannot_open_file, "unable to open %s: %s",
                  path, strerror(errno));
        return NULL;
    }

    result = json_loadf(fp, flags, error);

    fclose(fp);
    return result;
}

#define MAX_BUF_LEN 1024

typedef struct
{
    char data[MAX_BUF_LEN];
    size_t len;
    size_t pos;
    json_load_callback_t callback;
    void *arg;
} callback_data_t;

static int callback_get(void *data)
{
    char c;
    callback_data_t *stream = static_cast<callback_data_t *>(data);

    if(stream->pos >= stream->len) {
        stream->pos = 0;
        stream->len = stream->callback(stream->data, MAX_BUF_LEN, stream->arg);
        if(stream->len == 0 || stream->len == (size_t)-1)
            return EOF;
    }

    c = stream->data[stream->pos];
    stream->pos++;
    return (unsigned char)c;
}

json_t *json_load_callback(json_load_callback_t callback, void *arg, size_t flags, json_error_t *error)
{
    lex_t lex;
    json_t *result;

    callback_data_t stream_data;

    memset(&stream_data, 0, sizeof(stream_data));
    stream_data.callback = callback;
    stream_data.arg = arg;

    jsonp_error_init(error, "<callback>");

    if (callback == NULL) {
        error_set(error, NULL, json_error_invalid_argument, "wrong arguments");
        return NULL;
    }

    if(lex_init(&lex, (get_func)callback_get, flags, &stream_data))
        return NULL;

    result = parse_json(&lex, flags, error);

    lex_close(&lex);
    return result;
}
#pragma endregion // load

#pragma region hashtable
typedef Tcl_HashTable hashtable_t;
int hashtable_del(hashtable_t *hashtable, const char *key){
	Tcl_HashEntry *ent = Tcl_FindHashEntry(hashtable, key);
	if (!ent) return -1;
	::Tcl_DeleteHashEntry(ent);
	return 0;
}

int hashtable_init(hashtable_t *hashtable){
	::Tcl_InitHashTable(hashtable, TCL_STRING_KEYS);
	return 0;
}

void hashtable_close(hashtable_t *hashtable){
	::Tcl_DeleteHashTable(hashtable);
}

int hashtable_set(hashtable_t *hashtable, const char *key, void *value){
	int addNew;
	Tcl_HashEntry *ent = Tcl_CreateHashEntry(hashtable, key, &addNew);
	if (!ent) return -1;
	Tcl_SetHashValue(ent, value);
	return 0;
}

void *hashtable_get(hashtable_t *hashtable, const char *key){
	Tcl_HashEntry *ent = Tcl_FindHashEntry(hashtable, key);
	if (!ent) return 0;
	return Tcl_GetHashValue(ent);
}

#pragma endregion // hashtable

#pragma region dump
#define MAX_INTEGER_STR_LENGTH  100
#define MAX_REAL_STR_LENGTH     100

#define JSON_MAX_INDENT         0x1F
#define JSON_INDENT(n)          ((n) & JSON_MAX_INDENT)
#define JSON_COMPACT            0x20
#define JSON_ENSURE_ASCII       0x40
#define JSON_SORT_KEYS          0x80
#define JSON_PRESERVE_ORDER     0x100
#define JSON_ENCODE_ANY         0x200
#define JSON_ESCAPE_SLASH       0x400
#define JSON_REAL_PRECISION(n)  (((n) & 0x1F) << 11)
#define JSON_EMBED              0x10000

#define FLAGS_TO_INDENT(f)      ((f) & 0x1F)
#define FLAGS_TO_PRECISION(f)   (((f) >> 11) & 0x1F)

typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);

static int dump_to_dstring(const char *str, size_t size, void *data)
{
	Tcl_DString *dstr = static_cast<Tcl_DString *>(data);
	::Tcl_DStringAppend(dstr, str, size);
	return 0;
}

static int dump_to_stringobj(const char *str, size_t size, void *data)
{
	Tcl_Obj *strobj = static_cast<Tcl_Obj *>(data);
	::Tcl_AppendToObj(strobj, str, size);
	return 0;
}

static int dump_to_file(const char *buffer, size_t size, void *data)
{
    FILE *dest = (FILE *)data;
    if(fwrite(buffer, size, 1, dest) != 1)
        return -1;
    return 0;
}
#if 0
static int dump_to_fd(const char *buffer, size_t size, void *data)
{
    int *dest = (int *)data;
#ifdef HAVE_UNISTD_H
    if(write(*dest, buffer, size) == (ssize_t)size)
        return 0;
#endif
    return -1;
}
#endif

/* 32 spaces (the maximum indentation size) */
static const char whitespace[] = "                                ";

static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)
{
    if(FLAGS_TO_INDENT(flags) > 0)
    {
        unsigned int ws_count = FLAGS_TO_INDENT(flags), n_spaces = depth * ws_count;

        if(dump("\n", 1, data))
            return -1;

        while(n_spaces > 0)
        {
            int cur_n = n_spaces < sizeof whitespace - 1 ? n_spaces : sizeof whitespace - 1;

            if(dump(whitespace, cur_n, data))
                return -1;

            n_spaces -= cur_n;
        }
    }
    else if(space && !(flags & JSON_COMPACT))
    {
        return dump(" ", 1, data);
    }
    return 0;
}

static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data, size_t flags)
{
    const char *pos, *end, *lim;
    int32_t codepoint;

    if(dump("\"", 1, data))
        return -1;

    end = pos = str;
    lim = str + len;
    while(1)
    {
        const char *text;
        char seq[13];
        int length;

        while(end < lim)
        {
            end = utf8_iterate(pos, lim - pos, &codepoint);
            if(!end)
                return -1;

            /* mandatory escape or control char */
            if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
                break;

            /* slash */
            if((flags & JSON_ESCAPE_SLASH) && codepoint == '/')
                break;

            /* non-ASCII */
            if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F)
                break;

            pos = end;
        }

        if(pos != str) {
            if(dump(str, pos - str, data))
                return -1;
        }

        if(end == pos)
            break;

        /* handle \, /, ", and control codes */
        length = 2;
        switch(codepoint)
        {
            case '\\': text = "\\\\"; break;
            case '\"': text = "\\\""; break;
            case '\b': text = "\\b"; break;
            case '\f': text = "\\f"; break;
            case '\n': text = "\\n"; break;
            case '\r': text = "\\r"; break;
            case '\t': text = "\\t"; break;
            case '/':  text = "\\/"; break;
            default:
            {
                /* codepoint is in BMP */
                if(codepoint < 0x10000)
                {
                    snprintf(seq, sizeof(seq), "\\u%04X", (unsigned int)codepoint);
                    length = 6;
                }

                /* not in BMP -> construct a UTF-16 surrogate pair */
                else
                {
                    int32_t first, last;

                    codepoint -= 0x10000;
                    first = 0xD800 | ((codepoint & 0xffc00) >> 10);
                    last = 0xDC00 | (codepoint & 0x003ff);

                    snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first, (unsigned int)last);
                    length = 12;
                }

                text = seq;
                break;
            }
        }

        if(dump(text, length, data))
            return -1;

        str = pos = end;
    }

    return dump("\"", 1, data);
}

static int compare_keys(const void *key1, const void *key2)
{
    return strcmp(*(const char **)key1, *(const char **)key2);
}

static int loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size)
{
    snprintf(key, key_size, "%p", json);
    if (hashtable_get(parents, key))
        return -1;

    return hashtable_set(parents, key, json_create_null());
}

static int do_dump(const json_t *json, size_t flags, int depth,
                   hashtable_t *parents, json_dump_callback_t dump, void *data)
{
    int embed = flags & JSON_EMBED;

    flags &= ~JSON_EMBED;

    if(!json)
        return -1;

	if (json_is_null(json)){
		return dump("null", 4, data);
	}else if (json_is_true(json)){
		return dump("true", 4, data);
	}else if (json_is_false(json)){
		return dump("false", 5, data);
	}else if (json_is_integer(json)){
		char buffer[MAX_INTEGER_STR_LENGTH];
		int size = snprintf(buffer, MAX_INTEGER_STR_LENGTH,
                            "%" JSON_INTEGER_FORMAT,
                            json_integer_value(const_cast<Tcl_Obj *>(json)));
		if(size < 0 || size >= MAX_INTEGER_STR_LENGTH)
			return -1;
		return dump(buffer, size, data);
	}else if (json_is_real(json)){
		char buffer[TCL_DOUBLE_SPACE + 1];
		::Tcl_PrintDouble(0, TTW_GetDouble(const_cast<Tcl_Obj *>(json)), buffer);

		// json では非数、及び無限大を扱うことはできないため、
		// Tcl_PrintDouble が NaN、-Inf、Inf が出力した場合
		// 正しい json ではなくなる

		return dump(buffer, std::strlen(buffer), data);
/*  Tcl_Obj は基本的に常に string に変換可能なので、どの型にもあてはまらないときにstringとして扱う
	}else if (json_is_string(json)){
		int size;
		char *str = ::Tcl_GetStringFromObj(json, &size);
		return dump_string(str, size, dump, data, flags);
*/
	}else if (json_is_array(json)){
		size_t n, i;
		/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
		char key[2 + (sizeof(json) * 2) + 1];

		/* detect circular references */
		if (loop_check(parents, json, key, sizeof(key)))
			return -1;

		TTW_ListObj lst; lst.Borrow(const_cast<Tcl_Obj *>(json));

		n = lst.Count();

		if(!embed && dump("[", 1, data))
			return -1;
		if(n == 0) {
			hashtable_del(parents, key);
			return embed ? 0 : dump("]", 1, data);
		}
		if(dump_indent(flags, depth + 1, 0, dump, data))
			return -1;

		for(i = 0; i < n; ++i) {
			if(do_dump(lst[i], flags, depth + 1,
				parents, dump, data))
				return -1;

			if(i < n - 1)
			{
				if(dump(",", 1, data) ||
					dump_indent(flags, depth + 1, 1, dump, data))
					return -1;
			}
			else
			{
				if(dump_indent(flags, depth, 0, dump, data))
					return -1;
			}
		}

		hashtable_del(parents, key);
		return embed ? 0 : dump("]", 1, data);
	}else if (json_is_object(json)){
		const char *separator;
		int separator_length;
		/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
		char key[2 + (sizeof(json) * 2) + 1];

		if(flags & JSON_COMPACT) {
			separator = ":";
			separator_length = 1;
		}
		else {
			separator = ": ";
			separator_length = 2;
		}

		/* detect circular references */
		if (loop_check(parents, json, key, sizeof(key)))
			return -1;

		TTW_DictObj dct; dct.Borrow(const_cast<Tcl_Obj *>(json));
		TTW_DictObj::Iter iter(dct);

		if(!embed && dump("{", 1, data))
			return -1;
		if(iter.Done()) {
			hashtable_del(parents, key);
               return embed ? 0 : dump("}", 1, data);
		}
		if(dump_indent(flags, depth + 1, 0, dump, data))
			return -1;

		if(flags & JSON_SORT_KEYS)
		{
			const char **keys;
			size_t size, i;

			size = dct.Count();
			keys = reinterpret_cast<const char **>(jsonp_malloc(size * sizeof(const char *)));
			if(!keys)
				return -1;

			i = 0;
			do{
				keys[i] = ::Tcl_GetStringFromObj(iter.Key(), 0);
				i++;
			}while(iter.Next());
			assert(i == size);

			qsort(keys, size, sizeof(const char *), compare_keys);

			for(i = 0; i < size; i++)
			{
				const char *key;
				json_t *value;

				key = keys[i];
				value = dct[key];
				assert(value);

				dump_string(key, strlen(key), dump, data, flags);
				if(dump(separator, separator_length, data) ||
				   do_dump(value, flags, depth + 1, parents, dump, data))
				{
					jsonp_free(reinterpret_cast<char *>(keys));
					return -1;
				}

				if(i < size - 1)
				{
					if(dump(",", 1, data) ||
					   dump_indent(flags, depth + 1, 1, dump, data))
					{
						jsonp_free(reinterpret_cast<char *>(keys));
						return -1;
					}
				}
				else
				{
					if(dump_indent(flags, depth, 0, dump, data))
					{
						jsonp_free(reinterpret_cast<char *>(keys));
						return -1;
					}
				}
			}

			jsonp_free(reinterpret_cast<char *>(keys));
		}
		else
		{
			/* Don't sort keys */

			for(;;){
				int len;
				const char *key = ::Tcl_GetStringFromObj(iter.Key(), &len);

				dump_string(key, len, dump, data, flags);
				if(dump(separator, separator_length, data) ||
				   do_dump(iter.Value(), flags, depth + 1,
						   parents, dump, data))
					return -1;

				if(iter.Next())
				{
					if(dump(",", 1, data) ||
					   dump_indent(flags, depth + 1, 1, dump, data))
						return -1;
				}
				else
				{
					if(dump_indent(flags, depth, 0, dump, data))
						return -1;
					break;
				}
			}
		}

		hashtable_del(parents, key);
		return embed ? 0 : dump("}", 1, data);
	}else{
		int size;
		char *str = ::Tcl_GetStringFromObj(const_cast<Tcl_Obj *>(json), &size);
		return dump_string(str, size, dump, data, flags);
	}
}

int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
{
    int res;
    hashtable_t parents_set;

    if(!(flags & JSON_ENCODE_ANY)) {
        if(!json_is_array(json) && !json_is_object(json))
           return -1;
    }

    if (hashtable_init(&parents_set))
        return -1;
    res = do_dump(json, flags, 0, &parents_set, callback, data);
    hashtable_close(&parents_set);

    return res;
}

#if 0
int json_dumpfd(const json_t *json, int output, size_t flags)
{
    return json_dump_callback(json, dump_to_fd, (void *)&output, flags);
}
#endif

int json_dump_file(const json_t *json, const char *path, size_t flags)
{
    int result;

    FILE *output = fopen(path, "w");
    if(!output)
        return -1;

    result = json_dump_callback(json, dump_to_file, (void *)output, flags);

    if(fclose(output) != 0)
        return -1;

    return result;
}
#pragma endregion // dump
}

Tcl_Obj *TTW_json_create_null(){
	return json_create_null();
}
Tcl_Obj *TTW_json_create_true(){
	return json_create_true();
}
Tcl_Obj *TTW_json_create_false(){
	return json_create_false();
}

bool TTW_json_is_null(const Tcl_Obj *json){
	if (!json) return false;
	return json_is_null(json);
}
bool TTW_json_is_object(const Tcl_Obj *json){
	if (!json) return false;
	return json_is_object(json);
}
bool TTW_json_is_array(const Tcl_Obj *json){
	if (!json) return false;
	return json_is_array(json);
}
bool TTW_json_is_integer(const Tcl_Obj *json){
	if (!json) return false;
	return json_is_integer(json);
}
bool TTW_json_is_real(const Tcl_Obj *json){
	if (!json) return false;
	return json_is_real(json);
}
bool TTW_json_is_number(const Tcl_Obj *json){
	if (!json) return false;
	return json_is_number(json);
}
bool TTW_json_is_boolean(const Tcl_Obj *json){
	if (!json) return false;
	return json_is_boolean(json);
}
int TTW_json_array_count(const Tcl_Obj *json){
	if (!json) return -1;
	if (!json_is_array(json)) return -1;
	TTW_ListObj lst; lst.Borrow(const_cast<Tcl_Obj *>(json));
	return lst.Count();
}

Tcl_Obj *TTW_json_load(const char *string, size_t flags, Tcl_Obj *error){
	return json_loads(string, flags, error);
}
Tcl_Obj *TTW_json_load(const char *buffer, size_t buflen, size_t flags, Tcl_Obj *error){
	return json_loadb(buffer, buflen, flags, error);
}
Tcl_Obj *TTW_json_load_file(const char *path, size_t flags, Tcl_Obj *error){
	return json_load_file(path, flags, error);
}

bool TTW_json_dump(Tcl_Obj *json, size_t flags, TTW_DString &output){
	Tcl_DString &dstr = output;
	return TTW_json_dump(json, flags, &dstr);
}
bool TTW_json_dump(Tcl_Obj *json, size_t flags, Tcl_DString *output){
    if(json_dump_callback(json, dump_to_dstring, (void *)output, flags))
		return false;
	return true;
}
bool TTW_json_dump(Tcl_Obj *json, size_t flags, Tcl_Obj *output){
    if(json_dump_callback(json, dump_to_stringobj, (void *)output, flags))
		return false;
	return true;
}
bool TTW_json_dump_file(Tcl_Obj *json, size_t flags, const char *path){
	if(json_dump_file(json, path, flags))
		return false;
	return true;
}
