void binary_deserialize()

in lib/php/src/ext/thrift_protocol/php_thrift_protocol.cpp [507:672]


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);
      ZVAL_DEREF(spec);
      if (EG(exception)) {
        zend_object *ex = EG(exception);
        EG(exception) = nullptr;
        throw PHPExceptionWrapper(ex);
      }
      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);
}