static bool decode_struct()

in thrift/lib/py/protocol/fastproto.cpp [626:781]


static bool decode_struct(
    Reader* reader, PyObject* value, StructTypeArgs* args, int utf8strings) {
  int speclen = PyTuple_Size(args->spec);
  if (speclen == -1) {
    return false;
  }

  std::string sname;
  reader->readStructBegin(sname);

  std::string fname;
  TType ftype;
  int16_t fid;
  PyObject* itemspec;
  StructItemSpec parsedspec;

  int first_tag = 0;
  bool first_tag_read = false;
  PyObject* first_item_spec;
  StructItemSpec first_parsed_spec;

  while (true) {
    PyObject* fieldval = nullptr;
    reader->readFieldBegin(fname, ftype, fid);
    if (ftype == TType::T_STOP) {
      break;
    }

    if (!first_tag_read) {
      first_tag_read = true;
      if (speclen == 0) { // Empty struct and all fields will be skipped
        first_item_spec = Py_None;
      } else {
        first_item_spec = PyTuple_GET_ITEM(args->spec, 0);
        if (first_item_spec != Py_None) {
          if (!parse_struct_item_spec(&first_parsed_spec, first_item_spec)) {
            return false;
          }
          first_tag = first_parsed_spec.tag;
        }
      }
    }

    fid -= first_tag;
    if (fid > 0 && fid < speclen) {
      itemspec = PyTuple_GET_ITEM(args->spec, fid);
    } else if (fid == 0) {
      itemspec = first_item_spec;
    } else {
      itemspec = Py_None;
    }

    if (itemspec == Py_None) {
      reader->skip(ftype);
      continue;
    }

    if (fid == 0) {
      parsedspec = first_parsed_spec;
    } else if (!parse_struct_item_spec(&parsedspec, itemspec)) {
      return false;
    }

    if (parsedspec.type != ftype) {
      reader->skip(ftype);
      continue;
    }

    fieldval = decode_val(
        reader, parsedspec.type, parsedspec.typeargs, utf8strings, args);

    if (!fieldval) {
      PyObject *pType, *pValue, *pTraceback;
      PyErr_Fetch(&pType, &pValue, &pTraceback);
      if (!PyErr_GivenExceptionMatches(pType, PyExc_UnicodeDecodeError)) {
        PyErr_Restore(pType, pValue, pTraceback);
        return false;
      }
#if PY_MAJOR_VERSION >= 3
      const char* fieldName = PyUnicode_AsUTF8(parsedspec.attrname);
#else
      const char* fieldName = PyString_AsString(parsedspec.attrname);
#endif
      PyObject* create_func_method_name = Py_BuildValue(
          "s", "create_ThriftUnicodeDecodeError_from_UnicodeDecodeError");
      PyObject* pFieldName = Py_BuildValue("s", fieldName);

      PyObject* decode_error = PyObject_CallMethodObjArgs(
          ExceptionsModule, create_func_method_name, pValue, pFieldName, NULL);
      PyObject* thrift_decode_error_type = PyObject_Type(decode_error);
      PyErr_SetObject(thrift_decode_error_type, decode_error);
      Py_DECREF(pType);
      Py_DECREF(pValue);
      if (pTraceback != nullptr) {
        Py_DECREF(pTraceback);
      }
      return false;
    }

    if (args->isunion) {
      PyObject* fieldobj = PyObject_GetAttrString(value, "field");
      if (!fieldobj) {
        Py_DECREF(fieldval);
        return false;
      }
      int field = (int)AS_LONG(fieldobj);
      if (field == -1 && PyErr_Occurred()) {
        Py_DECREF(fieldval);
        Py_DECREF(fieldobj);
        return false;
      }
      if (field != 0) {
        PyErr_SetString(PyExc_AssertionError, "field already set in union");
        Py_DECREF(fieldval);
        Py_DECREF(fieldobj);
        return false;
      }
      Py_DECREF(fieldobj);

      PyObject* valueobj = PyObject_GetAttrString(value, "value");
      if (!valueobj) {
        Py_DECREF(fieldobj);
        return false;
      }
      if (valueobj != Py_None) {
        PyErr_SetString(PyExc_AssertionError, "value already set in union");
        Py_DECREF(fieldval);
        Py_DECREF(valueobj);
        return false;
      }
      Py_DECREF(valueobj);

      PyObject* tagobj = FROM_LONG(parsedspec.tag);
      if (!tagobj) {
        return false;
      }
      if (PyObject_SetAttrString(value, "value", fieldval) == -1 ||
          PyObject_SetAttrString(value, "field", tagobj) == -1) {
        Py_DECREF(fieldval);
        Py_DECREF(tagobj);
        return false;
      }
      Py_DECREF(tagobj);
    } else {
      if (PyObject_SetAttr(value, parsedspec.attrname, fieldval) == -1) {
        Py_DECREF(fieldval);
        return false;
      }
    }
    Py_DECREF(fieldval);
    reader->readFieldEnd();
  }

  reader->readStructEnd();
  return true;
}