Rev 30 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/** Licensed to the Apache Software Foundation (ASF) under one* or more contributor license agreements. See the NOTICE file* distributed with this work for additional information* regarding copyright ownership. The ASF licenses this file* to you under the Apache License, Version 2.0 (the* "License"); you may not use this file except in compliance* with the License. You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing,* software distributed under the License is distributed on an* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY* KIND, either express or implied. See the License for the* specific language governing permissions and limitations* under the License.*/#include <Python.h>#include "cStringIO.h"#include <stdbool.h>#include <stdint.h>#include <netinet/in.h>/* Fix endianness issues on Solaris */#if defined (__SVR4) && defined (__sun)#if defined(__i386) && !defined(__i386__)#define __i386__#endif#ifndef BIG_ENDIAN#define BIG_ENDIAN (4321)#endif#ifndef LITTLE_ENDIAN#define LITTLE_ENDIAN (1234)#endif/* I386 is LE, even on Solaris */#if !defined(BYTE_ORDER) && defined(__i386__)#define BYTE_ORDER LITTLE_ENDIAN#endif#endif// 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 boolcheck_ssize_t_32(Py_ssize_t len) {// error from getting the intif (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 boolparse_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 boolparse_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 boolparse_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 boolparse_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 intparse_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 intoutput_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 dictswhile (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;}Py_DECREF(k);Py_DECREF(v);}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 voidfree_decodebuf(DecodeBuffer* d) {Py_XDECREF(d->stringiobuf);Py_XDECREF(d->refill_callable);}static booldecode_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 codepathnewiobuf = 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 bufferPy_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 boolcheckTypeByte(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 boolskip(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); // tagif (!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 booldecode_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) {if (!skip(input, type)) {PyErr_SetString(PyExc_TypeError, "struct field had wrong type while reading and can't be skipped");return false;} else {continue;}}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 versionssetret = PyObject_CallFunctionObjArgs((PyObject*)&PySet_Type, ret, NULL);#else// official versionsetret = PySet_New(ret);#endifPy_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_FUNCinitfastbinary(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_STRINGPycString_IMPORT;if (PycStringIO == NULL) return;(void) Py_InitModule("thrift.protocol.fastbinary", ThriftFastBinaryMethods);}