in lib/py/src/ext/protocol.tcc [339:556]
bool ProtocolBase<Impl>::encodeValue(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.
* encodeValue 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;
}
impl()->writeBool(v);
return true;
}
case T_I08: {
int8_t val;
if (!parse_pyint(value, &val, (std::numeric_limits<int8_t>::min)(),
(std::numeric_limits<int8_t>::max)())) {
return false;
}
impl()->writeI8(val);
return true;
}
case T_I16: {
int16_t val;
if (!parse_pyint(value, &val, (std::numeric_limits<int16_t>::min)(),
(std::numeric_limits<int16_t>::max)())) {
return false;
}
impl()->writeI16(val);
return true;
}
case T_I32: {
int32_t val;
if (!parse_pyint(value, &val, (std::numeric_limits<int32_t>::min)(),
(std::numeric_limits<int32_t>::max)())) {
return false;
}
impl()->writeI32(val);
return true;
}
case T_I64: {
int64_t nval = PyLong_AsLongLong(value);
if (INT_CONV_ERROR_OCCURRED(nval)) {
return false;
}
if (!CHECK_RANGE(nval, (std::numeric_limits<int64_t>::min)(),
(std::numeric_limits<int64_t>::max)())) {
PyErr_SetString(PyExc_OverflowError, "int out of range");
return false;
}
impl()->writeI64(nval);
return true;
}
case T_DOUBLE: {
double nval = PyFloat_AsDouble(value);
if (nval == -1.0 && PyErr_Occurred()) {
return false;
}
impl()->writeDouble(nval);
return true;
}
case T_STRING: {
ScopedPyObject nval;
if (PyUnicode_Check(value)) {
nval.reset(PyUnicode_AsUTF8String(value));
if (!nval) {
return false;
}
} else {
Py_INCREF(value);
nval.reset(value);
}
Py_ssize_t len = PyBytes_Size(nval.get());
if (!detail::check_ssize_t_32(len)) {
return false;
}
impl()->writeString(nval.get(), static_cast<int32_t>(len));
return true;
}
case T_LIST:
case T_SET: {
SetListTypeArgs parsedargs;
if (!parse_set_list_args(&parsedargs, typeargs)) {
return false;
}
Py_ssize_t len = PyObject_Length(value);
if (!detail::check_ssize_t_32(len)) {
return false;
}
if (!impl()->writeListBegin(value, parsedargs, static_cast<int32_t>(len)) || PyErr_Occurred()) {
return false;
}
ScopedPyObject iterator(PyObject_GetIter(value));
if (!iterator) {
return false;
}
while (PyObject* rawItem = PyIter_Next(iterator.get())) {
ScopedPyObject item(rawItem);
if (!encodeValue(item.get(), parsedargs.element_type, parsedargs.typeargs)) {
return false;
}
}
return true;
}
case T_MAP: {
Py_ssize_t len = PyDict_Size(value);
if (!detail::check_ssize_t_32(len)) {
return false;
}
MapTypeArgs parsedargs;
if (!parse_map_args(&parsedargs, typeargs)) {
return false;
}
if (!impl()->writeMapBegin(value, parsedargs, static_cast<int32_t>(len)) || PyErr_Occurred()) {
return false;
}
Py_ssize_t pos = 0;
PyObject* k = nullptr;
PyObject* v = nullptr;
// TODO(bmaurer): should support any mapping, not just dicts
while (PyDict_Next(value, &pos, &k, &v)) {
if (!encodeValue(k, parsedargs.ktag, parsedargs.ktypeargs)
|| !encodeValue(v, parsedargs.vtag, parsedargs.vtypeargs)) {
return false;
}
}
return true;
}
case T_STRUCT: {
StructTypeArgs parsedargs;
if (!parse_struct_args(&parsedargs, typeargs)) {
return false;
}
Py_ssize_t nspec = PyTuple_Size(parsedargs.spec);
if (nspec == -1) {
PyErr_SetString(PyExc_TypeError, "spec is not a tuple");
return false;
}
detail::WriteStructScope<Impl> scope = detail::writeStructScope(this);
if (!scope) {
return false;
}
for (Py_ssize_t i = 0; i < nspec; i++) {
PyObject* spec_tuple = PyTuple_GET_ITEM(parsedargs.spec, i);
if (spec_tuple == Py_None) {
continue;
}
StructItemSpec parsedspec;
if (!parse_struct_item_spec(&parsedspec, spec_tuple)) {
return false;
}
ScopedPyObject instval(PyObject_GetAttr(value, parsedspec.attrname));
if (!instval) {
return false;
}
if (instval.get() == Py_None) {
continue;
}
bool res = impl()->writeField(instval.get(), parsedspec);
if (!res) {
return false;
}
}
impl()->writeFieldStop();
return true;
}
case T_STOP:
case T_VOID:
case T_UTF16:
case T_UTF8:
case T_U64:
default:
PyErr_Format(PyExc_TypeError, "Unexpected TType for encodeValue: %d", type);
return false;
}
return true;
}