/* * 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 #include "cStringIO.h" #include #include #include /* 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 # 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; } 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 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) { 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 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); }