<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/drive/drive_system_service.h"

#include "base/bind.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/drive/drive_api_service.h"
#include "chrome/browser/chromeos/drive/drive_download_observer.h"
#include "chrome/browser/chromeos/drive/drive_file_system.h"
#include "chrome/browser/chromeos/drive/drive_file_system_proxy.h"
#include "chrome/browser/chromeos/drive/drive_file_system_util.h"
#include "chrome/browser/chromeos/drive/drive_prefetcher.h"
#include "chrome/browser/chromeos/drive/drive_sync_client.h"
#include "chrome/browser/chromeos/drive/drive_webapps_registry.h"
#include "chrome/browser/chromeos/drive/file_write_helper.h"
#include "chrome/browser/chromeos/drive/stale_cache_files_remover.h"
#include "chrome/browser/download/download_service.h"
#include "chrome/browser/download/download_service_factory.h"
#include "chrome/browser/download/download_util.h"
#include "chrome/browser/google_apis/drive_uploader.h"
#include "chrome/browser/google_apis/gdata_util.h"
#include "chrome/browser/google_apis/gdata_wapi_service.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_dependency_manager.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
#include "webkit/fileapi/file_system_context.h"
#include "webkit/fileapi/file_system_mount_point_provider.h"

using content::BrowserContext;
using content::BrowserThread;

namespace drive {
namespace {

// Used in test to setup system service.
google_apis::DriveServiceInterface* g_test_drive_service = NULL;
const std::string* g_test_cache_root = NULL;

// Returns true if Drive is enabled for the given Profile.
bool IsDriveEnabledForProfile(Profile* profile) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!google_apis::AuthService::CanAuthenticate(profile))
    return false;

  // Disable Drive if preference is set.  This can happen with commandline flag
  // --disable-gdata or enterprise policy, or probably with user settings too
  // in the future.
  if (profile->GetPrefs()->GetBoolean(prefs::kDisableDrive))
    return false;

  return true;
}

// The sync invalidation object source ID for Google Drive.
// TODO(kochi): Remove this constant once this is upstreamed in
// google-invalidation-api.
const int kCosmoChangelog = 1014;

// The sync invalidation object ID for Google Drive.
const char kDriveInvalidationObjectId[] = "CHANGELOG";

}  // namespace

DriveSystemService::DriveSystemService(Profile* profile)
    : profile_(profile),
      drive_disabled_(false),
      push_notification_registered_(false),
      cache_(NULL),
      ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  base::SequencedWorkerPool* blocking_pool = BrowserThread::GetBlockingPool();
  blocking_task_runner_ = blocking_pool->GetSequencedTaskRunner(
      blocking_pool->GetSequenceToken());
}

DriveSystemService::~DriveSystemService() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  cache_->DestroyOnUIThread();
}

void DriveSystemService::Initialize(
    google_apis::DriveServiceInterface* drive_service,
    const FilePath& cache_root) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  drive_service_.reset(drive_service);
  cache_ = DriveCache::CreateDriveCacheOnUIThread(
      cache_root,
      blocking_task_runner_);
  uploader_.reset(new google_apis::DriveUploader(drive_service_.get()));
  webapps_registry_.reset(new DriveWebAppsRegistry);
  file_system_.reset(new DriveFileSystem(profile_,
                                         cache(),
                                         drive_service_.get(),
                                         uploader(),
                                         webapps_registry(),
                                         blocking_task_runner_));
  file_write_helper_.reset(new FileWriteHelper(file_system()));
  download_observer_.reset(new DriveDownloadObserver(uploader(),
                                                     file_system()));
  sync_client_.reset(new DriveSyncClient(profile_, file_system(), cache()));
  prefetcher_.reset(new DrivePrefetcher(file_system(),
                                        DrivePrefetcherOptions()));
  sync_client_->AddObserver(prefetcher_.get());
  stale_cache_files_remover_.reset(new StaleCacheFilesRemover(file_system(),
                                                              cache()));

  sync_client_->Initialize();
  file_system_->Initialize();
  cache_->RequestInitializeOnUIThread(
      base::Bind(&DriveSystemService::OnCacheInitialized,
                 weak_ptr_factory_.GetWeakPtr()));
}

void DriveSystemService::Shutdown() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  ProfileSyncService* profile_sync_service =
      profile_ ? ProfileSyncServiceFactory::GetForProfile(profile_) : NULL;
  if (profile_sync_service && push_notification_registered_) {
    // TODO(kochi): Once DriveSystemService gets started / stopped at runtime,
    // this ID needs to be unregistered *before* the handler is unregistered
    // as ID persists across browser restarts.
    if (!IsDriveEnabledForProfile(profile_)) {
      profile_sync_service->UpdateRegisteredInvalidationIds(
          this, syncer::ObjectIdSet());
    }
    profile_sync_service->UnregisterInvalidationHandler(this);
    push_notification_registered_ = false;
  }

  RemoveDriveMountPoint();

  // Shut down the member objects in the reverse order of creation.
  stale_cache_files_remover_.reset();
  sync_client_->RemoveObserver(prefetcher_.get());
  prefetcher_.reset();
  sync_client_.reset();
  download_observer_.reset();
  file_write_helper_.reset();
  file_system_.reset();
  webapps_registry_.reset();
  uploader_.reset();
  drive_service_.reset();
}

bool DriveSystemService::IsDriveEnabled() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!IsDriveEnabledForProfile(profile_))
    return false;

  // Drive may be disabled for cache initialization failure, etc.
  if (drive_disabled_)
    return false;

  return true;
}

void DriveSystemService::OnInvalidatorStateChange(
    syncer::InvalidatorState state) {
  file_system_->SetPushNotificationEnabled(
      state == syncer::INVALIDATIONS_ENABLED);
}

void DriveSystemService::OnIncomingInvalidation(
    const syncer::ObjectIdInvalidationMap& invalidation_map,
    syncer::IncomingInvalidationSource source) {
  DCHECK_EQ(1U, invalidation_map.size());
  const invalidation::ObjectId oid(kCosmoChangelog, kDriveInvalidationObjectId);
  DCHECK_EQ(1U, invalidation_map.count(oid));

  file_system_->CheckForUpdates();
}

void DriveSystemService::ClearCacheAndRemountFileSystem(
    const base::Callback<void(bool)>& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  RemoveDriveMountPoint();
  drive_service()->CancelAll();
  cache_->ClearAllOnUIThread(
      base::Bind(&DriveSystemService::AddBackDriveMountPoint,
                 weak_ptr_factory_.GetWeakPtr(),
                 callback));
}

void DriveSystemService::AddBackDriveMountPoint(
    const base::Callback<void(bool)>& callback,
    DriveFileError error,
    const FilePath& file_path) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  file_system_->Initialize();
  AddDriveMountPoint();

  if (!callback.is_null())
    callback.Run(error == DRIVE_FILE_OK);
}

void DriveSystemService::AddDriveMountPoint() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  const FilePath mount_point = util::GetDriveMountPointPath();
  fileapi::ExternalFileSystemMountPointProvider* provider =
      BrowserContext::GetDefaultStoragePartition(profile_)->
          GetFileSystemContext()->external_provider();
  if (provider && !provider->HasMountPoint(mount_point)) {
    provider->AddRemoteMountPoint(
        mount_point,
        new DriveFileSystemProxy(file_system_.get()));
  }

  file_system_->NotifyFileSystemMounted();
}

void DriveSystemService::RemoveDriveMountPoint() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  file_system_->NotifyFileSystemToBeUnmounted();
  file_system_->StopPolling();

  const FilePath mount_point = util::GetDriveMountPointPath();
  fileapi::ExternalFileSystemMountPointProvider* provider =
      BrowserContext::GetDefaultStoragePartition(profile_)->
          GetFileSystemContext()->external_provider();
  if (provider && provider->HasMountPoint(mount_point))
    provider->RemoveMountPoint(mount_point);
}

void DriveSystemService::OnCacheInitialized(bool success) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!success) {
    LOG(WARNING) << "Failed to initialize the cache. Disabling Drive";
    DisableDrive();
    return;
  }

  content::DownloadManager* download_manager =
    g_browser_process->download_status_updater() ?
        BrowserContext::GetDownloadManager(profile_) : NULL;
  download_observer_->Initialize(
      download_manager,
      cache_->GetCacheDirectoryPath(
          DriveCache::CACHE_TYPE_TMP_DOWNLOADS));

  // Register for Google Drive invalidation notifications.
  ProfileSyncService* profile_sync_service =
      profile_ ? ProfileSyncServiceFactory::GetForProfile(profile_) : NULL;
  if (profile_sync_service) {
    DCHECK(!push_notification_registered_);
    profile_sync_service->RegisterInvalidationHandler(this);
    syncer::ObjectIdSet ids;
    ids.insert(invalidation::ObjectId(kCosmoChangelog,
                                      kDriveInvalidationObjectId));
    profile_sync_service->UpdateRegisteredInvalidationIds(this, ids);
    push_notification_registered_ = true;
    file_system_->SetPushNotificationEnabled(
        profile_sync_service->GetInvalidatorState() ==
        syncer::INVALIDATIONS_ENABLED);
  }

  AddDriveMountPoint();

  // Start prefetching of Drive metadata.
  file_system_->StartInitialFeedFetch();
}

void DriveSystemService::DisableDrive() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  drive_disabled_ = true;
  // Change the download directory to the default value if the download
  // destination is set to under Drive mount point.
  PrefService* pref_service = profile_->GetPrefs();
  if (util::IsUnderDriveMountPoint(
          pref_service->GetFilePath(prefs::kDownloadDefaultDirectory))) {
    pref_service->SetFilePath(prefs::kDownloadDefaultDirectory,
                              download_util::GetDefaultDownloadDirectory());
  }
}

//===================== DriveSystemServiceFactory =============================

// static
DriveSystemService* DriveSystemServiceFactory::GetForProfile(
    Profile* profile) {
  DriveSystemService* service = static_cast<DriveSystemService*>(
      GetInstance()->GetServiceForProfile(profile, true));
  if (service && !service->IsDriveEnabled())
    return NULL;

  return service;
}

// static
DriveSystemService* DriveSystemServiceFactory::FindForProfile(
    Profile* profile) {
  DriveSystemService* service = static_cast<DriveSystemService*>(
      GetInstance()->GetServiceForProfile(profile, false));
  if (service && !service->IsDriveEnabled())
    return NULL;

  return service;
}

// static
DriveSystemServiceFactory* DriveSystemServiceFactory::GetInstance() {
  return Singleton<DriveSystemServiceFactory>::get();
}

DriveSystemServiceFactory::DriveSystemServiceFactory()
    : ProfileKeyedServiceFactory("DriveSystemService",
                                 ProfileDependencyManager::GetInstance()) {
  DependsOn(ProfileSyncServiceFactory::GetInstance());
  DependsOn(DownloadServiceFactory::GetInstance());
}

DriveSystemServiceFactory::~DriveSystemServiceFactory() {
}

// static
void DriveSystemServiceFactory::set_drive_service_for_test(
    google_apis::DriveServiceInterface* drive_service) {
  if (g_test_drive_service)
    delete g_test_drive_service;
  g_test_drive_service = drive_service;
}

