void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval** value, HashTable* fieldspec) { // 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: { TSRMLS_FETCH(); if (Z_TYPE_PP(value) != IS_OBJECT) { throw_tprotocolexception("Attempt to send non-object type as a T_STRUCT", INVALID_DATA); } zval* spec = zend_read_static_property(zend_get_class_entry(*value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC); binary_serialize_spec(*value, transport, Z_ARRVAL_P(spec)); } return; case T_BOOL: if (Z_TYPE_PP(value) != IS_BOOL) convert_to_boolean(*value); transport.writeI8(Z_BVAL_PP(value) ? 1 : 0); return; case T_BYTE: if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value); transport.writeI8(Z_LVAL_PP(value)); return; case T_I16: if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value); transport.writeI16(Z_LVAL_PP(value)); return; case T_I32: if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value); transport.writeI32(Z_LVAL_PP(value)); return; case T_I64: case T_U64: if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value); transport.writeI64(Z_LVAL_PP(value)); return; case T_DOUBLE: { union { int64_t c; double d; } a; if (Z_TYPE_PP(value) != IS_DOUBLE) convert_to_double(*value); a.d = Z_DVAL_PP(value); transport.writeI64(a.c); } return; //case T_UTF7: case T_UTF8: case T_UTF16: case T_STRING: if (Z_TYPE_PP(value) != IS_STRING) convert_to_string(*value); transport.writeString(Z_STRVAL_PP(value), Z_STRLEN_PP(value)); return; case T_MAP: { if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value); if (Z_TYPE_PP(value) != IS_ARRAY) { throw_tprotocolexception("Attempt to send an incompatible type as an array (T_MAP)", INVALID_DATA); } HashTable* ht = Z_ARRVAL_PP(value); zval** val_ptr; zend_hash_find(fieldspec, "ktype", 6, (void**)&val_ptr); if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr); uint8_t keytype = Z_LVAL_PP(val_ptr); transport.writeI8(keytype); zend_hash_find(fieldspec, "vtype", 6, (void**)&val_ptr); if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr); uint8_t valtype = Z_LVAL_PP(val_ptr); transport.writeI8(valtype); zend_hash_find(fieldspec, "val", 4, (void**)&val_ptr); HashTable* valspec = Z_ARRVAL_PP(val_ptr); transport.writeI32(zend_hash_num_elements(ht)); HashPosition key_ptr; for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) { binary_serialize_hashtable_key(keytype, transport, ht, key_ptr); binary_serialize(valtype, transport, val_ptr, valspec); } } return; case T_LIST: { if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value); if (Z_TYPE_PP(value) != IS_ARRAY) { throw_tprotocolexception("Attempt to send an incompatible type as an array (T_LIST)", INVALID_DATA); } HashTable* ht = Z_ARRVAL_PP(value); zval** val_ptr; zend_hash_find(fieldspec, "etype", 6, (void**)&val_ptr); if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr); uint8_t valtype = Z_LVAL_PP(val_ptr); transport.writeI8(valtype); zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr); HashTable* valspec = Z_ARRVAL_PP(val_ptr); transport.writeI32(zend_hash_num_elements(ht)); HashPosition key_ptr; for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) { binary_serialize(valtype, transport, val_ptr, valspec); } } return; case T_SET: { if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value); if (Z_TYPE_PP(value) != IS_ARRAY) { throw_tprotocolexception("Attempt to send an incompatible type as an array (T_SET)", INVALID_DATA); } HashTable* ht = Z_ARRVAL_PP(value); zval** val_ptr; zend_hash_find(fieldspec, "etype", 6, (void**)&val_ptr); if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr); uint8_t keytype = Z_LVAL_PP(val_ptr); transport.writeI8(keytype); transport.writeI32(zend_hash_num_elements(ht)); HashPosition key_ptr; for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) { binary_serialize_hashtable_key(keytype, transport, ht, key_ptr); } } return; }; char errbuf[128]; sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID); throw_tprotocolexception(errbuf, INVALID_DATA); }
void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, const Variant& value, const Array& fieldspec) { // 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 (!value.is(KindOfObject)) { throw_tprotocolexception("Attempt to send non-object " "type as a T_STRUCT", INVALID_DATA); } binary_serialize_spec(value.toObject(), transport, HHVM_FN(hphp_get_static_property)(value.toObject()-> getClassName(), s_TSPEC, false).toArray()); } return; case T_BOOL: transport.writeI8(value.toBoolean() ? 1 : 0); return; case T_BYTE: transport.writeI8(value.toByte()); return; case T_I16: transport.writeI16(value.toInt16()); return; case T_I32: transport.writeI32(value.toInt32()); return; case T_I64: case T_U64: transport.writeI64(value.toInt64()); return; case T_DOUBLE: { union { int64_t c; double d; } a; a.d = value.toDouble(); transport.writeI64(a.c); } return; case T_FLOAT: { union { int32_t c; float d; } a; a.d = (float)value.toDouble(); transport.writeI32(a.c); } return; //case T_UTF7: case T_UTF8: case T_UTF16: case T_STRING: { String sv = value.toString(); transport.writeString(sv.data(), sv.size()); } return; case T_MAP: { Array ht = value.toArray(); uint8_t keytype = fieldspec.rvalAt(PHPTransport::s_ktype, AccessFlags::Error_Key).toByte(); transport.writeI8(keytype); uint8_t valtype = fieldspec.rvalAt(PHPTransport::s_vtype, AccessFlags::Error_Key).toByte(); transport.writeI8(valtype); Array valspec = fieldspec.rvalAt(PHPTransport::s_val, AccessFlags::Error_Key).toArray(); transport.writeI32(ht.size()); for (ArrayIter key_ptr = ht.begin(); !key_ptr.end(); ++key_ptr) { binary_serialize_hashtable_key(keytype, transport, key_ptr.first()); binary_serialize(valtype, transport, key_ptr.second(), valspec); } } return; case T_LIST: { Array ht = value.toArray(); Variant val; uint8_t valtype = fieldspec.rvalAt(PHPTransport::s_etype, AccessFlags::Error_Key).toInt64(); transport.writeI8(valtype); Array valspec = fieldspec.rvalAt(PHPTransport::s_elem, AccessFlags::Error_Key).toArray(); transport.writeI32(ht.size()); for (ArrayIter key_ptr = ht.begin(); !key_ptr.end(); ++key_ptr) { binary_serialize(valtype, transport, key_ptr.second(), valspec); } } return; case T_SET: { Array ht = value.toArray(); uint8_t keytype = fieldspec.rvalAt(PHPTransport::s_etype, AccessFlags::Error_Key).toByte(); transport.writeI8(keytype); transport.writeI32(ht.size()); for (ArrayIter key_ptr = ht.begin(); !key_ptr.end(); ++key_ptr) { binary_serialize_hashtable_key(keytype, transport, key_ptr.first()); } } return; }; char errbuf[128]; sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID); throw_tprotocolexception(String(errbuf, CopyString), INVALID_DATA); }
static void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval* value, HashTable* fieldspec) { // 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, false); if (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); 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); 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); 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; 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); } } return; }; char errbuf[128]; snprintf(errbuf, 128, "Unknown thrift typeID %d", thrift_typeID); throw_tprotocolexception(errbuf, INVALID_DATA); }