[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/atomx/python-rapidjson/master/python-rapidjson/rapidjson.cpp [Back]  [Original]

#include 
#include 

#include 
#include 
#include 
#include 

#include "rapidjson/reader.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/error/en.h"

#include "docstrings.h"
#include "version.h"

using namespace rapidjson;


static PyObject* rapidjson_decimal_type = NULL;
static PyObject* rapidjson_timezone_type = NULL;
static PyObject* rapidjson_timezone_utc = NULL;

struct HandlerContext {
    PyObject* object;
    const char* key;
    SizeType keyLength;
    bool isObject;
};

enum DatetimeMode {
    DATETIME_MODE_NONE = 0,
    DATETIME_MODE_ISO8601 = 1,
    DATETIME_MODE_ISO8601_IGNORE_TZ = 2,
    DATETIME_MODE_ISO8601_UTC = 3
};

static int
days_per_month(int year, int month) {
    if (month == 1 || month == 3 || month == 5 || month == 7
        || month == 8 || month == 10 || month == 12)
        return 31;
    else if (month == 4 || month == 6 || month == 9 || month == 11)
        return 30;
    else if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
        return 29;
    else
        return 28;
}

struct PyHandler {
    int useDecimal;
    int allowNan;
    PyObject* root;
    PyObject* objectHook;
    DatetimeMode datetimeMode;
    std::vector stack;

    PyHandler(int ud, PyObject* hook, int an, DatetimeMode dm)
    : useDecimal(ud), allowNan(an), root(NULL), objectHook(hook), datetimeMode(dm)
    {
        stack.reserve(128);
    }

    bool Handle(PyObject* value) {
        if (root) {
            const HandlerContext& current = stack.back();

            if (current.isObject) {
                PyObject* key = PyUnicode_FromStringAndSize(current.key, current.keyLength);
                if (key == NULL) {
                    Py_DECREF(value);
                    return false;
                }

                int rc = PyDict_SetItem(current.object, key, value);
                Py_DECREF(key);
                Py_DECREF(value);

                if (rc == -1) {
                    return false;
                }
            }
            else {
                PyList_Append(current.object, value);
                Py_DECREF(value);
            }
        }
        else {
            root = value;
        }
        return true;
    }

    bool HandleSimpleType(PyObject* value) {
        if (!Handle(value)) {
            Py_DECREF(value);
            return false;
        }

        return true;
    }

    bool Key(const char* str, SizeType length, bool copy) {
        HandlerContext& current = stack.back();
        current.key = str;
        current.keyLength = length;

        return true;
    }

    bool StartObject() {
        PyObject* dict = PyDict_New();
        if (dict == NULL) {
            return false;
        }

        if (!Handle(dict)) {
            Py_DECREF(dict);
            return false;
        }

        HandlerContext ctx;
        ctx.isObject = true;
        ctx.object = dict;

        stack.push_back(ctx);

        return true;
    }

    bool EndObject(SizeType member_count) {
        if (!objectHook) {
            stack.pop_back();
            return true;
        }

        PyObject* dict = stack.back().object;
        stack.pop_back();

        PyObject* value = PyObject_CallFunctionObjArgs(objectHook, dict, NULL);
        // In case the dict is returned from the function.
        if (value == dict)
            Py_INCREF(value);

        if (value == NULL)
            return false;

        if (!stack.empty()) {
            const HandlerContext& current = stack.back();

            if (current.isObject) {
                PyObject* key = PyUnicode_FromStringAndSize(current.key, current.keyLength);
                if (key == NULL) {
                    Py_DECREF(value);
                    return false;
                }

                int rc = PyDict_SetItem(current.object, key, value);
                Py_DECREF(key);
                Py_DECREF(value);

                if (rc == -1)
                    return false;
            }
            else {
                Py_ssize_t listLen = PyList_GET_SIZE(current.object);
                int rc = PyList_SetItem(current.object, listLen - 1, value);

                if (rc == -1) {
                    Py_DECREF(value);
                    return false;
                }
            }
        }
        else {
            Py_DECREF(root);
            root = value;
        }

        Py_DECREF(dict);
        return true;
    }

    bool StartArray() {
        PyObject* list = PyList_New(0);
        if (list == NULL) {
            return false;
        }

        if (!Handle(list)) {
            Py_DECREF(list);
            return false;
        }

        HandlerContext ctx;
        ctx.isObject = false;
        ctx.object = list;

        stack.push_back(ctx);

        return true;
    }

    bool EndArray(SizeType elementCount) {
        stack.pop_back();
        return true;
    }

    bool NaN() {
        if (!allowNan) {
            PyErr_SetString(PyExc_ValueError, "Out of range float values are not JSON compliant");
            return false;
        }

        PyObject* str = PyUnicode_FromStringAndSize("nan", 3);
        if (str == NULL)
            return false;

        PyObject* value;
        if (!useDecimal)
            value = PyFloat_FromString(str);
        else
            value = PyObject_CallFunctionObjArgs(rapidjson_decimal_type, str, NULL);

        Py_DECREF(str);

        if (value == NULL)
            return false;

        return HandleSimpleType(value);
    }

    bool Infinity(bool minus) {
        if (!allowNan) {
            PyErr_SetString(PyExc_ValueError, "Out of range float values are not JSON compliant");
            return false;
        }

        PyObject* str;

        if (minus)
            str = PyUnicode_FromStringAndSize("-Infinity", 9);
        else
            str = PyUnicode_FromStringAndSize("Infinity", 8);

        if (str == NULL)
            return false;

        PyObject* value;
        if (!useDecimal)
            value = PyFloat_FromString(str);
        else
            value = PyObject_CallFunctionObjArgs(rapidjson_decimal_type, str, NULL);

        Py_DECREF(str);

        if (value == NULL)
            return false;

        return HandleSimpleType(value);
    }

    bool Null() {
        PyObject* value = Py_None;
        Py_INCREF(value);

        return HandleSimpleType(value);
    }

    bool Bool(bool b) {
        PyObject* value = b ? Py_True : Py_False;
        Py_INCREF(value);

        return HandleSimpleType(value);
    }

    bool Int(int i) {
        PyObject* value = PyLong_FromLong(i);
        return HandleSimpleType(value);
    }

    bool Uint(unsigned i) {
        PyObject* value = PyLong_FromUnsignedLong(i);
        return HandleSimpleType(value);
    }

    bool Int64(int64_t i) {
        PyObject* value = PyLong_FromLongLong(i);
        return HandleSimpleType(value);
    }

    bool Uint64(uint64_t i) {
        PyObject* value = PyLong_FromUnsignedLongLong(i);
        return HandleSimpleType(value);
    }

    bool Double(double d, const char* decimal, SizeType length, bool minus, size_t decimalPosition, int exp) {
        PyObject* value;
        if (!useDecimal)
            value = PyFloat_FromDouble(d);
        else {
            const int MAX_FINAL_SIZE = 512;
            const int MAX_EXP_SIZE = 11; // 32-bit
            const int MAX_DECIMAL_SIZE = MAX_FINAL_SIZE - MAX_EXP_SIZE - 3; // e, +/-, \0

            exp += decimalPosition - length;

            if (length > MAX_DECIMAL_SIZE)
                length = MAX_DECIMAL_SIZE;

            char finalStr[MAX_DECIMAL_SIZE];
            finalStr[0] = minus ? '-' : '+';
            memcpy(finalStr+1, decimal, length);

            if (exp == 0)
                finalStr[length+1] = 0;
            else {
                char expStr[MAX_EXP_SIZE];
                char* end = internal::i32toa(exp, expStr);
                size_t len = end - expStr;

                finalStr[length+1] = 'e';
                memcpy(finalStr+length+2, expStr, len);
                finalStr[length+2+len] = 0;
            }

            PyObject* raw = PyUnicode_FromString(finalStr);
            if (raw == NULL) {
                PyErr_SetString(PyExc_ValueError, "Error generating decimal representation");
                return false;
            }

            value = PyObject_CallFunctionObjArgs(rapidjson_decimal_type, raw, NULL);
            Py_DECREF(raw);
        }

        return HandleSimpleType(value);
    }

#define digit(idx) (str[idx] - '0')

    bool IsIso8601(const char* str, SizeType length) {
        bool res;
        int hours = 0, mins = 0, secs = 0;
        int year = -1, month = 0, day = 0;
        int hofs = 0, mofs = 0;

        switch(length) {
        case 8:                     /* 20:02:20 */
        case 9:                     /* 20:02:20Z */
        case 12:                    /* 20:02:20.123 */
        case 13:                    /* 20:02:20.123Z */
        case 14:                    /* 20:02:20-05:00 */
        case 15:                    /* 20:02:20.123456 */
        case 16:                    /* 20:02:20.123456Z */
        case 18:                    /* 20:02:20.123-05:00 */
        case 21:                    /* 20:02:20.123456-05:00 */
            res = (str[2] == ':' && str[5] == ':' &&
                   isdigit(str[0]) && isdigit(str[1]) &&
                   isdigit(str[3]) && isdigit(str[4]) &&
                   isdigit(str[6]) && isdigit(str[7]));
            if (res) {
                hours = digit(0)*10 + digit(1);
                mins = digit(3)*10 + digit(4);
                secs = digit(6)*10 + digit(7);

                if (length == 9)
                    res = str[8] == 'Z';
                else if (length == 14)
                    res = str[8] == '-';
                else if (length > 8)
                    res = str[8] == '.';
                if (res && (length == 13 || length == 16)) {
                    res = str[length-1] == 'Z';
                    length--;
                }
                if (res && (length == 14 || length == 18 || length == 21)) {
                    res = ((str[length-6] == '+' || str[length-6] == '-') &&
                           isdigit(str[length-5]) &&
                           isdigit(str[length-4]) &&
                           str[length-3] == ':' &&
                           isdigit(str[length-2]) &&
                           isdigit(str[length-1]));
                    if (res) {
                        hofs = digit(length-5)*10 + digit(length-4);
                        mofs = digit(length-2)*10 + digit(length-1);
                    }
                    length -= 6;
                }
                if (res && length > 9 && length != 14) {
                    res = isdigit(str[9]) && isdigit(str[10]) && isdigit(str[11]);
                    if (res && length > 12)
                        res = isdigit(str[12]) && isdigit(str[13]) && isdigit(str[14]);
                }
            }
            break;

        case 10:                    /* 1999-02-03 */
        case 19:                    /* 1999-02-03T10:20:30 */
        case 20:                    /* 1999-02-03T10:20:30Z */
        case 23:                    /* 1999-02-03T10:20:30.123 */
        case 24:                    /* 1999-02-03T10:20:30.123Z */
        case 25:                    /* 1999-02-03T10:20:30-05:00 */
        case 26:                    /* 1999-02-03T10:20:30.123456 */
        case 27:                    /* 1999-02-03T10:20:30.123456Z */
        case 29:                    /* 1999-02-03T10:20:30.123-05:00 */
        case 32:                    /* 1999-02-03T10:20:30.123456-05:00 */
            res = (str[4] == '-' && str[7] == '-' &&
                   isdigit(str[0]) && isdigit(str[1]) &&
                   isdigit(str[2]) && isdigit(str[3]) &&
                   isdigit(str[5]) && isdigit(str[6]) &&
                   isdigit(str[8]) && isdigit(str[9]));
            if (res) {
                year = digit(0)*1000
                    + digit(1)*100
                    + digit(2)*10
                    + digit(3);
                month = digit(5)*10 + digit(6);
                day = digit(8)*10 + digit(9);
            }
            if (res && length > 10) {
                if (str[10] == ' ' || str[10] == 'T') {
                    res = (str[13] == ':' && str[16] == ':' &&
                           isdigit(str[11]) && isdigit(str[12]) &&
                           isdigit(str[14]) && isdigit(str[15]) &&
                           isdigit(str[17]) && isdigit(str[18]));
                    if (res) {
                        hours = digit(11)*10 + digit(12);
                        mins = digit(14)*10 + digit(15);
                        secs = digit(17)*10 + digit(18);
                        if (length == 25 || length == 29 || length == 32) {
                            res = ((str[length-6] == '+' || str[length-6] == '-') &&
                                   isdigit(str[length-5]) &&
                                   isdigit(str[length-4]) &&
                                   str[length-3] == ':' &&
                                   isdigit(str[length-2]) &&
                                   isdigit(str[length-1]));
                            if (res) {
                                hofs = digit(length-5)*10 + digit(length-4);
                                mofs = digit(length-2)*10 + digit(length-1);
                            }
                            length -= 6;
                        }
                        if (res && (length == 20 || length == 24 || length == 27)) {
                            res = str[length-1] == 'Z';
                            length--;
                        }
                        if (res && length == 23)
                            res = (str[19] == '.' &&
                                   isdigit(str[20]) &&
                                   isdigit(str[21]) &&
                                   isdigit(str[22]));
                        else if (res && length == 26)
                            res = (str[19] == '.' &&
                                   isdigit(str[20]) &&
                                   isdigit(str[21]) &&
                                   isdigit(str[22]) &&
                                   isdigit(str[23]) &&
                                   isdigit(str[24]) &&
                                   isdigit(str[25]));
                    }
                } else
                    res = false;
            }
            break;

        default:
            res = false;
            break;
        }

        if (res && (hours > 23 || mins > 59 || secs > 59
                    || year == 0 || month > 12
                    || day > days_per_month(year, month)
                    || hofs > 23 || mofs > 59))
            res = false;

        return res;
    }

    bool HandleIso8601(const char* str, SizeType length) {
        PyObject *value;
        int hours, mins, secs, usecs;
        int year, month, day;

        switch(length) {
        case 8:                     /* 20:02:20 */
        case 9:                     /* 20:02:20Z */
        case 12:                    /* 20:02:20.123 */
        case 13:                    /* 20:02:20.123Z */
        case 14:                    /* 20:02:20-05:00 */
        case 15:                    /* 20:02:20.123456 */
        case 16:                    /* 20:02:20.123456Z */
        case 18:                    /* 20:02:20.123-05:00 */
        case 21:                    /* 20:02:20.123456-05:00 */
            hours = digit(0)*10 + digit(1);
            mins = digit(3)*10 + digit(4);
            secs = digit(6)*10 + digit(7);
            if (length == 8 || length == 9 || length == 14)
                usecs = 0;
            else {
                usecs = digit(9)*100000
                    + digit(10) * 10000
                    + digit(11) * 1000;
                if (length == 15 || length == 16 || length == 21)
                    usecs += digit(12)*100
                        + digit(13) * 10
                        + digit(14);
            }
            if (datetimeMode == DATETIME_MODE_ISO8601_IGNORE_TZ
                || length == 8 || length == 12 || length == 15)
                value = PyTime_FromTime(hours, mins, secs, usecs);
            else if (length == 9 || length == 13 || length == 16)
                value = PyDateTimeAPI->Time_FromTime(
                    hours, mins, secs, usecs, rapidjson_timezone_utc,
                    PyDateTimeAPI->TimeType);
            else /* if (length == 14 || length == 18 || length == 21) */ {
                int secsoffset = ((digit(length-5)*10 + digit(length-4)) * 3600
                                  + (digit(length-2)*10 + digit(length-1)) * 60);
                if (str[length-6] == '-')
                    secsoffset = -secsoffset;
                PyObject* offset = PyDateTimeAPI->Delta_FromDelta(
                    0, secsoffset, 0, 1, PyDateTimeAPI->DeltaType);
                if (offset == NULL)
                    value = NULL;
                else {
                    PyObject* tz = PyObject_CallFunctionObjArgs(
                        rapidjson_timezone_type, offset, NULL);
                    Py_DECREF(offset);
                    if (tz == NULL)
                        value = NULL;
                    else {
                        value = PyDateTimeAPI->Time_FromTime(
                            hours, mins, secs, usecs, tz,
                            PyDateTimeAPI->TimeType);
                        Py_DECREF(tz);

                        if (value != NULL && datetimeMode == DATETIME_MODE_ISO8601_UTC) {
                            PyObject* asUTC = PyObject_CallMethod(value, "astimezone", "O",
                                                                  rapidjson_timezone_utc);

                            Py_DECREF(value);

                            if (asUTC == NULL)
                                value = NULL;
                            else
                                value = asUTC;
                        }
                    }
                }
            }
            break;

        case 10:                    /* 1999-02-03 */
            year = digit(0)*1000
                + digit(1)*100
                + digit(2)*10
                + digit(3);
            month = digit(5)*10 + digit(6);
            day = digit(8)*10 + digit(9);
            value = PyDate_FromDate(year, month, day);
            break;

        case 19:                    /* 1999-02-03T10:20:30 */
        case 20:                    /* 1999-02-03T10:20:30Z */
        case 23:                    /* 1999-02-03T10:20:30.123 */
        case 24:                    /* 1999-02-03T10:20:30.123Z */
        case 25:                    /* 1999-02-03T10:20:30-05:00 */
        case 26:                    /* 1999-02-03T10:20:30.123456 */
        case 27:                    /* 1999-02-03T10:20:30.123456Z */
        case 29:                    /* 1999-02-03T10:20:30.123-05:00 */
        case 32:                    /* 1999-02-03T10:20:30.123456-05:00 */
            year = digit(0)*1000
                + digit(1)*100
                + digit(2)*10
                + digit(3);
            month = digit(5)*10 + digit(6);
            day = digit(8)*10 + digit(9);
            hours = digit(11)*10 + digit(12);
            mins = digit(14)*10 + digit(15);
            secs = digit(17)*10 + digit(18);
            if (length == 19 || length == 20 || length == 25)
                usecs = 0;
            else {
                usecs = digit(20)*100000
                    + digit(21)*10000
                    + digit(22)*1000;
                if (length == 26 || length == 27 || length == 32)
                    usecs += digit(23)*100
                        + digit(24)*10
                        + digit(25);
            }
            if (datetimeMode == DATETIME_MODE_ISO8601_IGNORE_TZ
                || length == 19 || length == 23 || length == 26)
                value = PyDateTime_FromDateAndTime(
                    year, month, day, hours, mins, secs, usecs);
            else if (length == 20 || length == 24 || length == 27)
                value = PyDateTimeAPI->DateTime_FromDateAndTime(
                    year, month, day, hours, mins, secs, usecs,
                    rapidjson_timezone_utc, PyDateTimeAPI->DateTimeType);
            else /* if (length == 25 || length == 29 || length == 32) */ {
                int secsoffset = ((digit(length-5)*10 + digit(length-4)) * 3600
                                  + (digit(length-2)*10 + digit(length-1)) * 60);
                if (str[length-6] == '-')
                    secsoffset = -secsoffset;
                PyObject* offset = PyDateTimeAPI->Delta_FromDelta(
                    0, secsoffset, 0, 1, PyDateTimeAPI->DeltaType);
                if (offset == NULL)
                    value = NULL;
                else {
                    PyObject* tz = PyObject_CallFunctionObjArgs(
                        rapidjson_timezone_type, offset, NULL);
                    Py_DECREF(offset);
                    if (tz == NULL)
                        value = NULL;
                    else {
                        value = PyDateTimeAPI->DateTime_FromDateAndTime(
                            year, month, day, hours, mins, secs, usecs,
                            tz, PyDateTimeAPI->DateTimeType);
                        Py_DECREF(tz);

                        if (value != NULL && datetimeMode == DATETIME_MODE_ISO8601_UTC) {
                            PyObject* asUTC = PyObject_CallMethod(value, "astimezone", "O",
                                                                  rapidjson_timezone_utc);

                            Py_DECREF(value);

                            if (asUTC == NULL)
                                value = NULL;
                            else
                                value = asUTC;
                        }
                    }
                }
            }
            break;

        default:
            PyErr_SetString(PyExc_ValueError,
                            "not a datetime, nor a date, nor a time");
            value = NULL;
            break;
        }

        if (value == NULL)
            return false;
        else
            return HandleSimpleType(value);
    }

#undef digit

    bool String(const char* str, SizeType length, bool copy) {
        if (datetimeMode != DATETIME_MODE_NONE && IsIso8601(str, length))
            return HandleIso8601(str, length);
        else {
            PyObject* value = PyUnicode_FromStringAndSize(str, length);
            return HandleSimpleType(value);
        }
    }
};

static PyObject*
rapidjson_loads(PyObject* self, PyObject* args, PyObject* kwargs)
{
    /* Converts a JSON encoded string to a Python object. */

    PyObject* jsonObject;
    PyObject* objectHook = NULL;
    int useDecimal = 0;
    int preciseFloat = 1;
    int allowNan = 1;
    PyObject* datetimeModeObj = NULL;
    DatetimeMode datetimeMode = DATETIME_MODE_NONE;

    static char* kwlist[] = {
        "s",
        "object_hook",
        "use_decimal",
        "precise_float",
        "allow_nan",
        "datetime_mode",
        NULL
    };

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OpppO:rapidjson.loads",
                                     kwlist,
                                     &jsonObject,
                                     &objectHook,
                                     &useDecimal,
                                     &preciseFloat,
                                     &allowNan,
                                     &datetimeModeObj))

    if (objectHook && !PyCallable_Check(objectHook)) {
        PyErr_SetString(PyExc_TypeError, "object_hook is not callable");
        return NULL;
    }

    Py_ssize_t jsonStrLen;
    char* jsonStr;

    if (PyBytes_Check(jsonObject)) {
        int rc = PyBytes_AsStringAndSize(jsonObject, &jsonStr, &jsonStrLen);
        if (rc == -1)
            return NULL;
    }
    else if (PyUnicode_Check(jsonObject)) {
        jsonStr = PyUnicode_AsUTF8AndSize(jsonObject, &jsonStrLen);
        if (jsonStr == NULL)
            return NULL;
    }
    else {
        PyErr_SetString(PyExc_TypeError, "Expected string or utf-8 encoded bytes");
        return NULL;
    }

    if (datetimeModeObj && PyLong_Check(datetimeModeObj)) {
        datetimeMode = (DatetimeMode) PyLong_AsLong(datetimeModeObj);
        if (datetimeMode < DATETIME_MODE_NONE || datetimeMode > DATETIME_MODE_ISO8601_UTC) {
            PyErr_SetString(PyExc_ValueError, "Invalid date_time");
            return NULL;
        }
    }

    char* jsonStrCopy = (char*) malloc(sizeof(char) * (jsonStrLen+1));
    memcpy(jsonStrCopy, jsonStr, jsonStrLen+1);

    PyHandler handler(useDecimal, objectHook, allowNan, datetimeMode);
    Reader reader;
    InsituStringStream ss(jsonStrCopy);

    if (preciseFloat)
        reader.Parse(ss, handler);
    else
        reader.Parse(ss, handler);

    if (reader.HasParseError()) {
        SizeType offset = reader.GetErrorOffset();
        ParseErrorCode code = reader.GetParseErrorCode();
        const char* msg = GetParseError_En(code);
        const char* fmt = "Parse error at offset %d: %s";

        if (PyErr_Occurred()) {
            PyObject* etype;
            PyObject* evalue;
            PyObject* etraceback;
            PyErr_Fetch(&etype, &evalue, &etraceback);

            const char* emsg = msg;
            if (PyUnicode_Check(evalue))
                emsg = PyUnicode_AsUTF8(evalue);

            PyErr_Format(etype, fmt, offset, emsg);
        }
        else
            PyErr_Format(PyExc_ValueError, fmt, offset, msg);

        Py_XDECREF(handler.root);
        free(jsonStrCopy);
        return NULL;
    }

    free(jsonStrCopy);
    return handler.root;
}