// static
void DriveSystemServiceFactory::set_cache_root_for_test(
    const std::string& cache_root) {
  if (g_test_cache_root)
    delete g_test_cache_root;
  g_test_cache_root = !cache_root.empty() ? new std::string(cache_root) : NULL;
}

ProfileKeyedService* DriveSystemServiceFactory::BuildServiceInstanceFor(
    Profile* profile) const {
  if (!IsDriveEnabledForProfile(profile))
    return NULL;

  DriveSystemService* service = new DriveSystemService(profile);

  google_apis::DriveServiceInterface* drive_service = g_test_drive_service;
  g_test_drive_service = NULL;
  if (!drive_service) {
    if (google_apis::util::IsDriveV2ApiEnabled())
      drive_service = new DriveAPIService();
    else
      drive_service = new google_apis::GDataWapiService();
  }

  FilePath cache_root =
      g_test_cache_root ? FilePath(*g_test_cache_root) :
                          DriveCache::GetCacheRootPath(profile);
  delete g_test_cache_root;
  g_test_cache_root = NULL;

  service->Initialize(drive_service, cache_root);
  return service;
}

}  // namespace drive
<commit_msg>drive: Fix a bug where Drive cannot be enabled<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/drive/drive_system_service.h"

#include "base/bind.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/drive/drive_api_service.h"
#include "chrome/browser/chromeos/drive/drive_download_observer.h"
#include "chrome/browser/chromeos/drive/drive_file_system.h"
#include "chrome/browser/chromeos/drive/drive_file_system_proxy.h"
#include "chrome/browser/chromeos/drive/drive_file_system_util.h"
#include "chrome/browser/chromeos/drive/drive_prefetcher.h"
#include "chrome/browser/chromeos/drive/drive_sync_client.h"
#include "chrome/browser/chromeos/drive/drive_webapps_registry.h"
#include "chrome/browser/chromeos/drive/file_write_helper.h"
#include "chrome/browser/chromeos/drive/stale_cache_files_remover.h"
#include "chrome/browser/download/download_service.h"
#include "chrome/browser/download/download_service_factory.h"
#include "chrome/browser/download/download_util.h"
#include "chrome/browser/google_apis/drive_uploader.h"
#include "chrome/browser/google_apis/gdata_util.h"
#include "chrome/browser/google_apis/gdata_wapi_service.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_dependency_manager.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
#include "webkit/fileapi/file_system_context.h"
#include "webkit/fileapi/file_system_mount_point_provider.h"

using content::BrowserContext;
using content::BrowserThread;

namespace drive {
namespace {

// Used in test to setup system service.
google_apis::DriveServiceInterface* g_test_drive_service = NULL;
const std::string* g_test_cache_root = NULL;

// Returns true if Drive is enabled for the given Profile.
bool IsDriveEnabledForProfile(Profile* profile) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!google_apis::AuthService::CanAuthenticate(profile))
    return false;

  // Disable Drive if preference is set.  This can happen with commandline flag
  // --disable-gdata or enterprise policy, or probably with user settings too
  // in the future.
  if (profile->GetPrefs()->GetBoolean(prefs::kDisableDrive))
    return false;

  return true;
}

// The sync invalidation object source ID for Google Drive.
// TODO(kochi): Remove this constant once this is upstreamed in
// google-invalidation-api.
const int kCosmoChangelog = 1014;

// The sync invalidation object ID for Google Drive.
const char kDriveInvalidationObjectId[] = "CHANGELOG";

}  // namespace

DriveSystemService::DriveSystemService(Profile* profile)
    : profile_(profile),
      drive_disabled_(false),
      push_notification_registered_(false),
      cache_(NULL),
      ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  base::SequencedWorkerPool* blocking_pool = BrowserThread::GetBlockingPool();
  blocking_task_runner_ = blocking_pool->GetSequencedTaskRunner(
      blocking_pool->GetSequenceToken());
}

DriveSystemService::~DriveSystemService() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  cache_->DestroyOnUIThread();
}

void DriveSystemService::Initialize(
    google_apis::DriveServiceInterface* drive_service,
    const FilePath& cache_root) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  drive_service_.reset(drive_service);
  cache_ = DriveCache::CreateDriveCacheOnUIThread(
      cache_root,
      blocking_task_runner_);
  uploader_.reset(new google_apis::DriveUploader(drive_service_.get()));
  webapps_registry_.reset(new DriveWebAppsRegistry);
  file_system_.reset(new DriveFileSystem(profile_,
                                         cache(),
                                         drive_service_.get(),
                                         uploader(),
                                         webapps_registry(),
                                         blocking_task_runner_));
  file_write_helper_.reset(new FileWriteHelper(file_system()));
  download_observer_.reset(new DriveDownloadObserver(uploader(),
                                                     file_system()));
  sync_client_.reset(new DriveSyncClient(profile_, file_system(), cache()));
  prefetcher_.reset(new DrivePrefetcher(file_system(),
                                        DrivePrefetcherOptions()));
  sync_client_->AddObserver(prefetcher_.get());
  stale_cache_files_remover_.reset(new StaleCacheFilesRemover(file_system(),
                                                              cache()));

  sync_client_->Initialize();
  file_system_->Initialize();
  cache_->RequestInitializeOnUIThread(
      base::Bind(&DriveSystemService::OnCacheInitialized,
                 weak_ptr_factory_.GetWeakPtr()));
}

void DriveSystemService::Shutdown() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  ProfileSyncService* profile_sync_service =
      profile_ ? ProfileSyncServiceFactory::GetForProfile(profile_) : NULL;
  if (profile_sync_service && push_notification_registered_) {
    // TODO(kochi): Once DriveSystemService gets started / stopped at runtime,
    // this ID needs to be unregistered *before* the handler is unregistered
    // as ID persists across browser restarts.
    if (!IsDriveEnabledForProfile(profile_)) {
      profile_sync_service->UpdateRegisteredInvalidationIds(
          this, syncer::ObjectIdSet());
    }
    profile_sync_service->UnregisterInvalidationHandler(this);
    push_notification_registered_ = false;
  }

  RemoveDriveMountPoint();

  // Shut down the member objects in the reverse order of creation.
  stale_cache_files_remover_.reset();
  sync_client_->RemoveObserver(prefetcher_.get());
  prefetcher_.reset();
  sync_client_.reset();
  download_observer_.reset();
  file_write_helper_.reset();
  file_system_.reset();
  webapps_registry_.reset();
  uploader_.reset();
  drive_service_.reset();
}

bool DriveSystemService::IsDriveEnabled() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!IsDriveEnabledForProfile(profile_))
    return false;

  // Drive may be disabled for cache initialization failure, etc.
  if (drive_disabled_)
    return false;

  return true;
}

void DriveSystemService::OnInvalidatorStateChange(
    syncer::InvalidatorState state) {
  file_system_->SetPushNotificationEnabled(
      state == syncer::INVALIDATIONS_ENABLED);
}

void DriveSystemService::OnIncomingInvalidation(
    const syncer::ObjectIdInvalidationMap& invalidation_map,
    syncer::IncomingInvalidationSource source) {
  DCHECK_EQ(1U, invalidation_map.size());
  const invalidation::ObjectId oid(kCosmoChangelog, kDriveInvalidationObjectId);
  DCHECK_EQ(1U, invalidation_map.count(oid));

  file_system_->CheckForUpdates();
}

void DriveSystemService::ClearCacheAndRemountFileSystem(
    const base::Callback<void(bool)>& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  RemoveDriveMountPoint();
  drive_service()->CancelAll();
  cache_->ClearAllOnUIThread(
      base::Bind(&DriveSystemService::AddBackDriveMountPoint,
                 weak_ptr_factory_.GetWeakPtr(),
                 callback));
}

void DriveSystemService::AddBackDriveMountPoint(
    const base::Callback<void(bool)>& callback,
    DriveFileError error,
    const FilePath& file_path) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  file_system_->Initialize();
  AddDriveMountPoint();

  if (!callback.is_null())
    callback.Run(error == DRIVE_FILE_OK);
}

void DriveSystemService::AddDriveMountPoint() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  const FilePath mount_point = util::GetDriveMountPointPath();
  fileapi::ExternalFileSystemMountPointProvider* provider =
      BrowserContext::GetDefaultStoragePartition(profile_)->
          GetFileSystemContext()->external_provider();
  if (provider && !provider->HasMountPoint(mount_point)) {
    provider->AddRemoteMountPoint(
        mount_point,
        new DriveFileSystemProxy(file_system_.get()));
  }

  file_system_->NotifyFileSystemMounted();
}

void DriveSystemService::RemoveDriveMountPoint() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  file_system_->NotifyFileSystemToBeUnmounted();
  file_system_->StopPolling();

  const FilePath mount_point = util::GetDriveMountPointPath();
  fileapi::ExternalFileSystemMountPointProvider* provider =
      BrowserContext::GetDefaultStoragePartition(profile_)->
          GetFileSystemContext()->external_provider();
  if (provider && provider->HasMountPoint(mount_point))
    provider->RemoveMountPoint(mount_point);
}

void DriveSystemService::OnCacheInitialized(bool success) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!success) {
    LOG(WARNING) << "Failed to initialize the cache. Disabling Drive";
    DisableDrive();
    return;
  }

  content::DownloadManager* download_manager =
    g_browser_process->download_status_updater() ?
        BrowserContext::GetDownloadManager(profile_) : NULL;
  download_observer_->Initialize(
      download_manager,
      cache_->GetCacheDirectoryPath(
          DriveCache::CACHE_TYPE_TMP_DOWNLOADS));

  // Register for Google Drive invalidation notifications.
  ProfileSyncService* profile_sync_service =
      profile_ ? ProfileSyncServiceFactory::GetForProfile(profile_) : NULL;
  if (profile_sync_service) {
    DCHECK(!push_notification_registered_);
    profile_sync_service->RegisterInvalidationHandler(this);
    syncer::ObjectIdSet ids;
    ids.insert(invalidation::ObjectId(kCosmoChangelog,
                                      kDriveInvalidationObjectId));
    profile_sync_service->UpdateRegisteredInvalidationIds(this, ids);
    push_notification_registered_ = true;
    file_system_->SetPushNotificationEnabled(
        profile_sync_service->GetInvalidatorState() ==
        syncer::INVALIDATIONS_ENABLED);
  }

  AddDriveMountPoint();

  // Start prefetching of Drive metadata.
  file_system_->StartInitialFeedFetch();
}

void DriveSystemService::DisableDrive() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  drive_disabled_ = true;
  // Change the download directory to the default value if the download
  // destination is set to under Drive mount point.
  PrefService* pref_service = profile_->GetPrefs();
  if (util::IsUnderDriveMountPoint(
          pref_service->GetFilePath(prefs::kDownloadDefaultDirectory))) {
    pref_service->SetFilePath(prefs::kDownloadDefaultDirectory,
                              download_util::GetDefaultDownloadDirectory());
  }
}

//===================== DriveSystemServiceFactory =============================

