<commit_before>// Copyright (c) 2006-2008 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.
//
// This file defines the methods useful for uninstalling Chrome.

#include "chrome/installer/setup/uninstall.h"

#include <atlbase.h>
#include <windows.h>
#include <msi.h>

#include "base/file_util.h"
#include "base/path_service.h"
#include "base/registry.h"
#include "base/scoped_ptr.h"
#include "base/string_util.h"
#include "base/win_util.h"
#include "base/wmi_util.h"
#include "chrome/app/result_codes.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/installer/setup/setup.h"
#include "chrome/installer/setup/setup_constants.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/helper.h"
#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/l10n_string_util.h"
#include "chrome/installer/util/logging_installer.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/google_update_settings.h"
#include "chrome/installer/util/shell_util.h"
#include "chrome/installer/util/util_constants.h"
#include "chrome/installer/util/version.h"

#include "installer_util_strings.h"

namespace {

// This method deletes Chrome shortcut folder from Windows Start menu. It
// checks system_uninstall to see if the shortcut is in all users start menu
// or current user start menu.
void DeleteChromeShortcut(bool system_uninstall) {
  std::wstring shortcut_path;
  if (system_uninstall) {
    PathService::Get(base::DIR_COMMON_START_MENU, &shortcut_path);
    // In case of system level uninstall, we want to remove desktop and
    // Quick Launch shortcuts also. In case of user level uninstall,
    // chrome.exe deletes these shortcuts.
    ShellUtil::RemoveChromeDesktopShortcut(ShellUtil::CURRENT_USER |
                                           ShellUtil::SYSTEM_LEVEL);
    ShellUtil::RemoveChromeQuickLaunchShortcut(ShellUtil::CURRENT_USER |
                                               ShellUtil::SYSTEM_LEVEL);
  } else {
    PathService::Get(base::DIR_START_MENU, &shortcut_path);
  }
  if (shortcut_path.empty()) {
    LOG(ERROR) << "Failed to get location for shortcut.";
  } else {
    BrowserDistribution* dist = BrowserDistribution::GetDistribution();
    file_util::AppendToPath(&shortcut_path, dist->GetApplicationName());
    LOG(INFO) << "Deleting shortcut " << shortcut_path;
    if (!file_util::Delete(shortcut_path, true))
      LOG(ERROR) << "Failed to delete folder: " << shortcut_path;
  }
}

// Deletes all installed files of Chromium and Folders. Before deleting it
// needs to move setup.exe in a temp folder because the current process
// is using that file. It returns false when it can not get the path to
// installation folder, in all other cases it returns true even in case
// of error (only logs the error).
bool DeleteFilesAndFolders(const std::wstring& exe_path, bool system_uninstall,
    const installer::Version& installed_version) {
  std::wstring install_path(installer::GetChromeInstallPath(system_uninstall));
  if (install_path.empty()) {
    LOG(ERROR) << "Could not get installation destination path.";
    return false; // Nothing else we can do for uninstall, so we return.
  } else {
    LOG(INFO) << "install destination path: " << install_path;
  }

  std::wstring setup_exe(installer::GetInstallerPathUnderChrome(
      install_path, installed_version.GetString()));
  file_util::AppendToPath(&setup_exe, file_util::GetFilenameFromPath(exe_path));

  std::wstring temp_file;
  file_util::CreateTemporaryFileName(&temp_file);
  file_util::Move(setup_exe, temp_file);

  LOG(INFO) << "Deleting install path " << install_path;
  if (!file_util::Delete(install_path, true)) {
    LOG(ERROR) << "Failed to delete folder (1st try): " << install_path;
    // This is to let any closing chrome.exe die before trying delete one
    // more time.
    Sleep(10000);
    if (!file_util::Delete(install_path, true))
      LOG(ERROR) << "Failed to delete folder (2nd try): " << install_path;
  }

  // Now check and delete if the parent directories are empty
  // For example Google\Chrome or Chromium
  std::wstring parent_dir = file_util::GetDirectoryFromPath(install_path);
  if (!parent_dir.empty() && file_util::IsDirectoryEmpty(parent_dir)) {
    if (!file_util::Delete(parent_dir, true))
      LOG(ERROR) << "Failed to delete folder: " << parent_dir;
    parent_dir = file_util::GetDirectoryFromPath(parent_dir);
    if (!parent_dir.empty() &&
        file_util::IsDirectoryEmpty(parent_dir)) {
      if (!file_util::Delete(parent_dir, true))
        LOG(ERROR) << "Failed to delete folder: " << parent_dir;
    }
  }
  return true;
}

// This method tries to delete a registry key and logs an error message
// in case of failure. It returns true if deletion is successful,
// otherwise false.
bool DeleteRegistryKey(RegKey& key, const std::wstring& key_path) {
  LOG(INFO) << "Deleting registry key " << key_path;
  if (!key.DeleteKey(key_path.c_str())) {
    LOG(ERROR) << "Failed to delete registry key: " << key_path;
    return false;
  }
  return true;
}

// This method tries to delete a registry value and logs an error message
// in case of failure. It returns true if deletion is successful,
// otherwise false.
bool DeleteRegistryValue(HKEY reg_root, const std::wstring& key_path,
                         const std::wstring& value_name) {
  RegKey key(reg_root, key_path.c_str(), KEY_ALL_ACCESS);
  LOG(INFO) << "Deleting registry value " << value_name;
  if (!key.DeleteValue(value_name.c_str())) {
    LOG(ERROR) << "Failed to delete registry value: " << value_name;
    return false;
  }
  return true;
}

// This method checks if Chrome is currently running or if the user has
// cancelled the uninstall operation by clicking Cancel on the confirmation
// box that Chrome pops up.
installer_util::InstallStatus IsChromeActiveOrUserCancelled(
    bool system_uninstall) {
  static const std::wstring kCmdLineOptions(L" --uninstall");
  int32 exit_code = ResultCodes::NORMAL_EXIT;

  // Here we want to save user from frustration (in case of Chrome crashes)
  // and continue with the uninstallation as long as chrome.exe process exit
  // code is NOT one of the following:
  // - UNINSTALL_CHROME_ALIVE - chrome.exe is currently running
  // - UNINSTALL_USER_CANCEL - User cancelled uninstallation
  // - HUNG - chrome.exe was killed by HuntForZombieProcesses() (until we can
  //          give this method some brains and not kill chrome.exe launched
  //          by us, we will not uninstall if we get this return code).
  LOG(INFO) << "Launching Chrome to do uninstall tasks.";
  if (installer::LaunchChromeAndWaitForResult(system_uninstall,
                                              kCmdLineOptions,
                                              &exit_code)) {
    LOG(INFO) << "chrome.exe launched for uninstall confirmation returned: "
              << exit_code;
    if ((exit_code == ResultCodes::UNINSTALL_CHROME_ALIVE) ||
        (exit_code == ResultCodes::UNINSTALL_USER_CANCEL) ||
        (exit_code == ResultCodes::HUNG))
      return installer_util::UNINSTALL_CANCELLED;
  } else {
    LOG(ERROR) << "Failed to launch chrome.exe for uninstall confirmation.";
  }

  return installer_util::UNINSTALL_CONFIRMED;
}
}  // namespace

installer_util::InstallStatus installer_setup::UninstallChrome(
    const std::wstring& exe_path, bool system_uninstall,
    const installer::Version& installed_version,
    bool remove_all, bool force_uninstall) {
  if (!force_uninstall) {
    installer_util::InstallStatus status =
        IsChromeActiveOrUserCancelled(system_uninstall);
    if (status != installer_util::UNINSTALL_CONFIRMED)
      return status;
  }

#if defined(GOOGLE_CHROME_BUILD)
  // TODO(rahulk): This should be done by DoPreUninstallOperations call above
  wchar_t product[39];  // GUID + '\0'
  MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);  // Don't show any UI to user.
  for (int i = 0; MsiEnumRelatedProducts(google_update::kGearsUpgradeCode, 0, i,
                                         product) != ERROR_NO_MORE_ITEMS; ++i) {
    LOG(INFO) << "Uninstalling Gears - " << product;
    unsigned int ret = MsiConfigureProduct(product, INSTALLLEVEL_MAXIMUM,
                                           INSTALLSTATE_ABSENT);
    if (ret != ERROR_SUCCESS)
      LOG(ERROR) << "Failed to uninstall Gears " << product << ": " << ret;
  }
#endif

  // Chrome is not in use so lets uninstall Chrome by deleting various files
  // and registry entries. Here we will just make best effort and keep going
  // in case of errors.
  // First delete shortcut from Start->Programs.
  DeleteChromeShortcut(system_uninstall);

  // Delete the registry keys (Uninstall key and Version key).
  HKEY reg_root = system_uninstall ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  RegKey key(reg_root, L"", KEY_ALL_ACCESS);
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  DeleteRegistryKey(key, dist->GetUninstallRegPath());
  DeleteRegistryKey(key, dist->GetVersionKey());

  // Delete Software\Classes\ChromeHTML,
  // Software\Clients\StartMenuInternet\chrome.exe and
  // Software\RegisteredApplications\Chrome
  std::wstring html_prog_id(ShellUtil::kRegClasses);
  file_util::AppendToPath(&html_prog_id, ShellUtil::kChromeHTMLProgId);
  DeleteRegistryKey(key, html_prog_id);

  std::wstring set_access_key(ShellUtil::kRegStartMenuInternet);
  file_util::AppendToPath(&set_access_key, installer_util::kChromeExe);
  DeleteRegistryKey(key, set_access_key);

  DeleteRegistryValue(reg_root, ShellUtil::kRegRegisteredApplications,
                      dist->GetApplicationName());
  key.Close();

  // Delete shared registry keys as well (these require admin rights) if
  // remove_all option is specified.
  if (remove_all) {
    RegKey hklm_key(HKEY_LOCAL_MACHINE, L"", KEY_ALL_ACCESS);
    DeleteRegistryKey(hklm_key, set_access_key);
    DeleteRegistryKey(hklm_key, html_prog_id);
    DeleteRegistryValue(HKEY_LOCAL_MACHINE,
                        ShellUtil::kRegRegisteredApplications,
                        dist->GetApplicationName());

    // Delete media player registry key that exists only in HKLM.
    std::wstring reg_path(installer::kMediaPlayerRegPath);
    file_util::AppendToPath(&reg_path, installer_util::kChromeExe);
    DeleteRegistryKey(hklm_key, reg_path);
    hklm_key.Close();
  }

  // Finally delete all the files from Chrome folder after moving setup.exe
  // to a temp location.
  if (!DeleteFilesAndFolders(exe_path, system_uninstall, installed_version))
    return installer_util::UNINSTALL_FAILED;

  LOG(INFO) << "Uninstallation complete. Launching Uninstall survey.";
  dist->DoPostUninstallOperations(installed_version);
  return installer_util::UNINSTALL_SUCCESSFUL;
}

<commit_msg>On silent uninstall (--force-uninstall) close all Chrome processes without prompting the user.<commit_after>// Copyright (c) 2006-2008 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.
//
// This file defines the methods useful for uninstalling Chrome.

#include "chrome/installer/setup/uninstall.h"

#include <atlbase.h>
#include <windows.h>
#include <msi.h>

