python-cjson源码分析

343 阅读2分钟

Python-cjson为python实现了一个非常快速的json编码器/解码器。同其他用python实现的json编码器/解码器比较,在编码方面,速度快10-200倍;在解码方面,速度快100-250倍。

github地址如下:github.com/AGProjects/…

源码只包含一个文件,cjson.c。提供的两个接口是:encodedecode。其中encode把Python对象转成json对象,decode是把json对象,转成Python的对象。

encode

依次检查Python的对象类型,然后转成string。

static PyObject*
encode_object(PyObject *object)
{
    if (object == Py_True) {
        return PyString_FromString("true");
    } else if (object == Py_False) {
        return PyString_FromString("false");
    } else if (object == Py_None) {
        return PyString_FromString("null");
    } else if (PyString_Check(object)) {
        return encode_string(object);
    } else if (PyUnicode_Check(object)) {
        return encode_unicode(object);
    } else if (PyFloat_Check(object)) {
        double val = PyFloat_AS_DOUBLE(object);
        if (Py_IS_NAN(val)) {
            return PyString_FromString("NaN");
        } else if (Py_IS_INFINITY(val)) {
            if (val > 0) {
                return PyString_FromString("Infinity");
            } else {
                return PyString_FromString("-Infinity");
            }
        } else {
            return PyObject_Repr(object);
        }
    } else if (PyInt_Check(object) || PyLong_Check(object)) {
        return PyObject_Str(object);
    } else if (PyList_Check(object)) {
        PyObject *result;
        if (Py_EnterRecursiveCall(" while encoding a JSON array from a Python list"))
            return NULL;
        result = encode_list(object);
        Py_LeaveRecursiveCall();
        return result;
    } else if (PyTuple_Check(object)) {
        PyObject *result;
        if (Py_EnterRecursiveCall(" while encoding a JSON array from a Python tuple"))
            return NULL;
        result = encode_tuple(object);
        Py_LeaveRecursiveCall();
        return result;
    } else if (PyDict_Check(object)) { // use PyMapping_Check(object) instead? -Dan
        PyObject *result;
        if (Py_EnterRecursiveCall(" while encoding a JSON object"))
            return NULL;
        result = encode_dict(object);
        Py_LeaveRecursiveCall();
        return result;
    } else {
        PyErr_SetString(JSON_EncodeError, "object is not JSON encodable");
        return NULL;
    }
}

对于容器类型,如listdict, 递归的调用encode_object

 else if (PyList_Check(object)) {
        PyObject *result;
        if (Py_EnterRecursiveCall(" while encoding a JSON array from a Python list"))
            return NULL;
        result = encode_list(object);
        Py_LeaveRecursiveCall();
        return result;
}
static PyObject*
encode_list(PyObject *list)
{
    Py_ssize_t i;
    PyObject *s, *temp;
    PyObject *pieces = NULL, *result = NULL;
    PyListObject *v = (PyListObject*) list;

    i = Py_ReprEnter((PyObject*)v);
    if (i != 0) {
        if (i > 0) {
            PyErr_SetString(JSON_EncodeError, "a list with references to "
                            "itself is not JSON encodable");
        }
        return NULL;
    }

    if (v->ob_size == 0) {
        result = PyString_FromString("[]");
        goto Done;
    }

    pieces = PyList_New(0);
    if (pieces == NULL)
        goto Done;

    /* Do repr() on each element.  Note that this may mutate the list,
     * so must refetch the list size on each iteration. */
    for (i = 0; i < v->ob_size; ++i) {
        int status;
        s = encode_object(v->ob_item[i]);        // 对list的每一项,递归调用encode_object
        if (s == NULL)
            goto Done;
        status = PyList_Append(pieces, s);
        Py_DECREF(s);  /* append created a new ref */
        if (status < 0)
            goto Done;
    }

    /* Add "[]" decorations to the first and last items. */
    assert(PyList_GET_SIZE(pieces) > 0);
    s = PyString_FromString("[");
    if (s == NULL)
        goto Done;
    temp = PyList_GET_ITEM(pieces, 0);
    PyString_ConcatAndDel(&s, temp);
    PyList_SET_ITEM(pieces, 0, s);
    if (s == NULL)
        goto Done;

    s = PyString_FromString("]");
    if (s == NULL)
        goto Done;
    temp = PyList_GET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1);
    PyString_ConcatAndDel(&temp, s);
    PyList_SET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1, temp);
    if (temp == NULL)
        goto Done;

    /* Paste them all together with ", " between. */
    s = PyString_FromString(", ");
    if (s == NULL)
        goto Done;
    result = _PyString_Join(s, pieces);
    Py_DECREF(s);

Done:
    Py_XDECREF(pieces);
    Py_ReprLeave((PyObject *)v);
    return result;
}

decode

static PyObject*
decode_json(JSONData *jsondata)
{
    PyObject *object;

    skipSpaces(jsondata);
    switch(*jsondata->ptr) {
    case 0:
        PyErr_SetString(JSON_DecodeError, "empty JSON description");
        return NULL;
    case '{':
        if (Py_EnterRecursiveCall(" while decoding a JSON object"))
            return NULL;
        object = decode_object(jsondata);
        Py_LeaveRecursiveCall();
        break;
    case '[':
        if (Py_EnterRecursiveCall(" while decoding a JSON array"))
            return NULL;
        object = decode_array(jsondata);
        Py_LeaveRecursiveCall();
        break;
    case '"':
        object = decode_string(jsondata);
        break;
    case 't':
    case 'f':
        object = decode_bool(jsondata);
        break;
    case 'n':
        object = decode_null(jsondata);
        break;
    case 'N':
        object = decode_nan(jsondata);
        break;
    case 'I':
        object = decode_inf(jsondata);
        break;
    case '+':
    case '-':
        if (*(jsondata->ptr+1) == 'I') {
            object = decode_inf(jsondata);
            break;
        }
        // fall through
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
        object = decode_number(jsondata);
        break;
    default:
        PyErr_SetString(JSON_DecodeError, "cannot parse JSON description");
        return NULL;
    }

    return object;
}