// static
DriveSystemService* DriveSystemServiceFactory::GetForProfile(
    Profile* profile) {
  DriveSystemService* service = static_cast<DriveSystemService*>(
      GetInstance()->GetServiceForProfile(profile, true));
  if (service && !service->IsDriveEnabled())
    return NULL;

  return service;
}

// static
DriveSystemService* DriveSystemServiceFactory::FindForProfile(
    Profile* profile) {
  DriveSystemService* service = static_cast<DriveSystemService*>(
      GetInstance()->GetServiceForProfile(profile, false));
  if (service && !service->IsDriveEnabled())
    return NULL;

  return service;
}

// static
DriveSystemServiceFactory* DriveSystemServiceFactory::GetInstance() {
  return Singleton<DriveSystemServiceFactory>::get();
}

DriveSystemServiceFactory::DriveSystemServiceFactory()
    : ProfileKeyedServiceFactory("DriveSystemService",
                                 ProfileDependencyManager::GetInstance()) {
  DependsOn(ProfileSyncServiceFactory::GetInstance());
  DependsOn(DownloadServiceFactory::GetInstance());
}

DriveSystemServiceFactory::~DriveSystemServiceFactory() {
}

// static
void DriveSystemServiceFactory::set_drive_service_for_test(
    google_apis::DriveServiceInterface* drive_service) {
  if (g_test_drive_service)
    delete g_test_drive_service;
  g_test_drive_service = drive_service;
}

// static
void DriveSystemServiceFactory::set_cache_root_for_test(
    const std::string& cache_root) {
  if (g_test_cache_root)
    delete g_test_cache_root;
  g_test_cache_root = !cache_root.empty() ? new std::string(cache_root) : NULL;
}

ProfileKeyedService* DriveSystemServiceFactory::BuildServiceInstanceFor(
    Profile* profile) const {
  DriveSystemService* service = new DriveSystemService(profile);

  google_apis::DriveServiceInterface* drive_service = g_test_drive_service;
  g_test_drive_service = NULL;
  if (!drive_service) {
    if (google_apis::util::IsDriveV2ApiEnabled())
      drive_service = new DriveAPIService();
    else
      drive_service = new google_apis::GDataWapiService();
  }

  FilePath cache_root =
      g_test_cache_root ? FilePath(*g_test_cache_root) :
                          DriveCache::GetCacheRootPath(profile);
  delete g_test_cache_root;
  g_test_cache_root = NULL;

  service->Initialize(drive_service, cache_root);
  return service;
}

}  // namespace drive
<|endoftext|>


<commit_before>#include "ShardQuorumContext.h"
#include "Framework/Replication/ReplicationConfig.h"
#include "Framework/Replication/Paxos/PaxosMessage.h"
#include "Application/Common/ContextTransport.h"
#include "ShardQuorumProcessor.h"
#include "ShardServer.h"

#define PAXOS_QUEUE_LENGTH  1000

void ShardQuorumContext::Init(ConfigQuorum* configQuorum,
 ShardQuorumProcessor* quorumProcessor_)
{
    uint64_t*               it;
    SortedList<uint64_t>    activeNodes;

    quorumProcessor = quorumProcessor_;
    quorumID = configQuorum->quorumID;
    numPendingPaxos = 0;

    configQuorum->GetVolatileActiveNodes(activeNodes);

    FOREACH (it, activeNodes)
        quorum.AddNode(*it);

    transport.SetQuorum(&quorum);
    transport.SetQuorumID(quorumID);

    database.Init(this,
     quorumProcessor->GetShardServer()->GetDatabaseManager()->GetQuorumPaxosShard(quorumID),
     quorumProcessor->GetShardServer()->GetDatabaseManager()->GetQuorumLogShard(quorumID));

    replicatedLog.Init(this);
    transport.SetQuorumID(quorumID);
    highestPaxosID = 0;
    isReplicationActive = true;
}

void ShardQuorumContext::Shutdown()
{
    replicatedLog.Shutdown();
}

void ShardQuorumContext::SetQuorumNodes(SortedList<uint64_t>& activeNodes)
{
    uint64_t*   it;

    quorum.ClearNodes();
    FOREACH (it, activeNodes)
        quorum.AddNode(*it);
}

void ShardQuorumContext::RestartReplication()
{
    replicatedLog.Restart();
}

void ShardQuorumContext::TryReplicationCatchup()
{
    replicatedLog.TryCatchup();
}

void ShardQuorumContext::AppendDummy()
{
    Log_Trace();

    replicatedLog.TryAppendDummy();
}

void ShardQuorumContext::Append()
{
    ASSERT(nextValue.GetLength() > 0);
    replicatedLog.TryAppendNextValue();
}

bool ShardQuorumContext::IsAppending()
{
    return (nextValue.GetLength() != 0);
}

void ShardQuorumContext::OnAppendComplete()
{
    replicatedLog.OnAppendComplete();
}

void ShardQuorumContext::WriteReplicationState()
{
    replicatedLog.WriteState();
}

uint64_t ShardQuorumContext::GetMemoryUsage()
{
    return nextValue.GetSize() + replicatedLog.GetMemoryUsage();
}

bool ShardQuorumContext::IsLeaseOwner()
{
    return quorumProcessor->IsPrimary();
}

bool ShardQuorumContext::IsLeaseKnown()
{
    return quorumProcessor->GetShardServer()->IsLeaseKnown(quorumID);
}

uint64_t ShardQuorumContext::GetLeaseOwner()
{
    return quorumProcessor->GetShardServer()->GetLeaseOwner(quorumID);
}

bool ShardQuorumContext::IsLeader()
{
    return IsLeaseOwner() && replicatedLog.IsMultiPaxosEnabled();
}

void ShardQuorumContext::OnLearnLease()
{
    replicatedLog.OnLearnLease();
}

void ShardQuorumContext::OnLeaseTimeout()
{
    nextValue.Clear();
    replicatedLog.OnLeaseTimeout();
}

void ShardQuorumContext::OnIsLeader()
{
    // nothing
}

uint64_t ShardQuorumContext::GetQuorumID()
{
    return quorumID;
}

void ShardQuorumContext::SetPaxosID(uint64_t paxosID)
{
    replicatedLog.SetPaxosID(paxosID);
}

uint64_t ShardQuorumContext::GetPaxosID()
{
    return replicatedLog.GetPaxosID();
}

uint64_t ShardQuorumContext::GetHighestPaxosID()
{
    return highestPaxosID;
}

uint64_t ShardQuorumContext::GetLastLearnChosenTime()
{
    return replicatedLog.GetLastLearnChosenTime();
}

uint64_t ShardQuorumContext::GetReplicationThroughput()
{
    return replicatedLog.GetReplicationThroughput();
}

Quorum* ShardQuorumContext::GetQuorum()
{
    return &quorum;
}

QuorumDatabase* ShardQuorumContext::GetDatabase()
{
    return &database;
}

QuorumTransport* ShardQuorumContext::GetTransport()
{
    return &transport;
}

void ShardQuorumContext::OnStartProposing()
{
    quorumProcessor->OnStartProposing();
}

void ShardQuorumContext::OnAppend(uint64_t paxosID, Buffer& value, bool ownAppend)
{
    nextValue.Clear();

    quorumProcessor->OnAppend(paxosID, value, ownAppend);
}

bool ShardQuorumContext::UseSyncCommit()
{
    return false; // async
}

bool ShardQuorumContext::UseProposeTimeouts()
{
    return false;
}

bool ShardQuorumContext::UseCommitChaining()
{
    return true;
}

bool ShardQuorumContext::AlwaysUseDatabaseCatchup()
{
    return false;
}

bool ShardQuorumContext::IsPaxosBlocked()
{
    return quorumProcessor->IsPaxosBlocked();
}

Buffer& ShardQuorumContext::GetNextValue()
{
    return nextValue;
}

void ShardQuorumContext::OnStartCatchup()
{
    quorumProcessor->OnStartCatchup();
}

void ShardQuorumContext::OnCatchupStarted()
{
    replicatedLog.OnCatchupStarted();
}

void ShardQuorumContext::OnCatchupComplete(uint64_t paxosID)
{
    replicatedLog.OnCatchupComplete(paxosID);
}

void ShardQuorumContext::StopReplication()
{
    isReplicationActive = false;
    replicatedLog.Stop();
}

void ShardQuorumContext::ContinueReplication()
{
    isReplicationActive = true;
    replicatedLog.Continue();
}

bool ShardQuorumContext::IsWaitingOnAppend()
{
    return replicatedLog.IsWaitingOnAppend();
}

void ShardQuorumContext::OnMessage(ReadBuffer buffer)
{
    char    proto;
    Buffer* message;

    Log_Trace("%R", &buffer);

    if (buffer.GetLength() < 2)
        ASSERT_FAIL();

    proto = buffer.GetCharAt(0);
    ASSERT(buffer.GetCharAt(1) == ':');

    switch(proto)
    {
        case PAXOS_PROTOCOL_ID:             // 'P':
            if (!isReplicationActive)
                break;
            if (numPendingPaxos == 0)
            {
                numPendingPaxos++;
                OnPaxosMessage(buffer);
            }
            else
            {
                if (paxosMessageQueue.GetLength() < PAXOS_QUEUE_LENGTH)
                {
                    message = new Buffer;
                    message->Write(buffer);
                    paxosMessageQueue.Enqueue(message);
                }
                else
                {
                    Log_Debug("Dropping paxos msg because queue is too long. Queue stuck?");
                }
            }
            break;
        default:
            ASSERT_FAIL();
            break;
    }
}

void ShardQuorumContext::OnMessageProcessed()
{
    ReadBuffer  buffer;
    Buffer*     message;

    ASSERT(numPendingPaxos == 1);
    numPendingPaxos--;

    if (!isReplicationActive)
        paxosMessageQueue.DeleteQueue(); // drop messages

    if (paxosMessageQueue.GetLength() > 0)
    {
        message = paxosMessageQueue.Dequeue();
        buffer.Wrap(*message);
        OnMessage(buffer);
    }
}

void ShardQuorumContext::RegisterPaxosID(uint64_t paxosID)
{
    if (paxosID > highestPaxosID)
        highestPaxosID = paxosID;

    // ReplicatedLog::RegisterPaxosID() will perform a RequestChosen()
    // so don't call it if replication is inactive,
    // because catchup is already running
    if (isReplicationActive && IsLeaseKnown())
        replicatedLog.RegisterPaxosID(paxosID, GetLeaseOwner());
}

void ShardQuorumContext::OnPaxosMessage(ReadBuffer buffer)
{
    PaxosMessage msg;

    msg.Read(buffer);

    if (msg.IsPaxosRequest() || msg.IsPaxosResponse() || msg.IsLearn())
    {
        if (!quorum.IsMember(msg.nodeID))
        {
            Log_Debug("Dropping paxos msg from %U because that node is not a quourm member", msg.nodeID);
            OnMessageProcessed();
            return;
        }
    }

    RegisterPaxosID(msg.paxosID);
    replicatedLog.RegisterPaxosID(msg.paxosID, msg.nodeID);
    replicatedLog.OnMessage(msg);
}
<commit_msg>Fix bug where paxos message queue item is not deleted.<commit_after>#include "ShardQuorumContext.h"
#include "Framework/Replication/ReplicationConfig.h"
#include "Framework/Replication/Paxos/PaxosMessage.h"
#include "Application/Common/ContextTransport.h"
#include "ShardQuorumProcessor.h"
#include "ShardServer.h"

