static bool encode_impl()

in thrift/lib/py/protocol/fastproto.cpp [283:615]


static bool encode_impl(
    Writer* writer,
    PyObject* value,
    PyObject* typeargs,
    TType type,
    int utf8strings) {
  switch (type) {
    case TType::T_BOOL: {
      int v = PyObject_IsTrue(value);
      if (v == -1) {
        return false;
      }
      writer->writeBool(v == 1);
      break;
    }
    case TType::T_I08: {
      int32_t val;
      if (!parse_pyint(value, &val, INT8_MIN, INT8_MAX)) {
        return false;
      }
      writer->writeByte((int8_t)val);
      break;
    }
    case TType::T_I16: {
      int32_t val;
      if (!parse_pyint(value, &val, INT16_MIN, INT16_MAX)) {
        return false;
      }
      writer->writeI16((int16_t)val);
      break;
    }
    case TType::T_I32: {
      int32_t val;
      if (!parse_pyint(value, &val, INT32_MIN, INT32_MAX)) {
        return false;
      }
      writer->writeI32(val);
      break;
    }
    case TType::T_I64: {
      int64_t val = PyLong_AsLongLong(value);
      if (INT_CONV_ERROR_OCCURRED(val)) {
        return false;
      }
      if (!CHECK_RANGE(val, INT64_MIN, INT64_MAX)) {
        PyErr_SetString(PyExc_OverflowError, "int out of range");
        return false;
      }
      writer->writeI64(val);
      break;
    }
    case TType::T_DOUBLE: {
      double val;
      if (!parse_pyfloat(value, &val)) {
        return false;
      }
      writer->writeDouble(val);
      break;
    }
    case TType::T_FLOAT: {
      double val;
      if (!parse_pyfloat(value, &val)) {
        return false;
      }
      writer->writeFloat((float)val);
      break;
    }
    case TType::T_STRING: {
      bool encoded = false;
      Py_ssize_t len;

#if PY_MAJOR_VERSION >= 3
      if (!PyBytes_Check(value)) {
        value = PyUnicode_AsUTF8String(value);
        if (value == nullptr) {
          PyErr_SetString(PyExc_TypeError, "can not encode");
          return false;
        }
        encoded = true;
      }
      len = PyBytes_Size(value);
#else
      if (utf8strings && value->ob_type == &PyUnicode_Type) {
        value = PyUnicode_AsUTF8String(value);
        if (value == nullptr) {
          PyErr_SetString(PyExc_TypeError, "can not encode using utf8");
          return false;
        }
        encoded = true;
      }
      len = PyString_Size(value);
#endif

      if (!check_ssize_t_32(len)) {
        return false;
      }

#if PY_MAJOR_VERSION >= 3
      folly::StringPiece str(PyBytes_AsString(value), len);
#else
      folly::StringPiece str(PyString_AsString(value), len);
#endif
      writer->writeString(str);
      if (encoded) {
        Py_DECREF(value);
      }
      break;
    }
    case TType::T_LIST:
    case TType::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;
      }

      writer->writeListBegin(parsedargs.element_type, (uint32_t)len);
      iterator = PyObject_GetIter(value);
      if (iterator == nullptr) {
        return false;
      }

      while ((item = PyIter_Next(iterator))) {
        if (!encode_impl(
                writer,
                item,
                parsedargs.typeargs,
                parsedargs.element_type,
                utf8strings)) {
          Py_DECREF(item);
          Py_DECREF(iterator);
          return false;
        }
        Py_DECREF(item);
      }

      Py_DECREF(iterator);
      writer->writeListEnd();

      if (PyErr_Occurred()) {
        return false;
      }
      break;
    }
    case TType::T_MAP: {
      PyObject *k, *v;
      Py_ssize_t pos = 0, len;

      MapTypeArgs parsedargs;

      len = PyDict_Size(value);
      if (!check_ssize_t_32(len)) {
        return false;
      }

      if (!parse_map_args(&parsedargs, typeargs)) {
        return false;
      }

      writer->writeMapBegin(parsedargs.ktype, parsedargs.vtype, (uint32_t)len);
      while (PyDict_Next(value, &pos, &k, &v)) {
        Py_INCREF(k);
        Py_INCREF(v);

        if (!encode_impl(
                writer,
                k,
                parsedargs.ktypeargs,
                parsedargs.ktype,
                utf8strings) ||
            !encode_impl(
                writer,
                v,
                parsedargs.vtypeargs,
                parsedargs.vtype,
                utf8strings)) {
          Py_DECREF(k);
          Py_DECREF(v);
          return false;
        }
        Py_DECREF(k);
        Py_DECREF(v);
      }
      writer->writeMapEnd();
      break;
    }
    case TType::T_STRUCT: {
      StructTypeArgs parsedargs;

      if (!parse_struct_args(&parsedargs, typeargs)) {
        return false;
      }

      Py_ssize_t nspec = PyTuple_Size(parsedargs.spec);
      if (nspec == -1) {
        return false;
      }

      auto guard = folly::makeDismissedGuard([&] { Py_DECREF(value); });
      if (parsedargs.adapter != Py_None && value != Py_None) {
        value = PyObject_CallMethodObjArgs(
            parsedargs.adapter, INTERN_STRING(to_thrift), value, nullptr);
        if (!value) {
          return false;
        }
        guard.rehire();
      }

      if (parsedargs.isunion) {
        // Union only has a field and a value.
        writer->writeStructBegin("");
        PyObject* field = PyObject_GetAttrString(value, "field");
        if (!field) {
          return false;
        }

        int fid = static_cast<int>(AS_LONG(field));
        PyObject* spec_tuple = PyTuple_GET_ITEM(parsedargs.spec, fid);
        if (spec_tuple != Py_None) {
          StructItemSpec parsedspec;
          PyObject* instval = nullptr;

          if (!parse_struct_item_spec(&parsedspec, spec_tuple)) {
            return false;
          }

          instval = PyObject_GetAttrString(value, "value");
          if (!instval) {
            return false;
          }

          if (instval == Py_None) {
            Py_DECREF(instval);
            return false;
          }

#if PY_MAJOR_VERSION >= 3
          const char* fieldname = PyUnicode_AsUTF8(parsedspec.attrname);
#else
          const char* fieldname = PyString_AsString(parsedspec.attrname);
#endif
          writer->writeFieldBegin(fieldname, parsedspec.type, parsedspec.tag);
          if (!encode_impl(
                  writer,
                  instval,
                  parsedspec.typeargs,
                  parsedspec.type,
                  utf8strings)) {
            Py_DECREF(instval);
            return false;
          }

          Py_DECREF(instval);
          writer->writeFieldEnd();
        }

        writer->writeFieldStop();
        writer->writeStructEnd();
        break;
      }

      // Both binary and compact ignore the struct name.
      writer->writeStructBegin("");
      for (Py_ssize_t i = 0; i < nspec; i++) {
        StructItemSpec parsedspec;
        PyObject* spec_tuple;
        PyObject* instval = nullptr;

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

#if PY_MAJOR_VERSION >= 3
        const char* fieldname = PyUnicode_AsUTF8(parsedspec.attrname);
#else
        const char* fieldname = PyString_AsString(parsedspec.attrname);
#endif
        writer->writeFieldBegin(fieldname, parsedspec.type, parsedspec.tag);
        if (!encode_impl(
                writer,
                instval,
                parsedspec.typeargs,
                parsedspec.type,
                utf8strings)) {
          Py_DECREF(instval);
          return false;
        }

        Py_DECREF(instval);
        writer->writeFieldEnd();
      }

      writer->writeFieldStop();
      writer->writeStructEnd();
      break;
    }
    case TType::T_STOP:
    case TType::T_VOID:
    case TType::T_UTF8:
    case TType::T_UTF16:
    case TType::T_U64:
    case TType::T_STREAM:
    default:
      PyErr_SetString(PyExc_TypeError, "Unexpected TType");
      return false;
  }

  return true;
}