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; }
static PyObject* decode_struct(DecodeBuffer* input, PyObject* output, PyObject* klass, PyObject* spec_seq, long string_limit, long container_limit) { int spec_seq_len = PyTuple_Size(spec_seq); bool immutable = output == Py_None; PyObject* kwargs = NULL; if (spec_seq_len == -1) { return NULL; } if (immutable) { kwargs = PyDict_New(); if (!kwargs) { PyErr_SetString(PyExc_TypeError, "failed to prepare kwargument storage"); return NULL; } } while (true) { TType type; int16_t tag; PyObject* item_spec; PyObject* fieldval = NULL; StructItemSpec parsedspec; type = readByte(input); if (type == -1) { goto error; } if (type == T_STOP) { break; } tag = readI16(input); if (INT_CONV_ERROR_OCCURRED(tag)) { goto error; } 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)) { goto error; } else { continue; } } if (!parse_struct_item_spec(&parsedspec, item_spec)) { goto error; } if (parsedspec.type != type) { if (!skip(input, type)) { PyErr_Format(PyExc_TypeError, "struct field had wrong type: expected %d but got %d", parsedspec.type, type); goto error; } else { continue; } } fieldval = decode_val(input, parsedspec.type, parsedspec.typeargs, string_limit, container_limit); if (fieldval == NULL) { goto error; } if ((immutable && PyDict_SetItem(kwargs, parsedspec.attrname, fieldval) == -1) || (!immutable && PyObject_SetAttr(output, parsedspec.attrname, fieldval) == -1)) { Py_DECREF(fieldval); goto error; } Py_DECREF(fieldval); } if (immutable) { PyObject* args = PyTuple_New(0); PyObject* ret = NULL; if (!args) { PyErr_SetString(PyExc_TypeError, "failed to prepare argument storage"); goto error; } ret = PyObject_Call(klass, args, kwargs); Py_DECREF(kwargs); Py_DECREF(args); return ret; } Py_INCREF(output); return output; error: Py_XDECREF(kwargs); return NULL; }
static bool 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 = 0; if (is_utf8(typeargs) && PyUnicode_Check(value)) value = PyUnicode_AsUTF8String(value); 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; }