#define PAXOS_QUEUE_LENGTH  1000

void ShardQuorumContext::Init(ConfigQuorum* configQuorum,
 ShardQuorumProcessor* quorumProcessor_)
{
    uint64_t*               it;
    SortedList<uint64_t>    activeNodes;

    quorumProcessor = quorumProcessor_;
    quorumID = configQuorum->quorumID;
    numPendingPaxos = 0;

    configQuorum->GetVolatileActiveNodes(activeNodes);

    FOREACH (it, activeNodes)
        quorum.AddNode(*it);

    transport.SetQuorum(&quorum);
    transport.SetQuorumID(quorumID);

    database.Init(this,
     quorumProcessor->GetShardServer()->GetDatabaseManager()->GetQuorumPaxosShard(quorumID),
     quorumProcessor->GetShardServer()->GetDatabaseManager()->GetQuorumLogShard(quorumID));

    replicatedLog.Init(this);
    transport.SetQuorumID(quorumID);
    highestPaxosID = 0;
    isReplicationActive = true;
}

void ShardQuorumContext::Shutdown()
{
    replicatedLog.Shutdown();
}

void ShardQuorumContext::SetQuorumNodes(SortedList<uint64_t>& activeNodes)
{
    uint64_t*   it;

    quorum.ClearNodes();
    FOREACH (it, activeNodes)
        quorum.AddNode(*it);
}

void ShardQuorumContext::RestartReplication()
{
    replicatedLog.Restart();
}

void ShardQuorumContext::TryReplicationCatchup()
{
    replicatedLog.TryCatchup();
}

void ShardQuorumContext::AppendDummy()
{
    Log_Trace();

    replicatedLog.TryAppendDummy();
}

void ShardQuorumContext::Append()
{
    ASSERT(nextValue.GetLength() > 0);
    replicatedLog.TryAppendNextValue();
}

bool ShardQuorumContext::IsAppending()
{
    return (nextValue.GetLength() != 0);
}

void ShardQuorumContext::OnAppendComplete()
{
    replicatedLog.OnAppendComplete();
}

void ShardQuorumContext::WriteReplicationState()
{
    replicatedLog.WriteState();
}

uint64_t ShardQuorumContext::GetMemoryUsage()
{
    return nextValue.GetSize() + replicatedLog.GetMemoryUsage();
}

bool ShardQuorumContext::IsLeaseOwner()
{
    return quorumProcessor->IsPrimary();
}

bool ShardQuorumContext::IsLeaseKnown()
{
    return quorumProcessor->GetShardServer()->IsLeaseKnown(quorumID);
}

uint64_t ShardQuorumContext::GetLeaseOwner()
{
    return quorumProcessor->GetShardServer()->GetLeaseOwner(quorumID);
}

bool ShardQuorumContext::IsLeader()
{
    return IsLeaseOwner() && replicatedLog.IsMultiPaxosEnabled();
}

void ShardQuorumContext::OnLearnLease()
{
    replicatedLog.OnLearnLease();
}

void ShardQuorumContext::OnLeaseTimeout()
{
    nextValue.Clear();
    replicatedLog.OnLeaseTimeout();
}

void ShardQuorumContext::OnIsLeader()
{
    // nothing
}

uint64_t ShardQuorumContext::GetQuorumID()
{
    return quorumID;
}

void ShardQuorumContext::SetPaxosID(uint64_t paxosID)
{
    replicatedLog.SetPaxosID(paxosID);
}

uint64_t ShardQuorumContext::GetPaxosID()
{
    return replicatedLog.GetPaxosID();
}

uint64_t ShardQuorumContext::GetHighestPaxosID()
{
    return highestPaxosID;
}

uint64_t ShardQuorumContext::GetLastLearnChosenTime()
{
    return replicatedLog.GetLastLearnChosenTime();
}

uint64_t ShardQuorumContext::GetReplicationThroughput()
{
    return replicatedLog.GetReplicationThroughput();
}

Quorum* ShardQuorumContext::GetQuorum()
{
    return &quorum;
}

QuorumDatabase* ShardQuorumContext::GetDatabase()
{
    return &database;
}

QuorumTransport* ShardQuorumContext::GetTransport()
{
    return &transport;
}

void ShardQuorumContext::OnStartProposing()
{
    quorumProcessor->OnStartProposing();
}

void ShardQuorumContext::OnAppend(uint64_t paxosID, Buffer& value, bool ownAppend)
{
    nextValue.Clear();

    quorumProcessor->OnAppend(paxosID, value, ownAppend);
}

bool ShardQuorumContext::UseSyncCommit()
{
    return false; // async
}

bool ShardQuorumContext::UseProposeTimeouts()
{
    return false;
}

bool ShardQuorumContext::UseCommitChaining()
{
    return true;
}

bool ShardQuorumContext::AlwaysUseDatabaseCatchup()
{
    return false;
}

bool ShardQuorumContext::IsPaxosBlocked()
{
    return quorumProcessor->IsPaxosBlocked();
}

Buffer& ShardQuorumContext::GetNextValue()
{
    return nextValue;
}

void ShardQuorumContext::OnStartCatchup()
{
    quorumProcessor->OnStartCatchup();
}

void ShardQuorumContext::OnCatchupStarted()
{
    replicatedLog.OnCatchupStarted();
}

void ShardQuorumContext::OnCatchupComplete(uint64_t paxosID)
{
    replicatedLog.OnCatchupComplete(paxosID);
}

void ShardQuorumContext::StopReplication()
{
    isReplicationActive = false;
    replicatedLog.Stop();
}

void ShardQuorumContext::ContinueReplication()
{
    isReplicationActive = true;
    replicatedLog.Continue();
}

bool ShardQuorumContext::IsWaitingOnAppend()
{
    return replicatedLog.IsWaitingOnAppend();
}

void ShardQuorumContext::OnMessage(ReadBuffer buffer)
{
    char    proto;
    Buffer* message;

    Log_Trace("%R", &buffer);

    if (buffer.GetLength() < 2)
        ASSERT_FAIL();

    proto = buffer.GetCharAt(0);
    ASSERT(buffer.GetCharAt(1) == ':');

    switch(proto)
    {
        case PAXOS_PROTOCOL_ID:             // 'P':
            if (!isReplicationActive)
                break;
            if (numPendingPaxos == 0)
            {
                numPendingPaxos++;
                OnPaxosMessage(buffer);
            }
            else
            {
                if (paxosMessageQueue.GetLength() < PAXOS_QUEUE_LENGTH)
                {
                    message = new Buffer;
                    message->Write(buffer);
                    paxosMessageQueue.Enqueue(message);
                }
                else
                {
                    Log_Debug("Dropping paxos msg because queue is too long. Queue stuck?");
                }
            }
            break;
        default:
            ASSERT_FAIL();
            break;
    }
}

void ShardQuorumContext::OnMessageProcessed()
{
    ReadBuffer  buffer;
    Buffer*     message;

    ASSERT(numPendingPaxos == 1);
    numPendingPaxos--;

    if (!isReplicationActive)
        paxosMessageQueue.DeleteQueue(); // drop messages

    if (paxosMessageQueue.GetLength() > 0)
    {
        message = paxosMessageQueue.Dequeue();
        buffer.Wrap(*message);
        OnMessage(buffer);
        delete message;
    }
}

void ShardQuorumContext::RegisterPaxosID(uint64_t paxosID)
{
    if (paxosID > highestPaxosID)
        highestPaxosID = paxosID;

    // ReplicatedLog::RegisterPaxosID() will perform a RequestChosen()
    // so don't call it if replication is inactive,
    // because catchup is already running
    if (isReplicationActive && IsLeaseKnown())
        replicatedLog.RegisterPaxosID(paxosID, GetLeaseOwner());
}

void ShardQuorumContext::OnPaxosMessage(ReadBuffer buffer)
{
    PaxosMessage msg;

    msg.Read(buffer);

    if (msg.IsPaxosRequest() || msg.IsPaxosResponse() || msg.IsLearn())
    {
        if (!quorum.IsMember(msg.nodeID))
        {
            Log_Debug("Dropping paxos msg from %U because that node is not a quourm member", msg.nodeID);
            OnMessageProcessed();
            return;
        }
    }

    RegisterPaxosID(msg.paxosID);
    replicatedLog.RegisterPaxosID(msg.paxosID, msg.nodeID);
    replicatedLog.OnMessage(msg);
}
<|endoftext|>


<commit_before>/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
 * Use of this file is governed by the BSD 3-clause license that
 * can be found in the LICENSE.txt file in the project root.
 */

#include "Exceptions.h"

using namespace antlr4;

RuntimeException::RuntimeException(const std::string &msg) : std::exception(), _message(msg) {
}

const char* RuntimeException::what() const NOEXCEPT {
  return _message.c_str();
}

//------------------ IOException ---------------------------------------------------------------------------------------

IOException::IOException(const std::string &msg) : std::exception(), _message(msg) {
}

const char* IOException::what() const NOEXCEPT {
  return _message.c_str();
}

IllegalStateException::~IllegalStateException() = default;
IllegalArgumentException::~IllegalArgumentException() = default;
NullPointerException::~NullPointerException() = default;
IndexOutOfBoundsException::~IndexOutOfBoundsException() = default;
UnsupportedOperationException::~UnsupportedOperationException() = default;
EmptyStackException::~EmptyStackException() = default;
CancellationException::~CancellationException() = default;
ParseCancellationException::~ParseCancellationException() = default;
<commit_msg>Convention: Change virtual dtor to empty bodies<commit_after>/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
 * Use of this file is governed by the BSD 3-clause license that
 * can be found in the LICENSE.txt file in the project root.
 */

#include "Exceptions.h"

using namespace antlr4;

RuntimeException::RuntimeException(const std::string &msg) : std::exception(), _message(msg) {
}

const char* RuntimeException::what() const NOEXCEPT {
  return _message.c_str();
}

//------------------ IOException ---------------------------------------------------------------------------------------

IOException::IOException(const std::string &msg) : std::exception(), _message(msg) {
}

const char* IOException::what() const NOEXCEPT {
  return _message.c_str();
}

//------------------ IllegalStateException -----------------------------------------------------------------------------

IllegalStateException::~IllegalStateException() {
}

//------------------ IllegalArgumentException --------------------------------------------------------------------------

IllegalArgumentException::~IllegalArgumentException() {
}

//------------------ NullPointerException ------------------------------------------------------------------------------

NullPointerException::~NullPointerException() {
}

//------------------ IndexOutOfBoundsException -------------------------------------------------------------------------

IndexOutOfBoundsException::~IndexOutOfBoundsException() {
}

//------------------ UnsupportedOperationException ---------------------------------------------------------------------

UnsupportedOperationException::~UnsupportedOperationException() {
}

//------------------ EmptyStackException -------------------------------------------------------------------------------

EmptyStackException::~EmptyStackException() {
}

//------------------ CancellationException -----------------------------------------------------------------------------

