/tags/release-0.1-rc2/hive/external/service/lib/py/thrift/protocol/fastbinary.c
C | 1171 lines | 856 code | 223 blank | 92 comment | 178 complexity | 9301a1fc32150646407bd114fb0c6776 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause, JSON, CPL-1.0
- // Copyright (c) 2006- Facebook
- // Distributed under the Thrift Software License
- //
- // See accompanying file LICENSE or visit the Thrift site at:
- // http://developers.facebook.com/thrift/
- //
- // NOTE: This code was contributed by an external developer.
- // The internal Thrift team has reviewed and tested it,
- // but we cannot guarantee that it is production-ready.
- // Please feel free to report bugs and/or success stories
- // to the public mailing list.
- #include <Python.h>
- #include "cStringIO.h"
- #include <stdbool.h>
- #include <stdint.h>
- #include <netinet/in.h>
- // TODO(dreiss): defval appears to be unused. Look into removing it.
- // TODO(dreiss): Make parse_spec_args recursive, and cache the output
- // permanently in the object. (Malloc and orphan.)
- // TODO(dreiss): Why do we need cStringIO for reading, why not just char*?
- // Can cStringIO let us work with a BufferedTransport?
- // TODO(dreiss): Don't ignore the rv from cwrite (maybe).
- /* ====== BEGIN UTILITIES ====== */
- #define INIT_OUTBUF_SIZE 128
- // Stolen out of TProtocol.h.
- // It would be a huge pain to have both get this from one place.
- typedef enum TType {
- T_STOP = 0,
- T_VOID = 1,
- T_BOOL = 2,
- T_BYTE = 3,
- T_I08 = 3,
- T_I16 = 6,
- T_I32 = 8,
- T_U64 = 9,
- T_I64 = 10,
- T_DOUBLE = 4,
- T_STRING = 11,
- T_UTF7 = 11,
- T_STRUCT = 12,
- T_MAP = 13,
- T_SET = 14,
- T_LIST = 15,
- T_UTF8 = 16,
- T_UTF16 = 17
- } TType;
- #ifndef __BYTE_ORDER
- # if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
- # define __BYTE_ORDER BYTE_ORDER
- # define __LITTLE_ENDIAN LITTLE_ENDIAN
- # define __BIG_ENDIAN BIG_ENDIAN
- # else
- # error "Cannot determine endianness"
- # endif
- #endif
- // Same comment as the enum. Sorry.
- #if __BYTE_ORDER == __BIG_ENDIAN
- # define ntohll(n) (n)
- # define htonll(n) (n)
- #elif __BYTE_ORDER == __LITTLE_ENDIAN
- # if defined(__GNUC__) && defined(__GLIBC__)
- # include <byteswap.h>
- # define ntohll(n) bswap_64(n)
- # define htonll(n) bswap_64(n)
- # else /* GNUC & GLIBC */
- # define ntohll(n) ( (((unsigned long long)ntohl(n)) << 32) + ntohl(n >> 32) )
- # define htonll(n) ( (((unsigned long long)htonl(n)) << 32) + htonl(n >> 32) )
- # endif /* GNUC & GLIBC */
- #else /* __BYTE_ORDER */
- # error "Can't define htonll or ntohll!"
- #endif
- // Doing a benchmark shows that interning actually makes a difference, amazingly.
- #define INTERN_STRING(value) _intern_ ## value
- #define INT_CONV_ERROR_OCCURRED(v) ( ((v) == -1) && PyErr_Occurred() )
- #define CHECK_RANGE(v, min, max) ( ((v) <= (max)) && ((v) >= (min)) )
- // Py_ssize_t was not defined before Python 2.5
- #if (PY_VERSION_HEX < 0x02050000)
- typedef int Py_ssize_t;
- #endif
- /**
- * A cache of the spec_args for a set or list,
- * so we don't have to keep calling PyTuple_GET_ITEM.
- */
- typedef struct {
- TType element_type;
- PyObject* typeargs;
- } SetListTypeArgs;
- /**
- * A cache of the spec_args for a map,
- * so we don't have to keep calling PyTuple_GET_ITEM.
- */
- typedef struct {
- TType ktag;
- TType vtag;
- PyObject* ktypeargs;
- PyObject* vtypeargs;
- } MapTypeArgs;
- /**
- * A cache of the spec_args for a struct,
- * so we don't have to keep calling PyTuple_GET_ITEM.
- */
- typedef struct {
- PyObject* klass;
- PyObject* spec;
- } StructTypeArgs;
- /**
- * A cache of the item spec from a struct specification,
- * so we don't have to keep calling PyTuple_GET_ITEM.
- */
- typedef struct {
- int tag;
- TType type;
- PyObject* attrname;
- PyObject* typeargs;
- PyObject* defval;
- } StructItemSpec;
- /**
- * A cache of the two key attributes of a CReadableTransport,
- * so we don't have to keep calling PyObject_GetAttr.
- */
- typedef struct {
- PyObject* stringiobuf;
- PyObject* refill_callable;
- } DecodeBuffer;
- /** Pointer to interned string to speed up attribute lookup. */
- static PyObject* INTERN_STRING(cstringio_buf);
- /** Pointer to interned string to speed up attribute lookup. */
- static PyObject* INTERN_STRING(cstringio_refill);
- static inline bool
- check_ssize_t_32(Py_ssize_t len) {
- // error from getting the int
- if (INT_CONV_ERROR_OCCURRED(len)) {
- return false;
- }
- if (!CHECK_RANGE(len, 0, INT32_MAX)) {
- PyErr_SetString(PyExc_OverflowError, "string size out of range");
- return false;
- }
- return true;
- }
- static inline bool
- parse_pyint(PyObject* o, int32_t* ret, int32_t min, int32_t max) {
- long val = PyInt_AsLong(o);
- if (INT_CONV_ERROR_OCCURRED(val)) {
- return false;
- }
- if (!CHECK_RANGE(val, min, max)) {
- PyErr_SetString(PyExc_OverflowError, "int out of range");
- return false;
- }
- *ret = (int32_t) val;
- return true;
- }
- /* --- FUNCTIONS TO PARSE STRUCT SPECIFICATOINS --- */
- static bool
- parse_set_list_args(SetListTypeArgs* dest, PyObject* typeargs) {
- if (PyTuple_Size(typeargs) != 2) {
- PyErr_SetString(PyExc_TypeError, "expecting tuple of size 2 for list/set type args");
- return false;
- }
- dest->element_type = PyInt_AsLong(PyTuple_GET_ITEM(typeargs, 0));
- if (INT_CONV_ERROR_OCCURRED(dest->element_type)) {
- return false;
- }
- dest->typeargs = PyTuple_GET_ITEM(typeargs, 1);
- return true;
- }
- static bool
- parse_map_args(MapTypeArgs* dest, PyObject* typeargs) {
- if (PyTuple_Size(typeargs) != 4) {
- PyErr_SetString(PyExc_TypeError, "expecting 4 arguments for typeargs to map");
- return false;
- }
- dest->ktag = PyInt_AsLong(PyTuple_GET_ITEM(typeargs, 0));
- if (INT_CONV_ERROR_OCCURRED(dest->ktag)) {
- return false;
- }
- dest->vtag = PyInt_AsLong(PyTuple_GET_ITEM(typeargs, 2));
- if (INT_CONV_ERROR_OCCURRED(dest->vtag)) {
- return false;
- }
- dest->ktypeargs = PyTuple_GET_ITEM(typeargs, 1);
- dest->vtypeargs = PyTuple_GET_ITEM(typeargs, 3);
- return true;
- }
- static bool
- parse_struct_args(StructTypeArgs* dest, PyObject* typeargs) {
- if (PyTuple_Size(typeargs) != 2) {
- PyErr_SetString(PyExc_TypeError, "expecting tuple of size 2 for struct args");
- return false;
- }
- dest->klass = PyTuple_GET_ITEM(typeargs, 0);
- dest->spec = PyTuple_GET_ITEM(typeargs, 1);
- return true;
- }
- static int
- parse_struct_item_spec(StructItemSpec* dest, PyObject* spec_tuple) {
- // i'd like to use ParseArgs here, but it seems to be a bottleneck.
- if (PyTuple_Size(spec_tuple) != 5) {
- PyErr_SetString(PyExc_TypeError, "expecting 5 arguments for spec tuple");
- return false;
- }
- dest->tag = PyInt_AsLong(PyTuple_GET_ITEM(spec_tuple, 0));
- if (INT_CONV_ERROR_OCCURRED(dest->tag)) {
- return false;
- }
- dest->type = PyInt_AsLong(PyTuple_GET_ITEM(spec_tuple, 1));
- if (INT_CONV_ERROR_OCCURRED(dest->type)) {
- return false;
- }
- dest->attrname = PyTuple_GET_ITEM(spec_tuple, 2);
- dest->typeargs = PyTuple_GET_ITEM(spec_tuple, 3);
- dest->defval = PyTuple_GET_ITEM(spec_tuple, 4);
- return true;
- }
- /* ====== END UTILITIES ====== */
- /* ====== BEGIN WRITING FUNCTIONS ====== */
- /* --- LOW-LEVEL WRITING FUNCTIONS --- */
- static void writeByte(PyObject* outbuf, int8_t val) {
- int8_t net = val;
- PycStringIO->cwrite(outbuf, (char*)&net, sizeof(int8_t));
- }
- static void writeI16(PyObject* outbuf, int16_t val) {
- int16_t net = (int16_t)htons(val);
- PycStringIO->cwrite(outbuf, (char*)&net, sizeof(int16_t));
- }
- static void writeI32(PyObject* outbuf, int32_t val) {
- int32_t net = (int32_t)htonl(val);
- PycStringIO->cwrite(outbuf, (char*)&net, sizeof(int32_t));
- }
- static void writeI64(PyObject* outbuf, int64_t val) {
- int64_t net = (int64_t)htonll(val);
- PycStringIO->cwrite(outbuf, (char*)&net, sizeof(int64_t));
- }
- static void writeDouble(PyObject* outbuf, double dub) {
- // Unfortunately, bitwise_cast doesn't work in C. Bad C!
- union {
- double f;
- int64_t t;
- } transfer;
- transfer.f = dub;
- writeI64(outbuf, transfer.t);
- }
- /* --- MAIN RECURSIVE OUTPUT FUCNTION -- */
- static int
- output_val(PyObject* output, PyObject* value, TType type, PyObject* typeargs) {
- /*
- * Refcounting Strategy:
- *
- * We assume that elements of the thrift_spec tuple are not going to be
- * mutated, so we don't ref count those at all. Other than that, we try to
- * keep a reference to all the user-created objects while we work with them.
- * output_val assumes that a reference is already held. The *caller* is
- * responsible for handling references
- */
- switch (type) {
- case T_BOOL: {
- int v = PyObject_IsTrue(value);
- if (v == -1) {
- return false;
- }
- writeByte(output, (int8_t) v);
- break;
- }
- case T_I08: {
- int32_t val;
- if (!parse_pyint(value, &val, INT8_MIN, INT8_MAX)) {
- return false;
- }
- writeByte(output, (int8_t) val);
- break;
- }
- case T_I16: {
- int32_t val;
- if (!parse_pyint(value, &val, INT16_MIN, INT16_MAX)) {
- return false;
- }
- writeI16(output, (int16_t) val);
- break;
- }
- case T_I32: {
- int32_t val;
- if (!parse_pyint(value, &val, INT32_MIN, INT32_MAX)) {
- return false;
- }
- writeI32(output, val);
- break;
- }
- case T_I64: {
- int64_t nval = PyLong_AsLongLong(value);
- if (INT_CONV_ERROR_OCCURRED(nval)) {
- return false;
- }
- if (!CHECK_RANGE(nval, INT64_MIN, INT64_MAX)) {
- PyErr_SetString(PyExc_OverflowError, "int out of range");
- return false;
- }
- writeI64(output, nval);
- break;
- }
- case T_DOUBLE: {
- double nval = PyFloat_AsDouble(value);
- if (nval == -1.0 && PyErr_Occurred()) {
- return false;
- }
- writeDouble(output, nval);
- break;
- }
- case T_STRING: {
- Py_ssize_t len = PyString_Size(value);
- if (!check_ssize_t_32(len)) {
- return false;
- }
- writeI32(output, (int32_t) len);
- PycStringIO->cwrite(output, PyString_AsString(value), (int32_t) len);
- break;
- }
- case T_LIST:
- case T_SET: {
- Py_ssize_t len;
- SetListTypeArgs parsedargs;
- PyObject *item;
- PyObject *iterator;
- if (!parse_set_list_args(&parsedargs, typeargs)) {
- return false;
- }
- len = PyObject_Length(value);
- if (!check_ssize_t_32(len)) {
- return false;
- }
- writeByte(output, parsedargs.element_type);
- writeI32(output, (int32_t) len);
- iterator = PyObject_GetIter(value);
- if (iterator == NULL) {
- return false;
- }
- while ((item = PyIter_Next(iterator))) {
- if (!output_val(output, item, parsedargs.element_type, parsedargs.typeargs)) {
- Py_DECREF(item);
- Py_DECREF(iterator);
- return false;
- }
- Py_DECREF(item);
- }
- Py_DECREF(iterator);
- if (PyErr_Occurred()) {
- return false;
- }
- break;
- }
- case T_MAP: {
- PyObject *k, *v;
- Py_ssize_t pos = 0;
- Py_ssize_t len;
- MapTypeArgs parsedargs;
- len = PyDict_Size(value);
- if (!check_ssize_t_32(len)) {
- return false;
- }
- if (!parse_map_args(&parsedargs, typeargs)) {
- return false;
- }
- writeByte(output, parsedargs.ktag);
- writeByte(output, parsedargs.vtag);
- writeI32(output, len);
- // TODO(bmaurer): should support any mapping, not just dicts
- while (PyDict_Next(value, &pos, &k, &v)) {
- // TODO(dreiss): Think hard about whether these INCREFs actually
- // turn any unsafe scenarios into safe scenarios.
- Py_INCREF(k);
- Py_INCREF(v);
- if (!output_val(output, k, parsedargs.ktag, parsedargs.ktypeargs)
- || !output_val(output, v, parsedargs.vtag, parsedargs.vtypeargs)) {
- Py_DECREF(k);
- Py_DECREF(v);
- return false;
- }
- }
- break;
- }
- // TODO(dreiss): Consider breaking this out as a function
- // the way we did for decode_struct.
- case T_STRUCT: {
- StructTypeArgs parsedargs;
- Py_ssize_t nspec;
- Py_ssize_t i;
- if (!parse_struct_args(&parsedargs, typeargs)) {
- return false;
- }
- nspec = PyTuple_Size(parsedargs.spec);
- if (nspec == -1) {
- return false;
- }
- for (i = 0; i < nspec; i++) {
- StructItemSpec parsedspec;
- PyObject* spec_tuple;
- PyObject* instval = NULL;
- spec_tuple = PyTuple_GET_ITEM(parsedargs.spec, i);
- if (spec_tuple == Py_None) {
- continue;
- }
- if (!parse_struct_item_spec (&parsedspec, spec_tuple)) {
- return false;
- }
- instval = PyObject_GetAttr(value, parsedspec.attrname);
- if (!instval) {
- return false;
- }
- if (instval == Py_None) {
- Py_DECREF(instval);
- continue;
- }
- writeByte(output, (int8_t) parsedspec.type);
- writeI16(output, parsedspec.tag);
- if (!output_val(output, instval, parsedspec.type, parsedspec.typeargs)) {
- Py_DECREF(instval);
- return false;
- }
- Py_DECREF(instval);
- }
- writeByte(output, (int8_t)T_STOP);
- break;
- }
- case T_STOP:
- case T_VOID:
- case T_UTF16:
- case T_UTF8:
- case T_U64:
- default:
- PyErr_SetString(PyExc_TypeError, "Unexpected TType");
- return false;
- }
- return true;
- }
- /* --- TOP-LEVEL WRAPPER FOR OUTPUT -- */
- static PyObject *
- encode_binary(PyObject *self, PyObject *args) {
- PyObject* enc_obj;
- PyObject* type_args;
- PyObject* buf;
- PyObject* ret = NULL;
- if (!PyArg_ParseTuple(args, "OO", &enc_obj, &type_args)) {
- return NULL;
- }
- buf = PycStringIO->NewOutput(INIT_OUTBUF_SIZE);
- if (output_val(buf, enc_obj, T_STRUCT, type_args)) {
- ret = PycStringIO->cgetvalue(buf);
- }
- Py_DECREF(buf);
- return ret;
- }
- /* ====== END WRITING FUNCTIONS ====== */
- /* ====== BEGIN READING FUNCTIONS ====== */
- /* --- LOW-LEVEL READING FUNCTIONS --- */
- static void
- free_decodebuf(DecodeBuffer* d) {
- Py_XDECREF(d->stringiobuf);
- Py_XDECREF(d->refill_callable);
- }
- static bool
- decode_buffer_from_obj(DecodeBuffer* dest, PyObject* obj) {
- dest->stringiobuf = PyObject_GetAttr(obj, INTERN_STRING(cstringio_buf));
- if (!dest->stringiobuf) {
- return false;
- }
- if (!PycStringIO_InputCheck(dest->stringiobuf)) {
- free_decodebuf(dest);
- PyErr_SetString(PyExc_TypeError, "expecting stringio input");
- return false;
- }
- dest->refill_callable = PyObject_GetAttr(obj, INTERN_STRING(cstringio_refill));
- if(!dest->refill_callable) {
- free_decodebuf(dest);
- return false;
- }
- if (!PyCallable_Check(dest->refill_callable)) {
- free_decodebuf(dest);
- PyErr_SetString(PyExc_TypeError, "expecting callable");
- return false;
- }
- return true;
- }
- static bool readBytes(DecodeBuffer* input, char** output, int len) {
- int read;
- // TODO(dreiss): Don't fear the malloc. Think about taking a copy of
- // the partial read instead of forcing the transport
- // to prepend it to its buffer.
- read = PycStringIO->cread(input->stringiobuf, output, len);
- if (read == len) {
- return true;
- } else if (read == -1) {
- return false;
- } else {
- PyObject* newiobuf;
- // using building functions as this is a rare codepath
- newiobuf = PyObject_CallFunction(
- input->refill_callable, "s#i", *output, read, len, NULL);
- if (newiobuf == NULL) {
- return false;
- }
- // must do this *AFTER* the call so that we don't deref the io buffer
- Py_CLEAR(input->stringiobuf);
- input->stringiobuf = newiobuf;
- read = PycStringIO->cread(input->stringiobuf, output, len);
- if (read == len) {
- return true;
- } else if (read == -1) {
- return false;
- } else {
- // TODO(dreiss): This could be a valid code path for big binary blobs.
- PyErr_SetString(PyExc_TypeError,
- "refill claimed to have refilled the buffer, but didn't!!");
- return false;
- }
- }
- }
- static int8_t readByte(DecodeBuffer* input) {
- char* buf;
- if (!readBytes(input, &buf, sizeof(int8_t))) {
- return -1;
- }
- return *(int8_t*) buf;
- }
- static int16_t readI16(DecodeBuffer* input) {
- char* buf;
- if (!readBytes(input, &buf, sizeof(int16_t))) {
- return -1;
- }
- return (int16_t) ntohs(*(int16_t*) buf);
- }
- static int32_t readI32(DecodeBuffer* input) {
- char* buf;
- if (!readBytes(input, &buf, sizeof(int32_t))) {
- return -1;
- }
- return (int32_t) ntohl(*(int32_t*) buf);
- }
- static int64_t readI64(DecodeBuffer* input) {
- char* buf;
- if (!readBytes(input, &buf, sizeof(int64_t))) {
- return -1;
- }
- return (int64_t) ntohll(*(int64_t*) buf);
- }
- static double readDouble(DecodeBuffer* input) {
- union {
- int64_t f;
- double t;
- } transfer;
- transfer.f = readI64(input);
- if (transfer.f == -1) {
- return -1;
- }
- return transfer.t;
- }
- static bool
- checkTypeByte(DecodeBuffer* input, TType expected) {
- TType got = readByte(input);
- if (INT_CONV_ERROR_OCCURRED(got)) {
- return false;
- }
- if (expected != got) {
- PyErr_SetString(PyExc_TypeError, "got wrong ttype while reading field");
- return false;
- }
- return true;
- }
- static bool
- skip(DecodeBuffer* input, TType type) {
- #define SKIPBYTES(n) \
- do { \
- if (!readBytes(input, &dummy_buf, (n))) { \
- return false; \
- } \
- } while(0)
- char* dummy_buf;
- switch (type) {
- case T_BOOL:
- case T_I08: SKIPBYTES(1); break;
- case T_I16: SKIPBYTES(2); break;
- case T_I32: SKIPBYTES(4); break;
- case T_I64:
- case T_DOUBLE: SKIPBYTES(8); break;
- case T_STRING: {
- // TODO(dreiss): Find out if these check_ssize_t32s are really necessary.
- int len = readI32(input);
- if (!check_ssize_t_32(len)) {
- return false;
- }
- SKIPBYTES(len);
- break;
- }
- case T_LIST:
- case T_SET: {
- TType etype;
- int len, i;
- etype = readByte(input);
- if (etype == -1) {
- return false;
- }
- len = readI32(input);
- if (!check_ssize_t_32(len)) {
- return false;
- }
- for (i = 0; i < len; i++) {
- if (!skip(input, etype)) {
- return false;
- }
- }
- break;
- }
- case T_MAP: {
- TType ktype, vtype;
- int len, i;
- ktype = readByte(input);
- if (ktype == -1) {
- return false;
- }
- vtype = readByte(input);
- if (vtype == -1) {
- return false;
- }
- len = readI32(input);
- if (!check_ssize_t_32(len)) {
- return false;
- }
- for (i = 0; i < len; i++) {
- if (!(skip(input, ktype) && skip(input, vtype))) {
- return false;
- }
- }
- break;
- }
- case T_STRUCT: {
- while (true) {
- TType type;
- type = readByte(input);
- if (type == -1) {
- return false;
- }
- if (type == T_STOP)
- break;
- SKIPBYTES(2); // tag
- if (!skip(input, type)) {
- return false;
- }
- }
- break;
- }
- case T_STOP:
- case T_VOID:
- case T_UTF16:
- case T_UTF8:
- case T_U64:
- default:
- PyErr_SetString(PyExc_TypeError, "Unexpected TType");
- return false;
- }
- return true;
- #undef SKIPBYTES
- }
- /* --- HELPER FUNCTION FOR DECODE_VAL --- */
- static PyObject*
- decode_val(DecodeBuffer* input, TType type, PyObject* typeargs);
- static bool
- decode_struct(DecodeBuffer* input, PyObject* output, PyObject* spec_seq) {
- int spec_seq_len = PyTuple_Size(spec_seq);
- if (spec_seq_len == -1) {
- return false;
- }
- while (true) {
- TType type;
- int16_t tag;
- PyObject* item_spec;
- PyObject* fieldval = NULL;
- StructItemSpec parsedspec;
- type = readByte(input);
- if (type == -1) {
- return false;
- }
- if (type == T_STOP) {
- break;
- }
- tag = readI16(input);
- if (INT_CONV_ERROR_OCCURRED(tag)) {
- return false;
- }
- if (tag >= 0 && tag < spec_seq_len) {
- item_spec = PyTuple_GET_ITEM(spec_seq, tag);
- } else {
- item_spec = Py_None;
- }
- if (item_spec == Py_None) {
- if (!skip(input, type)) {
- return false;
- } else {
- continue;
- }
- }
- if (!parse_struct_item_spec(&parsedspec, item_spec)) {
- return false;
- }
- if (parsedspec.type != type) {
- PyErr_SetString(PyExc_TypeError, "struct field had wrong type while reading");
- return false;
- }
- fieldval = decode_val(input, parsedspec.type, parsedspec.typeargs);
- if (fieldval == NULL) {
- return false;
- }
- if (PyObject_SetAttr(output, parsedspec.attrname, fieldval) == -1) {
- Py_DECREF(fieldval);
- return false;
- }
- Py_DECREF(fieldval);
- }
- return true;
- }
- /* --- MAIN RECURSIVE INPUT FUCNTION --- */
- // Returns a new reference.
- static PyObject*
- decode_val(DecodeBuffer* input, TType type, PyObject* typeargs) {
- switch (type) {
- case T_BOOL: {
- int8_t v = readByte(input);
- if (INT_CONV_ERROR_OCCURRED(v)) {
- return NULL;
- }
- switch (v) {
- case 0: Py_RETURN_FALSE;
- case 1: Py_RETURN_TRUE;
- // Don't laugh. This is a potentially serious issue.
- default: PyErr_SetString(PyExc_TypeError, "boolean out of range"); return NULL;
- }
- break;
- }
- case T_I08: {
- int8_t v = readByte(input);
- if (INT_CONV_ERROR_OCCURRED(v)) {
- return NULL;
- }
- return PyInt_FromLong(v);
- }
- case T_I16: {
- int16_t v = readI16(input);
- if (INT_CONV_ERROR_OCCURRED(v)) {
- return NULL;
- }
- return PyInt_FromLong(v);
- }
- case T_I32: {
- int32_t v = readI32(input);
- if (INT_CONV_ERROR_OCCURRED(v)) {
- return NULL;
- }
- return PyInt_FromLong(v);
- }
- case T_I64: {
- int64_t v = readI64(input);
- if (INT_CONV_ERROR_OCCURRED(v)) {
- return NULL;
- }
- // TODO(dreiss): Find out if we can take this fastpath always when
- // sizeof(long) == sizeof(long long).
- if (CHECK_RANGE(v, LONG_MIN, LONG_MAX)) {
- return PyInt_FromLong((long) v);
- }
- return PyLong_FromLongLong(v);
- }
- case T_DOUBLE: {
- double v = readDouble(input);
- if (v == -1.0 && PyErr_Occurred()) {
- return false;
- }
- return PyFloat_FromDouble(v);
- }
- case T_STRING: {
- Py_ssize_t len = readI32(input);
- char* buf;
- if (!readBytes(input, &buf, len)) {
- return NULL;
- }
- return PyString_FromStringAndSize(buf, len);
- }
- case T_LIST:
- case T_SET: {
- SetListTypeArgs parsedargs;
- int32_t len;
- PyObject* ret = NULL;
- int i;
- if (!parse_set_list_args(&parsedargs, typeargs)) {
- return NULL;
- }
- if (!checkTypeByte(input, parsedargs.element_type)) {
- return NULL;
- }
- len = readI32(input);
- if (!check_ssize_t_32(len)) {
- return NULL;
- }
- ret = PyList_New(len);
- if (!ret) {
- return NULL;
- }
- for (i = 0; i < len; i++) {
- PyObject* item = decode_val(input, parsedargs.element_type, parsedargs.typeargs);
- if (!item) {
- Py_DECREF(ret);
- return NULL;
- }
- PyList_SET_ITEM(ret, i, item);
- }
- // TODO(dreiss): Consider biting the bullet and making two separate cases
- // for list and set, avoiding this post facto conversion.
- if (type == T_SET) {
- PyObject* setret;
- #if (PY_VERSION_HEX < 0x02050000)
- // hack needed for older versions
- setret = PyObject_CallFunctionObjArgs((PyObject*)&PySet_Type, ret, NULL);
- #else
- // official version
- setret = PySet_New(ret);
- #endif
- Py_DECREF(ret);
- return setret;
- }
- return ret;
- }
- case T_MAP: {
- int32_t len;
- int i;
- MapTypeArgs parsedargs;
- PyObject* ret = NULL;
- if (!parse_map_args(&parsedargs, typeargs)) {
- return NULL;
- }
- if (!checkTypeByte(input, parsedargs.ktag)) {
- return NULL;
- }
- if (!checkTypeByte(input, parsedargs.vtag)) {
- return NULL;
- }
- len = readI32(input);
- if (!check_ssize_t_32(len)) {
- return false;
- }
- ret = PyDict_New();
- if (!ret) {
- goto error;
- }
- for (i = 0; i < len; i++) {
- PyObject* k = NULL;
- PyObject* v = NULL;
- k = decode_val(input, parsedargs.ktag, parsedargs.ktypeargs);
- if (k == NULL) {
- goto loop_error;
- }
- v = decode_val(input, parsedargs.vtag, parsedargs.vtypeargs);
- if (v == NULL) {
- goto loop_error;
- }
- if (PyDict_SetItem(ret, k, v) == -1) {
- goto loop_error;
- }
- Py_DECREF(k);
- Py_DECREF(v);
- continue;
- // Yuck! Destructors, anyone?
- loop_error:
- Py_XDECREF(k);
- Py_XDECREF(v);
- goto error;
- }
- return ret;
- error:
- Py_XDECREF(ret);
- return NULL;
- }
- case T_STRUCT: {
- StructTypeArgs parsedargs;
- if (!parse_struct_args(&parsedargs, typeargs)) {
- return NULL;
- }
- PyObject* ret = PyObject_CallObject(parsedargs.klass, NULL);
- if (!ret) {
- return NULL;
- }
- if (!decode_struct(input, ret, parsedargs.spec)) {
- Py_DECREF(ret);
- return NULL;
- }
- return ret;
- }
- case T_STOP:
- case T_VOID:
- case T_UTF16:
- case T_UTF8:
- case T_U64:
- default:
- PyErr_SetString(PyExc_TypeError, "Unexpected TType");
- return NULL;
- }
- }
- /* --- TOP-LEVEL WRAPPER FOR INPUT -- */
- static PyObject*
- decode_binary(PyObject *self, PyObject *args) {
- PyObject* output_obj = NULL;
- PyObject* transport = NULL;
- PyObject* typeargs = NULL;
- StructTypeArgs parsedargs;
- DecodeBuffer input = {};
- if (!PyArg_ParseTuple(args, "OOO", &output_obj, &transport, &typeargs)) {
- return NULL;
- }
- if (!parse_struct_args(&parsedargs, typeargs)) {
- return NULL;
- }
- if (!decode_buffer_from_obj(&input, transport)) {
- return NULL;
- }
- if (!decode_struct(&input, output_obj, parsedargs.spec)) {
- free_decodebuf(&input);
- return NULL;
- }
- free_decodebuf(&input);
- Py_RETURN_NONE;
- }
- /* ====== END READING FUNCTIONS ====== */
- /* -- PYTHON MODULE SETUP STUFF --- */
- static PyMethodDef ThriftFastBinaryMethods[] = {
- {"encode_binary", encode_binary, METH_VARARGS, ""},
- {"decode_binary", decode_binary, METH_VARARGS, ""},
- {NULL, NULL, 0, NULL} /* Sentinel */
- };
- PyMODINIT_FUNC
- initfastbinary(void) {
- #define INIT_INTERN_STRING(value) \
- do { \
- INTERN_STRING(value) = PyString_InternFromString(#value); \
- if(!INTERN_STRING(value)) return; \
- } while(0)
- INIT_INTERN_STRING(cstringio_buf);
- INIT_INTERN_STRING(cstringio_refill);
- #undef INIT_INTERN_STRING
- PycString_IMPORT;
- if (PycStringIO == NULL) return;
- (void) Py_InitModule("thrift.protocol.fastbinary", ThriftFastBinaryMethods);
- }