#include "base/file_util.h"
#include "base/path_service.h"
#include "base/process_util.h"
#include "base/registry.h"
#include "base/scoped_ptr.h"
#include "base/string_util.h"
#include "base/win_util.h"
#include "base/wmi_util.h"
#include "chrome/app/result_codes.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/installer/setup/setup.h"
#include "chrome/installer/setup/setup_constants.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/helper.h"
#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/l10n_string_util.h"
#include "chrome/installer/util/logging_installer.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/google_update_settings.h"
#include "chrome/installer/util/shell_util.h"
#include "chrome/installer/util/util_constants.h"
#include "chrome/installer/util/version.h"

#include "installer_util_strings.h"

namespace {

// This functions checks for any Chrome instances that are
// running and first asks them to close politely by sending a Windows message.
// If there is an error while sending message or if there are still Chrome
// procesess active after the message has been sent, this function will try
// to kill them.
void CloseAllChromeProcesses() {
  for (int j = 0; j < 4; ++j) {
    std::wstring wnd_class(L"Chrome_ContainerWin_");
    wnd_class.append(IntToWString(j));
    HWND window = FindWindowEx(NULL, NULL, wnd_class.c_str(), NULL);
    while (window) {
      HWND tmpWnd = window;
      window = FindWindowEx(NULL, window, wnd_class.c_str(), NULL);
      if (!SendMessageTimeout(tmpWnd, WM_CLOSE, 0, 0, SMTO_BLOCK, 3000, NULL) &&
          (GetLastError() == ERROR_TIMEOUT)) {
        process_util::CleanupProcesses(installer_util::kChromeExe, 0,
                                       ResultCodes::HUNG, NULL);
        return;
      }
    }
  }

  // If asking politely didn't work, wait for 15 seconds and then kill all
  // chrome.exe. This check is just in case Chrome is ignoring WM_CLOSE messages.
  process_util::CleanupProcesses(installer_util::kChromeExe, 15000,
                                 ResultCodes::HUNG, NULL);
}

// This method deletes Chrome shortcut folder from Windows Start menu. It
// checks system_uninstall to see if the shortcut is in all users start menu
// or current user start menu.
void DeleteChromeShortcut(bool system_uninstall) {
  std::wstring shortcut_path;
  if (system_uninstall) {
    PathService::Get(base::DIR_COMMON_START_MENU, &shortcut_path);
    // In case of system level uninstall, we want to remove desktop and
    // Quick Launch shortcuts also. In case of user level uninstall,
    // chrome.exe deletes these shortcuts.
    ShellUtil::RemoveChromeDesktopShortcut(ShellUtil::CURRENT_USER |
                                           ShellUtil::SYSTEM_LEVEL);
    ShellUtil::RemoveChromeQuickLaunchShortcut(ShellUtil::CURRENT_USER |
                                               ShellUtil::SYSTEM_LEVEL);
  } else {
    PathService::Get(base::DIR_START_MENU, &shortcut_path);
  }
  if (shortcut_path.empty()) {
    LOG(ERROR) << "Failed to get location for shortcut.";
  } else {
    BrowserDistribution* dist = BrowserDistribution::GetDistribution();
    file_util::AppendToPath(&shortcut_path, dist->GetApplicationName());
    LOG(INFO) << "Deleting shortcut " << shortcut_path;
    if (!file_util::Delete(shortcut_path, true))
      LOG(ERROR) << "Failed to delete folder: " << shortcut_path;
  }
}

// Deletes all installed files of Chromium and Folders. Before deleting it
// needs to move setup.exe in a temp folder because the current process
// is using that file. It returns false when it can not get the path to
// installation folder, in all other cases it returns true even in case
// of error (only logs the error).
bool DeleteFilesAndFolders(const std::wstring& exe_path, bool system_uninstall,
    const installer::Version& installed_version) {
  std::wstring install_path(installer::GetChromeInstallPath(system_uninstall));
  if (install_path.empty()) {
    LOG(ERROR) << "Could not get installation destination path.";
    return false; // Nothing else we can do for uninstall, so we return.
  } else {
    LOG(INFO) << "install destination path: " << install_path;
  }

  std::wstring setup_exe(installer::GetInstallerPathUnderChrome(
      install_path, installed_version.GetString()));
  file_util::AppendToPath(&setup_exe, file_util::GetFilenameFromPath(exe_path));

  std::wstring temp_file;
  file_util::CreateTemporaryFileName(&temp_file);
  file_util::Move(setup_exe, temp_file);

  LOG(INFO) << "Deleting install path " << install_path;
  if (!file_util::Delete(install_path, true)) {
    LOG(ERROR) << "Failed to delete folder (1st try): " << install_path;
    // This is to let any closing chrome.exe die before trying delete one
    // more time.
    Sleep(10000);
    if (!file_util::Delete(install_path, true))
      LOG(ERROR) << "Failed to delete folder (2nd try): " << install_path;
  }

  // Now check and delete if the parent directories are empty
  // For example Google\Chrome or Chromium
  std::wstring parent_dir = file_util::GetDirectoryFromPath(install_path);
  if (!parent_dir.empty() && file_util::IsDirectoryEmpty(parent_dir)) {
    if (!file_util::Delete(parent_dir, true))
      LOG(ERROR) << "Failed to delete folder: " << parent_dir;
    parent_dir = file_util::GetDirectoryFromPath(parent_dir);
    if (!parent_dir.empty() &&
        file_util::IsDirectoryEmpty(parent_dir)) {
      if (!file_util::Delete(parent_dir, true))
        LOG(ERROR) << "Failed to delete folder: " << parent_dir;
    }
  }
  return true;
}

// This method tries to delete a registry key and logs an error message
// in case of failure. It returns true if deletion is successful,
// otherwise false.
bool DeleteRegistryKey(RegKey& key, const std::wstring& key_path) {
  LOG(INFO) << "Deleting registry key " << key_path;
  if (!key.DeleteKey(key_path.c_str())) {
    LOG(ERROR) << "Failed to delete registry key: " << key_path;
    return false;
  }
  return true;
}

// This method tries to delete a registry value and logs an error message
// in case of failure. It returns true if deletion is successful,
// otherwise false.
bool DeleteRegistryValue(HKEY reg_root, const std::wstring& key_path,
                         const std::wstring& value_name) {
  RegKey key(reg_root, key_path.c_str(), KEY_ALL_ACCESS);
  LOG(INFO) << "Deleting registry value " << value_name;
  if (!key.DeleteValue(value_name.c_str())) {
    LOG(ERROR) << "Failed to delete registry value: " << value_name;
    return false;
  }
  return true;
}

// This method checks if Chrome is currently running or if the user has
// cancelled the uninstall operation by clicking Cancel on the confirmation
// box that Chrome pops up.
installer_util::InstallStatus IsChromeActiveOrUserCancelled(
    bool system_uninstall) {
  static const std::wstring kCmdLineOptions(L" --uninstall");
  int32 exit_code = ResultCodes::NORMAL_EXIT;

  // Here we want to save user from frustration (in case of Chrome crashes)
  // and continue with the uninstallation as long as chrome.exe process exit
  // code is NOT one of the following:
  // - UNINSTALL_CHROME_ALIVE - chrome.exe is currently running
  // - UNINSTALL_USER_CANCEL - User cancelled uninstallation
  // - HUNG - chrome.exe was killed by HuntForZombieProcesses() (until we can
  //          give this method some brains and not kill chrome.exe launched
  //          by us, we will not uninstall if we get this return code).
  LOG(INFO) << "Launching Chrome to do uninstall tasks.";
  if (installer::LaunchChromeAndWaitForResult(system_uninstall,
                                              kCmdLineOptions,
                                              &exit_code)) {
    LOG(INFO) << "chrome.exe launched for uninstall confirmation returned: "
              << exit_code;
    if ((exit_code == ResultCodes::UNINSTALL_CHROME_ALIVE) ||
        (exit_code == ResultCodes::UNINSTALL_USER_CANCEL) ||
        (exit_code == ResultCodes::HUNG))
      return installer_util::UNINSTALL_CANCELLED;
  } else {
    LOG(ERROR) << "Failed to launch chrome.exe for uninstall confirmation.";
  }

  return installer_util::UNINSTALL_CONFIRMED;
}
}  // namespace

installer_util::InstallStatus installer_setup::UninstallChrome(
    const std::wstring& exe_path, bool system_uninstall,
    const installer::Version& installed_version,
    bool remove_all, bool force_uninstall) {
  if (!force_uninstall) {
    installer_util::InstallStatus status =
        IsChromeActiveOrUserCancelled(system_uninstall);
    if (status != installer_util::UNINSTALL_CONFIRMED)
      return status;
  } else {
    // Since --force-uninstall command line option is used, we are going to
    // do silent uninstall. Try to close all running Chrome instances.
    CloseAllChromeProcesses();
  }

#if defined(GOOGLE_CHROME_BUILD)
  // TODO(rahulk): This should be done by DoPreUninstallOperations call above
  wchar_t product[39];  // GUID + '\0'
  MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);  // Don't show any UI to user.
  for (int i = 0; MsiEnumRelatedProducts(google_update::kGearsUpgradeCode, 0, i,
                                         product) != ERROR_NO_MORE_ITEMS; ++i) {
    LOG(INFO) << "Uninstalling Gears - " << product;
    unsigned int ret = MsiConfigureProduct(product, INSTALLLEVEL_MAXIMUM,
                                           INSTALLSTATE_ABSENT);
    if (ret != ERROR_SUCCESS)
      LOG(ERROR) << "Failed to uninstall Gears " << product << ": " << ret;
  }
#endif

  // Chrome is not in use so lets uninstall Chrome by deleting various files
  // and registry entries. Here we will just make best effort and keep going
  // in case of errors.
  // First delete shortcut from Start->Programs.
  DeleteChromeShortcut(system_uninstall);

  // Delete the registry keys (Uninstall key and Version key).
  HKEY reg_root = system_uninstall ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  RegKey key(reg_root, L"", KEY_ALL_ACCESS);
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  DeleteRegistryKey(key, dist->GetUninstallRegPath());
  DeleteRegistryKey(key, dist->GetVersionKey());

  // Delete Software\Classes\ChromeHTML,
  // Software\Clients\StartMenuInternet\chrome.exe and
  // Software\RegisteredApplications\Chrome
  std::wstring html_prog_id(ShellUtil::kRegClasses);
  file_util::AppendToPath(&html_prog_id, ShellUtil::kChromeHTMLProgId);
  DeleteRegistryKey(key, html_prog_id);

  std::wstring set_access_key(ShellUtil::kRegStartMenuInternet);
  file_util::AppendToPath(&set_access_key, installer_util::kChromeExe);
  DeleteRegistryKey(key, set_access_key);

  DeleteRegistryValue(reg_root, ShellUtil::kRegRegisteredApplications,
                      dist->GetApplicationName());
  key.Close();

  // Delete shared registry keys as well (these require admin rights) if
  // remove_all option is specified.
  if (remove_all) {
    RegKey hklm_key(HKEY_LOCAL_MACHINE, L"", KEY_ALL_ACCESS);
    DeleteRegistryKey(hklm_key, set_access_key);
    DeleteRegistryKey(hklm_key, html_prog_id);
    DeleteRegistryValue(HKEY_LOCAL_MACHINE,
                        ShellUtil::kRegRegisteredApplications,
                        dist->GetApplicationName());

    // Delete media player registry key that exists only in HKLM.
    std::wstring reg_path(installer::kMediaPlayerRegPath);
    file_util::AppendToPath(&reg_path, installer_util::kChromeExe);
    DeleteRegistryKey(hklm_key, reg_path);
    hklm_key.Close();
  }

  // Finally delete all the files from Chrome folder after moving setup.exe
  // to a temp location.
  if (!DeleteFilesAndFolders(exe_path, system_uninstall, installed_version))
    return installer_util::UNINSTALL_FAILED;

  LOG(INFO) << "Uninstallation complete. Launching Uninstall survey.";
  dist->DoPostUninstallOperations(installed_version);
  return installer_util::UNINSTALL_SUCCESSFUL;
}