CancellationException::~CancellationException() {
}

//------------------ ParseCancellationException ------------------------------------------------------------------------

ParseCancellationException::~ParseCancellationException() {
}
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// driver.cpp
// Entry point for the primary driver program.
//
// File is under the MIT license; see LICENSE for details
//------------------------------------------------------------------------------

#include <fmt/format.h>
#include <fstream>
#include <iostream>
#include <nlohmann/json.hpp>

#include "slang/compilation/Compilation.h"
#include "slang/diagnostics/DiagnosticEngine.h"
#include "slang/diagnostics/TextDiagnosticClient.h"
#include "slang/parsing/Preprocessor.h"
#include "slang/symbols/CompilationUnitSymbols.h"
#include "slang/syntax/SyntaxPrinter.h"
#include "slang/syntax/SyntaxTree.h"
#include "slang/text/SourceManager.h"
#include "slang/util/CommandLine.h"
#include "slang/util/String.h"
#include "slang/util/Version.h"

using namespace slang;

template<typename... Args>
void print(string_view format, const Args&... args);

void writeToFile(string_view fileName, string_view contents);

bool runPreprocessor(SourceManager& sourceManager, const Bag& options,
                     const std::vector<SourceBuffer>& buffers, bool includeComments,
                     bool includeDirectives) {
    BumpAllocator alloc;

    bool success = true;
    for (const SourceBuffer& buffer : buffers) {
        Diagnostics diagnostics;
        Preprocessor preprocessor(sourceManager, alloc, diagnostics, options);
        preprocessor.pushSource(buffer);

        SyntaxPrinter output;
        output.setIncludeComments(includeComments);
        output.setIncludeDirectives(includeDirectives);

        while (true) {
            Token token = preprocessor.next();
            output.print(token);
            if (token.kind == TokenKind::EndOfFile)
                break;
        }

        if (diagnostics.empty())
            print("{}:\n", sourceManager.getRawFileName(buffer.id));
        else {
            print("{}", DiagnosticEngine::reportAll(sourceManager, diagnostics));
            success = false;
        }

        print("==============================\n{}\n", output.str());
    }
    return success;
}

void printMacros(SourceManager& sourceManager, const Bag& options,
                 const std::vector<SourceBuffer>& buffers) {
    BumpAllocator alloc;

    for (const SourceBuffer& buffer : buffers) {
        Diagnostics diagnostics;
        Preprocessor preprocessor(sourceManager, alloc, diagnostics, options);
        preprocessor.pushSource(buffer);

        while (true) {
            Token token = preprocessor.next();
            if (token.kind == TokenKind::EndOfFile)
                break;
        }

        for (auto macro : preprocessor.getDefinedMacros()) {
            SyntaxPrinter printer;
            printer.setIncludeComments(false);
            printer.setIncludeTrivia(false);
            printer.print(macro->name);

            printer.setIncludeTrivia(true);
            if (macro->formalArguments)
                printer.print(*macro->formalArguments);
            printer.print(macro->body);

            print("{}\n", printer.str());
        }
    }
}

bool runCompiler(SourceManager& sourceManager, const Bag& options,
                 const std::vector<SourceBuffer>& buffers,
                 const std::vector<std::string>& warningOptions, bool onlyParse,
                 const optional<std::string>& astJsonFile) {
    Compilation compilation;
    for (const SourceBuffer& buffer : buffers)
        compilation.addSyntaxTree(SyntaxTree::fromBuffer(buffer, sourceManager, options));

    DiagnosticEngine diagEngine(sourceManager);
    Diagnostics optionDiags = diagEngine.setWarningOptions(warningOptions);

    auto client = std::make_shared<TextDiagnosticClient>();
    diagEngine.addClient(client);

    for (auto& diag : optionDiags)
        diagEngine.issue(diag);

    if (onlyParse) {
        for (auto& diag : compilation.getParseDiagnostics())
            diagEngine.issue(diag);
    }
    else {
        for (auto& diag : compilation.getAllDiagnostics())
            diagEngine.issue(diag);
    }

    print("{}", client->getString());

    if (astJsonFile) {
        json output = compilation.getRoot();
        writeToFile(*astJsonFile, output.dump(2));
    }

    return diagEngine.getNumErrors() == 0;
}

template<typename TArgs>
int driverMain(int argc, TArgs argv) try {
    CommandLine cmdLine;

    // General
    optional<bool> showHelp;
    optional<bool> showVersion;
    cmdLine.add("-h,--help", showHelp, "Display available options");
    cmdLine.add("-v,--version", showVersion, "Display version information and exit");

    // Output control
    optional<bool> onlyPreprocess;
    optional<bool> onlyParse;
    optional<bool> onlyMacros;
    cmdLine.add("-E,--preprocess", onlyPreprocess,
                "Only run the preprocessor (and print preprocessed files to stdout)");
    cmdLine.add("--macros-only", onlyMacros, "Print a list of found macros and exit");
    cmdLine.add("--parse-only", onlyParse,
                "Stop after parsing input files, don't perform elaboration or type checking");

    // Include paths
    std::vector<std::string> includeDirs;
    std::vector<std::string> includeSystemDirs;
    cmdLine.add("-I,--include-directory", includeDirs, "Additional include search paths", "<dir>");
    cmdLine.add("--isystem", includeSystemDirs, "Additional system include search paths", "<dir>");

    // Preprocessor
    optional<bool> includeComments;
    optional<bool> includeDirectives;
    optional<uint32_t> maxIncludeDepth;
    std::vector<std::string> defines;
    std::vector<std::string> undefines;
    cmdLine.add("-D,--define-macro", defines,
                "Define <macro> to <value> (or 1 if <value> ommitted) in all source files",
                "<macro>=<value>");
    cmdLine.add("-U,--undefine-macro", undefines,
                "Undefine macro name at the start of all source files", "<macro>");
    cmdLine.add("--comments", includeComments, "Include comments in preprocessed output (with -E)");
    cmdLine.add("--directives", includeDirectives,
                "Include compiler directives in preprocessed output (with -E)");
    cmdLine.add("--max-include-depth", maxIncludeDepth,
                "Maximum depth of nested include files allowed");

    // JSON dumping
    optional<std::string> astJsonFile;
    cmdLine.add("--ast-json", astJsonFile,
                "Dump the compiled AST in JSON format to the specified file, or '-' for stdout",
                "<file>");

    // Diagnostics control
    std::vector<std::string> warningOptions;
    cmdLine.add("-W", warningOptions, "Control the specified warning", "<warning>");

    // File list
    std::vector<std::string> sourceFiles;
    cmdLine.setPositional(sourceFiles, "files");

    if (!cmdLine.parse(argc, argv)) {
        for (auto& err : cmdLine.getErrors())
            print("{}\n", err);
        return 1;
    }

    if (showHelp == true) {
        print("{}", cmdLine.getHelpText("slang SystemVerilog compiler"));
        return 0;
    }

    if (showVersion == true) {
        print("slang version {}.{}.{}\n", VersionInfo::getMajor(), VersionInfo::getMinor(),
              VersionInfo::getRevision());
        return 0;
    }

    bool anyErrors = false;
    SourceManager sourceManager;
    for (const std::string& dir : includeDirs) {
        try {
            sourceManager.addUserDirectory(string_view(dir));
        }
        catch (const std::exception&) {
            print("error: include directory '{}' does not exist\n", dir);
            anyErrors = true;
        }
    }

    for (const std::string& dir : includeSystemDirs) {
        try {
            sourceManager.addSystemDirectory(string_view(dir));
        }
        catch (const std::exception&) {
            print("error: include directory '{}' does not exist\n", dir);
            anyErrors = true;
        }
    }

    PreprocessorOptions ppoptions;
    ppoptions.predefines = defines;
    ppoptions.undefines = undefines;
    ppoptions.predefineSource = "<command-line>";

    if (maxIncludeDepth.has_value())
        ppoptions.maxIncludeDepth = *maxIncludeDepth;

    Bag options;
    options.add(ppoptions);

    std::vector<SourceBuffer> buffers;
    for (const std::string& file : sourceFiles) {
        SourceBuffer buffer = sourceManager.readSource(file);
        if (!buffer) {
            print("error: no such file or directory: '{}'\n", file);
            anyErrors = true;
            continue;
        }

        buffers.push_back(buffer);
    }

    if (anyErrors)
        return 2;

    if (buffers.empty()) {
        print("error: no input files\n");
        return 3;
    }

    if (onlyParse.has_value() + onlyPreprocess.has_value() + onlyMacros.has_value() > 1) {
        print("Can only specify one of --preprocess, --macros-only, --parse-only");
        return 4;
    }

    try {
        if (onlyPreprocess == true) {
            anyErrors = !runPreprocessor(sourceManager, options, buffers, includeComments == true,
                                         includeDirectives == true);
        }
        else if (onlyMacros == true) {
            printMacros(sourceManager, options, buffers);
        }
        else {
            anyErrors = !runCompiler(sourceManager, options, buffers, warningOptions,
                                     onlyParse == true, astJsonFile);
        }
    }
    catch (const std::exception& e) {
#ifdef FUZZ_TARGET
        (void)e;
        throw;
#else
        print("internal compiler error: {}\n", e.what());
        return 4;
#endif
    }

    return anyErrors ? 1 : 0;
}
catch (const std::exception& e) {
#ifdef FUZZ_TARGET
    (void)e;
    throw;
#else
    print("{}\n", e.what());
    return 5;
#endif
}

template<typename Stream, typename String>
void writeToFile(Stream& os, string_view fileName, String contents) {
    os.write(contents.data(), contents.size());
    os.flush();
    if (!os)
        throw std::runtime_error(fmt::format("Unable to write AST to '{}'", fileName));
}

#if defined(_MSC_VER)
#    include <fcntl.h>
#    include <io.h>

void writeToFile(string_view fileName, string_view contents) {
    if (fileName == "-") {
        writeToFile(std::wcout, "stdout", widen(contents));
    }
    else {
        std::ofstream file(widen(fileName));
        writeToFile(file, fileName, contents);
    }
}

template<typename T>
T convert(T&& t) {
    return std::forward<T>(t);
}

std::wstring convert(const std::string& s) {
    return widen(s);
}

std::wstring convert(const std::string_view& s) {
    return widen(s);
}

std::wstring convert(const char* s) {
    return widen(s);
}

template<typename... Args>
void print(string_view format, const Args&... args) {
    fmt::vprint(widen(format), fmt::make_format_args<fmt::wformat_context>(convert(args)...));
}

int wmain(int argc, wchar_t** argv) {
    _setmode(_fileno(stdout), _O_U16TEXT);
    return driverMain(argc, argv);
}

#else

void writeToFile(string_view fileName, string_view contents) {
    if (fileName == "-") {
        writeToFile(std::cout, "stdout", contents);
    }
    else {
        std::ofstream file{ std::string(fileName) };
        writeToFile(file, fileName, contents);
    }
}

template<typename... Args>
void print(string_view format, const Args&... args) {
    fmt::vprint(format, fmt::make_format_args(args...));
}

int main(int argc, char** argv) {
    return driverMain(argc, argv);
}

