From 46bd6170d4ac3ad2df15ab5935f6710a627b1b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20T=E4rnstr=F6m?= Date: Mon, 28 Feb 2011 01:18:35 +0100 Subject: [PATCH] - Initial commit for Windows --- msvc9/msvc9.sln | 20 ++ msvc9/msvc9.vcproj | 191 +++++++++++++ python/objToJSON.c | 693 +++++++++++++++++++++++++++++++++++++++++++++ python/setup.py | 15 + python/ujson.c | 21 ++ ultrajson.c | 390 +++++++++++++++++++++++++ ultrajson.h | 249 ++++++++++++++++ 7 files changed, 1579 insertions(+) create mode 100644 msvc9/msvc9.sln create mode 100644 msvc9/msvc9.vcproj create mode 100644 python/objToJSON.c create mode 100644 python/setup.py create mode 100644 python/ujson.c create mode 100644 ultrajson.c create mode 100644 ultrajson.h diff --git a/msvc9/msvc9.sln b/msvc9/msvc9.sln new file mode 100644 index 0000000..2ebd965 --- /dev/null +++ b/msvc9/msvc9.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dll", "msvc9.vcproj", "{6C9C8F34-FA23-43CB-9405-E2FC0BD7BED6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6C9C8F34-FA23-43CB-9405-E2FC0BD7BED6}.Debug|Win32.ActiveCfg = Debug|Win32 + {6C9C8F34-FA23-43CB-9405-E2FC0BD7BED6}.Debug|Win32.Build.0 = Debug|Win32 + {6C9C8F34-FA23-43CB-9405-E2FC0BD7BED6}.Release|Win32.ActiveCfg = Release|Win32 + {6C9C8F34-FA23-43CB-9405-E2FC0BD7BED6}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/msvc9/msvc9.vcproj b/msvc9/msvc9.vcproj new file mode 100644 index 0000000..bc1bfac --- /dev/null +++ b/msvc9/msvc9.vcproj @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/python/objToJSON.c b/python/objToJSON.c new file mode 100644 index 0000000..6a19e8a --- /dev/null +++ b/python/objToJSON.c @@ -0,0 +1,693 @@ +#include +#include +#include "../ultrajson.h" + +static PyObject* meth_timegm; +static PyObject* mod_calendar; + +enum PRIVATE +{ + PRV_CONV_FUNC, // Function pointer to converter function + PRV_CONV_NEWOBJ, // Any new PyObject created by converter function that should be released by releaseValue + PRV_ITER_BEGIN_FUNC, // Function pointer to iterBegin for specific type + PRV_ITER_END_FUNC, // Function pointer to iterEnd for specific type + PRV_ITER_NEXT_FUNC, // Function pointer to iterNext for specific type + PRV_ITER_GETVALUE_FUNC, + PRV_ITER_GETNAME_FUNC, + PRV_ITER_INDEX, // Index in the iteration list + PRV_ITER_SIZE, // Size of the iteration list + PRV_ITER_ITEM, // Current iter item + PRV_ITER_ITEM_NAME, // Name of iter item + PRV_ITER_ITEM_VALUE, // Value of iteritem + PRV_ITER_DICTITEMS, + PRV_ITER_DICTOBJ, + PRV_ITER_ATTRLIST, +}; + +struct PyDictIterState +{ + PyObject *keys; + size_t i; + size_t sz; +}; + + +//#define PRINTMARK() fprintf(stderr, "%s: MARK(%d)\n", __FILE__, __LINE__) +#define PRINTMARK() + +void initObjToJSON() +{ + //FIXME: DECREF on these? + PyDateTime_IMPORT; + + /* + FIXME: Find the direct function pointer here instead and use it when time conversion is performed */ + + meth_timegm = PyString_FromString("timegm"); + mod_calendar = PyImport_ImportModule("calendar"); + + Py_INCREF(mod_calendar); + +} + + +typedef void *(*PFN_PyTypeToJSON)(JSOBJ obj, JSTYPEINFO *ti, void *outValue, size_t *_outLen); + +static void *PyNoneToNULL(JSOBJ _obj, JSTYPEINFO *ti, void *outValue, size_t *_outLen) +{ + return NULL; +} + +static void *PyBoolToBOOLEAN(JSOBJ _obj, JSTYPEINFO *ti, void *outValue, size_t *_outLen) +{ + PyObject *obj = (PyObject *) _obj; + //FIXME: Perhaps we can use the PyBoolObject->ival directly here? + *((int *) outValue) = (obj == Py_True) ? 1 : 0; + return NULL; +} + +static void *PyIntToINTEGER(JSOBJ _obj, JSTYPEINFO *ti, void *outValue, size_t *_outLen) +{ + PyObject *obj = (PyObject *) _obj; + *((JSLONG *) outValue) = PyInt_AS_LONG (obj); + return NULL; +} + +static void *PyLongToINTEGER(JSOBJ _obj, JSTYPEINFO *ti, void *outValue, size_t *_outLen) +{ + PyObject *obj = (PyObject *) _obj; + *((JSLONG *) outValue) = PyLong_AsLongLong (obj); + return NULL; +} + +static void *PyLongToUTF8(JSOBJ _obj, JSTYPEINFO *ti, void *outValue, size_t *_outLen) +{ + PyObject *obj = (PyObject *) _obj; + PyObject *newobj; + char *ret; + PRINTMARK(); + + + newobj = PyObject_Str(obj); + + if (newobj) + { + ti->prv[PRV_CONV_NEWOBJ] = newobj; + ti->release = 1; + *_outLen = PyString_GET_SIZE(newobj); + //fprintf (stderr, "%s: Object %p needs release!\n", __FUNCTION__, newobj); + ret = PyString_AS_STRING(newobj); + } + else + { + ret = ""; + *_outLen = 0; + } + return ret; +} + +static void *PyFloatToDOUBLE(JSOBJ _obj, JSTYPEINFO *ti, void *outValue, size_t *_outLen) +{ + PyObject *obj = (PyObject *) _obj; + *((double *) outValue) = PyFloat_AS_DOUBLE (obj); + return NULL; +} + +static void *PyStringToUTF8(JSOBJ _obj, JSTYPEINFO *ti, void *outValue, size_t *_outLen) +{ + PyObject *obj = (PyObject *) _obj; + *_outLen = PyString_GET_SIZE(obj); + return PyString_AS_STRING(obj); +} + +static void *PyUnicodeToUTF8(JSOBJ _obj, JSTYPEINFO *ti, void *outValue, size_t *_outLen) +{ + PyObject *obj = (PyObject *) _obj; + PyObject *newObj = PyUnicode_EncodeUTF8 (PyUnicode_AS_UNICODE(obj), PyUnicode_GET_SIZE(obj), NULL); + ti->release = 1; + ti->prv[PRV_CONV_NEWOBJ] = (void *) newObj; + + *_outLen = PyString_GET_SIZE(newObj); + return PyString_AS_STRING(newObj); +} + +static void *PyDateTimeToINTEGER(JSOBJ _obj, JSTYPEINFO *ti, void *outValue, size_t *_outLen) +{ + PyObject *obj = (PyObject *) _obj; + + PyObject* timetuple = PyObject_CallMethod(obj, "utctimetuple", NULL); + PyObject* unixTimestamp = PyObject_CallMethodObjArgs(mod_calendar, meth_timegm, timetuple, NULL); + + *( (JSLONG *) outValue) = PyLong_AsLongLong (unixTimestamp); + Py_DECREF(timetuple); + Py_DECREF(unixTimestamp); + return NULL; +} + +static void *PyDateToINTEGER(JSOBJ _obj, JSTYPEINFO *ti, void *outValue, size_t *_outLen) +{ + PyObject *obj = (PyObject *) _obj; + + PyObject* timetuple = PyTuple_New(6); + PyObject* year = PyObject_GetAttrString(obj, "year"); + PyObject* month = PyObject_GetAttrString(obj, "month"); + PyObject* day = PyObject_GetAttrString(obj, "day"); + PyObject* unixTimestamp; + + PyTuple_SET_ITEM(timetuple, 0, year); + PyTuple_SET_ITEM(timetuple, 1, month); + PyTuple_SET_ITEM(timetuple, 2, day); + PyTuple_SET_ITEM(timetuple, 3, PyInt_FromLong(0)); + PyTuple_SET_ITEM(timetuple, 4, PyInt_FromLong(0)); + PyTuple_SET_ITEM(timetuple, 5, PyInt_FromLong(0)); + + unixTimestamp = PyObject_CallMethodObjArgs(mod_calendar, meth_timegm, timetuple, NULL); + + *( (JSLONG *) outValue) = PyLong_AsLongLong (unixTimestamp); + Py_DECREF(timetuple); + Py_DECREF(unixTimestamp); + return NULL; +} + +//============================================================================= +// Tuple iteration functions +// itemValue is borrowed reference, no ref counting +//============================================================================= +void Tuple_iterBegin(JSOBJ obj, JSTYPEINFO *ti) +{ + ti->prv[PRV_ITER_INDEX] = (void *) 0; + ti->prv[PRV_ITER_SIZE] = (void *) PyTuple_GET_SIZE( (PyObject *) obj); + ti->prv[PRV_ITER_ITEM_VALUE] = NULL; +} + +int Tuple_iterNext(JSOBJ obj, JSTYPEINFO *ti) +{ + size_t i = (size_t) ti->prv[PRV_ITER_INDEX]; + size_t sz = (size_t) ti->prv[PRV_ITER_SIZE]; + PyObject *item; + + if (i >= sz) + { + return 0; + } + + item = PyTuple_GET_ITEM (obj, i); + + ti->prv[PRV_ITER_ITEM_VALUE] = item; + i ++; + ti->prv[PRV_ITER_INDEX] = (void *) i; + return 1; +} + +void Tuple_iterEnd(JSOBJ obj, JSTYPEINFO *ti) +{ +} + +JSOBJ Tuple_iterGetValue(JSOBJ obj, JSTYPEINFO *ti) +{ + PyObject *curr = (PyObject *) ti->prv[PRV_ITER_ITEM_VALUE]; + return curr; +} + +char *Tuple_iterGetName(JSOBJ obj, JSTYPEINFO *ti, size_t *outLen) +{ + return NULL; +} + +//============================================================================= +// Dir iteration functions +// itemName ref is borrowed from PyObject_Dir (attrList). No refcount +// itemValue ref is from PyObject_GetAttr. Ref counted +//============================================================================= +void Dir_iterBegin(JSOBJ obj, JSTYPEINFO *ti) +{ + PyObject* attrList = PyObject_Dir(obj); + ti->prv[PRV_ITER_INDEX] = (void *) 0; + ti->prv[PRV_ITER_SIZE] = (void *) PyList_GET_SIZE(attrList); + ti->prv[PRV_ITER_ITEM] = NULL; + ti->prv[PRV_ITER_ITEM_NAME] = NULL; + ti->prv[PRV_ITER_ITEM_VALUE] = NULL; + ti->prv[PRV_ITER_ATTRLIST] = attrList; + PRINTMARK(); +} + +void Dir_iterEnd(JSOBJ obj, JSTYPEINFO *ti) +{ + PyObject *itemValue = (PyObject *) ti->prv[PRV_ITER_ITEM_VALUE]; + + if (itemValue) + { + Py_DECREF(itemValue); + ti->prv[PRV_ITER_ITEM_VALUE] = itemValue = NULL; + } + + Py_DECREF( (PyObject *) ti->prv[PRV_ITER_ATTRLIST]); + PRINTMARK(); +} + +int Dir_iterNext(JSOBJ _obj, JSTYPEINFO *ti) +{ + PyObject *obj = (PyObject *) _obj; + size_t i = (size_t) ti->prv[PRV_ITER_INDEX]; + size_t sz = (size_t) ti->prv[PRV_ITER_SIZE]; + PyObject *attrList = (PyObject *) ti->prv[PRV_ITER_ATTRLIST]; + PyObject *itemName = NULL; + PyObject *itemValue = (PyObject *) ti->prv[PRV_ITER_ITEM_VALUE]; + + if (itemValue) + { + Py_DECREF(itemValue); + ti->prv[PRV_ITER_ITEM_VALUE] = itemValue = NULL; + } + + //fprintf (stderr, "%s: ti=%p obj=%p, i=%u, sz=%u\n", __FUNCTION__, ti, _obj, i, sz); + + for (; i < sz; i ++) + { + PyObject* attr = PyList_GET_ITEM(attrList, i); + char* attrStr = PyString_AS_STRING(attr); + + if (attrStr[0] == '_') + { + PRINTMARK(); + continue; + } + + itemValue = PyObject_GetAttr(obj, attr); + if (itemValue == NULL) + { + PyErr_Clear(); + PRINTMARK(); + continue; + } + + if (PyCallable_Check(itemValue)) + { + Py_DECREF(itemValue); + PRINTMARK(); + continue; + } + + PRINTMARK(); + itemName = attr; + break; + } + + if (itemName == NULL) + { + i = sz; + ti->prv[PRV_ITER_ITEM_VALUE] = NULL; + return 0; + } + + ti->prv[PRV_ITER_ITEM_NAME] = itemName; + ti->prv[PRV_ITER_ITEM_VALUE] = itemValue; + ti->prv[PRV_ITER_INDEX] = (void *) (i + 1); + PRINTMARK(); + return 1; +} + + + +JSOBJ Dir_iterGetValue(JSOBJ obj, JSTYPEINFO *ti) +{ + PRINTMARK(); + return (PyObject *) ti->prv[PRV_ITER_ITEM_VALUE]; +} + +char *Dir_iterGetName(JSOBJ obj, JSTYPEINFO *ti, size_t *outLen) +{ + PyObject *curr = (PyObject *) ti->prv[PRV_ITER_ITEM_NAME]; + PRINTMARK(); + *outLen = PyString_GET_SIZE(curr); + return PyString_AS_STRING(curr); +} + + + + +//============================================================================= +// List iteration functions +// itemValue is borrowed from object (which is list). No refcounting +//============================================================================= +void List_iterBegin(JSOBJ obj, JSTYPEINFO *ti) +{ + ti->prv[PRV_ITER_INDEX] = (void *) 0; + ti->prv[PRV_ITER_SIZE] = (void *) PyList_GET_SIZE( (PyObject *) obj); + ti->prv[PRV_ITER_ITEM_VALUE] = NULL; +} + +int List_iterNext(JSOBJ obj, JSTYPEINFO *ti) +{ + size_t i = (size_t) ti->prv[PRV_ITER_INDEX]; + size_t sz = (size_t) ti->prv[PRV_ITER_SIZE]; + PyObject *item; + + if (i >= sz) + { + return 0; + } + + item = PyList_GET_ITEM (obj, i); + + ti->prv[PRV_ITER_ITEM_VALUE] = item; + i ++; + ti->prv[PRV_ITER_INDEX] = (void *) i; + return 1; +} + +void List_iterEnd(JSOBJ obj, JSTYPEINFO *ti) +{ +} + +JSOBJ List_iterGetValue(JSOBJ obj, JSTYPEINFO *ti) +{ + return (PyObject *) ti->prv[PRV_ITER_ITEM_VALUE]; +} + +char *List_iterGetName(JSOBJ obj, JSTYPEINFO *ti, size_t *outLen) +{ + return NULL; +} + +//============================================================================= +// Dict iteration functions +// itemName might converted to string (Python_Str). Do refCounting +// itemValue is borrowed from object (which is dict). No refCounting +//============================================================================= +void Dict_iterBegin(JSOBJ obj, JSTYPEINFO *ti) +{ + ti->prv[PRV_ITER_INDEX] = (void *) 0; + ti->prv[PRV_ITER_ITEM_NAME] = NULL; + ti->prv[PRV_ITER_ITEM_VALUE] = NULL; + PRINTMARK(); +} + +int Dict_iterNext(JSOBJ _obj, JSTYPEINFO *ti) +{ + size_t i = (size_t) ti->prv[PRV_ITER_INDEX]; + PyObject *obj = (PyObject *) ti->prv[PRV_ITER_DICTOBJ]; + PyObject *itemName = (PyObject *) ti->prv[PRV_ITER_ITEM_NAME]; + PyObject *itemValue; + + //fprintf (stderr, "%s: ti=%p obj=%p, i=%u, sz=%u\n", __FUNCTION__, ti, _obj, i, sz); + if (itemName) + { + Py_DECREF(itemName); + ti->prv[PRV_ITER_ITEM_NAME] = itemName = NULL; + } + + + if (!PyDict_Next (obj, &((size_t) ti->prv[PRV_ITER_INDEX]), &itemName, &itemValue)) + { + return 0; + } + if (!PyString_Check(itemName)) + itemName = PyObject_Str(itemName); + else + Py_INCREF(itemName); + + ti->prv[PRV_ITER_ITEM_NAME] = itemName; + ti->prv[PRV_ITER_ITEM_VALUE] = itemValue; + + PRINTMARK(); + return 1; +} + +void Dict_iterEnd(JSOBJ obj, JSTYPEINFO *ti) +{ + PyObject *itemName = (PyObject *) ti->prv[PRV_ITER_ITEM_NAME]; + + if (itemName) + { + Py_DECREF(itemName); + ti->prv[PRV_ITER_ITEM_NAME] = itemName = NULL; + } + Py_DECREF( (PyObject *) ti->prv[PRV_ITER_DICTOBJ]); + PRINTMARK(); +} + +JSOBJ Dict_iterGetValue(JSOBJ obj, JSTYPEINFO *ti) +{ + return (PyObject *) ti->prv[PRV_ITER_ITEM_VALUE]; +} + +char *Dict_iterGetName(JSOBJ obj, JSTYPEINFO *ti, size_t *outLen) +{ + PyObject *curr = (PyObject *) ti->prv[PRV_ITER_ITEM_NAME]; + *outLen = PyString_GET_SIZE(curr); + return PyString_AS_STRING(curr); +} + + +static void Object_getType(JSOBJ _obj, JSTYPEINFO *ti) +{ + PyObject *obj = (PyObject *) _obj; + PyObject *toDictFunc; + + if (PyIter_Check(obj)) + { + goto ISITERABLE; + } + + if (PyInt_Check(obj)) + { + PRINTMARK(); + ti->prv[PRV_CONV_FUNC] = (void *) PyIntToINTEGER; ti->type = JT_INTEGER; + return; + } + else + if (PyLong_Check(obj)) + { + PRINTMARK(); + ti->prv[PRV_CONV_FUNC] = (void *) PyLongToINTEGER; ti->type = JT_INTEGER; + return; + } + else + if (PyString_Check(obj)) + { + PRINTMARK(); + ti->prv[PRV_CONV_FUNC] = (void *) PyStringToUTF8; ti->type = JT_UTF8; + return; + } + else + if (PyUnicode_Check(obj)) + { + PRINTMARK(); + ti->prv[PRV_CONV_FUNC] = (void *) PyUnicodeToUTF8; ti->type = JT_UTF8; + return; + } + else + if (PyBool_Check(obj)) + { + PRINTMARK(); + ti->prv[PRV_CONV_FUNC] = NULL; + ti->type = (obj == Py_True) ? JT_TRUE : JT_FALSE; + return; + } + else + if (PyFloat_Check(obj)) + { + PRINTMARK(); + ti->prv[PRV_CONV_FUNC] = (void *) PyFloatToDOUBLE; ti->type = JT_DOUBLE; + return; + } + else + if (PyDateTime_Check(obj)) + { + PRINTMARK(); + ti->prv[PRV_CONV_FUNC] = (void *) PyDateTimeToINTEGER; ti->type = JT_INTEGER; + return; + } + else + if (PyDate_Check(obj)) + { + PRINTMARK(); + ti->prv[PRV_CONV_FUNC] = (void *) PyDateToINTEGER; ti->type = JT_INTEGER; + return; + } + else + if (obj == Py_None) + { + PRINTMARK(); + ti->prv[PRV_CONV_FUNC] = NULL; ti->type = JT_NULL; + return; + } + + +ISITERABLE: + + if (PyDict_Check(obj)) + { + PRINTMARK(); + ti->prv[PRV_CONV_FUNC] = NULL; ti->type = JT_OBJECT; + ti->prv[PRV_ITER_BEGIN_FUNC] = Dict_iterBegin; + ti->prv[PRV_ITER_END_FUNC] = Dict_iterEnd; + ti->prv[PRV_ITER_NEXT_FUNC] = Dict_iterNext; + ti->prv[PRV_ITER_GETVALUE_FUNC] = Dict_iterGetValue; + ti->prv[PRV_ITER_GETNAME_FUNC] = Dict_iterGetName; + ti->prv[PRV_ITER_DICTOBJ] = obj; + Py_INCREF(obj); + + return; + } + else + if (PyList_Check(obj)) + { + PRINTMARK(); + ti->prv[PRV_CONV_FUNC] = NULL; ti->type = JT_ARRAY; + ti->prv[PRV_ITER_BEGIN_FUNC] = List_iterBegin; + ti->prv[PRV_ITER_END_FUNC] = List_iterEnd; + ti->prv[PRV_ITER_NEXT_FUNC] = List_iterNext; + ti->prv[PRV_ITER_GETVALUE_FUNC] = List_iterGetValue; + ti->prv[PRV_ITER_GETNAME_FUNC] = List_iterGetName; + return; + } + else + if (PyTuple_Check(obj)) + { + PRINTMARK(); + ti->prv[PRV_CONV_FUNC] = NULL; ti->type = JT_ARRAY; + ti->prv[PRV_ITER_BEGIN_FUNC] = Tuple_iterBegin; + ti->prv[PRV_ITER_END_FUNC] = Tuple_iterEnd; + ti->prv[PRV_ITER_NEXT_FUNC] = Tuple_iterNext; + ti->prv[PRV_ITER_GETVALUE_FUNC] = Tuple_iterGetValue; + ti->prv[PRV_ITER_GETNAME_FUNC] = Tuple_iterGetName; + return; + } + + + toDictFunc = PyObject_GetAttrString(obj, "toDict"); + + if (toDictFunc) + { + PyObject* tuple = PyTuple_New(0); + PyObject* toDictResult = PyObject_Call(toDictFunc, tuple, NULL); + Py_DECREF(tuple); + Py_DECREF(toDictFunc); + + if (toDictResult == NULL) + { + PyErr_Clear(); + ti->prv[PRV_CONV_FUNC] = NULL; ti->type = JT_NULL; + return; + } + + if (!PyDict_Check(toDictResult)) + { + Py_DECREF(toDictResult); + ti->prv[PRV_CONV_FUNC] = NULL; ti->type = JT_NULL; + return; + } + + PRINTMARK(); + ti->prv[PRV_CONV_FUNC] = NULL; ti->type = JT_OBJECT; + ti->prv[PRV_ITER_BEGIN_FUNC] = Dict_iterBegin; + ti->prv[PRV_ITER_END_FUNC] = Dict_iterEnd; + ti->prv[PRV_ITER_NEXT_FUNC] = Dict_iterNext; + ti->prv[PRV_ITER_GETVALUE_FUNC] = Dict_iterGetValue; + ti->prv[PRV_ITER_GETNAME_FUNC] = Dict_iterGetName; + ti->prv[PRV_ITER_DICTOBJ] = toDictResult; + return; + } + + PyErr_Clear(); + + ti->prv[PRV_CONV_FUNC] = NULL; ti->type = JT_OBJECT; + ti->prv[PRV_ITER_BEGIN_FUNC] = Dir_iterBegin; + ti->prv[PRV_ITER_END_FUNC] = Dir_iterEnd; + ti->prv[PRV_ITER_NEXT_FUNC] = Dir_iterNext; + ti->prv[PRV_ITER_GETVALUE_FUNC] = Dir_iterGetValue; + ti->prv[PRV_ITER_GETNAME_FUNC] = Dir_iterGetName; + + return; +} + + +static void *Object_getValue(JSOBJ obj, JSTYPEINFO *ti, void *outValue, size_t *_outLen) +{ + void *ret; + PFN_PyTypeToJSON pfnFunc = (PFN_PyTypeToJSON) ti->prv[PRV_CONV_FUNC]; + PRINTMARK(); + ret = pfnFunc (obj, ti, outValue, _outLen); + PRINTMARK(); + return ret; +} + +void Object_releaseObject(JSOBJ *_obj) +{ + PyObject *obj = (PyObject *) _obj; + Py_DECREF(obj); +} + +void Object_releaseValue(JSTYPEINFO *ti) +{ + PyObject *obj = (PyObject *) ti->prv[PRV_CONV_NEWOBJ]; + //fprintf (stderr, "%s: Object %p was released!\n", __FUNCTION__, obj); + Py_DECREF(obj); +} + +void Object_iterBegin(JSOBJ obj, JSTYPEINFO *ti) +{ + JSPFN_ITERBEGIN pfnFunc = (JSPFN_ITERBEGIN) ti->prv[PRV_ITER_BEGIN_FUNC]; + pfnFunc (obj, ti); +} + +int Object_iterNext(JSOBJ obj, JSTYPEINFO *ti) +{ + JSPFN_ITERNEXT pfnFunc = (JSPFN_ITERNEXT) ti->prv[PRV_ITER_NEXT_FUNC]; + return pfnFunc (obj, ti); +} + +void Object_iterEnd(JSOBJ obj, JSTYPEINFO *ti) +{ + JSPFN_ITEREND pfnFunc = (JSPFN_ITEREND) ti->prv[PRV_ITER_END_FUNC]; + pfnFunc (obj, ti); +} + +JSOBJ Object_iterGetValue(JSOBJ obj, JSTYPEINFO *ti) +{ + JSPFN_ITERGETVALUE pfnFunc = (JSPFN_ITERGETVALUE) ti->prv[PRV_ITER_GETVALUE_FUNC]; + return pfnFunc (obj, ti); +} + +char *Object_iterGetName(JSOBJ obj, JSTYPEINFO *ti, size_t *outLen) +{ + JSPFN_ITERGETNAME pfnFunc = (JSPFN_ITERGETNAME) ti->prv[PRV_ITER_GETNAME_FUNC]; + return pfnFunc (obj, ti, outLen); +} + + +static JSONObjectEncoder g_encState = +{ + Object_getType, //void (*getType)(JSOBJ obj, JSTYPEINFO *ti); + Object_getValue, //void *(*getValue)(JSOBJ obj, JSTYPEINFO *ti, void *outValue, size_t *_outLen); + Object_iterBegin, //JSPFN_ITERBEGIN iterBegin; + Object_iterNext, //JSPFN_ITERNEXT iterNext; + Object_iterEnd, //JSPFN_ITEREND iterEnd; + Object_iterGetValue, //JSPFN_ITERGETVALUE iterGetValue; + Object_iterGetName, //JSPFN_ITERGETNAME iterGetName; + Object_releaseValue, //void (*releaseValue)(JSTYPEINFO *ti); + PyObject_Malloc, //JSPFN_MALLOC malloc; + PyObject_Realloc, //JSPFN_REALLOC realloc; + PyObject_Free//JSPFN_FREE free; +}; + + +PyObject* objToJSON(PyObject* self, PyObject *arg) +{ + char buffer[65536]; + char *ret; + PyObject *newobj; + + //FIXME: Add support for max recursion to eliminiate OOM scenario on cyclic references + ret = JSON_EncodeObject (arg, &g_encState, buffer, sizeof (buffer)); + newobj = PyString_FromString (ret); + + if (ret != buffer) + { + g_encState.free (ret); + } + + return newobj; +} diff --git a/python/setup.py b/python/setup.py new file mode 100644 index 0000000..9f456fe --- /dev/null +++ b/python/setup.py @@ -0,0 +1,15 @@ +from distutils.core import setup, Extension +import distutils.sysconfig + +module1 = Extension('ujson', + sources = ['ujson.c', 'objToJSON.c'], + include_dirs = ['../'], + library_dirs = ['./lib/'], + libraries=['libultrajson']) + +setup (name = 'ujson', + version = '1.0', + description = 'UltraJSON', + ext_modules = [module1], + data_files = [(distutils.sysconfig.get_python_lib(), ["./lib/libultrajson.dll"])]) + diff --git a/python/ujson.c b/python/ujson.c new file mode 100644 index 0000000..27b9817 --- /dev/null +++ b/python/ujson.c @@ -0,0 +1,21 @@ +#include + +/* objToJSON */ +PyObject* objToJSON(PyObject* self, PyObject *arg); +void initObjToJSON(); + + +static PyMethodDef ujsonMethods[] = { + {"encode", objToJSON, METH_O, "Converts arbitrary object recursivly into JSON as string"}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + + + +PyMODINIT_FUNC +initujson(void) +{ + initObjToJSON(); + Py_InitModule("ujson", ujsonMethods); + +} \ No newline at end of file diff --git a/ultrajson.c b/ultrajson.c new file mode 100644 index 0000000..214efc5 --- /dev/null +++ b/ultrajson.c @@ -0,0 +1,390 @@ +/* +Copyright (c) 2011, Jonas Tarnstrom and ESN Social Software AB +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + This product includes software developed by ESN Social Software AB (www.esn.me). +4. Neither the name of the ESN Social Software AB nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY ESN SOCIAL SOFTWARE AB ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALLESN SOCIAL SOFTWARE AB BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "ultrajson.h" +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#define INLINEFUNCTION __inline +#else +#error "Fix inline definition for non MSVC" +#endif + +typedef struct _Buffer +{ + char *start; + char *offset; + char *end; + int heap; + JSPFN_MALLOC malloc; + JSPFN_REALLOC realloc; + JSPFN_FREE free; + +} Buffer; + +static void Buffer_Realloc (Buffer *buffer); + +#define FastMemCopy memcpy + +#define Buffer_AppendEscape(__buffer, __pstr, __len) \ + while ((__buffer)->offset + ((__len * 2) + 2) > (__buffer)->end) \ + { \ + Buffer_Realloc((__buffer)); \ + } \ + *((__buffer)->offset++) = '\"'; \ + Buffer_Escape((__buffer), (char *)__pstr, __len); \ + *((__buffer)->offset++) = '\"'; + +#define Buffer_AppendEscapeUnchecked(__buffer, __pstr, __len) \ + *((__buffer)->offset++) = '\"'; \ + Buffer_Escape((__buffer), (char *)__pstr, __len); \ + *((__buffer)->offset++) = '\"'; + + +#define Buffer_Append(__buffer, __pstr, __len) \ + while ((__buffer)->offset + __len > (__buffer)->end) \ + { \ + Buffer_Realloc((__buffer)); \ + } \ + FastMemCopy ((__buffer)->offset, __pstr, __len); \ + (__buffer)->offset += __len; + +#define Buffer_AppendUnchecked(__buffer, __pstr, __len) \ + FastMemCopy ((__buffer)->offset, __pstr, __len); \ + (__buffer)->offset += __len; + + +#define Buffer_AppendCharUnchecked(__buffer, __chr) \ + *((__buffer)->offset++) = __chr; \ + +#define Buffer_Reserve(__buffer, __len) \ + while ((__buffer)->offset + (__len) > (__buffer)->end) \ + { \ + Buffer_Realloc((__buffer)); \ + } \ + +INLINEFUNCTION static void strreverse(char* begin, char* end) +{ + char aux; + while (end > begin) + aux = *end, *end-- = *begin, *begin++ = aux; +} + +INLINEFUNCTION void Buffer_AppendIntUnchecked(Buffer *buffer, JSLONG value) +{ + char* wstr; + JSULONG uvalue = (value < 0) ? -value : value; + + // Reserve space + //FIXME: 33 seems a bit magic? + /* + while(buffer->offset + 33 > buffer->end) + { + Buffer_Realloc(buffer); + }*/ + + wstr = buffer->offset; + + + // Conversion. Number is reversed. + do *wstr++ = (char)(48 + (uvalue % 10)); while(uvalue /= 10); + if (value < 0) *wstr++ = '-'; + + // Reverse string + strreverse(buffer->offset,wstr - 1); + buffer->offset += (wstr - 1 - (buffer->offset)); +} + + + +INLINEFUNCTION void Buffer_AppendDoubleUnchecked(Buffer *buffer, double fp) +{ + char intPart_reversed[64]; + int i, charCount = 0; + double fp_int, fp_frac; + + fp_frac = modf(fp,&fp_int); //Separate integer/fractional parts + + while (fp_int > 0) //Convert integer part, if any + { + intPart_reversed[charCount++] = '0' + (int)fmod(fp_int,10.0); + fp_int = floor(fp_int/10.0); + } + + for (i=0; ioffset++) = intPart_reversed[charCount-i-1]; + + *(buffer->offset++) = '.'; //Decimal point + + i = 0; + + while (fp_frac > 0 && i < JSON_DOUBLE_MAX_DECIMALS) //Convert fractional part, if any + { + fp_frac*=10; + fp_frac = modf(fp_frac,&fp_int); + *(buffer->offset++) = '0' + (int)fp_int; + i ++; + } +} + +static void Buffer_Realloc (Buffer *buffer) +{ + size_t newSize = buffer->end - buffer->start; + size_t offset = buffer->offset - buffer->start; + newSize *= 2; + + if (buffer->heap) + { + buffer->start = (char *) buffer->realloc (buffer->start, newSize); + } + else + { + char *oldStart = buffer->start; + buffer->heap = 1; + buffer->start = (char *) buffer->malloc (newSize); + memcpy (buffer->start, oldStart, offset); + } + buffer->offset = buffer->start + offset; + buffer->end = buffer->start + newSize; + +} + +static char g_escapeLookup[] = { + /* 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f */ + /* 0x00 */ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 'b', 't', 'n', '\0', 'f', 'r', '\0', '\0', + /* 0x10 */ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + /* 0x20 */ '\0', '\0', '\"', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + /* 0x30 */ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + /* 0x40 */ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + /* 0x50 */ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\\', '\0', '\0', '\0', + /* 0x60 */ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + /* 0x70 */ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + /* 0x80 */ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + /* 0x90 */ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + /* 0xa0 */ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + /* 0xb0 */ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + /* 0xc0 */ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + /* 0xd0 */ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + /* 0xe0 */ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', +}; + +static void Buffer_Escape (Buffer *buffer, char *inputOffset, size_t len) +{ + char *inputEnd = inputOffset + len; + char output; + + while (inputOffset < inputEnd) + { + output = g_escapeLookup[(unsigned char) *(inputOffset)]; + + if (output == '\0') + { + *(buffer->offset++) = *(inputOffset++); + } + else + { + *(buffer->offset++) = '\\'; + *(buffer->offset++) = output; + inputOffset ++; + } + } +} + +static void encode(char *_name, size_t _cbName, int level, JSOBJ obj, JSONObjectEncoder *def, Buffer *buffer, int insideList) +{ + JSTYPEINFO ti; + size_t szlen; + const char *name = (insideList || level == 0) ? NULL : _name; + + Buffer_Reserve(buffer, 100 + (_cbName * 2)); + + if (name != NULL) + { + Buffer_AppendEscapeUnchecked (buffer, name, _cbName); + Buffer_AppendCharUnchecked (buffer, ':'); + Buffer_AppendCharUnchecked (buffer, ' '); + } + + ti.release = 0; + def->getType(obj, &ti); + + switch (ti.type) + { + case JT_ARRAY: + { + int count = 0; + JSOBJ iterObj; + def->iterBegin(obj, &ti); + + Buffer_AppendCharUnchecked (buffer, '['); + + while (def->iterNext(obj, &ti)) + { + if (count > 0) + { + Buffer_AppendCharUnchecked (buffer, ','); + Buffer_AppendCharUnchecked (buffer, ' '); + } + + iterObj = def->iterGetValue(obj, &ti); + encode (NULL, 0, level + 1, iterObj, def, buffer, 1); + count ++; + } + + def->iterEnd(obj, &ti); + Buffer_AppendCharUnchecked (buffer, ']'); + break; + } + + case JT_OBJECT: + { + int count = 0; + JSOBJ iterObj; + char *objName; + def->iterBegin(obj, &ti); + + Buffer_AppendCharUnchecked (buffer, '{'); + + while (def->iterNext(obj, &ti)) + { + if (count > 0) + { + Buffer_AppendCharUnchecked (buffer, ','); + Buffer_AppendCharUnchecked (buffer, ' '); + } + + iterObj = def->iterGetValue(obj, &ti); + objName = def->iterGetName(obj, &ti, &szlen); + + encode (objName, szlen, level + 1, iterObj, def, buffer, 0); + count ++; + } + + def->iterEnd(obj, &ti); + Buffer_AppendCharUnchecked (buffer, '}'); + break; + } + + case JT_INTEGER: + { + JSLONG value; + def->getValue(obj, &ti, &value, &szlen); + Buffer_AppendIntUnchecked (buffer, value); + break; + } + + case JT_TRUE: + { + //Buffer_AppendUnchecked (buffer, "true", 4); + Buffer_AppendCharUnchecked (buffer, 't'); + Buffer_AppendCharUnchecked (buffer, 'r'); + Buffer_AppendCharUnchecked (buffer, 'u'); + Buffer_AppendCharUnchecked (buffer, 'e'); + break; + } + + case JT_FALSE: + { + //Buffer_AppendUnchecked (buffer, "false", 5); + Buffer_AppendCharUnchecked (buffer, 'f'); + Buffer_AppendCharUnchecked (buffer, 'a'); + Buffer_AppendCharUnchecked (buffer, 'l'); + Buffer_AppendCharUnchecked (buffer, 's'); + Buffer_AppendCharUnchecked (buffer, 'e'); + break; + } + + + case JT_NULL: + { + //Buffer_AppendUnchecked(buffer, "null", 4); + Buffer_AppendCharUnchecked (buffer, 'n'); + Buffer_AppendCharUnchecked (buffer, 'u'); + Buffer_AppendCharUnchecked (buffer, 'l'); + Buffer_AppendCharUnchecked (buffer, 'l'); + break; + } + + case JT_DOUBLE: + { + double value; + def->getValue(obj, &ti, &value, &szlen); + Buffer_AppendDoubleUnchecked (buffer, value); + break; + } + + case JT_UTF8: + { + char *value; + value = (char *) def->getValue(obj, &ti, &value, &szlen); + Buffer_AppendEscape(buffer, value, szlen); + break; + } + } + + if (ti.release) + { + def->releaseValue(&ti); + } +} + +char *JSON_EncodeObject(JSOBJ obj, JSONObjectEncoder *def, char *_buffer, size_t _cbBuffer) +{ + Buffer buffer; + + buffer.malloc = def->malloc ? def->malloc : malloc; + buffer.free = def->free ? def->free : free; + buffer.realloc = def->realloc ? def->realloc : realloc; + + if (_buffer == NULL) + { + _cbBuffer = 32768; + buffer.start = (char *) buffer.malloc (_cbBuffer); + buffer.heap = 1; + } + else + { + buffer.start = _buffer; + buffer.heap = 0; + } + + buffer.end = buffer.start + _cbBuffer; + buffer.offset = buffer.start; + + encode(NULL, 0, 0, obj, def, &buffer, 0); + + Buffer_Append(&buffer, "\0", 1); + //return buffer.start; + return buffer.start; +} \ No newline at end of file diff --git a/ultrajson.h b/ultrajson.h new file mode 100644 index 0000000..a7da368 --- /dev/null +++ b/ultrajson.h @@ -0,0 +1,249 @@ +/* +Copyright (c) 2011, Jonas Tarnstrom and ESN Social Software AB +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + This product includes software developed by ESN Social Software AB (www.esn.me). +4. Neither the name of the ESN Social Software AB nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY ESN SOCIAL SOFTWARE AB ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALLESN SOCIAL SOFTWARE AB BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* +Ultra fast JSON encoder +Developed by Jonas Tärnström (jonas@esn.me). + +Notes: + +:: Float poing valus :: +All floating point values are converted as doubles using an optimized algorithm which is close but not entirely IEEE complaint. +Known issues with floating point to string conversion: +x) NaN, -Inf or Inf are not supported +x) Exponents are not supported +x) Max 10 decimals are converted +x) Result might differ in general from IEEE complaint conversion + +:: Strings :: +Characters are assumed to be 1 octet and be ASCII < 127. This makes ISO-8859-* or UTF8 suitable as input data. + +The following characters are escaped: +\" +\\ +\/ +\b +\f +\n +\r +\t + +All other characters are accepted making control characters harmful if present in the string octet stream. +The JSON '\uXXXX' conversion is not implemented + +:: Integers :: +All integers are converted as signed 64-bit. See JSLONG + +How to implement: + +1. Add ultrajson.c and ultrajson.h to your project or makefile +2. Fill out JSONObjectEncoder +3. Call JSON_EncodeObject with root object and a suitable buffer size + +Encoding in details: + +1. encode(obj): + 1. call getType(obj) + 2. if JT_ARRAY: + call iterBegin + while iterNext + call iterGetValue + call encode(value) + if ti->release is 1: + call releaseValue + call iterEnd + + 3. if JT_OBJECT: + call iterBegin + while iterNext + call iterGetName + call iterGetValue + call encode (value, name) + call iterEnd + + 4. if JT_*: + call getValue + writeOutput + + 5. if ti->release is 1: + call releaseValue + +*/ + +#ifndef __ULTRAJSON_H__ +#define __ULTRAJSON_H__ + +#include + +#ifndef JSON_DOUBLE_MAX_DECIMALS +#define JSON_DOUBLE_MAX_DECIMALS 10 +#endif + +#ifdef _WIN32 +typedef __int64 JSLONG; +typedef unsigned __int64 JSULONG; +#else +#include +typedef int64_t JSLONG; +typedef u_int64_t JSULONG; +#endif + +enum JSTYPES +{ + JT_NULL, // NULL + JT_TRUE, //boolean true + JT_FALSE, //boolean false + JT_INTEGER, //(JSLONG (signed 64-bit)) + JT_DOUBLE, //(double) + JT_UTF8, //(char) + JT_ARRAY, // Array structure + JT_OBJECT, // Key/Value structure +}; + +typedef void * JSOBJ; +typedef void * JSITER; + +typedef struct __JSTYPEINFO +{ + // If release=1, the releaseValue function will be called with the JSTYPEINFO as argument + int release; + // Type of value see JSTYPES + int type; + // Private fields to be used by the implementor for state keeping, life cycle etc + void *prv[32]; +} JSTYPEINFO; + +/* +Function pointer declarations, suitable for implementing Ultra JSON */ +typedef void (*JSPFN_ITERBEGIN)(JSOBJ obj, JSTYPEINFO *ti); +typedef int (*JSPFN_ITERNEXT)(JSOBJ obj, JSTYPEINFO *ti); +typedef void (*JSPFN_ITEREND)(JSOBJ obj, JSTYPEINFO *ti); +typedef JSOBJ (*JSPFN_ITERGETVALUE)(JSOBJ obj, JSTYPEINFO *ti); +typedef char *(*JSPFN_ITERGETNAME)(JSOBJ obj, JSTYPEINFO *ti, size_t *outLen); +typedef void *(*JSPFN_MALLOC)(size_t size); +typedef void (*JSPFN_FREE)(void *pptr); +typedef void *(*JSPFN_REALLOC)(void *base, size_t size); + +typedef struct __JSONObjectEncoder +{ + /* + Return type of object as JSTYPES enum + Implementors should setup necessary pointers or state in ti->prv + */ + void (*getType)(JSOBJ obj, JSTYPEINFO *ti); + + /* + Get value of object of a specific type + + JT_NULL : getValue is never called for this type + JT_TRUE : getValue is never called for this type + JT_FALSE : getValue is never called for this type + JT_ARRAY : getValue is never called for this type + JT_OBJECT : getValue is never called for this type + JT_INTEGER, : return NULL, outValue points to a "JSLONG" (64-bit signed), _outLen is ignored + JT_DOUBLE, : return NULL, outValue points to a "double", _outLen is ignored + JT_BOOLEAN, : return NULL, outValue points to an "int", _outLen is ignored + + JT_UTF8, : + return pointer to the string buffer + outValue is ignored + set _outLen to length of returned string buffer (in bytes without trailing '\0') + + If it's required that returned resources are freed or released, set ti->release to 1 and releaseValue will be called with JSTYPEINFO as argument. + Use ti->prv fields to store state for this + */ + void *(*getValue)(JSOBJ obj, JSTYPEINFO *ti, void *outValue, size_t *_outLen); + + /* + Begin iteration of an iteratable object (JS_ARRAY or JS_OBJECT) + Implementor should setup iteration state in ti->prv + */ + JSPFN_ITERBEGIN iterBegin; + + /* + Retrieve next object in an iteration. Should return 0 to indicate iteration has reached end or 1 if there are more items. + Implementor is responsible for keeping state of the iteration. Use ti->prv fields for this + */ + JSPFN_ITERNEXT iterNext; + + /* + Ends the iteration of an iteratable object. + Any iteration state stored in ti->prv can be freed here + */ + JSPFN_ITEREND iterEnd; + + /* + Returns a reference to the value object of an iterator + The is responsible for the life-cycle of the returned string. Use iterNext/iterEnd and ti->prv to keep track of current object + */ + JSPFN_ITERGETVALUE iterGetValue; + + /* + Return name of iterator. + The is responsible for the life-cycle of the returned string. Use iterNext/iterEnd and ti->prv to keep track of current object + */ + JSPFN_ITERGETNAME iterGetName; + + /* + Release a value as indicated by setting ti->release = 1 in the previous getValue call. + The ti->prv array should contain the necessary context to release the value + */ + void (*releaseValue)(JSTYPEINFO *ti); + + /* Library functions + Set to NULL to use STDLIB malloc,realloc,free */ + JSPFN_MALLOC malloc; + JSPFN_REALLOC realloc; + JSPFN_FREE free; + +} JSONObjectEncoder; + +/* +Encode an object structure into JSON. + +Arguments: +obj - An anonymous type representing the object +def - Function definitions for querying JSOBJ type +buffer - Preallocated buffer to store result in. If NULL function allocates own buffer +cbBuffer - Length of buffer (ignored if buffer is NULL) + +Returns: +Encoded JSON object as a null terminated char string. + +NOTE: +If the supplied buffer wasn't enough to hold the result the function will allocate a new buffer. +Life cycle of the provided buffer must still be handled by caller. + +If the return value doesn't equal the specified buffer caller must release the memory using +JSONObjectEncoder.free or free() as specified when calling this function. +*/ + __declspec(dllexport) char *JSON_EncodeObject(JSOBJ obj, JSONObjectEncoder *def, char *buffer, size_t cbBuffer); + +#endif \ No newline at end of file