<|endoftext|>


<commit_before>// Copyright (c) 2009 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/renderer/paint_aggregator.h"

#include "base/logging.h"
#include "base/metrics/histogram.h"

// ----------------------------------------------------------------------------
// ALGORITHM NOTES
//
// We attempt to maintain a scroll rect in the presence of invalidations that
// are contained within the scroll rect.  If an invalidation crosses a scroll
// rect, then we just treat the scroll rect as an invalidation rect.
//
// For invalidations performed prior to scrolling and contained within the
// scroll rect, we offset the invalidation rects to account for the fact that
// the consumer will perform scrolling before painting.
//
// We only support scrolling along one axis at a time.  A diagonal scroll will
// therefore be treated as an invalidation.
// ----------------------------------------------------------------------------

// If the combined area of paint rects contained within the scroll rect grows
// too large, then we might as well just treat the scroll rect as a paint rect.
// This constant sets the max ratio of paint rect area to scroll rect area that
// we will tolerate before dograding the scroll into a repaint.
static const float kMaxRedundantPaintToScrollArea = 0.8f;

// The maximum number of paint rects.  If we exceed this limit, then we'll
// start combining paint rects (see CombinePaintRects).  This limiting is
// important since the WebKit code associated with deciding what to paint given
// a paint rect can be significant.
static const size_t kMaxPaintRects = 5;

// If the combined area of paint rects divided by the area of the union of all
// paint rects exceeds this threshold, then we will combine the paint rects.
static const float kMaxPaintRectsAreaRatio = 0.5f;

PaintAggregator::PendingUpdate::PendingUpdate() {}

PaintAggregator::PendingUpdate::~PendingUpdate() {}

gfx::Rect PaintAggregator::PendingUpdate::GetScrollDamage() const {
  // Should only be scrolling in one direction at a time.
  DCHECK(!(scroll_delta.x() && scroll_delta.y()));

  gfx::Rect damaged_rect;

  // Compute the region we will expose by scrolling, and paint that into a
  // shared memory section.
  if (scroll_delta.x()) {
    int dx = scroll_delta.x();
    damaged_rect.set_y(scroll_rect.y());
    damaged_rect.set_height(scroll_rect.height());
    if (dx > 0) {
      damaged_rect.set_x(scroll_rect.x());
      damaged_rect.set_width(dx);
    } else {
      damaged_rect.set_x(scroll_rect.right() + dx);
      damaged_rect.set_width(-dx);
    }
  } else {
    int dy = scroll_delta.y();
    damaged_rect.set_x(scroll_rect.x());
    damaged_rect.set_width(scroll_rect.width());
    if (dy > 0) {
      damaged_rect.set_y(scroll_rect.y());
      damaged_rect.set_height(dy);
    } else {
      damaged_rect.set_y(scroll_rect.bottom() + dy);
      damaged_rect.set_height(-dy);
    }
  }

  // In case the scroll offset exceeds the width/height of the scroll rect
  return scroll_rect.Intersect(damaged_rect);
}

gfx::Rect PaintAggregator::PendingUpdate::GetPaintBounds() const {
  gfx::Rect bounds;
  for (size_t i = 0; i < paint_rects.size(); ++i)
    bounds = bounds.Union(paint_rects[i]);
  return bounds;
}

bool PaintAggregator::HasPendingUpdate() const {
  return !update_.scroll_rect.IsEmpty() || !update_.paint_rects.empty();
}

void PaintAggregator::ClearPendingUpdate() {
  update_ = PendingUpdate();
}

void PaintAggregator::PopPendingUpdate(PendingUpdate* update) {
  // Combine paint rects if their combined area is not sufficiently less than
  // the area of the union of all paint rects.  We skip this if there is a
  // scroll rect since scrolling benefits from smaller paint rects.
  if (update_.scroll_rect.IsEmpty() && update_.paint_rects.size() > 1) {
    int paint_area = 0;
    gfx::Rect union_rect;
    for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
      paint_area += update_.paint_rects[i].size().GetArea();
      union_rect = union_rect.Union(update_.paint_rects[i]);
    }
    int union_area = union_rect.size().GetArea();
    if (float(paint_area) / float(union_area) > kMaxPaintRectsAreaRatio)
      CombinePaintRects();
  }
  *update = update_;
  ClearPendingUpdate();
}

void PaintAggregator::InvalidateRect(const gfx::Rect& rect) {
  // Combine overlapping paints using smallest bounding box.
  for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
    const gfx::Rect& existing_rect = update_.paint_rects[i];
    if (existing_rect.Contains(rect))  // Optimize out redundancy.
      return;
    if (rect.Intersects(existing_rect) || rect.SharesEdgeWith(existing_rect)) {
      // Re-invalidate in case the union intersects other paint rects.
      gfx::Rect combined_rect = existing_rect.Union(rect);
      update_.paint_rects.erase(update_.paint_rects.begin() + i);
      InvalidateRect(combined_rect);
      return;
    }
  }

  // Add a non-overlapping paint.
  update_.paint_rects.push_back(rect);

  // If the new paint overlaps with a scroll, then it forces an invalidation of
  // the scroll.  If the new paint is contained by a scroll, then trim off the
  // scroll damage to avoid redundant painting.
  if (!update_.scroll_rect.IsEmpty()) {
    if (ShouldInvalidateScrollRect(rect)) {
      InvalidateScrollRect();
    } else if (update_.scroll_rect.Contains(rect)) {
      update_.paint_rects[update_.paint_rects.size() - 1] =
          rect.Subtract(update_.GetScrollDamage());
      if (update_.paint_rects[update_.paint_rects.size() - 1].IsEmpty())
        update_.paint_rects.erase(update_.paint_rects.end() - 1);
    }
  }

  if (update_.paint_rects.size() > kMaxPaintRects)
    CombinePaintRects();

  // Track how large the paint_rects vector grows during an invalidation
  // sequence.  Note: A subsequent invalidation may end up being combined
  // with all existing paints, which means that tracking the size of
  // paint_rects at the time when PopPendingUpdate() is called may mask
  // certain performance problems.
  HISTOGRAM_COUNTS_100("MPArch.RW_IntermediatePaintRectCount",
                       update_.paint_rects.size());
}

void PaintAggregator::ScrollRect(int dx, int dy, const gfx::Rect& clip_rect) {
  // We only support scrolling along one axis at a time.
  if (dx != 0 && dy != 0) {
    InvalidateRect(clip_rect);
    return;
  }

  // We can only scroll one rect at a time.
  if (!update_.scroll_rect.IsEmpty() &&
      !update_.scroll_rect.Equals(clip_rect)) {
    InvalidateRect(clip_rect);
    return;
  }

  // Again, we only support scrolling along one axis at a time.  Make sure this
  // update doesn't scroll on a different axis than any existing one.
  if ((dx && update_.scroll_delta.y()) || (dy && update_.scroll_delta.x())) {
    InvalidateRect(clip_rect);
    return;
  }

  // The scroll rect is new or isn't changing (though the scroll amount may
  // be changing).
  update_.scroll_rect = clip_rect;
  update_.scroll_delta.Offset(dx, dy);

  // We might have just wiped out a pre-existing scroll.
  if (update_.scroll_delta == gfx::Point()) {
    update_.scroll_rect = gfx::Rect();
    return;
  }

  // Adjust any contained paint rects and check for any overlapping paints.
  for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
    if (update_.scroll_rect.Contains(update_.paint_rects[i])) {
      update_.paint_rects[i] = ScrollPaintRect(update_.paint_rects[i], dx, dy);
      // The rect may have been scrolled out of view.
      if (update_.paint_rects[i].IsEmpty()) {
        update_.paint_rects.erase(update_.paint_rects.begin() + i);
        i--;
      }
    } else if (update_.scroll_rect.Intersects(update_.paint_rects[i])) {
      InvalidateScrollRect();
      return;
    }
  }

  // If the new scroll overlaps too much with contained paint rects, then force
  // an invalidation of the scroll.
  if (ShouldInvalidateScrollRect(gfx::Rect()))
    InvalidateScrollRect();
}

gfx::Rect PaintAggregator::ScrollPaintRect(const gfx::Rect& paint_rect,
                                           int dx, int dy) const {
  gfx::Rect result = paint_rect;

  result.Offset(dx, dy);
  result = update_.scroll_rect.Intersect(result);

  // Subtract out the scroll damage rect to avoid redundant painting.
  return result.Subtract(update_.GetScrollDamage());
}

bool PaintAggregator::ShouldInvalidateScrollRect(const gfx::Rect& rect) const {
  if (!rect.IsEmpty()) {
    if (!update_.scroll_rect.Intersects(rect))
      return false;

    if (!update_.scroll_rect.Contains(rect))
      return true;
  }

  // Check if the combined area of all contained paint rects plus this new
  // rect comes too close to the area of the scroll_rect.  If so, then we
  // might as well invalidate the scroll rect.

  int paint_area = rect.size().GetArea();
  for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
    const gfx::Rect& existing_rect = update_.paint_rects[i];
    if (update_.scroll_rect.Contains(existing_rect))
      paint_area += existing_rect.size().GetArea();
  }
  int scroll_area = update_.scroll_rect.size().GetArea();
  if (float(paint_area) / float(scroll_area) > kMaxRedundantPaintToScrollArea)
    return true;

  return false;
}

void PaintAggregator::InvalidateScrollRect() {
  gfx::Rect scroll_rect = update_.scroll_rect;
  update_.scroll_rect = gfx::Rect();
  update_.scroll_delta = gfx::Point();
  InvalidateRect(scroll_rect);
}

void PaintAggregator::CombinePaintRects() {
  // Combine paint rects do to at most two rects: one inside the scroll_rect
  // and one outside the scroll_rect.  If there is no scroll_rect, then just
  // use the smallest bounding box for all paint rects.
  //
  // NOTE: This is a fairly simple algorithm.  We could get fancier by only
  // combining two rects to get us under the kMaxPaintRects limit, but if we
  // reach this method then it means we're hitting a rare case, so there's no
  // need to over-optimize it.
  //
  if (update_.scroll_rect.IsEmpty()) {
    gfx::Rect bounds = update_.GetPaintBounds();
    update_.paint_rects.clear();
    update_.paint_rects.push_back(bounds);
  } else {
    gfx::Rect inner, outer;
    for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
      const gfx::Rect& existing_rect = update_.paint_rects[i];
      if (update_.scroll_rect.Contains(existing_rect)) {
        inner = inner.Union(existing_rect);
      } else {
        outer = outer.Union(existing_rect);
      }
    }
    update_.paint_rects.clear();
    update_.paint_rects.push_back(inner);
    update_.paint_rects.push_back(outer);
  }
}
<commit_msg>Revert my change in r69993 to the paint aggregation threshold to see if it's responsible for the PLT regression.<commit_after>// Copyright (c) 2009 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/renderer/paint_aggregator.h"

