Variant HHVM_FUNCTION(thrift_protocol_read_binary, const Variant& transportobj, const String& obj_typename, bool strict_read) { PHPInputTransport transport(transportobj.toObject()); int8_t messageType = 0; int32_t sz = transport.readI32(); if (sz < 0) { // Check for correct version number int32_t version = sz & VERSION_MASK; if (version != VERSION_1) { char errbuf[128]; sprintf(errbuf, "Bad version identifier, sz=%d", sz); throw_tprotocolexception(String(errbuf, CopyString), BAD_VERSION); } messageType = (sz & 0x000000ff); int32_t namelen = transport.readI32(); // skip the name string and the sequence ID, we don't care about those transport.skip(namelen + 4); } else { if (strict_read) { char errbuf[128]; sprintf(errbuf, "No version identifier... old protocol client in strict mode? sz=%d", sz); throw_tprotocolexception(String(errbuf, CopyString), BAD_VERSION); } else { // Handle pre-versioned input transport.skip(sz); // skip string body messageType = transport.readI8(); transport.skip(4); // skip sequence number } } if (messageType == T_EXCEPTION) { Object ex = createObject("TApplicationException"); Variant spec = HHVM_FN(hphp_get_static_property)("TApplicationException", s_TSPEC, false); binary_deserialize_spec(ex, transport, spec.toArray()); throw ex; } Object ret_val = createObject(obj_typename); Variant spec = HHVM_FN(hphp_get_static_property)(obj_typename, s_TSPEC, false); binary_deserialize_spec(ret_val, transport, spec.toArray()); return ret_val; }
Variant HHVM_FUNCTION(thrift_protocol_read_binary_struct, const Variant& transportobj, const String& obj_typename) { PHPInputTransport transport(transportobj.toObject()); Object ret_val = createObject(obj_typename); Variant spec = HHVM_FN(hphp_get_static_property)(obj_typename, s_TSPEC, false); binary_deserialize_spec(ret_val, transport, spec.toArray()); return ret_val; }
Variant f_thrift_protocol_read_binary(CObjRef transportobj, CStrRef obj_typename, bool strict_read) { PHPInputTransport transport(transportobj); int8_t messageType = 0; int32_t sz = transport.readI32(); if (sz < 0) { // Check for correct version number int32_t version = sz & VERSION_MASK; if (version != VERSION_1) { throw_tprotocolexception("Bad version identifier", BAD_VERSION); } messageType = (sz & 0x000000ff); int32_t namelen = transport.readI32(); // skip the name string and the sequence ID, we don't care about those transport.skip(namelen + 4); } else { if (strict_read) { throw_tprotocolexception("No version identifier... old protocol client in strict mode?", BAD_VERSION); } else { // Handle pre-versioned input transport.skip(sz); // skip string body messageType = transport.readI8(); transport.skip(4); // skip sequence number } } if (messageType == T_EXCEPTION) { Object ex = createObject("TApplicationException"); Variant spec = f_hphp_get_static_property("TApplicationException", "_TSPEC"); binary_deserialize_spec(ex, transport, spec.toArray()); throw ex; } Object ret_val = createObject(obj_typename); Variant spec = f_hphp_get_static_property(obj_typename, "_TSPEC"); binary_deserialize_spec(ret_val, transport, spec.toArray()); return ret_val; }
Variant binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, const Array& fieldspec) { Variant ret; switch (thrift_typeID) { case T_STOP: case T_VOID: return init_null(); case T_STRUCT: { Variant val; if ((val = fieldspec.rvalAt(PHPTransport::s_class)).isNull()) { throw_tprotocolexception("no class type in spec", INVALID_DATA); skip_element(T_STRUCT, transport); return init_null(); } String structType = val.toString(); ret = createObject(structType); if (ret.isNull()) { // unable to create class entry skip_element(T_STRUCT, transport); return init_null(); } Variant spec = HHVM_FN(hphp_get_static_property)(structType, s_TSPEC, false); if (!spec.is(KindOfArray)) { char errbuf[128]; snprintf(errbuf, 128, "spec for %s is wrong type: %d\n", structType.data(), ret.getType()); throw_tprotocolexception(String(errbuf, CopyString), INVALID_DATA); return init_null(); } binary_deserialize_spec(ret.toObject(), transport, spec.toArray()); return ret; } break; case T_BOOL: { uint8_t c; transport.readBytes(&c, 1); return c != 0; } //case T_I08: // same numeric value as T_BYTE case T_BYTE: { uint8_t c; transport.readBytes(&c, 1); return Variant((int8_t)c); } case T_I16: { uint16_t c; transport.readBytes(&c, 2); return Variant((int16_t)ntohs(c)); } case T_I32: { uint32_t c; transport.readBytes(&c, 4); return Variant((int32_t)ntohl(c)); } case T_U64: case T_I64: { uint64_t c; transport.readBytes(&c, 8); return Variant((int64_t)ntohll(c)); } case T_DOUBLE: { union { uint64_t c; double d; } a; transport.readBytes(&(a.c), 8); a.c = ntohll(a.c); return a.d; } case T_FLOAT: { union { uint32_t c; float d; } a; transport.readBytes(&(a.c), 4); a.c = ntohl(a.c); return a.d; } //case T_UTF7: // aliases T_STRING case T_UTF8: case T_UTF16: case T_STRING: { uint32_t size = transport.readU32(); if (size && (size + 1)) { String s = String(size, ReserveString); char* strbuf = s.mutableData(); transport.readBytes(strbuf, size); s.setSize(size); return s; } else { return empty_string_variant(); } } case T_MAP: { // array of key -> value uint8_t types[2]; transport.readBytes(types, 2); uint32_t size = transport.readU32(); Array keyspec = fieldspec.rvalAt(PHPTransport::s_key, AccessFlags::Error_Key).toArray(); Array valspec = fieldspec.rvalAt(PHPTransport::s_val, AccessFlags::Error_Key).toArray(); String format = fieldspec.rvalAt(PHPTransport::s_format, AccessFlags::None).toString(); if (format.equal(PHPTransport::s_collection)) { ret = newobj<c_Map>(); for (uint32_t s = 0; s < size; ++s) { Variant key = binary_deserialize(types[0], transport, keyspec); Variant value = binary_deserialize(types[1], transport, valspec); collectionSet(ret.getObjectData(), key.asCell(), value.asCell()); } } else { ret = Array::Create(); for (uint32_t s = 0; s < size; ++s) { Variant key = binary_deserialize(types[0], transport, keyspec); Variant value = binary_deserialize(types[1], transport, valspec); ret.toArrRef().set(key, value); } } return ret; // return_value already populated } case T_LIST: { // array with autogenerated numeric keys int8_t type = transport.readI8(); uint32_t size = transport.readU32(); Variant elemvar = fieldspec.rvalAt(PHPTransport::s_elem, AccessFlags::Error_Key); Array elemspec = elemvar.toArray(); String format = fieldspec.rvalAt(PHPTransport::s_format, AccessFlags::None).toString(); if (format.equal(PHPTransport::s_collection)) { auto const pvec = newobj<c_Vector>(); ret = pvec; for (uint32_t s = 0; s < size; ++s) { Variant value = binary_deserialize(type, transport, elemspec); pvec->t_add(value); } } else { PackedArrayInit pai(size); for (auto s = uint32_t{0}; s < size; ++s) { pai.append(binary_deserialize(type, transport, elemspec)); } ret = pai.toArray(); } return ret; } case T_SET: { // array of key -> TRUE uint8_t type; uint32_t size; transport.readBytes(&type, 1); transport.readBytes(&size, 4); size = ntohl(size); Variant elemvar = fieldspec.rvalAt(PHPTransport::s_elem, AccessFlags::Error_Key); Array elemspec = elemvar.toArray(); String format = fieldspec.rvalAt(PHPTransport::s_format, AccessFlags::None).toString(); if (format.equal(PHPTransport::s_collection)) { auto set_ret = makeSmartPtr<c_Set>(); for (uint32_t s = 0; s < size; ++s) { Variant key = binary_deserialize(type, transport, elemspec); if (key.isInteger()) { set_ret->t_add(key); } else { set_ret->t_add(key.toString()); } } ret = Variant(std::move(set_ret)); } else { ArrayInit init(size, ArrayInit::Mixed{}); for (uint32_t s = 0; s < size; ++s) { Variant key = binary_deserialize(type, transport, elemspec); if (key.isInteger()) { init.set(key, true); } else { init.setKeyUnconverted(key, true); } } ret = init.toArray(); } return ret; } }; char errbuf[128]; sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID); throw_tprotocolexception(String(errbuf, CopyString), INVALID_DATA); return init_null(); }
void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval* return_value, HashTable* fieldspec) { zval** val_ptr; Z_TYPE_P(return_value) = IS_NULL; // just in case switch (thrift_typeID) { case T_STOP: case T_VOID: RETURN_NULL(); return; case T_STRUCT: { if (zend_hash_find(fieldspec, "class", 6, (void**)&val_ptr) != SUCCESS) { throw_tprotocolexception("no class type in spec", INVALID_DATA); skip_element(T_STRUCT, transport); RETURN_NULL(); } char* structType = Z_STRVAL_PP(val_ptr); createObject(structType, return_value); if (Z_TYPE_P(return_value) == IS_NULL) { // unable to create class entry skip_element(T_STRUCT, transport); RETURN_NULL(); } TSRMLS_FETCH(); zval* spec = zend_read_static_property(zend_get_class_entry(return_value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC); if (Z_TYPE_P(spec) != IS_ARRAY) { char errbuf[128]; snprintf(errbuf, 128, "spec for %s is wrong type: %d\n", structType, Z_TYPE_P(spec)); throw_tprotocolexception(errbuf, INVALID_DATA); RETURN_NULL(); } binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec)); return; } break; case T_BOOL: { uint8_t c; transport.readBytes(&c, 1); RETURN_BOOL(c != 0); } //case T_I08: // same numeric value as T_BYTE case T_BYTE: { uint8_t c; transport.readBytes(&c, 1); RETURN_LONG(c); } case T_I16: { uint16_t c; transport.readBytes(&c, 2); RETURN_LONG(ntohs(c)); } case T_I32: { uint32_t c; transport.readBytes(&c, 4); RETURN_LONG(ntohl(c)); } case T_U64: case T_I64: { uint64_t c; transport.readBytes(&c, 8); RETURN_LONG(ntohll(c)); } case T_DOUBLE: { union { uint64_t c; double d; } a; transport.readBytes(&(a.c), 8); a.c = ntohll(a.c); RETURN_DOUBLE(a.d); } //case T_UTF7: // aliases T_STRING case T_UTF8: case T_UTF16: case T_STRING: { uint32_t size = transport.readU32(); if (size) { char* strbuf = (char*) emalloc(size + 1); transport.readBytes(strbuf, size); strbuf[size] = '\0'; ZVAL_STRINGL(return_value, strbuf, size, 0); } else { ZVAL_EMPTY_STRING(return_value); } return; } case T_MAP: { // array of key -> value uint8_t types[2]; transport.readBytes(types, 2); uint32_t size = transport.readU32(); array_init(return_value); zend_hash_find(fieldspec, "key", 4, (void**)&val_ptr); HashTable* keyspec = Z_ARRVAL_PP(val_ptr); zend_hash_find(fieldspec, "val", 4, (void**)&val_ptr); HashTable* valspec = Z_ARRVAL_PP(val_ptr); for (uint32_t s = 0; s < size; ++s) { zval *value; MAKE_STD_ZVAL(value); zval* key; MAKE_STD_ZVAL(key); binary_deserialize(types[0], transport, key, keyspec); binary_deserialize(types[1], transport, value, valspec); if (Z_TYPE_P(key) == IS_LONG) { zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL); } else { if (Z_TYPE_P(key) != IS_STRING) convert_to_string(key); zend_hash_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL); } zval_ptr_dtor(&key); } return; // return_value already populated } case T_LIST: { // array with autogenerated numeric keys int8_t type = transport.readI8(); uint32_t size = transport.readU32(); zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr); HashTable* elemspec = Z_ARRVAL_PP(val_ptr); array_init(return_value); for (uint32_t s = 0; s < size; ++s) { zval *value; MAKE_STD_ZVAL(value); binary_deserialize(type, transport, value, elemspec); zend_hash_next_index_insert(return_value->value.ht, &value, sizeof(zval *), NULL); } return; } case T_SET: { // array of key -> TRUE uint8_t type; uint32_t size; transport.readBytes(&type, 1); transport.readBytes(&size, 4); size = ntohl(size); zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr); HashTable* elemspec = Z_ARRVAL_PP(val_ptr); array_init(return_value); for (uint32_t s = 0; s < size; ++s) { zval* key; zval* value; MAKE_STD_ZVAL(key); MAKE_STD_ZVAL(value); ZVAL_TRUE(value); binary_deserialize(type, transport, key, elemspec); if (Z_TYPE_P(key) == IS_LONG) { zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL); } else { if (Z_TYPE_P(key) != IS_STRING) convert_to_string(key); zend_hash_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL); } zval_ptr_dtor(&key); } return; } }; char errbuf[128]; sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID); throw_tprotocolexception(errbuf, INVALID_DATA); }
static void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval* return_value, HashTable* fieldspec) { ZVAL_NULL(return_value); switch (thrift_typeID) { case T_STOP: case T_VOID: RETURN_NULL(); return; case T_STRUCT: { zval* val_ptr = zend_hash_str_find(fieldspec, "class", sizeof("class")-1); if (val_ptr == nullptr) { throw_tprotocolexception("no class type in spec", INVALID_DATA); skip_element(T_STRUCT, transport); RETURN_NULL(); } char* structType = Z_STRVAL_P(val_ptr); // Create an object in PHP userland based on our spec createObject(structType, return_value); if (Z_TYPE_P(return_value) == IS_NULL) { // unable to create class entry skip_element(T_STRUCT, transport); RETURN_NULL(); } zval* spec = zend_read_static_property(Z_OBJCE_P(return_value), "_TSPEC", sizeof("_TSPEC")-1, false); if (Z_TYPE_P(spec) != IS_ARRAY) { char errbuf[128]; snprintf(errbuf, 128, "spec for %s is wrong type: %d\n", structType, Z_TYPE_P(spec)); throw_tprotocolexception(errbuf, INVALID_DATA); RETURN_NULL(); } binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec)); return; } break; case T_BOOL: { uint8_t c; transport.readBytes(&c, 1); RETURN_BOOL(c != 0); } //case T_I08: // same numeric value as T_BYTE case T_BYTE: { uint8_t c; transport.readBytes(&c, 1); RETURN_LONG((int8_t)c); } case T_I16: { uint16_t c; transport.readBytes(&c, 2); RETURN_LONG((int16_t)ntohs(c)); } case T_I32: { uint32_t c; transport.readBytes(&c, 4); RETURN_LONG((int32_t)ntohl(c)); } case T_U64: case T_I64: { uint64_t c; transport.readBytes(&c, 8); RETURN_LONG((int64_t)ntohll(c)); } case T_DOUBLE: { union { uint64_t c; double d; } a; transport.readBytes(&(a.c), 8); a.c = ntohll(a.c); RETURN_DOUBLE(a.d); } //case T_UTF7: // aliases T_STRING case T_UTF8: case T_UTF16: case T_STRING: { uint32_t size = transport.readU32(); if (size) { char strbuf[size+1]; transport.readBytes(strbuf, size); strbuf[size] = '\0'; ZVAL_STRINGL(return_value, strbuf, size); } else { ZVAL_EMPTY_STRING(return_value); } return; } case T_MAP: { // array of key -> value uint8_t types[2]; transport.readBytes(types, 2); uint32_t size = transport.readU32(); array_init(return_value); zval *val_ptr; val_ptr = zend_hash_str_find(fieldspec, "key", sizeof("key")-1); HashTable* keyspec = Z_ARRVAL_P(val_ptr); val_ptr = zend_hash_str_find(fieldspec, "val", sizeof("val")-1); HashTable* valspec = Z_ARRVAL_P(val_ptr); for (uint32_t s = 0; s < size; ++s) { zval key, value; binary_deserialize(types[0], transport, &key, keyspec); binary_deserialize(types[1], transport, &value, valspec); if (Z_TYPE(key) == IS_LONG) { zend_hash_index_update(Z_ARR_P(return_value), Z_LVAL(key), &value); } else { if (Z_TYPE(key) != IS_STRING) convert_to_string(&key); zend_symtable_update(Z_ARR_P(return_value), Z_STR(key), &value); } zval_dtor(&key); } return; // return_value already populated } case T_LIST: { // array with autogenerated numeric keys int8_t type = transport.readI8(); uint32_t size = transport.readU32(); zval *val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1); HashTable* elemspec = Z_ARRVAL_P(val_ptr); array_init(return_value); for (uint32_t s = 0; s < size; ++s) { zval value; binary_deserialize(type, transport, &value, elemspec); zend_hash_next_index_insert(Z_ARR_P(return_value), &value); } return; } case T_SET: { // array of key -> TRUE uint8_t type; uint32_t size; transport.readBytes(&type, 1); transport.readBytes(&size, 4); size = ntohl(size); zval *val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1); HashTable* elemspec = Z_ARRVAL_P(val_ptr); array_init(return_value); for (uint32_t s = 0; s < size; ++s) { zval key, value; ZVAL_TRUE(&value); binary_deserialize(type, transport, &key, elemspec); if (Z_TYPE(key) == IS_LONG) { zend_hash_index_update(Z_ARR_P(return_value), Z_LVAL(key), &value); } else { if (Z_TYPE(key) != IS_STRING) convert_to_string(&key); zend_symtable_update(Z_ARR_P(return_value), Z_STR(key), &value); } zval_dtor(&key); } return; } }; char errbuf[128]; sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID); throw_tprotocolexception(errbuf, INVALID_DATA); }
Variant binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, CArrRef fieldspec) { Variant ret; switch (thrift_typeID) { case T_STOP: case T_VOID: return uninit_null(); case T_STRUCT: { Variant val; if ((val = fieldspec.rvalAt(PHPTransport::s_class)).isNull()) { throw_tprotocolexception("no class type in spec", INVALID_DATA); skip_element(T_STRUCT, transport); return uninit_null(); } String structType = val.toString(); ret = createObject(structType); if (ret.isNull()) { // unable to create class entry skip_element(T_STRUCT, transport); return uninit_null(); } Variant spec = f_hphp_get_static_property(structType, s_TSPEC, false); if (!spec.is(KindOfArray)) { char errbuf[128]; snprintf(errbuf, 128, "spec for %s is wrong type: %d\n", structType.data(), ret.getType()); throw_tprotocolexception(String(errbuf, CopyString), INVALID_DATA); return uninit_null(); } binary_deserialize_spec(ret.toObject(), transport, spec.toArray()); return ret; } break; case T_BOOL: { uint8_t c; transport.readBytes(&c, 1); return c != 0; } //case T_I08: // same numeric value as T_BYTE case T_BYTE: { uint8_t c; transport.readBytes(&c, 1); return Variant((int8_t)c); } case T_I16: { uint16_t c; transport.readBytes(&c, 2); return Variant((int16_t)ntohs(c)); } case T_I32: { uint32_t c; transport.readBytes(&c, 4); return Variant((int32_t)ntohl(c)); } case T_U64: case T_I64: { uint64_t c; transport.readBytes(&c, 8); return Variant((int64_t)ntohll(c)); } case T_DOUBLE: { union { uint64_t c; double d; } a; transport.readBytes(&(a.c), 8); a.c = ntohll(a.c); return a.d; } case T_FLOAT: { union { uint32_t c; float d; } a; transport.readBytes(&(a.c), 4); a.c = ntohl(a.c); return a.d; } //case T_UTF7: // aliases T_STRING case T_UTF8: case T_UTF16: case T_STRING: { uint32_t size = transport.readU32(); if (size && (size + 1)) { String s = String(size, ReserveString); char* strbuf = s.bufferSlice().ptr; transport.readBytes(strbuf, size); return s.setSize(size); } else { return ""; } } case T_MAP: { // array of key -> value uint8_t types[2]; transport.readBytes(types, 2); uint32_t size = transport.readU32(); Array keyspec = fieldspec.rvalAt(PHPTransport::s_key, AccessFlags::Error_Key).toArray(); Array valspec = fieldspec.rvalAt(PHPTransport::s_val, AccessFlags::Error_Key).toArray(); ret = Array::Create(); for (uint32_t s = 0; s < size; ++s) { Variant key = binary_deserialize(types[0], transport, keyspec); Variant value = binary_deserialize(types[1], transport, valspec); ret.set(key, value); } return ret; // return_value already populated } case T_LIST: { // array with autogenerated numeric keys int8_t type = transport.readI8(); uint32_t size = transport.readU32(); Variant elemvar = fieldspec.rvalAt(PHPTransport::s_elem, AccessFlags::Error_Key); Array elemspec = elemvar.toArray(); ret = Array::Create(); for (uint32_t s = 0; s < size; ++s) { Variant value = binary_deserialize(type, transport, elemspec); ret.append(value); } return ret; } case T_SET: { // array of key -> TRUE uint8_t type; uint32_t size; transport.readBytes(&type, 1); transport.readBytes(&size, 4); size = ntohl(size); Variant elemvar = fieldspec.rvalAt(PHPTransport::s_elem, AccessFlags::Error_Key); Array elemspec = elemvar.toArray(); ret = Array::Create(); for (uint32_t s = 0; s < size; ++s) { Variant key = binary_deserialize(type, transport, elemspec); if (key.isInteger()) { ret.set(key, true); } else { ret.set(key.toString(), true); } } return ret; } }; char errbuf[128]; sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID); throw_tprotocolexception(String(errbuf, CopyString), INVALID_DATA); return uninit_null(); }