struct WriterContext {
    const char* key;
    PyObject* object;
    PyObject* decref;
    unsigned level;
    bool isObject;

    WriterContext(const char* k, PyObject* o, bool isO, int l, PyObject* d=NULL)
    : key(k), object(o), decref(d), level(l), isObject(isO)
    {}
};

struct DictItem {
    char* key_str;
    PyObject* item;

    DictItem(char* k, PyObject* i)
    : key_str(k), item(i)
    {}

    bool operatorkey_str) < 0;
    }
};

static const int MAX_RECURSION_DEPTH = 2048;


template
static PyObject*
rapidjson_dumps_internal(
    WriterT* writer,
    BufferT* buf,
    PyObject* value,
    int skipKeys,
    int allowNan,
    PyObject* defaultFn,
    int sortKeys,
    int useDecimal,
    unsigned maxRecursionDepth,
    DatetimeMode datetimeMode)
{
    int isDec;
    std::vector stack;
    stack.reserve(128);

    stack.push_back(WriterContext(NULL, value, false, 0));

    while (!stack.empty()) {
        const WriterContext& current = stack.back();
        stack.pop_back();

        const unsigned currentLevel = current.level;
        const unsigned nextLevel = current.level + 1;
        PyObject* const object = current.object;

        if (currentLevel > maxRecursionDepth) {
            PyErr_SetString(PyExc_OverflowError, "Max recursion depth reached");
            return NULL;
        }

        if (object == NULL) {
            if (current.key != NULL) {
                writer->Key(current.key);
            }
            else if (current.decref != NULL) {
                Py_DECREF(current.decref);
            }
            else if (current.isObject) {
                writer->EndObject();
            }
            else {
                writer->EndArray();
            }
        }
        else if (object == Py_None) {
            writer->Null();
        }
        else if (PyBool_Check(object)) {
            writer->Bool(object == Py_True);
        }
        else if (useDecimal && (isDec = PyObject_IsInstance(object, rapidjson_decimal_type))) {
            if (isDec == -1)
                return NULL;

            PyObject* decStrObj = PyObject_Str(object);
            if (decStrObj == NULL)
                return NULL;

            Py_ssize_t size;
            char* decStr = PyUnicode_AsUTF8AndSize(decStrObj, &size);
            if (decStr == NULL) {
                Py_DECREF(decStrObj);
                return NULL;
            }

            writer->RawNumber(decStr, size);
            Py_DECREF(decStrObj);
        }
        else if (PyLong_Check(object)) {
            int overflow;
            long long i = PyLong_AsLongLongAndOverflow(object, &overflow);
            if (i == -1 && PyErr_Occurred())
                return NULL;

            if (overflow == 0) {
                writer->Int64(i);
            } else {
                unsigned long long ui = PyLong_AsUnsignedLongLong(object);
                if (PyErr_Occurred())
                    return NULL;

                writer->Uint64(ui);
            }
        }
        else if (PyFloat_Check(object)) {
            double d = PyFloat_AsDouble(object);
            if (d == -1.0 && PyErr_Occurred())
                return NULL;

            if (Py_IS_NAN(d)) {
                if (allowNan)
                    writer->RawNumber("NaN", 3);
                else {
                    PyErr_SetString(PyExc_ValueError, "Out of range float values are not JSON compliant");
                    return NULL;
                }
            } else if (Py_IS_INFINITY(d)) {
                if (!allowNan) {
                    PyErr_SetString(PyExc_ValueError, "Out of range float values are not JSON compliant");
                    return NULL;
                }
                else if (d < 0)
                    writer->RawNumber("-Infinity", 9);
                else
                    writer->RawNumber("Infinity", 8);
            }
            else
                writer->Double(d);
        }
        else if (PyBytes_Check(object)) {
            char* s = PyBytes_AsString(object);
            if (s == NULL)
                goto error;

            writer->String(s);
        }
        else if (PyUnicode_Check(object)) {
            char* s = PyUnicode_AsUTF8(object);
            writer->String(s);
        }
        else if (PyList_Check(object)) {
            writer->StartArray();
            stack.push_back(WriterContext(NULL, NULL, false, currentLevel));

            Py_ssize_t size = PyList_GET_SIZE(object);

            for (Py_ssize_t i = size - 1; i >= 0; --i) {
                PyObject* item = PyList_GET_ITEM(object, i);
                stack.push_back(WriterContext(NULL, item, false, nextLevel));
            }
        }
        else if (PyTuple_Check(object)) {
            writer->StartArray();
            stack.push_back(WriterContext(NULL, NULL, false, currentLevel));

            Py_ssize_t size = PyTuple_GET_SIZE(object);

            for (Py_ssize_t i = size - 1; i >= 0; --i) {
                PyObject* item = PyTuple_GET_ITEM(object, i);
                stack.push_back(WriterContext(NULL, item, false, nextLevel));
            }
        }
        else if (PyDict_Check(object)) {
            writer->StartObject();
            stack.push_back(WriterContext(NULL, NULL, true, currentLevel));

            Py_ssize_t pos = 0;
            PyObject* key;
            PyObject* item;

            if (!sortKeys) {
                while (PyDict_Next(object, &pos, &key, &item)) {
                    if (PyUnicode_Check(key)) {
                        char* key_str = PyUnicode_AsUTF8(key);
                        stack.push_back(WriterContext(NULL, item, false, nextLevel));
                        stack.push_back(WriterContext(key_str, NULL, false, nextLevel));
                    }
                    else if (!skipKeys) {
                        PyErr_SetString(PyExc_TypeError, "keys must be a string");
                        goto error;
                    }
                }
            }
            else {
                std::vector items;

                while (PyDict_Next(object, &pos, &key, &item)) {
                    if (PyUnicode_Check(key)) {
                        char* key_str = PyUnicode_AsUTF8(key);
                        items.push_back(DictItem(key_str, item));
                    }
                    else if (!skipKeys) {
                        PyErr_SetString(PyExc_TypeError, "keys must be a string");
                        goto error;
                    }
                }

                std::sort(items.begin(), items.end());

                std::vector::const_iterator iter = items.begin();
                for (; iter != items.end(); ++iter) {
                    stack.push_back(WriterContext(NULL, iter->item, false, nextLevel));
                    stack.push_back(WriterContext(iter->key_str, NULL, false, nextLevel));
                }
            }
        }
        else if (datetimeMode != DATETIME_MODE_NONE
                 && (PyTime_Check(object) || PyDateTime_Check(object))) {
            int year, month, day, hour, min, sec, microsec;
            PyObject* dtObject = object;
            PyObject* asUTC = NULL;

            const int ISOFORMAT_LEN = 40;
            char isoformat[ISOFORMAT_LEN];
            memset(isoformat, 0, ISOFORMAT_LEN);

            const int TIMEZONE_LEN = 10;
            char timezone[TIMEZONE_LEN];
            memset(timezone, 0, TIMEZONE_LEN);

            if (datetimeMode != DATETIME_MODE_ISO8601_IGNORE_TZ
                && PyObject_HasAttrString(object, "utcoffset")) {
                PyObject* utcOffset = PyObject_CallMethod(object, "utcoffset", NULL);

                if (!utcOffset)
                    goto error;

                if (utcOffset != Py_None) {
                    if (datetimeMode == DATETIME_MODE_ISO8601_UTC && PyObject_IsTrue(utcOffset)) {
                        asUTC = PyObject_CallMethod(object, "astimezone", "O", rapidjson_timezone_utc);

                        if (asUTC == NULL) {
                            Py_DECREF(utcOffset);
                            goto error;
                        }

                        dtObject = asUTC;
                        strcpy(timezone, "+00:00");
                    } else {
                        PyObject* daysObj = PyObject_GetAttrString(utcOffset, "days");
                        PyObject* secondsObj = PyObject_GetAttrString(utcOffset, "seconds");

                        if (daysObj && secondsObj) {
                            int days = PyLong_AsLong(daysObj);
                            int seconds = PyLong_AsLong(secondsObj);

                            int total_seconds = days * 24 * 3600 + seconds;

                            char sign = '+';
                            if (total_seconds < 0) {
                                sign = '-';
                                total_seconds = -total_seconds;
                            }

                            int tz_hour = total_seconds / 3600;
                            int tz_min = (total_seconds % 3600) / 60;

                            snprintf(timezone, TIMEZONE_LEN-1, "%c%02d:%02d", sign, tz_hour, tz_min);
                        }

                        Py_XDECREF(daysObj);
                        Py_XDECREF(secondsObj);
                    }
                }
                Py_DECREF(utcOffset);
            }

            if (PyDateTime_Check(dtObject)) {
                year = PyDateTime_GET_YEAR(dtObject);
                month = PyDateTime_GET_MONTH(dtObject);
                day = PyDateTime_GET_DAY(dtObject);
                hour = PyDateTime_DATE_GET_HOUR(dtObject);
                min = PyDateTime_DATE_GET_MINUTE(dtObject);
                sec = PyDateTime_DATE_GET_SECOND(dtObject);
                microsec = PyDateTime_DATE_GET_MICROSECOND(dtObject);
                if (microsec > 0) {
                    snprintf(isoformat,
                             ISOFORMAT_LEN-1,
                             "%04d-%02d-%02dT%02d:%02d:%02d.%06d%s",
                             year, month, day,
                             hour, min, sec, microsec,
                             timezone);
                } else {
                    snprintf(isoformat,
                             ISOFORMAT_LEN-1,
                             "%04d-%02d-%02dT%02d:%02d:%02d%s",
                             year, month, day,
                             hour, min, sec,
                             timezone);
                }
            } else {
                hour = PyDateTime_TIME_GET_HOUR(dtObject);
                min = PyDateTime_TIME_GET_MINUTE(dtObject);
                sec = PyDateTime_TIME_GET_SECOND(dtObject);
                microsec = PyDateTime_TIME_GET_MICROSECOND(dtObject);
                if (microsec > 0) {
                    snprintf(isoformat,
                             ISOFORMAT_LEN-1,
                             "%02d:%02d:%02d.%06d%s",
                             hour, min, sec, microsec,
                             timezone);
                } else {
                    snprintf(isoformat,
                             ISOFORMAT_LEN-1,
                             "%02d:%02d:%02d%s",
                             hour, min, sec,
                             timezone);
                }
            }
            Py_XDECREF(asUTC);
            writer->String(isoformat);
        }
        else if (datetimeMode != DATETIME_MODE_NONE && PyDate_Check(object)) {
            int year = PyDateTime_GET_YEAR(object);
            int month = PyDateTime_GET_MONTH(object);
            int day = PyDateTime_GET_DAY(object);

            const int ISOFORMAT_LEN = 12;
            char isoformat[ISOFORMAT_LEN];
            memset(isoformat, 0, ISOFORMAT_LEN);

            snprintf(isoformat, ISOFORMAT_LEN-1, "%04d-%02d-%02d", year, month, day);
            writer->String(isoformat);
        }
        else if (defaultFn) {
            PyObject* retval = PyObject_CallFunctionObjArgs(defaultFn, object, NULL);
            if (retval == NULL)
                goto error;

            // Decref the return value once it's done being dumped to a string.
            stack.push_back(WriterContext(NULL, NULL, false, currentLevel, retval));
            stack.push_back(WriterContext(NULL, retval, false, currentLevel));
        }
        else {
            PyObject* repr = PyObject_Repr(object);
            PyErr_Format(PyExc_TypeError, "%s is not JSON serializable", PyUnicode_AsUTF8(repr));
            Py_XDECREF(repr);
            goto error;
        }
    }

    return PyUnicode_FromString(buf->GetString());

error:
    return NULL;
}