#include "base/logging.h"
#include "base/metrics/histogram.h"

// ----------------------------------------------------------------------------
// ALGORITHM NOTES
//
// We attempt to maintain a scroll rect in the presence of invalidations that
// are contained within the scroll rect.  If an invalidation crosses a scroll
// rect, then we just treat the scroll rect as an invalidation rect.
//
// For invalidations performed prior to scrolling and contained within the
// scroll rect, we offset the invalidation rects to account for the fact that
// the consumer will perform scrolling before painting.
//
// We only support scrolling along one axis at a time.  A diagonal scroll will
// therefore be treated as an invalidation.
// ----------------------------------------------------------------------------

// If the combined area of paint rects contained within the scroll rect grows
// too large, then we might as well just treat the scroll rect as a paint rect.
// This constant sets the max ratio of paint rect area to scroll rect area that
// we will tolerate before dograding the scroll into a repaint.
static const float kMaxRedundantPaintToScrollArea = 0.8f;

// The maximum number of paint rects.  If we exceed this limit, then we'll
// start combining paint rects (see CombinePaintRects).  This limiting is
// important since the WebKit code associated with deciding what to paint given
// a paint rect can be significant.
static const size_t kMaxPaintRects = 5;

// If the combined area of paint rects divided by the area of the union of all
// paint rects exceeds this threshold, then we will combine the paint rects.
static const float kMaxPaintRectsAreaRatio = 0.3f;

PaintAggregator::PendingUpdate::PendingUpdate() {}

PaintAggregator::PendingUpdate::~PendingUpdate() {}

gfx::Rect PaintAggregator::PendingUpdate::GetScrollDamage() const {
  // Should only be scrolling in one direction at a time.
  DCHECK(!(scroll_delta.x() && scroll_delta.y()));

  gfx::Rect damaged_rect;

  // Compute the region we will expose by scrolling, and paint that into a
  // shared memory section.
  if (scroll_delta.x()) {
    int dx = scroll_delta.x();
    damaged_rect.set_y(scroll_rect.y());
    damaged_rect.set_height(scroll_rect.height());
    if (dx > 0) {
      damaged_rect.set_x(scroll_rect.x());
      damaged_rect.set_width(dx);
    } else {
      damaged_rect.set_x(scroll_rect.right() + dx);
      damaged_rect.set_width(-dx);
    }
  } else {
    int dy = scroll_delta.y();
    damaged_rect.set_x(scroll_rect.x());
    damaged_rect.set_width(scroll_rect.width());
    if (dy > 0) {
      damaged_rect.set_y(scroll_rect.y());
      damaged_rect.set_height(dy);
    } else {
      damaged_rect.set_y(scroll_rect.bottom() + dy);
      damaged_rect.set_height(-dy);
    }
  }

  // In case the scroll offset exceeds the width/height of the scroll rect
  return scroll_rect.Intersect(damaged_rect);
}

gfx::Rect PaintAggregator::PendingUpdate::GetPaintBounds() const {
  gfx::Rect bounds;
  for (size_t i = 0; i < paint_rects.size(); ++i)
    bounds = bounds.Union(paint_rects[i]);
  return bounds;
}

bool PaintAggregator::HasPendingUpdate() const {
  return !update_.scroll_rect.IsEmpty() || !update_.paint_rects.empty();
}

void PaintAggregator::ClearPendingUpdate() {
  update_ = PendingUpdate();
}

void PaintAggregator::PopPendingUpdate(PendingUpdate* update) {
  // Combine paint rects if their combined area is not sufficiently less than
  // the area of the union of all paint rects.  We skip this if there is a
  // scroll rect since scrolling benefits from smaller paint rects.
  if (update_.scroll_rect.IsEmpty() && update_.paint_rects.size() > 1) {
    int paint_area = 0;
    gfx::Rect union_rect;
    for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
      paint_area += update_.paint_rects[i].size().GetArea();
      union_rect = union_rect.Union(update_.paint_rects[i]);
    }
    int union_area = union_rect.size().GetArea();
    if (float(paint_area) / float(union_area) > kMaxPaintRectsAreaRatio)
      CombinePaintRects();
  }
  *update = update_;
  ClearPendingUpdate();
}

void PaintAggregator::InvalidateRect(const gfx::Rect& rect) {
  // Combine overlapping paints using smallest bounding box.
  for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
    const gfx::Rect& existing_rect = update_.paint_rects[i];
    if (existing_rect.Contains(rect))  // Optimize out redundancy.
      return;
    if (rect.Intersects(existing_rect) || rect.SharesEdgeWith(existing_rect)) {
      // Re-invalidate in case the union intersects other paint rects.
      gfx::Rect combined_rect = existing_rect.Union(rect);
      update_.paint_rects.erase(update_.paint_rects.begin() + i);
      InvalidateRect(combined_rect);
      return;
    }
  }

  // Add a non-overlapping paint.
  update_.paint_rects.push_back(rect);

  // If the new paint overlaps with a scroll, then it forces an invalidation of
  // the scroll.  If the new paint is contained by a scroll, then trim off the
  // scroll damage to avoid redundant painting.
  if (!update_.scroll_rect.IsEmpty()) {
    if (ShouldInvalidateScrollRect(rect)) {
      InvalidateScrollRect();
    } else if (update_.scroll_rect.Contains(rect)) {
      update_.paint_rects[update_.paint_rects.size() - 1] =
          rect.Subtract(update_.GetScrollDamage());
      if (update_.paint_rects[update_.paint_rects.size() - 1].IsEmpty())
        update_.paint_rects.erase(update_.paint_rects.end() - 1);
    }
  }

  if (update_.paint_rects.size() > kMaxPaintRects)
    CombinePaintRects();

  // Track how large the paint_rects vector grows during an invalidation
  // sequence.  Note: A subsequent invalidation may end up being combined
  // with all existing paints, which means that tracking the size of
  // paint_rects at the time when PopPendingUpdate() is called may mask
  // certain performance problems.
  HISTOGRAM_COUNTS_100("MPArch.RW_IntermediatePaintRectCount",
                       update_.paint_rects.size());
}

void PaintAggregator::ScrollRect(int dx, int dy, const gfx::Rect& clip_rect) {
  // We only support scrolling along one axis at a time.
  if (dx != 0 && dy != 0) {
    InvalidateRect(clip_rect);
    return;
  }

  // We can only scroll one rect at a time.
  if (!update_.scroll_rect.IsEmpty() &&
      !update_.scroll_rect.Equals(clip_rect)) {
    InvalidateRect(clip_rect);
    return;
  }

  // Again, we only support scrolling along one axis at a time.  Make sure this
  // update doesn't scroll on a different axis than any existing one.
  if ((dx && update_.scroll_delta.y()) || (dy && update_.scroll_delta.x())) {
    InvalidateRect(clip_rect);
    return;
  }

  // The scroll rect is new or isn't changing (though the scroll amount may
  // be changing).
  update_.scroll_rect = clip_rect;
  update_.scroll_delta.Offset(dx, dy);

  // We might have just wiped out a pre-existing scroll.
  if (update_.scroll_delta == gfx::Point()) {
    update_.scroll_rect = gfx::Rect();
    return;
  }

  // Adjust any contained paint rects and check for any overlapping paints.
  for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
    if (update_.scroll_rect.Contains(update_.paint_rects[i])) {
      update_.paint_rects[i] = ScrollPaintRect(update_.paint_rects[i], dx, dy);
      // The rect may have been scrolled out of view.
      if (update_.paint_rects[i].IsEmpty()) {
        update_.paint_rects.erase(update_.paint_rects.begin() + i);
        i--;
      }
    } else if (update_.scroll_rect.Intersects(update_.paint_rects[i])) {
      InvalidateScrollRect();
      return;
    }
  }

  // If the new scroll overlaps too much with contained paint rects, then force
  // an invalidation of the scroll.
  if (ShouldInvalidateScrollRect(gfx::Rect()))
    InvalidateScrollRect();
}

gfx::Rect PaintAggregator::ScrollPaintRect(const gfx::Rect& paint_rect,
                                           int dx, int dy) const {
  gfx::Rect result = paint_rect;

  result.Offset(dx, dy);
  result = update_.scroll_rect.Intersect(result);

  // Subtract out the scroll damage rect to avoid redundant painting.
  return result.Subtract(update_.GetScrollDamage());
}

bool PaintAggregator::ShouldInvalidateScrollRect(const gfx::Rect& rect) const {
  if (!rect.IsEmpty()) {
    if (!update_.scroll_rect.Intersects(rect))
      return false;

    if (!update_.scroll_rect.Contains(rect))
      return true;
  }

  // Check if the combined area of all contained paint rects plus this new
  // rect comes too close to the area of the scroll_rect.  If so, then we
  // might as well invalidate the scroll rect.

  int paint_area = rect.size().GetArea();
  for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
    const gfx::Rect& existing_rect = update_.paint_rects[i];
    if (update_.scroll_rect.Contains(existing_rect))
      paint_area += existing_rect.size().GetArea();
  }
  int scroll_area = update_.scroll_rect.size().GetArea();
  if (float(paint_area) / float(scroll_area) > kMaxRedundantPaintToScrollArea)
    return true;

  return false;
}

void PaintAggregator::InvalidateScrollRect() {
  gfx::Rect scroll_rect = update_.scroll_rect;
  update_.scroll_rect = gfx::Rect();
  update_.scroll_delta = gfx::Point();
  InvalidateRect(scroll_rect);
}

void PaintAggregator::CombinePaintRects() {
  // Combine paint rects do to at most two rects: one inside the scroll_rect
  // and one outside the scroll_rect.  If there is no scroll_rect, then just
  // use the smallest bounding box for all paint rects.
  //
  // NOTE: This is a fairly simple algorithm.  We could get fancier by only
  // combining two rects to get us under the kMaxPaintRects limit, but if we
  // reach this method then it means we're hitting a rare case, so there's no
  // need to over-optimize it.
  //
  if (update_.scroll_rect.IsEmpty()) {
    gfx::Rect bounds = update_.GetPaintBounds();
    update_.paint_rects.clear();
    update_.paint_rects.push_back(bounds);
  } else {
    gfx::Rect inner, outer;
    for (size_t i = 0; i < update_.paint_rects.size(); ++i) {
      const gfx::Rect& existing_rect = update_.paint_rects[i];
      if (update_.scroll_rect.Contains(existing_rect)) {
        inner = inner.Union(existing_rect);
      } else {
        outer = outer.Union(existing_rect);
      }
    }
    update_.paint_rects.clear();
    update_.paint_rects.push_back(inner);
    update_.paint_rects.push_back(outer);
  }
}
<|endoftext|>


<commit_before>#include "UpdateSystem.hpp"
#include "RenderSystem.hpp"

#include "Concurrency/Async.hpp"

#include "Visitors/UpdateWorldState.hpp"
#include "Visitors/ComputeRenderQueue.hpp"
#include "Visitors/UpdateComponents.hpp"
#include "Visitors/ParallelApply.hpp"

