void binary_serialize()

in lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp [695:849]


void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval* value, HashTable* fieldspec) {
  if (value) {
    ZVAL_DEREF(value);
  }
  // At this point the typeID (and field num, if applicable) should've already been written to the output so all we need to do is write the payload.
  switch (thrift_typeID) {
    case T_STOP:
    case T_VOID:
      return;
    case T_STRUCT: {
      if (Z_TYPE_P(value) != IS_OBJECT) {
        throw_tprotocolexception("Attempt to send non-object type as a T_STRUCT", INVALID_DATA);
      }
      zval* spec = zend_read_static_property(Z_OBJCE_P(value), "_TSPEC", sizeof("_TSPEC")-1, true);
      if (spec && Z_TYPE_P(spec) == IS_REFERENCE) {
        ZVAL_DEREF(spec);
      }
      if (!spec || Z_TYPE_P(spec) != IS_ARRAY) {
        throw_tprotocolexception("Attempt to send non-Thrift object as a T_STRUCT", INVALID_DATA);
      }
      binary_serialize_spec(value, transport, Z_ARRVAL_P(spec));
    } return;
    case T_BOOL:
      if (!zval_is_bool(value)) convert_to_boolean(value);
      transport.writeI8(Z_TYPE_INFO_P(value) == IS_TRUE ? 1 : 0);
      return;
    case T_BYTE:
      if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
      transport.writeI8(Z_LVAL_P(value));
      return;
    case T_I16:
      if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
      transport.writeI16(Z_LVAL_P(value));
      return;
    case T_I32:
      if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
      transport.writeI32(Z_LVAL_P(value));
      return;
    case T_I64:
    case T_U64: {
      int64_t l_data;
#if defined(_LP64) || defined(_WIN64)
      if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value);
      l_data = Z_LVAL_P(value);
#else
      if (Z_TYPE_P(value) != IS_DOUBLE) convert_to_double(value);
      l_data = (int64_t)Z_DVAL_P(value);
#endif
      transport.writeI64(l_data);
    } return;
    case T_DOUBLE: {
      union {
        int64_t c;
        double d;
      } a;
      if (Z_TYPE_P(value) != IS_DOUBLE) convert_to_double(value);
      a.d = Z_DVAL_P(value);
      transport.writeI64(a.c);
    } return;
    case T_UTF8:
    case T_UTF16:
    case T_STRING:
      if (Z_TYPE_P(value) != IS_STRING) convert_to_string(value);
      transport.writeString(Z_STRVAL_P(value), Z_STRLEN_P(value));
      return;
    case T_MAP: {
      if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value);
      if (Z_TYPE_P(value) != IS_ARRAY) {
        throw_tprotocolexception("Attempt to send an incompatible type as an array (T_MAP)", INVALID_DATA);
      }
      HashTable* ht = Z_ARRVAL_P(value);
      zval* val_ptr;

      val_ptr = zend_hash_str_find(fieldspec, "ktype", sizeof("ktype")-1);
      if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
      uint8_t keytype = Z_LVAL_P(val_ptr);
      transport.writeI8(keytype);
      val_ptr = zend_hash_str_find(fieldspec, "vtype", sizeof("vtype")-1);
      if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
      uint8_t valtype = Z_LVAL_P(val_ptr);
      transport.writeI8(valtype);

      val_ptr = zend_hash_str_find(fieldspec, "val", sizeof("val")-1);
      HashTable* valspec = Z_ARRVAL_P(val_ptr);
      HashTable* keyspec = Z_ARRVAL_P(zend_hash_str_find(fieldspec, "key", sizeof("key")-1));

      transport.writeI32(zend_hash_num_elements(ht));
      HashPosition key_ptr;
      for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
           (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
           zend_hash_move_forward_ex(ht, &key_ptr)) {
        binary_serialize_hashtable_key(keytype, transport, ht, key_ptr, keyspec);
        binary_serialize(valtype, transport, val_ptr, valspec);
      }
    } return;
    case T_LIST: {
      if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value);
      if (Z_TYPE_P(value) != IS_ARRAY) {
        throw_tprotocolexception("Attempt to send an incompatible type as an array (T_LIST)", INVALID_DATA);
      }
      HashTable* ht = Z_ARRVAL_P(value);
      zval* val_ptr;

      val_ptr = zend_hash_str_find(fieldspec, "etype", sizeof("etype")-1);
      if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
      uint8_t valtype = Z_LVAL_P(val_ptr);
      transport.writeI8(valtype);

      val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1);
      HashTable* valspec = Z_ARRVAL_P(val_ptr);

      transport.writeI32(zend_hash_num_elements(ht));
      HashPosition key_ptr;
      for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
           (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
           zend_hash_move_forward_ex(ht, &key_ptr)) {
        binary_serialize(valtype, transport, val_ptr, valspec);
      }
    } return;
    case T_SET: {
      if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value);
      if (Z_TYPE_P(value) != IS_ARRAY) {
        throw_tprotocolexception("Attempt to send an incompatible type as an array (T_SET)", INVALID_DATA);
      }
      HashTable* ht = Z_ARRVAL_P(value);
      zval* val_ptr;

      val_ptr = zend_hash_str_find(fieldspec, "etype", sizeof("etype")-1);
      HashTable* spec = Z_ARRVAL_P(zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1));
      if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr);
      uint8_t keytype = Z_LVAL_P(val_ptr);
      transport.writeI8(keytype);

      transport.writeI32(zend_hash_num_elements(ht));
      HashPosition key_ptr;
      if(ttype_is_scalar(keytype)){
        for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
             (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
             zend_hash_move_forward_ex(ht, &key_ptr)) {
          binary_serialize_hashtable_key(keytype, transport, ht, key_ptr, spec);
        }
      } else {
        for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr);
             (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr;
             zend_hash_move_forward_ex(ht, &key_ptr)) {
          binary_serialize(keytype, transport, val_ptr, spec);
        }
      }
    } return;
  };

  char errbuf[128];
  snprintf(errbuf, 128, "Unknown thrift typeID %d", thrift_typeID);
  throw_tprotocolexception(errbuf, INVALID_DATA);
}