#endif<commit_msg>Hook up more options in driver<commit_after>//------------------------------------------------------------------------------
// driver.cpp
// Entry point for the primary driver program.
//
// File is under the MIT license; see LICENSE for details
//------------------------------------------------------------------------------

#include <fmt/format.h>
#include <fstream>
#include <iostream>
#include <nlohmann/json.hpp>

#include "slang/compilation/Compilation.h"
#include "slang/diagnostics/DiagnosticEngine.h"
#include "slang/diagnostics/TextDiagnosticClient.h"
#include "slang/parsing/Preprocessor.h"
#include "slang/symbols/CompilationUnitSymbols.h"
#include "slang/syntax/SyntaxPrinter.h"
#include "slang/syntax/SyntaxTree.h"
#include "slang/text/SourceManager.h"
#include "slang/util/CommandLine.h"
#include "slang/util/String.h"
#include "slang/util/Version.h"

using namespace slang;

template<typename... Args>
void print(string_view format, const Args&... args);

void writeToFile(string_view fileName, string_view contents);

bool runPreprocessor(SourceManager& sourceManager, const Bag& options,
                     const std::vector<SourceBuffer>& buffers, bool includeComments,
                     bool includeDirectives) {
    BumpAllocator alloc;

    bool success = true;
    for (const SourceBuffer& buffer : buffers) {
        Diagnostics diagnostics;
        Preprocessor preprocessor(sourceManager, alloc, diagnostics, options);
        preprocessor.pushSource(buffer);

        SyntaxPrinter output;
        output.setIncludeComments(includeComments);
        output.setIncludeDirectives(includeDirectives);

        while (true) {
            Token token = preprocessor.next();
            output.print(token);
            if (token.kind == TokenKind::EndOfFile)
                break;
        }

        if (diagnostics.empty())
            print("{}:\n", sourceManager.getRawFileName(buffer.id));
        else {
            print("{}", DiagnosticEngine::reportAll(sourceManager, diagnostics));
            success = false;
        }

        print("==============================\n{}\n", output.str());
    }
    return success;
}

void printMacros(SourceManager& sourceManager, const Bag& options,
                 const std::vector<SourceBuffer>& buffers) {
    BumpAllocator alloc;

    for (const SourceBuffer& buffer : buffers) {
        Diagnostics diagnostics;
        Preprocessor preprocessor(sourceManager, alloc, diagnostics, options);
        preprocessor.pushSource(buffer);

        while (true) {
            Token token = preprocessor.next();
            if (token.kind == TokenKind::EndOfFile)
                break;
        }

        for (auto macro : preprocessor.getDefinedMacros()) {
            SyntaxPrinter printer;
            printer.setIncludeComments(false);
            printer.setIncludeTrivia(false);
            printer.print(macro->name);

            printer.setIncludeTrivia(true);
            if (macro->formalArguments)
                printer.print(*macro->formalArguments);
            printer.print(macro->body);

            print("{}\n", printer.str());
        }
    }
}

bool runCompiler(SourceManager& sourceManager, const Bag& options,
                 const std::vector<SourceBuffer>& buffers,
                 const std::vector<std::string>& warningOptions, bool onlyParse,
                 const optional<std::string>& astJsonFile) {
    Compilation compilation(options);
    for (const SourceBuffer& buffer : buffers)
        compilation.addSyntaxTree(SyntaxTree::fromBuffer(buffer, sourceManager, options));

    DiagnosticEngine diagEngine(sourceManager);
    Diagnostics optionDiags = diagEngine.setWarningOptions(warningOptions);

    auto client = std::make_shared<TextDiagnosticClient>();
    diagEngine.addClient(client);

    for (auto& diag : optionDiags)
        diagEngine.issue(diag);

    if (onlyParse) {
        for (auto& diag : compilation.getParseDiagnostics())
            diagEngine.issue(diag);
    }
    else {
        for (auto& diag : compilation.getAllDiagnostics())
            diagEngine.issue(diag);
    }

    print("{}", client->getString());

    if (astJsonFile) {
        json output = compilation.getRoot();
        writeToFile(*astJsonFile, output.dump(2));
    }

    return diagEngine.getNumErrors() == 0;
}

template<typename TArgs>
int driverMain(int argc, TArgs argv) try {
    CommandLine cmdLine;

    // General
    optional<bool> showHelp;
    optional<bool> showVersion;
    cmdLine.add("-h,--help", showHelp, "Display available options");
    cmdLine.add("-v,--version", showVersion, "Display version information and exit");

    // Output control
    optional<bool> onlyPreprocess;
    optional<bool> onlyParse;
    optional<bool> onlyMacros;
    cmdLine.add("-E,--preprocess", onlyPreprocess,
                "Only run the preprocessor (and print preprocessed files to stdout)");
    cmdLine.add("--macros-only", onlyMacros, "Print a list of found macros and exit");
    cmdLine.add("--parse-only", onlyParse,
                "Stop after parsing input files, don't perform elaboration or type checking");

    // Include paths
    std::vector<std::string> includeDirs;
    std::vector<std::string> includeSystemDirs;
    cmdLine.add("-I,--include-directory", includeDirs, "Additional include search paths", "<dir>");
    cmdLine.add("--isystem", includeSystemDirs, "Additional system include search paths", "<dir>");

    // Preprocessor
    optional<bool> includeComments;
    optional<bool> includeDirectives;
    optional<uint32_t> maxIncludeDepth;
    std::vector<std::string> defines;
    std::vector<std::string> undefines;
    cmdLine.add("-D,--define-macro", defines,
                "Define <macro> to <value> (or 1 if <value> ommitted) in all source files",
                "<macro>=<value>");
    cmdLine.add("-U,--undefine-macro", undefines,
                "Undefine macro name at the start of all source files", "<macro>");
    cmdLine.add("--comments", includeComments, "Include comments in preprocessed output (with -E)");
    cmdLine.add("--directives", includeDirectives,
                "Include compiler directives in preprocessed output (with -E)");
    cmdLine.add("--max-include-depth", maxIncludeDepth,
                "Maximum depth of nested include files allowed", "<depth>");

    // Parsing
    optional<uint32_t> maxParseDepth;
    optional<uint32_t> maxLexerErrors;
    cmdLine.add("--max-parse-depth", maxParseDepth,
                "Maximum depth of nested language constructs allowed", "<depth>");
    cmdLine.add("--max-lexer-errors", maxLexerErrors,
                "Maximum number of errors that can occur during lexing before the rest of the file "
                "is skipped",
                "<count>");

    // JSON dumping
    optional<std::string> astJsonFile;
    cmdLine.add("--ast-json", astJsonFile,
                "Dump the compiled AST in JSON format to the specified file, or '-' for stdout",
                "<file>");

    // Compilation
    optional<uint32_t> maxInstanceDepth;
    optional<uint32_t> maxGenerateSteps;
    optional<uint32_t> maxConstexprDepth;
    optional<uint32_t> maxConstexprSteps;
    optional<uint32_t> maxConstexprBacktrace;
    cmdLine.add("--max-hierarchy-depth", maxInstanceDepth, "Maximum depth of the design hierarchy",
                "<depth>");
    cmdLine.add("--max-generate-steps", maxGenerateSteps,
                "Maximum number of steps that can occur during generate block "
                "evaluation before giving up",
                "<steps>");
    cmdLine.add("--max-constexpr-depth", maxConstexprDepth,
                "Maximum depth of a constant evaluation call stack", "<depth>");
    cmdLine.add("--max-constexpr-steps", maxConstexprSteps,
                "Maximum number of steps that can occur during constant "
                "evaluation before giving up",
                "<steps>");
    cmdLine.add("--constexpr-backtrace-limit", maxConstexprBacktrace,
                "Maximum number of frames to show when printing a constant evaluation "
                "backtrace; the rest will be abbreviated",
                "<limit>");

    // Diagnostics control
    std::vector<std::string> warningOptions;
    cmdLine.add("-W", warningOptions, "Control the specified warning", "<warning>");

    // File list
    std::vector<std::string> sourceFiles;
    cmdLine.setPositional(sourceFiles, "files");

    if (!cmdLine.parse(argc, argv)) {
        for (auto& err : cmdLine.getErrors())
            print("{}\n", err);
        return 1;
    }

    if (showHelp == true) {
        print("{}", cmdLine.getHelpText("slang SystemVerilog compiler"));
        return 0;
    }

    if (showVersion == true) {
        print("slang version {}.{}.{}\n", VersionInfo::getMajor(), VersionInfo::getMinor(),
              VersionInfo::getRevision());
        return 0;
    }

    bool anyErrors = false;
    SourceManager sourceManager;
    for (const std::string& dir : includeDirs) {
        try {
            sourceManager.addUserDirectory(string_view(dir));
        }
        catch (const std::exception&) {
            print("error: include directory '{}' does not exist\n", dir);
            anyErrors = true;
        }
    }

    for (const std::string& dir : includeSystemDirs) {
        try {
            sourceManager.addSystemDirectory(string_view(dir));
        }
        catch (const std::exception&) {
            print("error: include directory '{}' does not exist\n", dir);
            anyErrors = true;
        }
    }

    PreprocessorOptions ppoptions;
    ppoptions.predefines = defines;
    ppoptions.undefines = undefines;
    ppoptions.predefineSource = "<command-line>";
    if (maxIncludeDepth.has_value())
        ppoptions.maxIncludeDepth = *maxIncludeDepth;

    LexerOptions loptions;
    if (maxLexerErrors.has_value())
        loptions.maxErrors = *maxLexerErrors;

    ParserOptions poptions;
    if (maxParseDepth.has_value())
        poptions.maxRecursionDepth = *maxParseDepth;

    CompilationOptions coptions;
    if (maxInstanceDepth.has_value())
        coptions.maxInstanceDepth = *maxInstanceDepth;
    if (maxGenerateSteps.has_value())
        coptions.maxGenerateSteps = *maxGenerateSteps;
    if (maxConstexprDepth.has_value())
        coptions.maxConstexprDepth = *maxConstexprDepth;
    if (maxConstexprSteps.has_value())
        coptions.maxConstexprSteps = *maxConstexprSteps;
    if (maxConstexprBacktrace.has_value())
        coptions.maxConstexprBacktrace = *maxConstexprBacktrace;

    Bag options;
    options.add(ppoptions);
    options.add(loptions);
    options.add(poptions);
    options.add(coptions);

    std::vector<SourceBuffer> buffers;
    for (const std::string& file : sourceFiles) {
        SourceBuffer buffer = sourceManager.readSource(file);
        if (!buffer) {
            print("error: no such file or directory: '{}'\n", file);
            anyErrors = true;
            continue;
        }

        buffers.push_back(buffer);
    }

    if (anyErrors)
        return 2;

    if (buffers.empty()) {
        print("error: no input files\n");
        return 3;
    }

    if (onlyParse.has_value() + onlyPreprocess.has_value() + onlyMacros.has_value() > 1) {
        print("Can only specify one of --preprocess, --macros-only, --parse-only");
        return 4;
    }

    try {
        if (onlyPreprocess == true) {
            anyErrors = !runPreprocessor(sourceManager, options, buffers, includeComments == true,
                                         includeDirectives == true);
        }
        else if (onlyMacros == true) {
            printMacros(sourceManager, options, buffers);
        }
        else {
            anyErrors = !runCompiler(sourceManager, options, buffers, warningOptions,
                                     onlyParse == true, astJsonFile);
        }
    }
    catch (const std::exception& e) {
#ifdef FUZZ_TARGET
        (void)e;
        throw;
#else
        print("internal compiler error: {}\n", e.what());
        return 4;
#endif
    }

    return anyErrors ? 1 : 0;
}
catch (const std::exception& e) {
#ifdef FUZZ_TARGET
    (void)e;
    throw;
#else
    print("{}\n", e.what());
    return 5;
#endif
}

