bool ProtocolBase::encodeValue()

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;
}