#define RAPIDJSON_DUMPS_INTERNAL_CALL \
    rapidjson_dumps_internal( \
        &writer, \
        &buf, \
        value, \
        skipKeys, \
        allowNan, \
        defaultFn, \
        sortKeys, \
        useDecimal, \
        maxRecursionDepth, \
        datetimeMode)


static PyObject*
rapidjson_dumps(PyObject* self, PyObject* args, PyObject* kwargs)
{
    /* Converts a Python object to a JSON-encoded string. */

    PyObject* value;
    int skipKeys = 0;
    int ensureAscii = 1;
    int allowNan = 1;
    PyObject* indent = NULL;
    PyObject* defaultFn = NULL;
    int sortKeys = 0;
    int useDecimal = 0;
    unsigned maxRecursionDepth = MAX_RECURSION_DEPTH;
    PyObject* datetimeModeObj = NULL;
    DatetimeMode datetimeMode = DATETIME_MODE_NONE;

    bool prettyPrint = false;
    const char indentChar = ' ';
    unsigned char indentCharCount = 4;

    static char* kwlist[] = {
        "obj",
        "skipkeys",
        "ensure_ascii",
        "allow_nan",
        "indent",
        "default",
        "sort_keys",
        "use_decimal",
        "max_recursion_depth",
        "datetime_mode",
        NULL
    };
    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|pppOOppIO:rapidjson.dumps",
                                     kwlist,
                                     &value,
                                     &skipKeys,
                                     &ensureAscii,
                                     &allowNan,
                                     &indent,
                                     &defaultFn,
                                     &sortKeys,
                                     &useDecimal,
                                     &maxRecursionDepth,
                                     &datetimeModeObj))
        return NULL;

    if (defaultFn && !PyCallable_Check(defaultFn)) {
        PyErr_SetString(PyExc_TypeError, "default must be a callable");
        return NULL;
    }

    if (indent && indent != Py_None) {
        prettyPrint = true;

        if (PyLong_Check(indent)) {
            indentCharCount = PyLong_AsLong(indent);
        }
        else {
            PyErr_SetString(PyExc_TypeError, "indent must be an int");
            return NULL;
        }
    }

    if (datetimeModeObj && PyLong_Check(datetimeModeObj)) {
        datetimeMode = (DatetimeMode) PyLong_AsLong(datetimeModeObj);
        if (datetimeMode < DATETIME_MODE_NONE || datetimeMode > DATETIME_MODE_ISO8601_UTC) {
            PyErr_SetString(PyExc_ValueError, "Invalid date_time");
            return NULL;
        }
    }

    if (!prettyPrint) {
        if (ensureAscii) {
            GenericStringBuffer buf;
            Writer writer(buf);
            return RAPIDJSON_DUMPS_INTERNAL_CALL;
        }
        else {
            StringBuffer buf;
            Writer writer(buf);
            return RAPIDJSON_DUMPS_INTERNAL_CALL;
        }
    }
    else if (ensureAscii) {
        GenericStringBuffer buf;
        PrettyWriter writer(buf);
        writer.SetIndent(indentChar, indentCharCount);
        return RAPIDJSON_DUMPS_INTERNAL_CALL;
    }
    else {
        StringBuffer buf;
        PrettyWriter writer(buf);
        writer.SetIndent(indentChar, indentCharCount);
        return RAPIDJSON_DUMPS_INTERNAL_CALL;
    }
}