template<typename Stream, typename String>
void writeToFile(Stream& os, string_view fileName, String contents) {
    os.write(contents.data(), contents.size());
    os.flush();
    if (!os)
        throw std::runtime_error(fmt::format("Unable to write AST to '{}'", fileName));
}

#if defined(_MSC_VER)
#    include <fcntl.h>
#    include <io.h>

void writeToFile(string_view fileName, string_view contents) {
    if (fileName == "-") {
        writeToFile(std::wcout, "stdout", widen(contents));
    }
    else {
        std::ofstream file(widen(fileName));
        writeToFile(file, fileName, contents);
    }
}

template<typename T>
T convert(T&& t) {
    return std::forward<T>(t);
}

std::wstring convert(const std::string& s) {
    return widen(s);
}

std::wstring convert(const std::string_view& s) {
    return widen(s);
}

std::wstring convert(const char* s) {
    return widen(s);
}

template<typename... Args>
void print(string_view format, const Args&... args) {
    fmt::vprint(widen(format), fmt::make_format_args<fmt::wformat_context>(convert(args)...));
}

int wmain(int argc, wchar_t** argv) {
    _setmode(_fileno(stdout), _O_U16TEXT);
    return driverMain(argc, argv);
}

#else

void writeToFile(string_view fileName, string_view contents) {
    if (fileName == "-") {
        writeToFile(std::cout, "stdout", contents);
    }
    else {
        std::ofstream file{ std::string(fileName) };
        writeToFile(file, fileName, contents);
    }
}

template<typename... Args>
void print(string_view format, const Args&... args) {
    fmt::vprint(format, fmt::make_format_args(args...));
}

int main(int argc, char** argv) {
    return driverMain(argc, argv);
}

#endif<|endoftext|>