#include "Rendering/RenderQueue.hpp"

#include "SceneGraph/Node.hpp"

#include "Simulation/Simulation.hpp"

using namespace crimild;

UpdateSystem::UpdateSystem( void )
{

}

UpdateSystem::~UpdateSystem( void )
{

}

bool UpdateSystem::start( void )
{
	if ( !System::start() ) {
		return false;
	}

    _accumulator = 0.0;

    crimild::concurrency::sync_frame( std::bind( &UpdateSystem::update, this ) );

	return true;
}

void UpdateSystem::update( void )
{
    CRIMILD_PROFILE( "Update System" )

    MessageQueue::getInstance()->dispatchDeferredMessages();

    auto scene = crimild::retain( Simulation::getInstance()->getScene() );
    if ( scene == nullptr ) {
    	// schedule next update
        crimild::concurrency::sync_frame( std::bind( &UpdateSystem::update, this ) );
        return;
    }

    // update simulation time
    // TODO: Should this system have its own clock?
    auto &c = Simulation::getInstance()->getSimulationClock();
    c.tick();

    // prevent integration errors when delta is too big (i.e. after loading a new scene)
    _accumulator += Numericd::min( 4 * Clock::getScaledTickTime(), c.getDeltaTime() );

	updateBehaviors( crimild::get_ptr( scene ) );

    computeRenderQueues( crimild::get_ptr( scene ) );

    // schedule next update
    crimild::concurrency::sync_frame( std::bind( &UpdateSystem::update, this ) );
}

void UpdateSystem::updateBehaviors( Node *scene )
{
    broadcastMessage( messaging::WillUpdateScene { scene } );

    // So, we're updating with a fixed time which may seem wrong
    // BUT! We're either sleeping on WindowSystem or waiting for OS display callback
    // TODO: This should be configurable.
    static const auto FIXED_CLOCK = Clock( Clock::DEFAULT_TICK_TIME );

	CRIMILD_PROFILE( "Updating Components" )

	scene->perform( Apply( []( Node *node ) {
		node->updateComponents( FIXED_CLOCK );
	}));

    updateWorldState( scene );

    broadcastMessage( messaging::DidUpdateScene { scene } );
}

void UpdateSystem::updateWorldState( Node *scene )
{
	CRIMILD_PROFILE( "Updating World State" )

    scene->perform( UpdateWorldState() );
}

void UpdateSystem::computeRenderQueues( Node *scene )
{
	containers::Array< SharedPointer< RenderQueue >> renderQueues;

	{
		CRIMILD_PROFILE( "Compute Render Queue" )

		Simulation::getInstance()->forEachCamera( [ &renderQueues, scene ]( Camera *camera ) {
			if ( camera != nullptr && camera->isEnabled() ) {
				auto renderQueue = crimild::alloc< RenderQueue >();
				scene->perform( ComputeRenderQueue( camera, crimild::get_ptr( renderQueue ) ) );
				renderQueues.add( renderQueue );
			}
		});
	}

    crimild::concurrency::sync_frame( [ this, renderQueues ]() {
        broadcastMessage( messaging::RenderQueueAvailable { renderQueues } );
    });
}

void UpdateSystem::stop( void )
{
	System::stop();

    unregisterMessageHandler< messaging::SimulationWillUpdate >();
}

<commit_msg>Another fix for timestep<commit_after>#include "UpdateSystem.hpp"
#include "RenderSystem.hpp"

#include "Concurrency/Async.hpp"

#include "Visitors/UpdateWorldState.hpp"
#include "Visitors/ComputeRenderQueue.hpp"
#include "Visitors/UpdateComponents.hpp"
#include "Visitors/ParallelApply.hpp"

#include "Rendering/RenderQueue.hpp"

#include "SceneGraph/Node.hpp"

#include "Simulation/Simulation.hpp"

using namespace crimild;

UpdateSystem::UpdateSystem( void )
{

}

UpdateSystem::~UpdateSystem( void )
{

}

bool UpdateSystem::start( void )
{
	if ( !System::start() ) {
		return false;
	}

    _accumulator = 0.0;

    crimild::concurrency::sync_frame( std::bind( &UpdateSystem::update, this ) );

	return true;
}

void UpdateSystem::update( void )
{
    CRIMILD_PROFILE( "Update System" )

    MessageQueue::getInstance()->dispatchDeferredMessages();

    auto scene = crimild::retain( Simulation::getInstance()->getScene() );
    if ( scene == nullptr ) {
    	// schedule next update
        crimild::concurrency::sync_frame( std::bind( &UpdateSystem::update, this ) );
        return;
    }

    // update simulation time
    // TODO: Should this system have its own clock?
    auto &c = Simulation::getInstance()->getSimulationClock();

    _accumulator += c.getDeltaTime();
    if ( _accumulator > 2 * Clock::DEFAULT_TICK_TIME ) {
        _accumulator = Clock::DEFAULT_TICK_TIME;
    }

	updateBehaviors( crimild::get_ptr( scene ) );

    computeRenderQueues( crimild::get_ptr( scene ) );

    // tick after update
    c.tick();

    // schedule next update
    crimild::concurrency::sync_frame( std::bind( &UpdateSystem::update, this ) );
}

void UpdateSystem::updateBehaviors( Node *scene )
{
    broadcastMessage( messaging::WillUpdateScene { scene } );

    static const auto FIXED_CLOCK = Clock( Clock::DEFAULT_TICK_TIME );

    while ( _accumulator >= Clock::DEFAULT_TICK_TIME ) {
        CRIMILD_PROFILE( "Updating Components" )

        scene->perform( Apply( []( Node *node ) {
            node->updateComponents( FIXED_CLOCK );
        }));

        updateWorldState( scene );

        _accumulator -= Clock::DEFAULT_TICK_TIME;
    }

    broadcastMessage( messaging::DidUpdateScene { scene } );
}

void UpdateSystem::updateWorldState( Node *scene )
{
	CRIMILD_PROFILE( "Updating World State" )

    scene->perform( UpdateWorldState() );
}

void UpdateSystem::computeRenderQueues( Node *scene )
{
	containers::Array< SharedPointer< RenderQueue >> renderQueues;

	{
		CRIMILD_PROFILE( "Compute Render Queue" )

		Simulation::getInstance()->forEachCamera( [ &renderQueues, scene ]( Camera *camera ) {
			if ( camera != nullptr && camera->isEnabled() ) {
				auto renderQueue = crimild::alloc< RenderQueue >();
				scene->perform( ComputeRenderQueue( camera, crimild::get_ptr( renderQueue ) ) );
				renderQueues.add( renderQueue );
			}
		});
	}

    crimild::concurrency::sync_frame( [ this, renderQueues ]() {
        broadcastMessage( messaging::RenderQueueAvailable { renderQueues } );
    });
}

void UpdateSystem::stop( void )
{
	System::stop();

    unregisterMessageHandler< messaging::SimulationWillUpdate >();
}

<|endoftext|>


<commit_before><commit_msg>main_pt.cc added for knn<commit_after><|endoftext|>


<commit_before>#include "MrlServo.h"

MrlServo::MrlServo() : Device(DEVICE_TYPE_SERVO) {
   isMoving = false;
   isSweeping = false;
   speed = 100;// 100% speed
   // TODO: target/curent position?
   // create the servo
   servo = new Servo();
   lastUpdate = 0;
   currentPos = 0.0;
   targetPos = 0;
   velocity = 0;
}

MrlServo::~MrlServo() {
  if (servo){
    servo->detach();
    delete servo;
  }
}

// this method "may" be called with a pin or pin & pos depending on
// config size
bool MrlServo::deviceAttach(unsigned char config[], int configSize){
  if (configSize < 1 || configSize > 2){
    MrlMsg msg(PUBLISH_MRLCOMM_ERROR);
    msg.addData(ERROR_DOES_NOT_EXIST);
    msg.addData(String(F("MrlServo invalid attach config size")));
    return false;
  }
  attachDevice();
  pin = config[0];
  attach(pin);
  if (configSize == 2){
    targetPos = config[1];
    servo->write(targetPos);
    currentPos = targetPos;
  }
  return true;
}

// This method is equivalent to Arduino's Servo.attach(pin) - (no pos)
void MrlServo::attach(int pin){
  servo->attach(pin);
  servo->write((int)currentPos); //return to it's last know state (may be 0 if currentPos is not set)
  // TODO-KW: we should always have a moveTo for safety, o/w we have no idea what angle we're going to start up at.. maybe
}

void MrlServo::detach(){
  servo->detach();
}

void MrlServo::update() {
  if (lastUpdate+10>millis() || servo == NULL)
    return;
  if (isMoving) {
    if ((int)currentPos != targetPos) {
      currentPos += step;
      if((step > 0.0 && (int)currentPos > targetPos) || (step < 0.0 && (int)currentPos < targetPos)) {
        currentPos=targetPos;
      }
      servo->write((int)currentPos);
    } else {
      if (isSweeping) {
        if (targetPos == min) {
          targetPos = max;
        } else {
          targetPos = min;
        }
        step*=-1;
      } else {
        isMoving = false;
      }
    }
  }
  lastUpdate=millis();
}

void MrlServo::publishServoEvent(int eventType) {
  MrlMsg msg(PUBLISH_SERVO_EVENT,id);
  msg.addData(eventType);
  msg.addData((int)currentPos);
  msg.addData(targetPos);
  msg.sendMsg();
}

void MrlServo::servoWrite(int position) {
  if (servo == NULL)
    return;
  if (speed == 100 && velocity == 0) {
    // move at regular/full 100% speed
    targetPos = position;
    isMoving = true;
    step=targetPos-(int)currentPos;
  } else /*if (speed < 100 && speed > 0)*/ {
    targetPos = position;
    isMoving = true;
    //int baseSpeed=(int)(60.0/0.14); // deg/sec base on speed of HS805B servo 6V under no load //should be modifiable
    long delta=targetPos-(int)currentPos;
    float currentSpeed=(velocity*speed)/100;
    long timeToReach=abs((delta))*1000/currentSpeed; // time to reach target in ms
    if(timeToReach==0){
      timeToReach=1;
    }
    step=((float)delta*10/timeToReach);
  }
}

void MrlServo::servoWriteMicroseconds(int position) {
  if (servo) {
    servo->writeMicroseconds(position);
  }
}

void MrlServo::setSpeed(int speed) {
  this->speed = speed;
}

void MrlServo::startSweep(int min, int max, int step) {
  this->min = min;
  this->max = max;
  this->step = step;
  targetPos = max;
  isMoving = true;
  isSweeping = true;
}

void MrlServo::stopSweep() {
  isMoving = false;
  isSweeping = false;
}

void MrlServo::setMaxVelocity(unsigned int velocity){
	baseSpeed = velocity;
}

void MrlServo::setVelocity(unsigned int velocity) {
	this->velocity = velocity;
}

<commit_msg>some change to speed setting<commit_after>#include "MrlServo.h"

MrlServo::MrlServo() : Device(DEVICE_TYPE_SERVO) {
   isMoving = false;
   isSweeping = false;
   speed = 100;// 100% speed
   // TODO: target/curent position?
   // create the servo
   servo = new Servo();
   lastUpdate = 0;
   currentPos = 0.0;
   targetPos = 0;
   velocity = 0;
}

