352 lines
9.5 KiB
C
352 lines
9.5 KiB
C
/*
|
|
* Copyright 2009, R. Tyler Ballance <tyler@monkeypox.org>
|
|
*
|
|
* 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. Neither the name of R. Tyler Ballance 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 THE AUTHOR ``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 SHALL THE AUTHOR 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 <Python.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <yajl/yajl_parse.h>
|
|
#include <yajl/yajl_gen.h>
|
|
|
|
#include "py_yajl.h"
|
|
|
|
int _PlaceObject(_YajlDecoder *self, PyObject *parent, PyObject *child)
|
|
{
|
|
if ( (!self) || (!child) || (!parent) )
|
|
return failure;
|
|
|
|
if (PyList_Check(parent)) {
|
|
PyList_Append(parent, child);
|
|
// child is now owned by parent!
|
|
if ((child) && (child != Py_None)) {
|
|
Py_XDECREF(child);
|
|
}
|
|
return success;
|
|
} else if (PyDict_Check(parent)) {
|
|
PyObject* key = py_yajl_ps_current(self->keys);
|
|
PyDict_SetItem(parent, key, child);
|
|
py_yajl_ps_pop(self->keys);
|
|
// child is now owned by parent!
|
|
Py_XDECREF(key);
|
|
if ((child) && (child != Py_None)) {
|
|
Py_XDECREF(child);
|
|
}
|
|
return success;
|
|
}
|
|
return failure;
|
|
}
|
|
|
|
int PlaceObject(_YajlDecoder *self, PyObject *object)
|
|
{
|
|
unsigned int length = py_yajl_ps_length(self->elements);
|
|
|
|
if (length == 0) {
|
|
/*
|
|
* When the length is zero, and we're entering this code path
|
|
* we should only be handling "primitive types" i.e. strings and
|
|
* numbers, not dict/list.
|
|
*/
|
|
self->root = object;
|
|
return success;
|
|
}
|
|
return _PlaceObject(self, py_yajl_ps_current(self->elements), object);
|
|
}
|
|
|
|
|
|
static int handle_null(void *ctx)
|
|
{
|
|
Py_INCREF(Py_None);
|
|
return PlaceObject(ctx, Py_None);
|
|
}
|
|
|
|
static int handle_bool(void *ctx, int value)
|
|
{
|
|
return PlaceObject(ctx, PyBool_FromLong((long)(value)));
|
|
}
|
|
|
|
static int handle_number(void *ctx, const char *value, unsigned int length)
|
|
{
|
|
_YajlDecoder *self = (_YajlDecoder *)(ctx);
|
|
PyObject *object;
|
|
#ifdef IS_PYTHON3
|
|
PyBytesObject *string;
|
|
#else
|
|
PyObject *string;
|
|
#endif
|
|
|
|
int floaty_char;
|
|
|
|
// take a moment here to scan the input string to see if there's
|
|
// any chars which suggest this is a floating point number
|
|
for (floaty_char = 0; floaty_char < length; floaty_char++) {
|
|
switch (value[floaty_char]) {
|
|
case '.': case 'e': case 'E': goto floatin;
|
|
}
|
|
}
|
|
|
|
floatin:
|
|
#ifdef IS_PYTHON3
|
|
string = (PyBytesObject *)PyBytes_FromStringAndSize(value, length);
|
|
if (floaty_char >= length) {
|
|
object = PyLong_FromString(string->ob_sval, NULL, 10);
|
|
} else {
|
|
object = PyFloat_FromString((PyObject *)string);
|
|
}
|
|
#else
|
|
string = PyString_FromStringAndSize(value, length);
|
|
if (floaty_char >= length) {
|
|
object = PyInt_FromString(PyString_AS_STRING(string), NULL, 10);
|
|
} else {
|
|
object = PyFloat_FromString(string, NULL);
|
|
}
|
|
#endif
|
|
Py_XDECREF(string);
|
|
return PlaceObject(self, object);
|
|
}
|
|
|
|
static int handle_string(void *ctx, const unsigned char *value, unsigned int length)
|
|
{
|
|
return PlaceObject(ctx, PyUnicode_FromStringAndSize((char *)value, length));
|
|
}
|
|
|
|
static int handle_start_dict(void *ctx)
|
|
{
|
|
PyObject *object = PyDict_New();
|
|
if (!object)
|
|
return failure;
|
|
|
|
py_yajl_ps_push(((_YajlDecoder *)(ctx))->elements, object);
|
|
return success;
|
|
}
|
|
|
|
static int handle_dict_key(void *ctx, const unsigned char *value, unsigned int length)
|
|
{
|
|
PyObject *object = PyUnicode_FromStringAndSize((const char *) value, length);
|
|
|
|
if (object == NULL)
|
|
return failure;
|
|
|
|
py_yajl_ps_push(((_YajlDecoder *)(ctx))->keys, object);
|
|
return success;
|
|
}
|
|
|
|
static int handle_end_dict(void *ctx)
|
|
{
|
|
_YajlDecoder *self = (_YajlDecoder *)(ctx);
|
|
PyObject *last, *popped;
|
|
unsigned int length;
|
|
|
|
length = py_yajl_ps_length(self->elements);
|
|
if (length == 1) {
|
|
/*
|
|
* If this is the last element in the stack
|
|
* then it's "root" and we should finish up
|
|
*/
|
|
self->root = py_yajl_ps_current(self->elements);
|
|
py_yajl_ps_pop(self->elements);
|
|
return success;
|
|
} else if (length < 2) {
|
|
return failure;
|
|
}
|
|
|
|
/*
|
|
* If not, then we should properly add this dict
|
|
* to it's appropriate parent
|
|
*/
|
|
popped = py_yajl_ps_current(self->elements);
|
|
py_yajl_ps_pop(self->elements);
|
|
last = py_yajl_ps_current(self->elements);
|
|
|
|
return _PlaceObject(self, last, popped);
|
|
}
|
|
|
|
static int handle_start_list(void *ctx)
|
|
{
|
|
PyObject *object = PyList_New(0);
|
|
|
|
if (!object)
|
|
return failure;
|
|
|
|
py_yajl_ps_push(((_YajlDecoder *)(ctx))->elements, object);
|
|
return success;
|
|
}
|
|
|
|
static int handle_end_list(void *ctx)
|
|
{
|
|
_YajlDecoder *self = (_YajlDecoder *)(ctx);
|
|
PyObject *last, *popped;
|
|
unsigned int length;
|
|
|
|
length = py_yajl_ps_length(self->elements);
|
|
if (length == 1) {
|
|
self->root = py_yajl_ps_current(self->elements);
|
|
py_yajl_ps_pop(self->elements);
|
|
return success;
|
|
} else if (length < 2) {
|
|
return failure;
|
|
}
|
|
|
|
popped = py_yajl_ps_current(self->elements);
|
|
py_yajl_ps_pop(self->elements);
|
|
last = py_yajl_ps_current(self->elements);
|
|
|
|
return _PlaceObject(self, last, popped);
|
|
}
|
|
|
|
static yajl_callbacks decode_callbacks = {
|
|
handle_null,
|
|
handle_bool,
|
|
NULL,
|
|
NULL,
|
|
handle_number,
|
|
handle_string,
|
|
handle_start_dict,
|
|
handle_dict_key,
|
|
handle_end_dict,
|
|
handle_start_list,
|
|
handle_end_list
|
|
};
|
|
|
|
PyObject *_internal_decode(_YajlDecoder *self, char *buffer, unsigned int buflen)
|
|
{
|
|
yajl_handle parser = NULL;
|
|
yajl_status yrc;
|
|
yajl_parser_config config = { 1, 1 };
|
|
|
|
if (self->elements.used > 0) {
|
|
py_yajl_ps_free(self->elements);
|
|
py_yajl_ps_init(self->elements);
|
|
}
|
|
if (self->keys.used > 0) {
|
|
py_yajl_ps_free(self->keys);
|
|
py_yajl_ps_init(self->keys);
|
|
}
|
|
|
|
/* callbacks, config, allocfuncs */
|
|
parser = yajl_alloc(&decode_callbacks, &config, NULL, (void *)(self));
|
|
yrc = yajl_parse(parser, (const unsigned char *)(buffer), buflen);
|
|
yajl_parse_complete(parser);
|
|
yajl_free(parser);
|
|
|
|
if (yrc != yajl_status_ok) {
|
|
PyErr_SetObject(PyExc_ValueError,
|
|
PyUnicode_FromString(yajl_status_to_string(yrc)));
|
|
return NULL;
|
|
}
|
|
|
|
if (self->root == NULL) {
|
|
PyErr_SetObject(PyExc_ValueError,
|
|
PyUnicode_FromString("The root object is NULL"));
|
|
return NULL;
|
|
}
|
|
|
|
// Callee now owns memory, we'll leave refcnt at one and
|
|
// null out our pointer.
|
|
PyObject *root = self->root;
|
|
self->root = NULL;
|
|
return root;
|
|
}
|
|
|
|
PyObject *py_yajldecoder_decode(PYARGS)
|
|
{
|
|
_YajlDecoder *decoder = (_YajlDecoder *)(self);
|
|
char *buffer = NULL;
|
|
PyObject *pybuffer = NULL;
|
|
PyObject *result = NULL;
|
|
Py_ssize_t buflen = 0;
|
|
|
|
if (!PyArg_ParseTuple(args, "O", &pybuffer))
|
|
return NULL;
|
|
|
|
Py_INCREF(pybuffer);
|
|
|
|
if (PyUnicode_Check(pybuffer)) {
|
|
if (!(result = PyUnicode_AsUTF8String(pybuffer))) {
|
|
Py_DECREF(pybuffer);
|
|
return NULL;
|
|
}
|
|
Py_DECREF(pybuffer);
|
|
pybuffer = result;
|
|
result = NULL;
|
|
}
|
|
|
|
if (PyString_Check(pybuffer)) {
|
|
if (PyString_AsStringAndSize(pybuffer, &buffer, &buflen)) {
|
|
Py_DECREF(pybuffer);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
/* really seems like this should be a TypeError, but
|
|
tests/unit.py:ErrorCasesTests.test_None disagrees */
|
|
Py_DECREF(pybuffer);
|
|
PyErr_SetString(PyExc_ValueError, "string or unicode expected");
|
|
return NULL;
|
|
}
|
|
|
|
if (!buflen) {
|
|
PyErr_SetObject(PyExc_ValueError,
|
|
PyUnicode_FromString("Cannot parse an empty buffer"));
|
|
return NULL;
|
|
}
|
|
|
|
result = _internal_decode(decoder, buffer, (unsigned int)buflen);
|
|
Py_DECREF(pybuffer);
|
|
return result;
|
|
}
|
|
|
|
int yajldecoder_init(PYARGS)
|
|
{
|
|
_YajlDecoder *me = (_YajlDecoder *)(self);
|
|
py_yajl_ps_init(me->elements);
|
|
py_yajl_ps_init(me->keys);
|
|
me->root = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void yajldecoder_dealloc(_YajlDecoder *self)
|
|
{
|
|
py_yajl_ps_free(self->elements);
|
|
py_yajl_ps_init(self->elements);
|
|
py_yajl_ps_free(self->keys);
|
|
py_yajl_ps_init(self->keys);
|
|
if (self->root) {
|
|
Py_XDECREF(self->root);
|
|
}
|
|
#ifdef IS_PYTHON3
|
|
Py_TYPE(self)->tp_free((PyObject*)self);
|
|
#else
|
|
self->ob_type->tp_free((PyObject*)self);
|
|
#endif
|
|
}
|