<commit_before>/* Copyright (c) 2017-2018 Hans-Kristian Arntzen
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#pragma once

#include <tuple>
#include <vector>
#include <memory>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include "object_pool.hpp"
#include "intrusive.hpp"
#include <assert.h>

namespace Granite
{
struct ComponentBase
{
};

namespace Internal
{
template <size_t Index>
struct HasComponent
{
	template <typename T>
	static bool has_component(const T &t, const ComponentBase *component)
	{
		return static_cast<ComponentBase *>(std::get<Index>(t)) == component ||
		       HasComponent<Index - 1>::has_component(t, component);
	}
};

template <>
struct HasComponent<0>
{
	template <typename T>
	static bool has_component(const T &t, const ComponentBase *component)
	{
		return static_cast<ComponentBase *>(std::get<0>(t)) == component;
	}
};
}

class Entity;

struct ComponentIDMapping
{
public:
	template <typename T>
	static uint32_t get_id()
	{
		static uint32_t id = ids++;
		return id;
	}

	template <typename... Ts>
	static uint32_t get_group_id()
	{
		static uint32_t id = group_ids++;
		return id;
	}

private:
	static uint32_t ids;
	static uint32_t group_ids;
};

class EntityGroupBase
{
public:
	virtual ~EntityGroupBase() = default;
	virtual void add_entity(Entity &entity) = 0;
	virtual void remove_component(ComponentBase *component) = 0;
};

class EntityPool;

struct EntityDeleter
{
	void operator()(Entity *entity);
};

class Entity : public Util::IntrusivePtrEnabled<Entity, EntityDeleter>
{
public:
	Entity(EntityPool *pool)
		: pool(pool)
	{
	}

	bool has_component(uint32_t id) const
	{
		auto itr = components.find(id);
		return itr != std::end(components) && itr->second;
	}

	template <typename T>
	bool has_component() const
	{
		return has_component(ComponentIDMapping::get_id<T>());
	}

	template <typename T>
	T *get_component()
	{
		auto itr = components.find(ComponentIDMapping::get_id<T>());
		if (itr == std::end(components))
			return nullptr;

		return static_cast<T *>(itr->second);
	}

	template <typename T>
	const T *get_component() const
	{
		auto itr = components.find(ComponentIDMapping::get_id<T>());
		if (itr == std::end(components))
			return nullptr;

		return static_cast<T *>(itr->second);
	}

	template <typename T, typename... Ts>
	T *allocate_component(Ts&&... ts);

	template <typename T>
	void free_component();

	std::unordered_map<uint32_t, ComponentBase *> &get_components()
	{
		return components;
	}

	EntityPool *get_pool()
	{
		return pool;
	}

private:
	EntityPool *pool;
	std::unordered_map<uint32_t, ComponentBase *> components;
};

template <typename... Ts>
class EntityGroup : public EntityGroupBase
{
public:
	void add_entity(Entity &entity) override final
	{
		if (has_all_components<Ts...>(entity))
		{
			entities.push_back(&entity);
			groups.push_back(std::make_tuple(entity.get_component<Ts>()...));
		}
	}

	void remove_component(ComponentBase *component) override final
	{
		auto itr = std::find_if(std::begin(groups), std::end(groups), [&](const std::tuple<Ts *...> &t) {
			return has_component(t, component);
		});

		if (itr == end(groups))
			return;

		auto offset = size_t(itr - begin(groups));
		if (offset != groups.size() - 1)
		{
			std::swap(groups[offset], groups.back());
			std::swap(entities[offset], entities.back());
		}
		groups.pop_back();
		entities.pop_back();
	}

	std::vector<std::tuple<Ts *...>> &get_groups()
	{
		return groups;
	}

private:
	std::vector<std::tuple<Ts *...>> groups;
	std::vector<Entity *> entities;

	template <typename... Us>
	struct HasAllComponents;

	template <typename U, typename... Us>
	struct HasAllComponents<U, Us...>
	{
		static bool has_component(const Entity &entity)
		{
			return entity.has_component(ComponentIDMapping::get_id<U>()) &&
		           HasAllComponents<Us...>::has_component(entity);
		}
	};

	template <typename U>
	struct HasAllComponents<U>
	{
		static bool has_component(const Entity &entity)
		{
			return entity.has_component(ComponentIDMapping::get_id<U>());
		}
	};

	template <typename... Us>
	bool has_all_components(const Entity &entity)
	{
		return HasAllComponents<Us...>::has_component(entity);
	}

	template <typename... Us>
	static bool has_component(const std::tuple<Us *...> &t, const ComponentBase *component)
	{
		return Internal::HasComponent<sizeof...(Us) - 1>::has_component(t, component);
	}
};

class ComponentAllocatorBase
{
public:
	virtual ~ComponentAllocatorBase() = default;
	virtual void free_component(ComponentBase *component) = 0;
};

template <typename T>
struct ComponentAllocator : public ComponentAllocatorBase
{
	Util::ObjectPool<T> pool;

	void free_component(ComponentBase *component) override final
	{
		pool.free(static_cast<T *>(component));
	}
};

using EntityHandle = Util::IntrusivePtr<Entity>;

class EntityPool
{
public:
	EntityHandle create_entity()
	{
		auto itr = EntityHandle(entity_pool.allocate(this));
		entities.push_back(itr.get());
		return itr;
	}

	void delete_entity(Entity *entity)
	{
		auto &components = entity->get_components();
		for (auto &component : components)
			if (component.second)
				free_component(component.first, component.second);
		entity_pool.free(entity);

		auto itr = std::find(std::begin(entities), std::end(entities), entity);
		auto offset = size_t(itr - std::begin(entities));
		if (offset != entities.size() - 1)
			std::swap(entities[offset], entities.back());
		entities.pop_back();
	}

	template <typename... Ts>
	std::vector<std::tuple<Ts *...>> &get_component_group()
	{
		uint32_t group_id = ComponentIDMapping::get_group_id<Ts...>();
		auto itr = groups.find(group_id);
		if (itr == std::end(groups))
		{
			register_group<Ts...>(group_id);
			auto tmp = groups.insert(std::make_pair(group_id, std::unique_ptr<EntityGroupBase>(new EntityGroup<Ts...>())));
			itr = tmp.first;

			auto *group = static_cast<EntityGroup<Ts...> *>(itr->second.get());
			for (auto &entity : entities)
				group->add_entity(*entity);
		}

		auto *group = static_cast<EntityGroup<Ts...> *>(itr->second.get());
		return group->get_groups();
	}

	template <typename T, typename... Ts>
	T *allocate_component(Entity &entity, Ts&&... ts)
	{
		uint32_t id = ComponentIDMapping::get_id<T>();
		auto itr = components.find(id);
		if (itr == std::end(components))
		{
			auto tmp = components.insert(std::make_pair(id, std::unique_ptr<ComponentAllocatorBase>(new ComponentAllocator<T>)));
			itr = tmp.first;
		}

		auto *allocator = static_cast<ComponentAllocator<T> *>(itr->second.get());
		auto &comp = entity.get_components()[id];
		if (comp)
		{
			allocator->free_component(comp);
			comp = allocator->pool.allocate(std::forward<Ts>(ts)...);
			for (auto &group : component_to_groups[id])
				groups[group]->add_entity(entity);
		}
		else
		{
			comp = allocator->pool.allocate(std::forward<Ts>(ts)...);
			for (auto &group : component_to_groups[id])
				groups[group]->add_entity(entity);
		}
		return static_cast<T *>(comp);
	}

	void free_component(uint32_t id, ComponentBase *component)
	{
		components[id]->free_component(component);
		for (auto &group : component_to_groups[id])
			groups[group]->remove_component(component);
	}

	void reset_groups();

private:
	Util::ObjectPool<Entity> entity_pool;
	std::unordered_map<uint32_t, std::unique_ptr<EntityGroupBase>> groups;
	std::unordered_map<uint32_t, std::unique_ptr<ComponentAllocatorBase>> components;
	std::unordered_map<uint32_t, std::unordered_set<uint32_t>> component_to_groups;
	std::vector<Entity *> entities;

	template <typename... Us>
	struct GroupRegisters;

	template <typename U, typename... Us>
	struct GroupRegisters<U, Us...>
	{
		static void register_group(std::unordered_map<uint32_t, std::unordered_set<uint32_t>> &groups,
		                           uint32_t group_id)
		{
			groups[ComponentIDMapping::get_id<U>()].insert(group_id);
			GroupRegisters<Us...>::register_group(groups, group_id);
		}
	};

	template <typename U>
	struct GroupRegisters<U>
	{
		static void register_group(std::unordered_map<uint32_t, std::unordered_set<uint32_t>> &groups,
		                           uint32_t group_id)
		{
			groups[ComponentIDMapping::get_id<U>()].insert(group_id);
		}
	};

	template <typename U, typename... Us>
	void register_group(uint32_t group_id)
	{
		GroupRegisters<U, Us...>::register_group(component_to_groups, group_id);
	}
};

template <typename T, typename... Ts>
T *Entity::allocate_component(Ts&&... ts)
{
	return pool->allocate_component<T>(*this, std::forward<Ts>(ts)...);
}

template <typename T>
void Entity::free_component()
{
	auto id = ComponentIDMapping::get_id<T>();
	auto itr = components.find(id);
	if (itr != std::end(components))
	{
		assert(itr->second);
		pool->free_component(id, itr->second);
		components.erase(itr);
	}
}

}<commit_msg>Remove some unneeded data structures.<commit_after>/* Copyright (c) 2017-2018 Hans-Kristian Arntzen
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#pragma once

#include <tuple>
#include <vector>
#include <memory>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include "object_pool.hpp"
#include "intrusive.hpp"
#include <assert.h>

namespace Granite
{
struct ComponentBase
{
};

namespace Internal
{
template <size_t Index>
struct HasComponent
{
	template <typename T>
	static bool has_component(const T &t, const ComponentBase *component)
	{
		return static_cast<ComponentBase *>(std::get<Index>(t)) == component ||
		       HasComponent<Index - 1>::has_component(t, component);
	}
};

template <>
struct HasComponent<0>
{
	template <typename T>
	static bool has_component(const T &t, const ComponentBase *component)
	{
		return static_cast<ComponentBase *>(std::get<0>(t)) == component;
	}
};
}

class Entity;

struct ComponentIDMapping
{
public:
	template <typename T>
	static uint32_t get_id()
	{
		static uint32_t id = ids++;
		return id;
	}

	template <typename... Ts>
	static uint32_t get_group_id()
	{
		static uint32_t id = group_ids++;
		return id;
	}

private:
	static uint32_t ids;
	static uint32_t group_ids;
};

class EntityGroupBase
{
public:
	virtual ~EntityGroupBase() = default;
	virtual void add_entity(Entity &entity) = 0;
	virtual void remove_component(ComponentBase *component) = 0;
};

class EntityPool;

struct EntityDeleter
{
	void operator()(Entity *entity);
};

class Entity : public Util::IntrusivePtrEnabled<Entity, EntityDeleter>
{
public:
	Entity(EntityPool *pool)
		: pool(pool)
	{
	}

	bool has_component(uint32_t id) const
	{
		auto itr = components.find(id);
		return itr != std::end(components) && itr->second;
	}

	template <typename T>
	bool has_component() const
	{
		return has_component(ComponentIDMapping::get_id<T>());
	}

	template <typename T>
	T *get_component()
	{
		auto itr = components.find(ComponentIDMapping::get_id<T>());
		if (itr == std::end(components))
			return nullptr;

		return static_cast<T *>(itr->second);
	}

	template <typename T>
	const T *get_component() const
	{
		auto itr = components.find(ComponentIDMapping::get_id<T>());
		if (itr == std::end(components))
			return nullptr;

		return static_cast<T *>(itr->second);
	}

	template <typename T, typename... Ts>
	T *allocate_component(Ts&&... ts);

	template <typename T>
	void free_component();

	std::unordered_map<uint32_t, ComponentBase *> &get_components()
	{
		return components;
	}

	EntityPool *get_pool()
	{
		return pool;
	}

private:
	EntityPool *pool;
	std::unordered_map<uint32_t, ComponentBase *> components;
};

template <typename... Ts>
class EntityGroup : public EntityGroupBase
{
public:
	void add_entity(Entity &entity) override final
	{
		if (has_all_components<Ts...>(entity))
		{
			//entities.push_back(&entity);
			groups.push_back(std::make_tuple(entity.get_component<Ts>()...));
		}
	}

	void remove_component(ComponentBase *component) override final
	{
		auto itr = std::find_if(std::begin(groups), std::end(groups), [&](const std::tuple<Ts *...> &t) {
			return has_component(t, component);
		});

		if (itr == end(groups))
			return;

		auto offset = size_t(itr - begin(groups));
		if (offset != groups.size() - 1)
		{
			std::swap(groups[offset], groups.back());
			//std::swap(entities[offset], entities.back());
		}
		groups.pop_back();
		//entities.pop_back();
	}

	std::vector<std::tuple<Ts *...>> &get_groups()
	{
		return groups;
	}

private:
	std::vector<std::tuple<Ts *...>> groups;
	//std::vector<Entity *> entities;

	template <typename... Us>
	struct HasAllComponents;

	template <typename U, typename... Us>
	struct HasAllComponents<U, Us...>
	{
		static bool has_component(const Entity &entity)
		{
			return entity.has_component(ComponentIDMapping::get_id<U>()) &&
		           HasAllComponents<Us...>::has_component(entity);
		}
	};

	template <typename U>
	struct HasAllComponents<U>
	{
		static bool has_component(const Entity &entity)
		{
			return entity.has_component(ComponentIDMapping::get_id<U>());
		}
	};

	template <typename... Us>
	bool has_all_components(const Entity &entity)
	{
		return HasAllComponents<Us...>::has_component(entity);
	}

	template <typename... Us>
	static bool has_component(const std::tuple<Us *...> &t, const ComponentBase *component)
	{
		return Internal::HasComponent<sizeof...(Us) - 1>::has_component(t, component);
	}
};

class ComponentAllocatorBase
{
public:
	virtual ~ComponentAllocatorBase() = default;
	virtual void free_component(ComponentBase *component) = 0;
};

template <typename T>
struct ComponentAllocator : public ComponentAllocatorBase
{
	Util::ObjectPool<T> pool;

	void free_component(ComponentBase *component) override final
	{
		pool.free(static_cast<T *>(component));
	}
};

using EntityHandle = Util::IntrusivePtr<Entity>;

class EntityPool
{
public:
	EntityHandle create_entity()
	{
		auto itr = EntityHandle(entity_pool.allocate(this));
		entities.push_back(itr.get());
		return itr;
	}

	void delete_entity(Entity *entity)
	{
		auto &components = entity->get_components();
		for (auto &component : components)
			if (component.second)
				free_component(component.first, component.second);
		entity_pool.free(entity);

		auto itr = std::find(std::begin(entities), std::end(entities), entity);
		auto offset = size_t(itr - std::begin(entities));
		if (offset != entities.size() - 1)
			std::swap(entities[offset], entities.back());
		entities.pop_back();
	}

	template <typename... Ts>
	std::vector<std::tuple<Ts *...>> &get_component_group()
	{
		uint32_t group_id = ComponentIDMapping::get_group_id<Ts...>();
		auto itr = groups.find(group_id);
		if (itr == std::end(groups))
		{
			register_group<Ts...>(group_id);
			auto tmp = groups.insert(std::make_pair(group_id, std::unique_ptr<EntityGroupBase>(new EntityGroup<Ts...>())));
			itr = tmp.first;

			auto *group = static_cast<EntityGroup<Ts...> *>(itr->second.get());
			for (auto &entity : entities)
				group->add_entity(*entity);
		}

		auto *group = static_cast<EntityGroup<Ts...> *>(itr->second.get());
		return group->get_groups();
	}

	template <typename T, typename... Ts>
	T *allocate_component(Entity &entity, Ts&&... ts)
	{
		uint32_t id = ComponentIDMapping::get_id<T>();
		auto itr = components.find(id);
		if (itr == std::end(components))
		{
			auto tmp = components.insert(std::make_pair(id, std::unique_ptr<ComponentAllocatorBase>(new ComponentAllocator<T>)));
			itr = tmp.first;
		}

		auto *allocator = static_cast<ComponentAllocator<T> *>(itr->second.get());
		auto &comp = entity.get_components()[id];
		assert(!comp);
		if (!comp)
		{
			comp = allocator->pool.allocate(std::forward<Ts>(ts)...);
			for (auto &group : component_to_groups[id])
				groups[group]->add_entity(entity);
		}
		return static_cast<T *>(comp);
	}

	void free_component(uint32_t id, ComponentBase *component)
	{
		components[id]->free_component(component);
		for (auto &group : component_to_groups[id])
			groups[group]->remove_component(component);
	}

	void reset_groups();

private:
	Util::ObjectPool<Entity> entity_pool;
	std::unordered_map<uint32_t, std::unique_ptr<EntityGroupBase>> groups;
	std::unordered_map<uint32_t, std::unique_ptr<ComponentAllocatorBase>> components;
	std::unordered_map<uint32_t, std::unordered_set<uint32_t>> component_to_groups;
	std::vector<Entity *> entities;

	template <typename... Us>
	struct GroupRegisters;

	template <typename U, typename... Us>
	struct GroupRegisters<U, Us...>
	{
		static void register_group(std::unordered_map<uint32_t, std::unordered_set<uint32_t>> &groups,
		                           uint32_t group_id)
		{
			groups[ComponentIDMapping::get_id<U>()].insert(group_id);
			GroupRegisters<Us...>::register_group(groups, group_id);
		}
	};

	template <typename U>
	struct GroupRegisters<U>
	{
		static void register_group(std::unordered_map<uint32_t, std::unordered_set<uint32_t>> &groups,
		                           uint32_t group_id)
		{
			groups[ComponentIDMapping::get_id<U>()].insert(group_id);
		}
	};

	template <typename U, typename... Us>
	void register_group(uint32_t group_id)
	{
		GroupRegisters<U, Us...>::register_group(component_to_groups, group_id);
	}
};

template <typename T, typename... Ts>
T *Entity::allocate_component(Ts&&... ts)
{
	return pool->allocate_component<T>(*this, std::forward<Ts>(ts)...);
}

template <typename T>
void Entity::free_component()
{
	auto id = ComponentIDMapping::get_id<T>();
	auto itr = components.find(id);
	if (itr != std::end(components))
	{
		assert(itr->second);
		pool->free_component(id, itr->second);
		components.erase(itr);
	}
}

}<|endoftext|>


<commit_before>#include "coroutines/generator.hpp"

#include <gtest/gtest.h>

TEST(Generator, Iterator)
{
    crs::generator_iterator<int> it([](crs::generator_output<int> out){
        for(int i = 0; i < 10; i++)
        {
            *(out++) = i;
        }
    });

    int expected = 0;
    for(; it != crs::generator_iterator<int>(); it++)
    {
        ASSERT_EQ(expected++, *it);
    }
}
<commit_msg>One more test for generator_iterator<commit_after>#include "coroutines/generator.hpp"

#include <gtest/gtest.h>

TEST(Generator, Iterator)
{
    crs::generator_iterator<int> it([](crs::generator_output<int> out){
        for(int i = 0; i < 10; i++)
        {
            *(out++) = i;
        }
    });

    int expected = 0;
    for(; it != crs::generator_iterator<int>(); it++)
    {
        ASSERT_EQ(expected++, *it);
    }
}

TEST(Generator, EmptyIterator)
{
    crs::generator_iterator<int> it([](crs::generator_output<int>){});

    for(; it != crs::generator_iterator<int>(); it++)
    {
        ASSERT_FALSE(true);
    }
}
<|endoftext|>