MrlServo::~MrlServo() {
  if (servo){
    servo->detach();
    delete servo;
  }
}

// this method "may" be called with a pin or pin & pos depending on
// config size
bool MrlServo::deviceAttach(unsigned char config[], int configSize){
  if (configSize < 1 || configSize > 2){
    MrlMsg msg(PUBLISH_MRLCOMM_ERROR);
    msg.addData(ERROR_DOES_NOT_EXIST);
    msg.addData(String(F("MrlServo invalid attach config size")));
    return false;
  }
  attachDevice();
  pin = config[0];
  attach(pin);
  if (configSize == 2){
    targetPos = config[1];
    servo->write(targetPos);
    currentPos = targetPos;
  }
  return true;
}

// This method is equivalent to Arduino's Servo.attach(pin) - (no pos)
void MrlServo::attach(int pin){
  servo->attach(pin);
  servo->write((int)currentPos); //return to it's last know state (may be 0 if currentPos is not set)
  // TODO-KW: we should always have a moveTo for safety, o/w we have no idea what angle we're going to start up at.. maybe
}

void MrlServo::detach(){
  servo->detach();
}

void MrlServo::update() {
  if (lastUpdate+10>millis() || servo == NULL)
    return;
  if (isMoving) {
    if ((int)currentPos != targetPos) {
      currentPos += step;
      if((step > 0.0 && (int)currentPos > targetPos) || (step < 0.0 && (int)currentPos < targetPos)) {
        currentPos=targetPos;
      }
      servo->write((int)currentPos);
    } else {
      if (isSweeping) {
        if (targetPos == min) {
          targetPos = max;
        } else {
          targetPos = min;
        }
        step*=-1;
      } else {
        isMoving = false;
      }
    }
  }
  lastUpdate=millis();
}

void MrlServo::publishServoEvent(int eventType) {
  MrlMsg msg(PUBLISH_SERVO_EVENT,id);
  msg.addData(eventType);
  msg.addData((int)currentPos);
  msg.addData(targetPos);
  msg.sendMsg();
}

void MrlServo::servoWrite(int position) {
  if (servo == NULL)
    return;
  if (speed == 100) {
	targetPos = position;
	isMoving = false;
	currentPos = position;
	servo->write(position);
  }
  else if (speed == 100 && velocity == 0) {
    // move at regular/full 100% speed
    targetPos = position;
    isMoving = true;
    step=targetPos-(int)currentPos;
  } else /*if (speed < 100 && speed > 0)*/ {
    targetPos = position;
    isMoving = true;
    //int baseSpeed=(int)(60.0/0.14); // deg/sec base on speed of HS805B servo 6V under no load //should be modifiable
    long delta=targetPos-(int)currentPos;
    float currentSpeed=(velocity*speed)/100;
    long timeToReach=abs((delta))*1000/currentSpeed; // time to reach target in ms
    if(timeToReach==0){
      timeToReach=1;
    }
    step=((float)delta*10/timeToReach);
  }
}

void MrlServo::servoWriteMicroseconds(int position) {
  if (servo) {
    servo->writeMicroseconds(position);
  }
}

void MrlServo::setSpeed(int speed) {
  this->speed = speed;
}

void MrlServo::startSweep(int min, int max, int step) {
  this->min = min;
  this->max = max;
  this->step = step;
  targetPos = max;
  isMoving = true;
  isSweeping = true;
}

void MrlServo::stopSweep() {
  isMoving = false;
  isSweeping = false;
}

void MrlServo::setMaxVelocity(unsigned int velocity){
	baseSpeed = velocity;
}