static PyMethodDef
rapidjson_functions[] = {
    {"loads", (PyCFunction) rapidjson_loads, METH_VARARGS | METH_KEYWORDS, rapidjson_loads_docstring},
    {"dumps", (PyCFunction) rapidjson_dumps, METH_VARARGS | METH_KEYWORDS, rapidjson_dumps_docstring},
    {NULL, NULL, 0, NULL} /* sentinel */
};

static PyModuleDef rapidjson_module = {
    PyModuleDef_HEAD_INIT,
    "rapidjson",
    rapidjson_module_docstring,
    -1,
    rapidjson_functions
};

PyMODINIT_FUNC
PyInit_rapidjson()
{
    PyDateTime_IMPORT;

    PyObject* datetimeModule = PyImport_ImportModule("datetime");
    if (datetimeModule == NULL)
        return NULL;

    rapidjson_timezone_type = PyObject_GetAttrString(datetimeModule, "timezone");
    Py_DECREF(datetimeModule);

    if (rapidjson_timezone_type == NULL)
        return NULL;

    rapidjson_timezone_utc = PyObject_GetAttrString(rapidjson_timezone_type, "utc");
    if (rapidjson_timezone_utc == NULL) {
        Py_DECREF(rapidjson_timezone_type);
        return NULL;
    }

    PyObject* decimalModule = PyImport_ImportModule("decimal");
    if (decimalModule == NULL)
        return NULL;

    rapidjson_decimal_type = PyObject_GetAttrString(decimalModule, "Decimal");
    Py_DECREF(decimalModule);

    PyObject* module;

    module = PyModule_Create(&rapidjson_module);
    if (module == NULL)
        return NULL;

    PyModule_AddIntConstant(module, "DATETIME_MODE_NONE", DATETIME_MODE_NONE);
    PyModule_AddIntConstant(module, "DATETIME_MODE_ISO8601", DATETIME_MODE_ISO8601);
    PyModule_AddIntConstant(module, "DATETIME_MODE_ISO8601_IGNORE_TZ", DATETIME_MODE_ISO8601_IGNORE_TZ);
    PyModule_AddIntConstant(module, "DATETIME_MODE_ISO8601_UTC", DATETIME_MODE_ISO8601_UTC);

    PyModule_AddStringConstant(module, "__version__", PYTHON_RAPIDJSON_VERSION);
    PyModule_AddStringConstant(
        module,
        "__author__",
        PYTHON_RAPIDJSON_AUTHOR " ");

    return module;
}

Web Proxy Viewer  |  New URL  |  Original Page