void MrlServo::setVelocity(unsigned int velocity) {
	this->velocity = velocity;
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 *
 * @file        crypto-logic.cpp
 * @author      Sebastian Grabowski (s.grabowski@samsung.com)
 * @author      Bartlomiej Grzelewski (b.grzelewski@samsung.com)
 * @version     1.0
 * @brief       Crypto module implementation.
 */

#include <iostream>
#include <fstream>
#include <utility>
#include <climits>

#include <stdio.h>
#include <string.h>

#include <openssl/evp.h>
#include <openssl/rand.h>

#include <ckm/ckm-error.h>

#include <dpl/log/log.h>

#include <base64.h>
#include <digest.h>
#include <crypto-logic.h>

#include <generic-backend/exception.h>
#include <sw-backend/internals.h>

namespace CKM {

namespace {

const static int AES_CBC_KEY_SIZE = 32;
const static int AES_GCM_TAG_SIZE = 16;

// Encryption scheme flags (enable/disable specific encryption type, multiple choice)
const int ENCR_BASE64 =   1 << 0;
const int ENCR_APPKEY =   1 << 1;
const int ENCR_PASSWORD = 1 << 2;

// Encryption order flags (single choice)
const int ENCR_ORDER_OFFSET = 24;
const int ENCR_ORDER_FILTER = INT_MAX << ENCR_ORDER_OFFSET; // 0xff000000
const int ENCR_ORDER_CLEAR = ~ENCR_ORDER_FILTER; // 0x00ffffff
/*
 * ENCR_ORDER_V1 - v1 encryption order. Token returned from store is encrypted with app key and
 * optionally by custom user password. In such form it is stored in db.
 */
const int ENCR_ORDER_V1 = CryptoLogic::ENCRYPTION_V1 << ENCR_ORDER_OFFSET;
/*
 * ENCR_ORDER_V2 - v2 encryption order. Stored data is optionally encrypted by store with
 * user password. Returned token is encrypted with app key and stored in db.
 */
const int ENCR_ORDER_V2 = CryptoLogic::ENCRYPTION_V2 << ENCR_ORDER_OFFSET;

} // anonymous namespace

CryptoLogic::CryptoLogic() {}

CryptoLogic::CryptoLogic(CryptoLogic &&second) {
    m_keyMap = std::move(second.m_keyMap);
}

CryptoLogic& CryptoLogic::operator=(CryptoLogic &&second) {
    if (this == &second)
        return *this;
    m_keyMap = std::move(second.m_keyMap);
    return *this;
}

bool CryptoLogic::haveKey(const Label &smackLabel)
{
    return (m_keyMap.count(smackLabel) > 0);
}

void CryptoLogic::pushKey(const Label &smackLabel,
                          const RawBuffer &applicationKey)
{
    if (smackLabel.length() == 0) {
        ThrowErr(Exc::InternalError, "Empty smack label.");
    }
    if (applicationKey.size() == 0) {
        ThrowErr(Exc::InternalError, "Empty application key.");
    }
    if (haveKey(smackLabel)) {
        ThrowErr(Exc::InternalError, "Application key for ", smackLabel,
            "label already exists.");
    }

    m_keyMap[smackLabel] = applicationKey;
}

void CryptoLogic::removeKey(const Label &smackLabel)
{
    m_keyMap.erase(smackLabel);
}

RawBuffer CryptoLogic::passwordToKey(
    const Password &password,
    const RawBuffer &salt,
    size_t keySize) const
{
    RawBuffer result(keySize);

    if (1 != PKCS5_PBKDF2_HMAC_SHA1(
                password.c_str(),
                password.size(),
                salt.data(),
                salt.size(),
                1024,
                result.size(),
                result.data()))
    {
        ThrowErr(Exc::InternalError, "PCKS5_PKKDF_HMAC_SHA1 failed.");
    }

    return result;
}

RawBuffer CryptoLogic::generateRandIV() const {
    RawBuffer civ(EVP_MAX_IV_LENGTH);

    if (1 != RAND_bytes(civ.data(), civ.size())) {
        ThrowErr(Exc::InternalError, "RAND_bytes failed to generate IV.");
    }

    return civ;
}

void CryptoLogic::encryptRow(DB::Row &row)
{
    try {
        DB::Row crow = row;
        RawBuffer key;
        RawBuffer result1;
        RawBuffer result2;

        crow.algorithmType = DBCMAlgType::AES_GCM_256;
        crow.dataSize = crow.data.size();

        if (crow.dataSize <= 0) {
            ThrowErr(Exc::InternalError, "Invalid dataSize.");
        }

        if (!haveKey(row.ownerLabel)) {
            ThrowErr(Exc::InternalError, "Missing application key for ",
              row.ownerLabel, " label.");
        }

        if (crow.iv.empty()) {
            crow.iv = generateRandIV();
        }

        key = m_keyMap[row.ownerLabel];
        crow.encryptionScheme = ENCR_APPKEY;

        auto dataPair = Crypto::SW::Internals::encryptDataAesGcm(key, crow.data, crow.iv, AES_GCM_TAG_SIZE);
        crow.data = dataPair.first;

        crow.tag = dataPair.second;

        encBase64(crow.data);
        crow.encryptionScheme |= ENCR_BASE64;
        encBase64(crow.iv);

        crow.encryptionScheme &= ENCR_ORDER_CLEAR;
        crow.encryptionScheme |= ENCR_ORDER_V2;

        row = std::move(crow);
    } catch(const CKM::Base64Encoder::Exception::Base &e) {
        ThrowErr(Exc::InternalError, e.GetMessage());
    } catch(const CKM::Base64Decoder::Exception::Base &e) {
        ThrowErr(Exc::InternalError, e.GetMessage());
    }
}

int CryptoLogic::getSchemeVersion(int encryptionScheme)
{
    return encryptionScheme >> ENCR_ORDER_OFFSET;
}

void CryptoLogic::decryptRow(const Password &password, DB::Row &row)
{
    try {
        DB::Row crow = row;
        RawBuffer key;
        RawBuffer digest, dataDigest;

        if (row.algorithmType != DBCMAlgType::AES_GCM_256) {
            ThrowErr(Exc::AuthenticationFailed, "Invalid algorithm type.");
        }

        if ((row.encryptionScheme & ENCR_PASSWORD) && password.empty()) {
            ThrowErr(Exc::AuthenticationFailed,
              "DB row is password protected, but given password is "
              "empty.");
        }

        if ((row.encryptionScheme & ENCR_APPKEY) && !haveKey(row.ownerLabel)) {
            ThrowErr(Exc::AuthenticationFailed, "Missing application key for ",
              row.ownerLabel, " label.");
        }

        decBase64(crow.iv);
        if (crow.encryptionScheme & ENCR_BASE64) {
            decBase64(crow.data);
        }

        if((crow.encryptionScheme >> ENCR_ORDER_OFFSET) == ENCR_ORDER_V2) {
            if (crow.encryptionScheme & ENCR_APPKEY) {
                key = m_keyMap[crow.ownerLabel];
                crow.data = Crypto::SW::Internals::decryptDataAesGcm(key, crow.data, crow.iv, crow.tag);
            }
        } else {
            if (crow.encryptionScheme & ENCR_PASSWORD) {
                key = passwordToKey(password, crow.iv, AES_CBC_KEY_SIZE);
                crow.data = Crypto::SW::Internals::decryptDataAes(AlgoType::AES_CBC, key, crow.data, crow.iv);
            }

            if (crow.encryptionScheme & ENCR_APPKEY) {
                key = m_keyMap[crow.ownerLabel];
                crow.data = Crypto::SW::Internals::decryptDataAesGcm(key, crow.data, crow.iv, crow.tag);
            }
        }
        if (static_cast<int>(crow.data.size()) < crow.dataSize) {
            ThrowErr(Exc::AuthenticationFailed, "Decrypted row size mismatch");
        }

        if (static_cast<int>(crow.data.size()) > crow.dataSize) {
            crow.data.resize(crow.dataSize);
        }

        row = std::move(crow);
    } catch(const CKM::Base64Encoder::Exception::Base &e) {
        ThrowErr(Exc::InternalError, e.GetMessage());
    } catch(const CKM::Base64Decoder::Exception::Base &e) {
        ThrowErr(Exc::InternalError, e.GetMessage());
    } catch(const Exc::Exception &e) {
        ThrowErr(Exc::AuthenticationFailed, e.message());
    }
}

void CryptoLogic::encBase64(RawBuffer &data)
{
    Base64Encoder benc;
    RawBuffer encdata;

    benc.append(data);
    benc.finalize();
    encdata = benc.get();

    if (encdata.size() == 0) {
        ThrowErr(Exc::InternalError, "Base64Encoder returned empty data.");
    }

    data = std::move(encdata);
}

void CryptoLogic::decBase64(RawBuffer &data)
{
    Base64Decoder bdec;
    RawBuffer decdata;

    bdec.reset();
    bdec.append(data);
    if (!bdec.finalize()) {
        ThrowErr(Exc::InternalError, "Failed in Base64Decoder.finalize.");
    }

    decdata = bdec.get();

    if (decdata.size() == 0) {
        ThrowErr(Exc::InternalError, "Base64Decoder returned empty data.");
    }

    data = std::move(decdata);
}

bool CryptoLogic::equalDigests(RawBuffer &dig1, RawBuffer &dig2)
{
    unsigned int dlen = Digest().length();

    if ((dig1.size() != dlen) || (dig2.size() != dlen))
        return false;
    return (dig1 == dig2);
}

} // namespace CKM

<commit_msg>Return error if password is not empty and row is not password protected<commit_after>/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 *
 * @file        crypto-logic.cpp
 * @author      Sebastian Grabowski (s.grabowski@samsung.com)
 * @author      Bartlomiej Grzelewski (b.grzelewski@samsung.com)
 * @version     1.0
 * @brief       Crypto module implementation.
 */

#include <iostream>
#include <fstream>
#include <utility>
#include <climits>

#include <stdio.h>
#include <string.h>

#include <openssl/evp.h>
#include <openssl/rand.h>

#include <ckm/ckm-error.h>

#include <dpl/log/log.h>

#include <base64.h>
#include <digest.h>
#include <crypto-logic.h>

#include <generic-backend/exception.h>
#include <sw-backend/internals.h>

namespace CKM {

namespace {

const static int AES_CBC_KEY_SIZE = 32;
const static int AES_GCM_TAG_SIZE = 16;

// Encryption scheme flags (enable/disable specific encryption type, multiple choice)
const int ENCR_BASE64 =   1 << 0;
const int ENCR_APPKEY =   1 << 1;
const int ENCR_PASSWORD = 1 << 2;

// Encryption order flags (single choice)
const int ENCR_ORDER_OFFSET = 24;
const int ENCR_ORDER_FILTER = INT_MAX << ENCR_ORDER_OFFSET; // 0xff000000
const int ENCR_ORDER_CLEAR = ~ENCR_ORDER_FILTER; // 0x00ffffff
/*
 * ENCR_ORDER_V1 - v1 encryption order. Token returned from store is encrypted with app key and
 * optionally by custom user password. In such form it is stored in db.
 */
const int ENCR_ORDER_V1 = CryptoLogic::ENCRYPTION_V1 << ENCR_ORDER_OFFSET;
/*
 * ENCR_ORDER_V2 - v2 encryption order. Stored data is optionally encrypted by store with
 * user password. Returned token is encrypted with app key and stored in db.
 */
const int ENCR_ORDER_V2 = CryptoLogic::ENCRYPTION_V2 << ENCR_ORDER_OFFSET;

} // anonymous namespace

CryptoLogic::CryptoLogic() {}

CryptoLogic::CryptoLogic(CryptoLogic &&second) {
    m_keyMap = std::move(second.m_keyMap);
}

CryptoLogic& CryptoLogic::operator=(CryptoLogic &&second) {
    if (this == &second)
        return *this;
    m_keyMap = std::move(second.m_keyMap);
    return *this;
}

bool CryptoLogic::haveKey(const Label &smackLabel)
{
    return (m_keyMap.count(smackLabel) > 0);
}

void CryptoLogic::pushKey(const Label &smackLabel,
                          const RawBuffer &applicationKey)
{
    if (smackLabel.length() == 0) {
        ThrowErr(Exc::InternalError, "Empty smack label.");
    }
    if (applicationKey.size() == 0) {
        ThrowErr(Exc::InternalError, "Empty application key.");
    }
    if (haveKey(smackLabel)) {
        ThrowErr(Exc::InternalError, "Application key for ", smackLabel,
            "label already exists.");
    }

    m_keyMap[smackLabel] = applicationKey;
}

void CryptoLogic::removeKey(const Label &smackLabel)
{
    m_keyMap.erase(smackLabel);
}

RawBuffer CryptoLogic::passwordToKey(
    const Password &password,
    const RawBuffer &salt,
    size_t keySize) const
{
    RawBuffer result(keySize);

    if (1 != PKCS5_PBKDF2_HMAC_SHA1(
                password.c_str(),
                password.size(),
                salt.data(),
                salt.size(),
                1024,
                result.size(),
                result.data()))
    {
        ThrowErr(Exc::InternalError, "PCKS5_PKKDF_HMAC_SHA1 failed.");
    }

    return result;
}

RawBuffer CryptoLogic::generateRandIV() const {
    RawBuffer civ(EVP_MAX_IV_LENGTH);

    if (1 != RAND_bytes(civ.data(), civ.size())) {
        ThrowErr(Exc::InternalError, "RAND_bytes failed to generate IV.");
    }

    return civ;
}

void CryptoLogic::encryptRow(DB::Row &row)
{
    try {
        DB::Row crow = row;
        RawBuffer key;
        RawBuffer result1;
        RawBuffer result2;

        crow.algorithmType = DBCMAlgType::AES_GCM_256;
        crow.dataSize = crow.data.size();

        if (crow.dataSize <= 0) {
            ThrowErr(Exc::InternalError, "Invalid dataSize.");
        }

        if (!haveKey(row.ownerLabel)) {
            ThrowErr(Exc::InternalError, "Missing application key for ",
              row.ownerLabel, " label.");
        }

        if (crow.iv.empty()) {
            crow.iv = generateRandIV();
        }

        key = m_keyMap[row.ownerLabel];
        crow.encryptionScheme = ENCR_APPKEY;

        auto dataPair = Crypto::SW::Internals::encryptDataAesGcm(key, crow.data, crow.iv, AES_GCM_TAG_SIZE);
        crow.data = dataPair.first;

        crow.tag = dataPair.second;

        encBase64(crow.data);
        crow.encryptionScheme |= ENCR_BASE64;
        encBase64(crow.iv);

        crow.encryptionScheme &= ENCR_ORDER_CLEAR;
        crow.encryptionScheme |= ENCR_ORDER_V2;

        row = std::move(crow);
    } catch(const CKM::Base64Encoder::Exception::Base &e) {
        ThrowErr(Exc::InternalError, e.GetMessage());
    } catch(const CKM::Base64Decoder::Exception::Base &e) {
        ThrowErr(Exc::InternalError, e.GetMessage());
    }
}

int CryptoLogic::getSchemeVersion(int encryptionScheme)
{
    return encryptionScheme >> ENCR_ORDER_OFFSET;
}

void CryptoLogic::decryptRow(const Password &password, DB::Row &row)
{
    try {
        DB::Row crow = row;
        RawBuffer key;
        RawBuffer digest, dataDigest;

        if (row.algorithmType != DBCMAlgType::AES_GCM_256) {
            ThrowErr(Exc::AuthenticationFailed, "Invalid algorithm type.");
        }

        if ((row.encryptionScheme & ENCR_PASSWORD) && password.empty()) {
            ThrowErr(Exc::AuthenticationFailed,
                     "DB row is password protected, but given password is empty.");
        }

        if(!(row.encryptionScheme & ENCR_PASSWORD) && !password.empty()) {
            ThrowErr(Exc::AuthenticationFailed,
                     "DB row is not password protected, but given password is not empty.");
        }

        if ((row.encryptionScheme & ENCR_APPKEY) && !haveKey(row.ownerLabel)) {
            ThrowErr(Exc::AuthenticationFailed,
                     "Missing application key for ",
                     row.ownerLabel,
                     " label.");
        }

        decBase64(crow.iv);
        if (crow.encryptionScheme & ENCR_BASE64) {
            decBase64(crow.data);
        }

        if((crow.encryptionScheme >> ENCR_ORDER_OFFSET) == ENCR_ORDER_V2) {
            if (crow.encryptionScheme & ENCR_APPKEY) {
                key = m_keyMap[crow.ownerLabel];
                crow.data = Crypto::SW::Internals::decryptDataAesGcm(key, crow.data, crow.iv, crow.tag);
            }
        } else {
            if (crow.encryptionScheme & ENCR_PASSWORD) {
                key = passwordToKey(password, crow.iv, AES_CBC_KEY_SIZE);
                crow.data = Crypto::SW::Internals::decryptDataAes(AlgoType::AES_CBC, key, crow.data, crow.iv);
            }

            if (crow.encryptionScheme & ENCR_APPKEY) {
                key = m_keyMap[crow.ownerLabel];
                crow.data = Crypto::SW::Internals::decryptDataAesGcm(key, crow.data, crow.iv, crow.tag);
            }
        }
        if (static_cast<int>(crow.data.size()) < crow.dataSize) {
            ThrowErr(Exc::AuthenticationFailed, "Decrypted row size mismatch");
        }

        if (static_cast<int>(crow.data.size()) > crow.dataSize) {
            crow.data.resize(crow.dataSize);
        }

        row = std::move(crow);
    } catch(const CKM::Base64Encoder::Exception::Base &e) {
        ThrowErr(Exc::InternalError, e.GetMessage());
    } catch(const CKM::Base64Decoder::Exception::Base &e) {
        ThrowErr(Exc::InternalError, e.GetMessage());
    } catch(const Exc::Exception &e) {
        ThrowErr(Exc::AuthenticationFailed, e.message());
    }
}

void CryptoLogic::encBase64(RawBuffer &data)
{
    Base64Encoder benc;
    RawBuffer encdata;

    benc.append(data);
    benc.finalize();
    encdata = benc.get();

    if (encdata.size() == 0) {
        ThrowErr(Exc::InternalError, "Base64Encoder returned empty data.");
    }

    data = std::move(encdata);
}

void CryptoLogic::decBase64(RawBuffer &data)
{
    Base64Decoder bdec;
    RawBuffer decdata;

    bdec.reset();
    bdec.append(data);
    if (!bdec.finalize()) {
        ThrowErr(Exc::InternalError, "Failed in Base64Decoder.finalize.");
    }

    decdata = bdec.get();

    if (decdata.size() == 0) {
        ThrowErr(Exc::InternalError, "Base64Decoder returned empty data.");
    }

    data = std::move(decdata);
}

bool CryptoLogic::equalDigests(RawBuffer &dig1, RawBuffer &dig2)
{
    unsigned int dlen = Digest().length();

    if ((dig1.size() != dlen) || (dig2.size() != dlen))
        return false;
    return (dig1 == dig2);
}

} // namespace CKM

<|endoftext|>


<commit_before>#include "memcached/memcached_client.hxx"
#include "http_cache_document.hxx"
#include "lease.hxx"
#include "system/SetupProcess.hxx"
#include "system/fd-util.h"
#include "strmap.hxx"
#include "tpool.hxx"
#include "RootPool.hxx"
#include "serialize.hxx"
#include "istream/sink_buffer.hxx"
#include "istream/istream.hxx"
#include "direct.hxx"
#include "pool.hxx"
#include "fb_pool.hxx"
#include "event/Loop.hxx"
#include "util/ConstBuffer.hxx"
#include "util/Cancellable.hxx"

#include <socket/resolver.h>
#include <socket/util.h>

#include <glib.h>

#include <unistd.h>
#include <stdio.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>

struct Context final : Lease{
    struct pool *pool;

    int fd;
    bool idle, reuse;

    Istream *value;
    bool value_eof, value_abort;

    CancellablePointer cancel_ptr;

    /* virtual methods from class Lease */
    void ReleaseLease(bool _reuse) override {
        assert(!idle);
        assert(fd >= 0);

        idle = true;
        reuse = _reuse;

        close(fd);
        fd = -1;
    }
};

static void
dump_choice(const HttpCacheDocument *document)
{
    const auto delta = document->info.expires - std::chrono::system_clock::now();
    const auto delta_s = std::chrono::duration_cast<std::chrono::seconds>(delta);
    printf("expires=%ld\n", (long)delta_s.count());

    for (const auto &i : document->vary)
        printf("\t%s: %s\n", i.key, i.value);
}

/*
 * sink_buffer callback
 *
 */

static void
my_sink_done(void *data0, size_t length, gcc_unused void *ctx)
{
    /*uint32_t magic;*/

    ConstBuffer<void> data(data0, length);

    while (!data.IsEmpty()) {
        const AutoRewindPool auto_rewind(*tpool);
        HttpCacheDocument document(*tpool);

        /*magic = */deserialize_uint32(data);
        /*
        if (magic != CHOICE_MAGIC)
            break;
        */

        document.info.expires = std::chrono::system_clock::from_time_t(deserialize_uint64(data));
        deserialize_strmap(data, document.vary);

        if (data.IsNull())
            /* deserialization failure */
            break;

        dump_choice(&document);
    }
}

static void
my_sink_error(GError *error, gcc_unused void *ctx)
{
    fprintf(stderr, "sink_buffer has failed: %s\n", error->message);
    g_error_free(error);
}

static const struct sink_buffer_handler my_sink_handler = {
    .done = my_sink_done,
    .error = my_sink_error,
};

/*
 * memcached_response_handler_t
 *
 */

static void
my_mcd_response(enum memcached_response_status status,
                gcc_unused const void *extras,
                gcc_unused size_t extras_length,
                gcc_unused const void *key,
                gcc_unused size_t key_length,
                Istream *value, void *ctx)
{
    auto *c = (Context *)ctx;

    if (status != MEMCACHED_STATUS_NO_ERROR || value == NULL) {
        fprintf(stderr, "status=%d\n", status);
        if (value != NULL)
            value->CloseUnused();
        return;
    }

    sink_buffer_new(*c->pool, *value,
                    my_sink_handler, c,
                    c->cancel_ptr);
}

static void
my_mcd_error(GError *error, gcc_unused void *ctx)
{
    fprintf(stderr, "%s\n", error->message);
    g_error_free(error);
}

static const struct memcached_client_handler my_mcd_handler = {
    .response = my_mcd_response,
    .error = my_mcd_error,
};

/*
 * main
 *
 */

int main(int argc, char **argv) {
    int fd, ret;
    struct addrinfo hints, *ai;
    const char *key;
    static Context ctx;

    if (argc != 3) {
        fprintf(stderr, "usage: run-memcached-client HOST[:PORT] URI\n");
        return 1;
    }

    direct_global_init();

    /* connect socket */

    memset(&hints, 0, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;

    ret = socket_resolve_host_port(argv[1], 11211, &hints, &ai);
    if (ret != 0) {
        fprintf(stderr, "Failed to resolve host name\n");
        return 2;
    }

    fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
    if (fd < 0) {
        fprintf(stderr, "socket() failed: %s\n", strerror(errno));
        return 2;
    }

    ret = connect(fd, ai->ai_addr, ai->ai_addrlen);
    if (ret < 0) {
        fprintf(stderr, "connect() failed: %s\n", strerror(errno));
        return 2;
    }

    freeaddrinfo(ai);

    fd_set_nonblock(fd, true);
    socket_set_nodelay(fd, true);

    /* initialize */

    SetupProcess();

    EventLoop event_loop;
    fb_pool_init(event_loop, false);

    RootPool root_pool;
    ctx.pool = pool_new_linear(root_pool, "test", 8192);

    key = p_strcat(ctx.pool, argv[2], " choice", NULL);
    printf("key='%s'\n", key);

    /* send memcached request */

    memcached_client_invoke(ctx.pool, event_loop, fd, FdType::FD_TCP,
                            ctx,
                            MEMCACHED_OPCODE_GET,
                            NULL, 0,
                            key, strlen(key),
                            NULL,
                            &my_mcd_handler, &ctx,
                            ctx.cancel_ptr);
    pool_unref(ctx.pool);
    pool_commit();

    event_loop.Dispatch();

    /* cleanup */

    fb_pool_deinit();

    return ctx.value_eof ? 0 : 2;
}
<commit_msg>test/dump_memcached_choice: replace value_eof/value_abort with "success"<commit_after>#include "memcached/memcached_client.hxx"
#include "http_cache_document.hxx"
#include "lease.hxx"
#include "system/SetupProcess.hxx"
#include "system/fd-util.h"
#include "strmap.hxx"
#include "tpool.hxx"
#include "RootPool.hxx"
#include "serialize.hxx"
#include "istream/sink_buffer.hxx"
#include "istream/istream.hxx"
#include "direct.hxx"
#include "pool.hxx"
#include "fb_pool.hxx"
#include "event/Loop.hxx"
#include "util/ConstBuffer.hxx"
#include "util/Cancellable.hxx"

#include <socket/resolver.h>
#include <socket/util.h>

#include <glib.h>

#include <unistd.h>
#include <stdio.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>

struct Context final : Lease{
    struct pool *pool;

    int fd;
    bool idle, reuse;

    Istream *value;

    bool success = false;

    CancellablePointer cancel_ptr;

    /* virtual methods from class Lease */
    void ReleaseLease(bool _reuse) override {
        assert(!idle);
        assert(fd >= 0);

        idle = true;
        reuse = _reuse;

        close(fd);
        fd = -1;
    }
};

static void
dump_choice(const HttpCacheDocument *document)
{
    const auto delta = document->info.expires - std::chrono::system_clock::now();
    const auto delta_s = std::chrono::duration_cast<std::chrono::seconds>(delta);
    printf("expires=%ld\n", (long)delta_s.count());

    for (const auto &i : document->vary)
        printf("\t%s: %s\n", i.key, i.value);
}

/*
 * sink_buffer callback
 *
 */

static void
my_sink_done(void *data0, size_t length, void *_ctx)
{
    auto &ctx = *(Context *)_ctx;

    /*uint32_t magic;*/

    ConstBuffer<void> data(data0, length);

    while (!data.IsEmpty()) {
        const AutoRewindPool auto_rewind(*tpool);
        HttpCacheDocument document(*tpool);

        /*magic = */deserialize_uint32(data);
        /*
        if (magic != CHOICE_MAGIC)
            break;
        */

        document.info.expires = std::chrono::system_clock::from_time_t(deserialize_uint64(data));
        deserialize_strmap(data, document.vary);

        if (data.IsNull())
            /* deserialization failure */
            return;

        dump_choice(&document);
    }

    ctx.success = true;
}

static void
my_sink_error(GError *error, gcc_unused void *ctx)
{
    fprintf(stderr, "sink_buffer has failed: %s\n", error->message);
    g_error_free(error);
}

static const struct sink_buffer_handler my_sink_handler = {
    .done = my_sink_done,
    .error = my_sink_error,
};

/*
 * memcached_response_handler_t
 *
 */

static void
my_mcd_response(enum memcached_response_status status,
                gcc_unused const void *extras,
                gcc_unused size_t extras_length,
                gcc_unused const void *key,
                gcc_unused size_t key_length,
                Istream *value, void *ctx)
{
    auto *c = (Context *)ctx;

    if (status != MEMCACHED_STATUS_NO_ERROR || value == NULL) {
        fprintf(stderr, "status=%d\n", status);
        if (value != NULL)
            value->CloseUnused();
        return;
    }

    sink_buffer_new(*c->pool, *value,
                    my_sink_handler, c,
                    c->cancel_ptr);
}

static void
my_mcd_error(GError *error, gcc_unused void *ctx)
{
    fprintf(stderr, "%s\n", error->message);
    g_error_free(error);
}

static const struct memcached_client_handler my_mcd_handler = {
    .response = my_mcd_response,
    .error = my_mcd_error,
};

/*
 * main
 *
 */

int main(int argc, char **argv) {
    int fd, ret;
    struct addrinfo hints, *ai;
    const char *key;
    static Context ctx;

    if (argc != 3) {
        fprintf(stderr, "usage: run-memcached-client HOST[:PORT] URI\n");
        return 1;
    }

    direct_global_init();

    /* connect socket */

    memset(&hints, 0, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;

    ret = socket_resolve_host_port(argv[1], 11211, &hints, &ai);
    if (ret != 0) {
        fprintf(stderr, "Failed to resolve host name\n");
        return 2;
    }

    fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
    if (fd < 0) {
        fprintf(stderr, "socket() failed: %s\n", strerror(errno));
        return 2;
    }

    ret = connect(fd, ai->ai_addr, ai->ai_addrlen);
    if (ret < 0) {
        fprintf(stderr, "connect() failed: %s\n", strerror(errno));
        return 2;
    }

    freeaddrinfo(ai);

    fd_set_nonblock(fd, true);
    socket_set_nodelay(fd, true);

    /* initialize */

    SetupProcess();

    EventLoop event_loop;
    fb_pool_init(event_loop, false);

    RootPool root_pool;
    ctx.pool = pool_new_linear(root_pool, "test", 8192);

    key = p_strcat(ctx.pool, argv[2], " choice", NULL);
    printf("key='%s'\n", key);

    /* send memcached request */

    memcached_client_invoke(ctx.pool, event_loop, fd, FdType::FD_TCP,
                            ctx,
                            MEMCACHED_OPCODE_GET,
                            NULL, 0,
                            key, strlen(key),
                            NULL,
                            &my_mcd_handler, &ctx,
                            ctx.cancel_ptr);
    pool_unref(ctx.pool);
    pool_commit();

    event_loop.Dispatch();

    /* cleanup */

    fb_pool_deinit();

    return ctx.success ? 0 : 2;
}
<|endoftext|>


<commit_before><commit_msg>Corrigé la déclaration de incidence dans FlotsCalc<commit_after><|endoftext|>


<commit_before><commit_msg>Forgotten semicolon.<commit_after><|endoftext|>