<commit_before>/*
 * SessionOptions.cpp
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include <session/SessionOptions.hpp>

#include <boost/foreach.hpp>

#include <core/FilePath.hpp>
#include <core/ProgramStatus.hpp>
#include <core/ProgramOptions.hpp>
#include <core/SafeConvert.hpp>
#include <core/system/System.hpp>
#include <core/system/Environment.hpp>

#include <core/Error.hpp>
#include <core/Log.hpp>

#include <session/SessionConstants.hpp>

using namespace core ;

namespace session {

namespace {

const char* const kDefaultPostbackPath = "bin/postback/rpostback";

void resolvePath(const FilePath& resourcePath, std::string* pPath)
{
   if (!pPath->empty())
      *pPath = resourcePath.complete(*pPath).absolutePath();
}

#ifdef __APPLE__

void resolvePostbackPath(const FilePath& resourcePath, std::string* pPath)
{
   // On OSX we keep the postback scripts over in the MacOS directory
   // rather than in the Resources directory -- make this adjustment
   // when the default postback path has been passed
   if (*pPath == kDefaultPostbackPath)
   {
      FilePath path = resourcePath.parent().complete("MacOS/postback/rpostback");
      *pPath = path.absolutePath();
   }
   else
   {
      resolvePath(resourcePath, pPath);
   }
}

#else

void resolvePostbackPath(const FilePath& resourcePath, std::string* pPath)
{
   resolvePath(resourcePath, pPath);
}

#endif

}

Options& options()
{
   static Options instance ;
   return instance ;
}

core::ProgramStatus Options::read(int argc, char * const argv[])
{
   using namespace boost::program_options ;

   // compute the resource path
   FilePath resourcePath;
   Error error = core::system::installPath("..", argc, argv, &resourcePath);
   if (error)
   {
      LOG_ERROR_MESSAGE("Unable to determine install path: "+error.summary());
      return ProgramStatus::exitFailure();
   }

   // detect running in OSX bundle and tweak resource path
#ifdef __APPLE__
   if (resourcePath.complete("Info.plist").exists())
      resourcePath = resourcePath.complete("Resources");
#endif

   // detect running in x64 directory and tweak resource path
#ifdef _WIN32
   if (resourcePath.complete("x64").exists())
      resourcePath = resourcePath.parent();
#endif

   // verify installation flag
   options_description verify("verify");
   verify.add_options()
     (kVerifyInstallationSessionOption,
     value<bool>(&verifyInstallation_)->default_value(false),
     "verify the current installation");

   // program - name and execution
   options_description program("program");
   program.add_options()
      (kProgramModeSessionOption,
         value<std::string>(&programMode_)->default_value("server"),
         "program mode (desktop or server");

   // agreement
   options_description agreement("agreement");
   agreement.add_options()
      ("agreement-file",
      value<std::string>(&agreementFilePath_)->default_value(""),
      "agreement file");

   // docs url
   options_description docs("docs");
   docs.add_options()
      ("docs-url",
       value<std::string>(&docsURL_)->default_value(""),
       "custom docs url");

   // www options
   options_description www("www") ;
   www.add_options()
      ("www-local-path",
         value<std::string>(&wwwLocalPath_)->default_value("www"),
         "www local path")
      ("www-port",
         value<std::string>(&wwwPort_)->default_value("8787"),
         "port to listen on");

   // session options
   options_description session("session") ;
   session.add_options()
      ("session-timeout-minutes",
         value<int>(&timeoutMinutes_)->default_value(120),
         "session timeout (minutes)" )
      ("session-preflight-script",
         value<std::string>(&preflightScript_)->default_value(""),
         "session preflight script")
      ("session-create-public-folder",
         value<bool>(&createPublicFolder_)->default_value(false),
         "automatically create public folder")
      ("session-rprofile-on-resume-default",
          value<bool>(&rProfileOnResumeDefault_)->default_value(false),
          "default user setting for running Rprofile on resume");

   // r options
   bool rShellEscape; // no longer works but don't want to break any
                      // config files which formerly used it
                      // TODO: eliminate this option entirely
   options_description r("r") ;
   r.add_options()
      ("r-core-source",
         value<std::string>(&coreRSourcePath_)->default_value("R"),
         "Core R source path")
      ("r-modules-source",
         value<std::string>(&modulesRSourcePath_)->default_value("R/modules"),
         "Modules R source path")
      ("r-session-packages",
         value<std::string>(&sessionPackagesPath_)->default_value("R/library"),
         "R packages path")
      ("r-libs-user",
         value<std::string>(&rLibsUser_)->default_value("~/R/library"),
         "R user library path")
      ("r-cran-repos",
         value<std::string>(&rCRANRepos_)->default_value(""),
         "Default CRAN repository")
      ("r-auto-reload-source",
         value<bool>(&autoReloadSource_)->default_value(false),
         "Reload R source if it changes during the session")
      ("r-compatible-graphics-engine-version",
         value<int>(&rCompatibleGraphicsEngineVersion_)->default_value(10),
         "Maximum graphics engine version we are compatible with")
      ("r-resources-path",
         value<std::string>(&rResourcesPath_)->default_value("resources"),
         "Directory containing external resources")
      ("r-shell-escape",
         value<bool>(&rShellEscape)->default_value(false),
         "Support shell escape (deprecated, no longer works)")
      ("r-home-dir-override",
         value<std::string>(&rHomeDirOverride_)->default_value(""),
         "Override for R_HOME (used for debug configurations)")
      ("r-doc-dir-override",
         value<std::string>(&rDocDirOverride_)->default_value(""),
         "Override for R_DOC_DIR (used for debug configurations)");

   // limits options
   options_description limits("limits");
   limits.add_options()
      ("limit-file-upload-size-mb",
       value<int>(&limitFileUploadSizeMb_)->default_value(0),
       "limit of file upload size")
      ("limit-cpu-time-minutes",
       value<int>(&limitCpuTimeMinutes_)->default_value(0),
       "limit on time of top level computations")
      ("limit-xfs-disk-quota",
       value<bool>(&limitXfsDiskQuota_)->default_value(false),
       "limit xfs disk quota");

   // external options
   options_description external("external");
   external.add_options()
      ("external-rpostback-path",
       value<std::string>(&rpostbackPath_)->default_value(kDefaultPostbackPath),
       "Path to rpostback executable")
      ("external-consoleio-path",
       value<std::string>(&consoleIoPath_)->default_value("bin/consoleio.exe"),
       "Path to consoleio executable")
      ("external-gnudiff-path",
       value<std::string>(&gnudiffPath_)->default_value("bin/gnudiff"),
       "Path to gnudiff utilities (windows-only)")
      ("external-gnugrep-path",
       value<std::string>(&gnugrepPath_)->default_value("bin/gnugrep"),
       "Path to gnugrep utilities (windows-only)")
      ("external-msysssh-path",
       value<std::string>(&msysSshPath_)->default_value("bin/msys_ssh"),
       "Path to msys_ssh utilities (windows-only)")
      ("external-sumatra-path",
       value<std::string>(&sumatraPath_)->default_value("bin/sumatra"),
       "Path to SumatraPDF (windows-only)")
      ("external-hunspell-dictionaries-path",
       value<std::string>(&hunspellDictionariesPath_)->default_value("resources/dictionaries"),
       "Path to hunspell dictionaries")
      ("external-mathjax-path",
        value<std::string>(&mathjaxPath_)->default_value("resources/mathjax"),
        "Path to mathjax library");

   // user options (default user identity to current username)
   std::string currentUsername = core::system::username();
   options_description user("user") ;
   user.add_options()
      (kUserIdentitySessionOption "," kUserIdentitySessionOptionShort,
       value<std::string>(&userIdentity_)->default_value(currentUsername),
       "user identity" );

   // define program options
   FilePath defaultConfigPath("/etc/rstudio/rsession.conf");
   std::string configFile = defaultConfigPath.exists() ?
                                 defaultConfigPath.absolutePath() : "";
   core::program_options::OptionsDescription optionsDesc("rsession",
                                                         configFile);

   optionsDesc.commandLine.add(verify);
   optionsDesc.commandLine.add(program);
   optionsDesc.commandLine.add(agreement);
   optionsDesc.commandLine.add(docs);
   optionsDesc.commandLine.add(www);
   optionsDesc.commandLine.add(session);
   optionsDesc.commandLine.add(r);
   optionsDesc.commandLine.add(limits);
   optionsDesc.commandLine.add(external);
   optionsDesc.commandLine.add(user);

   // define groups included in config-file processing
   optionsDesc.configFile.add(program);
   optionsDesc.configFile.add(agreement);
   optionsDesc.configFile.add(docs);
   optionsDesc.configFile.add(www);
   optionsDesc.configFile.add(session);
   optionsDesc.configFile.add(r);
   optionsDesc.configFile.add(limits);
   optionsDesc.configFile.add(external);
   optionsDesc.configFile.add(user);

   // read configuration
   ProgramStatus status = core::program_options::read(optionsDesc, argc,argv);
   if (status.exit())
      return status;

   // make sure the program mode is valid
   if (programMode_ != kSessionProgramModeDesktop &&
       programMode_ != kSessionProgramModeServer)
   {
      LOG_ERROR_MESSAGE("invalid program mode: " + programMode_);
      return ProgramStatus::exitFailure();
   }

   // compute program identity
   programIdentity_ = "rsession-" + userIdentity_;

   // provide special home path in temp directory if we are verifying
   if (verifyInstallation_)
   {
      // we create a special home directory in server mode (since the
      // user we are running under might not have a home directory)
      if (programMode_ == kSessionProgramModeServer)
      {
         verifyInstallationHomeDir_ = "/tmp/rstudio-verify-installation";
         Error error = FilePath(verifyInstallationHomeDir_).ensureDirectory();
         if (error)
         {
            LOG_ERROR(error);
            return ProgramStatus::exitFailure();
         }
         core::system::setenv("R_USER", verifyInstallationHomeDir_);
      }
   }

   // compute user home path
   FilePath userHomePath = core::system::userHomePath("R_USER|HOME");

   userHomePath_ = userHomePath.absolutePath();

   // compute user scratch path
   std::string scratchPathName;
   if (programMode_ == kSessionProgramModeDesktop)
      scratchPathName = "RStudio-Desktop";
   else
      scratchPathName = "RStudio";
   userScratchPath_ = core::system::userSettingsPath(
                                       userHomePath,
                                       scratchPathName).absolutePath();

   // session timeout seconds is always -1 in desktop mode
   if (programMode_ == kSessionProgramModeDesktop)
      timeoutMinutes_ = 0;

   // convert relative paths by completing from the app resource path
   resolvePath(resourcePath, &rResourcesPath_);
   resolvePath(resourcePath, &agreementFilePath_);
   resolvePath(resourcePath, &wwwLocalPath_);
   resolvePath(resourcePath, &coreRSourcePath_);
   resolvePath(resourcePath, &modulesRSourcePath_);
   resolvePath(resourcePath, &sessionPackagesPath_);
   resolvePostbackPath(resourcePath, &rpostbackPath_);
#ifdef _WIN32
   resolvePath(resourcePath, &consoleIoPath_);
   resolvePath(resourcePath, &gnudiffPath_);
   resolvePath(resourcePath, &gnugrepPath_);
   resolvePath(resourcePath, &msysSshPath_);
   resolvePath(resourcePath, &sumatraPath_);
#endif
   resolvePath(resourcePath, &hunspellDictionariesPath_);
   resolvePath(resourcePath, &mathjaxPath_);

   // shared secret with parent
   secret_ = core::system::getenv("RS_SHARED_SECRET");
   /* SECURITY: Need RS_SHARED_SECRET to be available to
      rpostback. However, we really ought to communicate
      it in a more secure manner than this, at least on
      Windows where even within the same user session some
      processes can have different priviliges (integrity
      levels) than others. For example, using a named pipe
      with proper SACL to retrieve the shared secret, where
      the name of the pipe is in an environment variable. */
   //core::system::unsetenv("RS_SHARED_SECRET");

   // initial working dir override
   initialWorkingDirOverride_ = core::system::getenv("RS_INITIAL_WD");
   core::system::unsetenv("RS_INITIAL_WD");

   // initial environment file override
   initialEnvironmentFileOverride_ = core::system::getenv("RS_INITIAL_ENV");
   core::system::unsetenv("RS_INITIAL_ENV");

   // initial project
   initialProjectPath_ = core::system::getenv("RS_INITIAL_PROJECT");
   core::system::unsetenv("RS_INITIAL_PROJECT");

   // limit rpc client uid
   limitRpcClientUid_ = -1;
   std::string limitUid = core::system::getenv(kRStudioLimitRpcClientUid);
   if (!limitUid.empty())
   {
      limitRpcClientUid_ = core::safe_convert::stringTo<int>(limitUid, -1);
      core::system::unsetenv(kRStudioLimitRpcClientUid);
   }

   // return status
   return status;
}

} // namespace session
<commit_msg>allow session timeout to be supressed via an environment variable<commit_after>/*
 * SessionOptions.cpp
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include <session/SessionOptions.hpp>

#include <boost/foreach.hpp>

#include <core/FilePath.hpp>
#include <core/ProgramStatus.hpp>
#include <core/ProgramOptions.hpp>
#include <core/SafeConvert.hpp>
#include <core/system/System.hpp>
#include <core/system/Environment.hpp>

#include <core/Error.hpp>
#include <core/Log.hpp>

#include <session/SessionConstants.hpp>

using namespace core ;

namespace session {

namespace {

const char* const kDefaultPostbackPath = "bin/postback/rpostback";

void resolvePath(const FilePath& resourcePath, std::string* pPath)
{
   if (!pPath->empty())
      *pPath = resourcePath.complete(*pPath).absolutePath();
}

#ifdef __APPLE__

void resolvePostbackPath(const FilePath& resourcePath, std::string* pPath)
{
   // On OSX we keep the postback scripts over in the MacOS directory
   // rather than in the Resources directory -- make this adjustment
   // when the default postback path has been passed
   if (*pPath == kDefaultPostbackPath)
   {
      FilePath path = resourcePath.parent().complete("MacOS/postback/rpostback");
      *pPath = path.absolutePath();
   }
   else
   {
      resolvePath(resourcePath, pPath);
   }
}

#else

void resolvePostbackPath(const FilePath& resourcePath, std::string* pPath)
{
   resolvePath(resourcePath, pPath);
}

#endif

}

Options& options()
{
   static Options instance ;
   return instance ;
}

core::ProgramStatus Options::read(int argc, char * const argv[])
{
   using namespace boost::program_options ;

   // compute the resource path
   FilePath resourcePath;
   Error error = core::system::installPath("..", argc, argv, &resourcePath);
   if (error)
   {
      LOG_ERROR_MESSAGE("Unable to determine install path: "+error.summary());
      return ProgramStatus::exitFailure();
   }

   // detect running in OSX bundle and tweak resource path
#ifdef __APPLE__
   if (resourcePath.complete("Info.plist").exists())
      resourcePath = resourcePath.complete("Resources");
#endif

   // detect running in x64 directory and tweak resource path
#ifdef _WIN32
   if (resourcePath.complete("x64").exists())
      resourcePath = resourcePath.parent();
#endif

   // verify installation flag
   options_description verify("verify");
   verify.add_options()
     (kVerifyInstallationSessionOption,
     value<bool>(&verifyInstallation_)->default_value(false),
     "verify the current installation");

   // program - name and execution
   options_description program("program");
   program.add_options()
      (kProgramModeSessionOption,
         value<std::string>(&programMode_)->default_value("server"),
         "program mode (desktop or server");

   // agreement
   options_description agreement("agreement");
   agreement.add_options()
      ("agreement-file",
      value<std::string>(&agreementFilePath_)->default_value(""),
      "agreement file");

   // docs url
   options_description docs("docs");
   docs.add_options()
      ("docs-url",
       value<std::string>(&docsURL_)->default_value(""),
       "custom docs url");

   // www options
   options_description www("www") ;
   www.add_options()
      ("www-local-path",
         value<std::string>(&wwwLocalPath_)->default_value("www"),
         "www local path")
      ("www-port",
         value<std::string>(&wwwPort_)->default_value("8787"),
         "port to listen on");

   // session options
   options_description session("session") ;
   session.add_options()
      ("session-timeout-minutes",
         value<int>(&timeoutMinutes_)->default_value(120),
         "session timeout (minutes)" )
      ("session-preflight-script",
         value<std::string>(&preflightScript_)->default_value(""),
         "session preflight script")
      ("session-create-public-folder",
         value<bool>(&createPublicFolder_)->default_value(false),
         "automatically create public folder")
      ("session-rprofile-on-resume-default",
          value<bool>(&rProfileOnResumeDefault_)->default_value(false),
          "default user setting for running Rprofile on resume");

   // r options
   bool rShellEscape; // no longer works but don't want to break any
                      // config files which formerly used it
                      // TODO: eliminate this option entirely
   options_description r("r") ;
   r.add_options()
      ("r-core-source",
         value<std::string>(&coreRSourcePath_)->default_value("R"),
         "Core R source path")
      ("r-modules-source",
         value<std::string>(&modulesRSourcePath_)->default_value("R/modules"),
         "Modules R source path")
      ("r-session-packages",
         value<std::string>(&sessionPackagesPath_)->default_value("R/library"),
         "R packages path")
      ("r-libs-user",
         value<std::string>(&rLibsUser_)->default_value("~/R/library"),
         "R user library path")
      ("r-cran-repos",
         value<std::string>(&rCRANRepos_)->default_value(""),
         "Default CRAN repository")
      ("r-auto-reload-source",
         value<bool>(&autoReloadSource_)->default_value(false),
         "Reload R source if it changes during the session")
      ("r-compatible-graphics-engine-version",
         value<int>(&rCompatibleGraphicsEngineVersion_)->default_value(10),
         "Maximum graphics engine version we are compatible with")
      ("r-resources-path",
         value<std::string>(&rResourcesPath_)->default_value("resources"),
         "Directory containing external resources")
      ("r-shell-escape",
         value<bool>(&rShellEscape)->default_value(false),
         "Support shell escape (deprecated, no longer works)")
      ("r-home-dir-override",
         value<std::string>(&rHomeDirOverride_)->default_value(""),
         "Override for R_HOME (used for debug configurations)")
      ("r-doc-dir-override",
         value<std::string>(&rDocDirOverride_)->default_value(""),
         "Override for R_DOC_DIR (used for debug configurations)");

   // limits options
   options_description limits("limits");
   limits.add_options()
      ("limit-file-upload-size-mb",
       value<int>(&limitFileUploadSizeMb_)->default_value(0),
       "limit of file upload size")
      ("limit-cpu-time-minutes",
       value<int>(&limitCpuTimeMinutes_)->default_value(0),
       "limit on time of top level computations")
      ("limit-xfs-disk-quota",
       value<bool>(&limitXfsDiskQuota_)->default_value(false),
       "limit xfs disk quota");

   // external options
   options_description external("external");
   external.add_options()
      ("external-rpostback-path",
       value<std::string>(&rpostbackPath_)->default_value(kDefaultPostbackPath),
       "Path to rpostback executable")
      ("external-consoleio-path",
       value<std::string>(&consoleIoPath_)->default_value("bin/consoleio.exe"),
       "Path to consoleio executable")
      ("external-gnudiff-path",
       value<std::string>(&gnudiffPath_)->default_value("bin/gnudiff"),
       "Path to gnudiff utilities (windows-only)")
      ("external-gnugrep-path",
       value<std::string>(&gnugrepPath_)->default_value("bin/gnugrep"),
       "Path to gnugrep utilities (windows-only)")
      ("external-msysssh-path",
       value<std::string>(&msysSshPath_)->default_value("bin/msys_ssh"),
       "Path to msys_ssh utilities (windows-only)")
      ("external-sumatra-path",
       value<std::string>(&sumatraPath_)->default_value("bin/sumatra"),
       "Path to SumatraPDF (windows-only)")
      ("external-hunspell-dictionaries-path",
       value<std::string>(&hunspellDictionariesPath_)->default_value("resources/dictionaries"),
       "Path to hunspell dictionaries")
      ("external-mathjax-path",
        value<std::string>(&mathjaxPath_)->default_value("resources/mathjax"),
        "Path to mathjax library");

   // user options (default user identity to current username)
   std::string currentUsername = core::system::username();
   options_description user("user") ;
   user.add_options()
      (kUserIdentitySessionOption "," kUserIdentitySessionOptionShort,
       value<std::string>(&userIdentity_)->default_value(currentUsername),
       "user identity" );

   // define program options
   FilePath defaultConfigPath("/etc/rstudio/rsession.conf");
   std::string configFile = defaultConfigPath.exists() ?
                                 defaultConfigPath.absolutePath() : "";
   core::program_options::OptionsDescription optionsDesc("rsession",
                                                         configFile);

   optionsDesc.commandLine.add(verify);
   optionsDesc.commandLine.add(program);
   optionsDesc.commandLine.add(agreement);
   optionsDesc.commandLine.add(docs);
   optionsDesc.commandLine.add(www);
   optionsDesc.commandLine.add(session);
   optionsDesc.commandLine.add(r);
   optionsDesc.commandLine.add(limits);
   optionsDesc.commandLine.add(external);
   optionsDesc.commandLine.add(user);

   // define groups included in config-file processing
   optionsDesc.configFile.add(program);
   optionsDesc.configFile.add(agreement);
   optionsDesc.configFile.add(docs);
   optionsDesc.configFile.add(www);
   optionsDesc.configFile.add(session);
   optionsDesc.configFile.add(r);
   optionsDesc.configFile.add(limits);
   optionsDesc.configFile.add(external);
   optionsDesc.configFile.add(user);

   // read configuration
   ProgramStatus status = core::program_options::read(optionsDesc, argc,argv);
   if (status.exit())
      return status;

   // make sure the program mode is valid
   if (programMode_ != kSessionProgramModeDesktop &&
       programMode_ != kSessionProgramModeServer)
   {
      LOG_ERROR_MESSAGE("invalid program mode: " + programMode_);
      return ProgramStatus::exitFailure();
   }

   // compute program identity
   programIdentity_ = "rsession-" + userIdentity_;

   // provide special home path in temp directory if we are verifying
   if (verifyInstallation_)
   {
      // we create a special home directory in server mode (since the
      // user we are running under might not have a home directory)
      if (programMode_ == kSessionProgramModeServer)
      {
         verifyInstallationHomeDir_ = "/tmp/rstudio-verify-installation";
         Error error = FilePath(verifyInstallationHomeDir_).ensureDirectory();
         if (error)
         {
            LOG_ERROR(error);
            return ProgramStatus::exitFailure();
         }
         core::system::setenv("R_USER", verifyInstallationHomeDir_);
      }
   }

   // compute user home path
   FilePath userHomePath = core::system::userHomePath("R_USER|HOME");

   userHomePath_ = userHomePath.absolutePath();

   // compute user scratch path
   std::string scratchPathName;
   if (programMode_ == kSessionProgramModeDesktop)
      scratchPathName = "RStudio-Desktop";
   else
      scratchPathName = "RStudio";
   userScratchPath_ = core::system::userSettingsPath(
                                       userHomePath,
                                       scratchPathName).absolutePath();

   // session timeout seconds is always -1 in desktop mode
   if (programMode_ == kSessionProgramModeDesktop)
      timeoutMinutes_ = 0;

   // allow session timeout to be supressed via environment variable
   if (!core::system::getenv("RSTUDIO_NO_SESSION_TIMEOUT").empty())
      timeoutMinutes_ = 0;

   // convert relative paths by completing from the app resource path
   resolvePath(resourcePath, &rResourcesPath_);
   resolvePath(resourcePath, &agreementFilePath_);
   resolvePath(resourcePath, &wwwLocalPath_);
   resolvePath(resourcePath, &coreRSourcePath_);
   resolvePath(resourcePath, &modulesRSourcePath_);
   resolvePath(resourcePath, &sessionPackagesPath_);
   resolvePostbackPath(resourcePath, &rpostbackPath_);
#ifdef _WIN32
   resolvePath(resourcePath, &consoleIoPath_);
   resolvePath(resourcePath, &gnudiffPath_);
   resolvePath(resourcePath, &gnugrepPath_);
   resolvePath(resourcePath, &msysSshPath_);
   resolvePath(resourcePath, &sumatraPath_);
#endif
   resolvePath(resourcePath, &hunspellDictionariesPath_);
   resolvePath(resourcePath, &mathjaxPath_);

   // shared secret with parent
   secret_ = core::system::getenv("RS_SHARED_SECRET");
   /* SECURITY: Need RS_SHARED_SECRET to be available to
      rpostback. However, we really ought to communicate
      it in a more secure manner than this, at least on
      Windows where even within the same user session some
      processes can have different priviliges (integrity
      levels) than others. For example, using a named pipe
      with proper SACL to retrieve the shared secret, where
      the name of the pipe is in an environment variable. */
   //core::system::unsetenv("RS_SHARED_SECRET");

   // initial working dir override
   initialWorkingDirOverride_ = core::system::getenv("RS_INITIAL_WD");
   core::system::unsetenv("RS_INITIAL_WD");

   // initial environment file override
   initialEnvironmentFileOverride_ = core::system::getenv("RS_INITIAL_ENV");
   core::system::unsetenv("RS_INITIAL_ENV");

   // initial project
   initialProjectPath_ = core::system::getenv("RS_INITIAL_PROJECT");
   core::system::unsetenv("RS_INITIAL_PROJECT");

   // limit rpc client uid
   limitRpcClientUid_ = -1;
   std::string limitUid = core::system::getenv(kRStudioLimitRpcClientUid);
   if (!limitUid.empty())
   {
      limitRpcClientUid_ = core::safe_convert::stringTo<int>(limitUid, -1);
      core::system::unsetenv(kRStudioLimitRpcClientUid);
   }

   // return status
   return status;
}

} // namespace session
<|endoftext|>


<commit_before>/**
 * @file device_properties.hpp
 *
 * @brief Classes for holding CUDA device properties and
 * CUDA compute capability values.
 *
 */
#pragma once
#ifndef CUDA_API_WRAPPERS_DEVICE_PROPERTIES_HPP_
#define CUDA_API_WRAPPERS_DEVICE_PROPERTIES_HPP_

#include <cuda/api/types.hpp>
#include <cuda/api/constants.hpp>
#include <cuda/api/pci_id.hpp>

#include <cuda_runtime_api.h>

namespace cuda {

namespace device {

/**
 * A numeric designator of an architectural generation of CUDA devices
 *
 * @note See @url https://en.wikipedia.org/wiki/Volta_(microarchitecture)
 * and previous architectures' pages via "previous" links.
 * Also see @ref compute_capability_t .
 */
struct compute_architecture_t {
	/**
	 * A @ref compute_capability_t has a "major" and a "minor" number,
	 * with "major" indicating the architecture; so this struct only
	 * has a "major" numner
	 */
	unsigned major;

	static const char* name(unsigned major_compute_capability_version);
	unsigned max_warp_schedulings_per_processor_cycle() const;
	unsigned max_resident_warps_per_processor() const;
	unsigned max_in_flight_threads_per_processor() const;
	/**
	 * @note On some architectures, the shared memory / L1 balance is configurable,
	 * so you might not get the maxima here without making this configuration
	 * setting
	 */
	memory::shared::size_t max_shared_memory_per_block() const;
	const char* name() const { return name(major); }

	bool is_valid() const noexcept
	{
		return (major > 0) and (major < 9999); // Picked this up from the CUDA code somwhere
	}

};

inline bool operator ==(const compute_architecture_t& lhs, const compute_architecture_t& rhs) noexcept
{
	return lhs.major == rhs.major;
}
inline bool operator !=(const compute_architecture_t& lhs, const compute_architecture_t& rhs) noexcept
{
	return lhs.major != rhs.major;
}
inline bool operator <(const compute_architecture_t& lhs, const compute_architecture_t& rhs) noexcept
{
	return lhs.major < rhs.major;
}
inline bool operator <=(const compute_architecture_t& lhs, const compute_architecture_t& rhs) noexcept
{
	return lhs.major < rhs.major;
}
inline bool operator >(const compute_architecture_t& lhs, const compute_architecture_t& rhs) noexcept
{
	return lhs.major > rhs.major;
}
inline bool operator >=(const compute_architecture_t& lhs, const compute_architecture_t& rhs) noexcept
{
	return lhs.major > rhs.major;
}

// TODO: Consider making this a non-POD struct,
// with a proper ctor checking validity, an operator converting to pair etc;
// however, that would require including at least std::utility, if not other
// stuff (e.g. for an std::hash specialization)
// TODO: If we constrained this to versions we know about, we could make the
// methods noexcept
/**
 * A numeric designator of the computational capabilities of a CUDA device
 *
 * @note See @url https://en.wikipedia.org/wiki/CUDA#Version_features_and_specifications
 * for a specification of capabilities by CC values
 */
struct compute_capability_t {

	compute_architecture_t architecture;
	unsigned minor_;

	unsigned as_combined_number() const noexcept { return major() * 10 + minor_; }
	unsigned max_warp_schedulings_per_processor_cycle() const;
	unsigned max_resident_warps_per_processor() const;
	unsigned max_in_flight_threads_per_processor() const;
	/**
	 * @note On some architectures, the shared memory / L1 balance is configurable,
	 * so you might not get the maxima here without making this configuration
	 * setting
	 */
	memory::shared::size_t max_shared_memory_per_block() const;

	unsigned major() const { return architecture.major; }

	// We don't really need this method, but it allows for the same access pattern as for the
	// major number, i.e. major() and minor(). Alternatively, we could have
	// used a proxy
	unsigned minor() const { return minor_; }

	bool is_valid() const
	{
		return (major() > 0) and (major() < 9999) and (minor_ > 0) and (minor_ < 9999);
			// Picked this up from the CUDA code somwhere
	}

	static compute_capability_t from_combined_number(unsigned combined)
	{
		return  { combined / 10, combined % 10 };
	}
};

inline bool operator ==(const compute_capability_t& lhs, const compute_capability_t& rhs) noexcept
{
	return lhs.major() == rhs.major() and lhs.minor_ == rhs.minor_;
}
inline bool operator !=(const compute_capability_t& lhs, const compute_capability_t& rhs) noexcept
{
	return lhs.major() != rhs.major() or lhs.minor_ != rhs.minor_;
}
inline bool operator <(const compute_capability_t& lhs, const compute_capability_t& rhs) noexcept
{
	return lhs.major() < rhs.major() or (lhs.major() == rhs.major() and lhs.minor_ < rhs.minor_);
}
inline bool operator <=(const compute_capability_t& lhs, const compute_capability_t& rhs) noexcept
{
	return lhs.major() < rhs.major() or (lhs.major() == rhs.major() and lhs.minor_ <= rhs.minor_);
}
inline bool operator >(const compute_capability_t& lhs, const compute_capability_t& rhs) noexcept
{
	return lhs.major() > rhs.major() or (lhs.major() == rhs.major() and lhs.minor_ > rhs.minor_);
}
inline bool operator >=(const compute_capability_t& lhs, const compute_capability_t& rhs) noexcept
{
	return lhs.major() > rhs.major() or (lhs.major() == rhs.major() and lhs.minor_ >= rhs.minor_);
}

inline compute_capability_t make_compute_capability(unsigned combined) noexcept
{
	return compute_capability_t::from_combined_number(combined);
}

inline compute_capability_t make_compute_capability(unsigned major, unsigned minor) noexcept
{
	return { major, minor };
}

/**
 * @brief A structure holding a collection various properties of a device
 *
 * @note Somewhat annoyingly, CUDA devices have attributes, properties and flags.
 * Attributes have integral number values; properties have all sorts of values,
 * including arrays and limited-length strings (see
 * @ref cuda::device::properties_t), and flags are either binary or
 * small-finite-domain type fitting into an overall flagss value (see
 * @ref cuda::device_t::flags_t). Flags and properties are obtained all at once,
 * attributes are more one-at-a-time.
 *
 */
struct properties_t : public cudaDeviceProp {

	properties_t() = default;
	properties_t(const cudaDeviceProp& cdp) noexcept : cudaDeviceProp(cdp) { };
	properties_t(cudaDeviceProp&& cdp) noexcept : cudaDeviceProp(cdp) { };
	bool usable_for_compute() const noexcept
	{
		return computeMode != cudaComputeModeProhibited;
	}
	compute_capability_t compute_capability() const { return { (unsigned) major, (unsigned) minor }; }
	compute_architecture_t compute_architecture() const noexcept { return { (unsigned) major }; };
	pci_location_t pci_id() const noexcept { return { pciDomainID, pciBusID, pciDeviceID }; }

	unsigned long long max_in_flight_threads_on_device() const
	{
		return compute_capability().max_in_flight_threads_per_processor() * multiProcessorCount;
	}

	grid_block_dimension_t max_threads_per_block() const noexcept { return maxThreadsPerBlock; }
	grid_block_dimension_t max_warps_per_block() const noexcept { return maxThreadsPerBlock / warp_size; }
	size_t max_shared_memory_per_block() const noexcept { return sharedMemPerBlock; }
	size_t global_memory_size() const noexcept { return totalGlobalMem; }
	bool can_map_host_memory() const noexcept { return canMapHostMemory != 0; }
};

} // namespace device
} // namespace cuda

#endif // CUDA_API_WRAPPERS_DEVICE_PROPERTIES_HPP_
<commit_msg>Trying to avoid a GNU C Library warning about the use of identifier named `major` and `minor`.<commit_after>/**
 * @file device_properties.hpp
 *
 * @brief Classes for holding CUDA device properties and
 * CUDA compute capability values.
 *
 */
#pragma once
#ifndef CUDA_API_WRAPPERS_DEVICE_PROPERTIES_HPP_
#define CUDA_API_WRAPPERS_DEVICE_PROPERTIES_HPP_

#include <cuda/api/types.hpp>
#include <cuda/api/constants.hpp>
#include <cuda/api/pci_id.hpp>

#include <cuda_runtime_api.h>

// The following un-definitions avoid warnings about
// the use of `major` and `minor` in certain versions
// of the GNU C library
#ifdef major
#undef major
#endif

#ifdef minor
#undef minor
#endif

namespace cuda {

namespace device {

/**
 * A numeric designator of an architectural generation of CUDA devices
 *
 * @note See @url https://en.wikipedia.org/wiki/Volta_(microarchitecture)
 * and previous architectures' pages via "previous" links.
 * Also see @ref compute_capability_t .
 */
struct compute_architecture_t {
	/**
	 * A @ref compute_capability_t has a "major" and a "minor" number,
	 * with "major" indicating the architecture; so this struct only
	 * has a "major" numner
	 */
	unsigned major;

	static const char* name(unsigned major_compute_capability_version);
	unsigned max_warp_schedulings_per_processor_cycle() const;
	unsigned max_resident_warps_per_processor() const;
	unsigned max_in_flight_threads_per_processor() const;
	/**
	 * @note On some architectures, the shared memory / L1 balance is configurable,
	 * so you might not get the maxima here without making this configuration
	 * setting
	 */
	memory::shared::size_t max_shared_memory_per_block() const;
	const char* name() const { return name(major); }

	bool is_valid() const noexcept
	{
		return (major > 0) and (major < 9999); // Picked this up from the CUDA code somwhere
	}

};

inline bool operator ==(const compute_architecture_t& lhs, const compute_architecture_t& rhs) noexcept
{
	return lhs.major == rhs.major;
}
inline bool operator !=(const compute_architecture_t& lhs, const compute_architecture_t& rhs) noexcept
{
	return lhs.major != rhs.major;
}
inline bool operator <(const compute_architecture_t& lhs, const compute_architecture_t& rhs) noexcept
{
	return lhs.major < rhs.major;
}
inline bool operator <=(const compute_architecture_t& lhs, const compute_architecture_t& rhs) noexcept
{
	return lhs.major < rhs.major;
}
inline bool operator >(const compute_architecture_t& lhs, const compute_architecture_t& rhs) noexcept
{
	return lhs.major > rhs.major;
}
inline bool operator >=(const compute_architecture_t& lhs, const compute_architecture_t& rhs) noexcept
{
	return lhs.major > rhs.major;
}

// TODO: Consider making this a non-POD struct,
// with a proper ctor checking validity, an operator converting to pair etc;
// however, that would require including at least std::utility, if not other
// stuff (e.g. for an std::hash specialization)
// TODO: If we constrained this to versions we know about, we could make the
// methods noexcept
/**
 * A numeric designator of the computational capabilities of a CUDA device
 *
 * @note See @url https://en.wikipedia.org/wiki/CUDA#Version_features_and_specifications
 * for a specification of capabilities by CC values
 */
struct compute_capability_t {

	compute_architecture_t architecture;
	unsigned minor_;

	unsigned as_combined_number() const noexcept { return major() * 10 + minor_; }
	unsigned max_warp_schedulings_per_processor_cycle() const;
	unsigned max_resident_warps_per_processor() const;
	unsigned max_in_flight_threads_per_processor() const;
	/**
	 * @note On some architectures, the shared memory / L1 balance is configurable,
	 * so you might not get the maxima here without making this configuration
	 * setting
	 */
	memory::shared::size_t max_shared_memory_per_block() const;

	unsigned major() const { return architecture.major; }

	// We don't really need this method, but it allows for the same access pattern as for the
	// major number, i.e. major() and minor(). Alternatively, we could have
	// used a proxy
	unsigned minor() const { return minor_; }

	bool is_valid() const
	{
		return (major() > 0) and (major() < 9999) and (minor_ > 0) and (minor_ < 9999);
			// Picked this up from the CUDA code somwhere
	}

	static compute_capability_t from_combined_number(unsigned combined)
	{
		return  { combined / 10, combined % 10 };
	}
};

inline bool operator ==(const compute_capability_t& lhs, const compute_capability_t& rhs) noexcept
{
	return lhs.major() == rhs.major() and lhs.minor_ == rhs.minor_;
}
inline bool operator !=(const compute_capability_t& lhs, const compute_capability_t& rhs) noexcept
{
	return lhs.major() != rhs.major() or lhs.minor_ != rhs.minor_;
}
inline bool operator <(const compute_capability_t& lhs, const compute_capability_t& rhs) noexcept
{
	return lhs.major() < rhs.major() or (lhs.major() == rhs.major() and lhs.minor_ < rhs.minor_);
}
inline bool operator <=(const compute_capability_t& lhs, const compute_capability_t& rhs) noexcept
{
	return lhs.major() < rhs.major() or (lhs.major() == rhs.major() and lhs.minor_ <= rhs.minor_);
}
inline bool operator >(const compute_capability_t& lhs, const compute_capability_t& rhs) noexcept
{
	return lhs.major() > rhs.major() or (lhs.major() == rhs.major() and lhs.minor_ > rhs.minor_);
}
inline bool operator >=(const compute_capability_t& lhs, const compute_capability_t& rhs) noexcept
{
	return lhs.major() > rhs.major() or (lhs.major() == rhs.major() and lhs.minor_ >= rhs.minor_);
}

inline compute_capability_t make_compute_capability(unsigned combined) noexcept
{
	return compute_capability_t::from_combined_number(combined);
}

inline compute_capability_t make_compute_capability(unsigned major, unsigned minor) noexcept
{
	return { major, minor };
}

/**
 * @brief A structure holding a collection various properties of a device
 *
 * @note Somewhat annoyingly, CUDA devices have attributes, properties and flags.
 * Attributes have integral number values; properties have all sorts of values,
 * including arrays and limited-length strings (see
 * @ref cuda::device::properties_t), and flags are either binary or
 * small-finite-domain type fitting into an overall flagss value (see
 * @ref cuda::device_t::flags_t). Flags and properties are obtained all at once,
 * attributes are more one-at-a-time.
 *
 */
struct properties_t : public cudaDeviceProp {

	properties_t() = default;
	properties_t(const cudaDeviceProp& cdp) noexcept : cudaDeviceProp(cdp) { };
	properties_t(cudaDeviceProp&& cdp) noexcept : cudaDeviceProp(cdp) { };
	bool usable_for_compute() const noexcept
	{
		return computeMode != cudaComputeModeProhibited;
	}
	compute_capability_t compute_capability() const { return { (unsigned) major, (unsigned) minor }; }
	compute_architecture_t compute_architecture() const noexcept { return { (unsigned) major }; };
	pci_location_t pci_id() const noexcept { return { pciDomainID, pciBusID, pciDeviceID }; }

	unsigned long long max_in_flight_threads_on_device() const
	{
		return compute_capability().max_in_flight_threads_per_processor() * multiProcessorCount;
	}

	grid_block_dimension_t max_threads_per_block() const noexcept { return maxThreadsPerBlock; }
	grid_block_dimension_t max_warps_per_block() const noexcept { return maxThreadsPerBlock / warp_size; }
	size_t max_shared_memory_per_block() const noexcept { return sharedMemPerBlock; }
	size_t global_memory_size() const noexcept { return totalGlobalMem; }
	bool can_map_host_memory() const noexcept { return canMapHostMemory != 0; }
};

} // namespace device
} // namespace cuda

#endif // CUDA_API_WRAPPERS_DEVICE_PROPERTIES_HPP_
<|endoftext|>


<commit_before>/*
    L3G4200D implementation based on code by:
    * http://bildr.org/2011/06/l3g4200d-arduino/
    * http://www.pieter-jan.com/node/7
    * https://github.com/sparkfun/Tri-Axis_Gyro_Breakout-L3G4200D
*/
#include "GY50.hpp"
#include "../../../utilities/Utilities.hpp"

namespace
{
const uint8_t kGyroAddress      = 105;
const auto kMeasurementInterval = 100;
const float kGyroSensitivity    = 0.07f;
const int kGyroThreshold        = 12; // Smaller changes are to be ignored
} // namespace

using namespace smartcarlib::utils;
using namespace smartcarlib::constants::gy50;

GY50::GY50(int offset, unsigned long samplingInterval, Runtime& runtime)
    : kOffset{ offset }
    , kSamplingInterval{ samplingInterval }
    , mRuntime(runtime)
    , mPreviousSample{ 0 }
    , mAttached{ false }
    , mAngularDisplacement{ 0 }
{
}

int GY50::getHeading()
{
    // Get the reading from (-180,180) to [0, 360) scale
    auto normalizedReading = static_cast<int>(mAngularDisplacement) % 360;
    return normalizedReading < 0 ? normalizedReading + 360 : normalizedReading;
}

void GY50::update()
{
    unsigned long currentTime = mRuntime.currentTimeMillis();
    unsigned long interval    = currentTime - mPreviousSample;
    if (interval <= kSamplingInterval)
    {
        return; // Not the time to read yet
    }

    int drift = kOffset - getAngularVelocity();

    if (getAbsolute(drift) > kGyroThreshold)
    {
        float gyroRate = drift * kGyroSensitivity;
        mAngularDisplacement += gyroRate / (1000.0 / interval);
    }
    mPreviousSample = currentTime;
}

void GY50::attach()
{
    if (mAttached)
    {
        return;
    }

    static const uint8_t controlRegister1 = 0x20;
    static const uint8_t controlRegister2 = 0x21;
    static const uint8_t controlRegister3 = 0x22;
    static const uint8_t controlRegister4 = 0x23;
    static const uint8_t controlRegister5 = 0x24;

    mRuntime.i2cInit();
    // Enable z and turn off power down
    writeL3G4200DRegister(controlRegister1, 0b00001100);
    // If you'd like to adjust/use the HPF, you can edit the line below to configure CTRL_REG2
    writeL3G4200DRegister(controlRegister2, 0b00000000);
    // Configure CTRL_REG3 to generate data ready interrupt on INT2
    // No interrupts used on INT1, if you'd like to configure INT1
    // or INT2 otherwise, consult the datasheet
    writeL3G4200DRegister(controlRegister3, 0b00001000);
    // CTRL_REG4 controls the full-scale range, among other things
    writeL3G4200DRegister(controlRegister4, 0b00110000);
    // CTRL_REG5 controls high-pass filtering of outputs, use it if you'd like
    writeL3G4200DRegister(controlRegister5, 0b00000000);

    mAttached = true;
}

int GY50::getOffset(int measurements)
{
    if (measurements <= 0)
    {
        return kError;
    }

    long sum = 0;
    for (auto i = 0; i < measurements; i++)
    {
        sum += getAngularVelocity();
        mRuntime.delayMillis(kMeasurementInterval);
    }

    return sum / measurements;
}

int GY50::getAngularVelocity()
{
    attach();

    static const uint8_t zAxisFirstByteRegister  = 0x2D;
    static const uint8_t zAxisSecondByteRegister = 0x2C;

    auto firstByte = readL3G4200DRegister(zAxisFirstByteRegister);
    // Serial.println("Bytes:");
    // Serial.println(firstByte, BIN);
    auto secondByte = readL3G4200DRegister(zAxisSecondByteRegister);

    return static_cast<int16_t>((firstByte << 8) | secondByte);
}

int GY50::readL3G4200DRegister(uint8_t registerAddress)
{
    mRuntime.i2cBeginTransmission(kGyroAddress);
    mRuntime.i2cWrite(registerAddress);
    mRuntime.i2cEndTransmission();
    mRuntime.i2cRequestFrom(kGyroAddress, 1);

    return mRuntime.i2cAvailable() ? mRuntime.i2cRead() : 0;
}

void GY50::writeL3G4200DRegister(uint8_t registerAddress, uint8_t value)
{
    mRuntime.i2cBeginTransmission(kGyroAddress);
    mRuntime.i2cWrite(registerAddress);
    mRuntime.i2cWrite(value);
    mRuntime.i2cEndTransmission();
}
<commit_msg>Remove unnecessary comments<commit_after>/*
    L3G4200D implementation based on code by:
    * http://bildr.org/2011/06/l3g4200d-arduino/
    * http://www.pieter-jan.com/node/7
    * https://github.com/sparkfun/Tri-Axis_Gyro_Breakout-L3G4200D
*/
#include "GY50.hpp"
#include "../../../utilities/Utilities.hpp"

namespace
{
const uint8_t kGyroAddress      = 105;
const auto kMeasurementInterval = 100;
const float kGyroSensitivity    = 0.07f;
const int kGyroThreshold        = 12; // Smaller changes are to be ignored
} // namespace

using namespace smartcarlib::utils;
using namespace smartcarlib::constants::gy50;

GY50::GY50(int offset, unsigned long samplingInterval, Runtime& runtime)
    : kOffset{ offset }
    , kSamplingInterval{ samplingInterval }
    , mRuntime(runtime)
    , mPreviousSample{ 0 }
    , mAttached{ false }
    , mAngularDisplacement{ 0 }
{
}

int GY50::getHeading()
{
    // Get the reading from (-180,180) to [0, 360) scale
    auto normalizedReading = static_cast<int>(mAngularDisplacement) % 360;
    return normalizedReading < 0 ? normalizedReading + 360 : normalizedReading;
}

void GY50::update()
{
    unsigned long currentTime = mRuntime.currentTimeMillis();
    unsigned long interval    = currentTime - mPreviousSample;
    if (interval <= kSamplingInterval)
    {
        return; // Not the time to read yet
    }

    int drift = kOffset - getAngularVelocity();

    if (getAbsolute(drift) > kGyroThreshold)
    {
        float gyroRate = drift * kGyroSensitivity;
        mAngularDisplacement += gyroRate / (1000.0 / interval);
    }
    mPreviousSample = currentTime;
}

void GY50::attach()
{
    if (mAttached)
    {
        return;
    }

    static const uint8_t controlRegister1 = 0x20;
    static const uint8_t controlRegister2 = 0x21;
    static const uint8_t controlRegister3 = 0x22;
    static const uint8_t controlRegister4 = 0x23;
    static const uint8_t controlRegister5 = 0x24;

    mRuntime.i2cInit();
    // Enable z and turn off power down
    writeL3G4200DRegister(controlRegister1, 0b00001100);
    // If you'd like to adjust/use the HPF, you can edit the line below to configure CTRL_REG2
    writeL3G4200DRegister(controlRegister2, 0b00000000);
    // Configure CTRL_REG3 to generate data ready interrupt on INT2
    // No interrupts used on INT1, if you'd like to configure INT1
    // or INT2 otherwise, consult the datasheet
    writeL3G4200DRegister(controlRegister3, 0b00001000);
    // CTRL_REG4 controls the full-scale range, among other things
    writeL3G4200DRegister(controlRegister4, 0b00110000);
    // CTRL_REG5 controls high-pass filtering of outputs, use it if you'd like
    writeL3G4200DRegister(controlRegister5, 0b00000000);

    mAttached = true;
}

int GY50::getOffset(int measurements)
{
    if (measurements <= 0)
    {
        return kError;
    }

    long sum = 0;
    for (auto i = 0; i < measurements; i++)
    {
        sum += getAngularVelocity();
        mRuntime.delayMillis(kMeasurementInterval);
    }

    return sum / measurements;
}

int GY50::getAngularVelocity()
{
    attach();

    static const uint8_t zAxisFirstByteRegister  = 0x2D;
    static const uint8_t zAxisSecondByteRegister = 0x2C;

    auto firstByte  = readL3G4200DRegister(zAxisFirstByteRegister);
    auto secondByte = readL3G4200DRegister(zAxisSecondByteRegister);

    return static_cast<int16_t>((firstByte << 8) | secondByte);
}

int GY50::readL3G4200DRegister(uint8_t registerAddress)
{
    mRuntime.i2cBeginTransmission(kGyroAddress);
    mRuntime.i2cWrite(registerAddress);
    mRuntime.i2cEndTransmission();
    mRuntime.i2cRequestFrom(kGyroAddress, 1);

    return mRuntime.i2cAvailable() ? mRuntime.i2cRead() : 0;
}

void GY50::writeL3G4200DRegister(uint8_t registerAddress, uint8_t value)
{
    mRuntime.i2cBeginTransmission(kGyroAddress);
    mRuntime.i2cWrite(registerAddress);
    mRuntime.i2cWrite(value);
    mRuntime.i2cEndTransmission();
}
<|endoftext|>


<commit_before>/*************************************************************************
*    UrBackup - Client/Server backup system
*    Copyright (C) 2011  Martin Raiber
*
*    This program is free software: you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/

#ifndef CLIENT_ONLY

#include "server_writer.h"
#include "../Interface/Mutex.h"
#include "../Interface/Condition.h"
#include "../Interface/Server.h"
#include "../fsimageplugin/IVHDFile.h"
#include "../fsimageplugin/IFSImageFactory.h"
#include "../stringtools.h"
#include "os_functions.h"
#include "server_log.h"
#include "server_cleanup.h"

extern IFSImageFactory *image_fak;
const size_t free_space_lim=1000*1024*1024; //1000MB
const uint64 filebuf_lim=1000*1024*1024; //1000MB

ServerVHDWriter::ServerVHDWriter(IVHDFile *pVHD, unsigned int blocksize, unsigned int nbufs, int pClientid)
{
	filebuffer=true;

	clientid=pClientid;
	vhd=pVHD;
	bufmgr=new CBufMgr2(nbufs, blocksize);

	if(filebuffer)
	{
		filebuf=new CFileBufMgr(500, false);
		filebuf_writer=new ServerFileBufferWriter(this, blocksize);
		filebuf_writer_ticket=Server->getThreadPool()->execute(filebuf_writer);
		currfile=filebuf->getBuffer();
		currfile_size=0;
	}

	mutex=Server->createMutex();
	vhd_mutex=Server->createMutex();
	cond=Server->createCondition();
	exit=false;
	exit_now=false;
	has_error=false;
	written=free_space_lim;
}

ServerVHDWriter::~ServerVHDWriter(void)
{
	delete bufmgr;

	if(filebuffer)
	{
		delete filebuf_writer;
		delete filebuf;
	}

	Server->destroy(mutex);
	Server->destroy(vhd_mutex);
	Server->destroy(cond);
}

void ServerVHDWriter::operator()(void)
{
	{
		while(!exit_now)
		{
			BufferVHDItem item;
			bool has_item=false;
			bool do_exit;
			{
				IScopedLock lock(mutex);
				do_exit=exit;
				if(tqueue.empty() && exit==false)
					cond->wait(&lock);
				if(!tqueue.empty())
				{
					item=tqueue.front();
					tqueue.pop();
					has_item=true;
				}
			}
			if(has_item)
			{
				if(!has_error)
				{
					if(!filebuffer)
					{
						writeVHD(item.pos, item.buf, item.bsize);
					}
					else
					{
						FileBufferVHDItem fbi;
						fbi.pos=item.pos;
						fbi.bsize=item.bsize;
						writeRetry(currfile, (char*)&fbi, sizeof(FileBufferVHDItem));
						currfile_size+=sizeof(FileBufferVHDItem);
						writeRetry(currfile, item.buf, item.bsize);
						currfile_size+=item.bsize;

						if(currfile_size>filebuf_lim)
						{
							filebuf_writer->writeBuffer(currfile);
							currfile=filebuf->getBuffer();
							currfile_size=0;
						}
					}
				}

				bufmgr->releaseBuffer(item.buf);
			}
			else if(do_exit)
			{
				break;
			}

			if(!filebuffer && written>=free_space_lim/2)
			{
				written=0;
				checkFreeSpaceAndCleanup();
			}
		}
	}
	if(filebuf)
	{
		filebuf_writer->writeBuffer(currfile);

		if(!exit_now)
			filebuf_writer->doExit();
		else
			filebuf_writer->doExitNow();

		Server->getThreadPool()->waitFor(filebuf_writer_ticket);
	}

	image_fak->destroyVHDFile(vhd);
}

void ServerVHDWriter::checkFreeSpaceAndCleanup(void)
{
	std::wstring p;
	{
		IScopedLock lock(vhd_mutex);
		p=ExtractFilePath(vhd->getFilename());
	}
	int64 fs=os_free_space(os_file_prefix()+p);
	if(fs!=-1 && fs <= free_space_lim )
	{
		Server->Log("Not enough free space. Waiting for cleanup...");
		if(!cleanupSpace())
		{
			Server->Log("Not enough free space.", LL_WARNING);
		}
	}
}

void ServerVHDWriter::writeVHD(uint64 pos, char *buf, unsigned int bsize)
{
	IScopedLock lock(vhd_mutex);
	vhd->Seek(pos);
	bool b=vhd->Write(buf, bsize);
	written+=bsize;
	if(!b)
	{
		std::wstring p=ExtractFilePath(vhd->getFilename());
		int64 fs=os_free_space(os_file_prefix()+p);
		if(fs!=-1 && fs <= free_space_lim )
		{
			Server->Log("Not enough free space. Waiting for cleanup...");
			if(cleanupSpace())
			{
				if(!vhd->Write(buf, bsize))
				{
					ServerLogger::Log(clientid, "FATAL: Writing failed after cleanup", LL_ERROR);
					has_error=true;
				}
			}
			else
			{
				has_error=true;
				Server->Log("FATAL: NOT ENOUGH free space. Cleanup failed.", LL_ERROR);
			}
		}
		else
		{
			has_error=true;
			ServerLogger::Log(clientid, "FATAL: Error writing to VHD-File.", LL_ERROR);
		}
	}
}

char *ServerVHDWriter::getBuffer(void)
{
	return bufmgr->getBuffer();
}

void ServerVHDWriter::writeBuffer(uint64 pos, char *buf, unsigned int bsize)
{
	IScopedLock lock(mutex);
	BufferVHDItem item;
	item.pos=pos;
	item.buf=buf;
	item.bsize=bsize;
	tqueue.push(item);
	cond->notify_all();
}

void ServerVHDWriter::freeBuffer(char *buf)
{
	bufmgr->releaseBuffer(buf);
}

void ServerVHDWriter::doExit(void)
{
	IScopedLock lock(mutex);
	exit=true;
	finish=true;
	cond->notify_all();
}

void ServerVHDWriter::doExitNow(void)
{
	IScopedLock lock(mutex);
	exit=true;
	exit_now=true;
	finish=true;
	cond->notify_all();
}

void ServerVHDWriter::doFinish(void)
{
	IScopedLock lock(mutex);
	finish=true;
	cond->notify_all();
}

size_t ServerVHDWriter::getQueueSize(void)
{
	IScopedLock lock(mutex);
	return tqueue.size();
}

bool ServerVHDWriter::hasError(void)
{
	return has_error;
}

IMutex * ServerVHDWriter::getVHDMutex(void)
{
	return vhd_mutex;
}

IVHDFile* ServerVHDWriter::getVHD(void)
{
	return vhd;
}

bool ServerVHDWriter::cleanupSpace(void)
{
	ServerLogger::Log(clientid, "Not enough free space. Cleaning up.", LL_INFO);
	ServerCleanupThread cleanup;
	if(!cleanup.do_cleanup(free_space_lim) )
	{
		ServerLogger::Log(clientid, "Could not free space for image. NOT ENOUGH FREE SPACE.", LL_ERROR);
		return false;
	}
	return true;
}

void ServerVHDWriter::freeFile(IFile *buf)
{
	filebuf->releaseBuffer(buf);
}

void ServerVHDWriter::writeRetry(IFile *f, char *buf, unsigned int bsize)
{
	unsigned int off=0;
	unsigned int r;
	while( (r=f->Write(buf+off, bsize-off))!=bsize-off)
	{
		off+=r;
		Server->Log("Error writing to file \""+f->getFilename()+"\". Retrying", LL_WARNING);
		Server->wait(10000);
	}
}

ServerFileBufferWriter::ServerFileBufferWriter(ServerVHDWriter *pParent, unsigned int pBlocksize) : parent(pParent), blocksize(pBlocksize)
{
	mutex=Server->createMutex();
	cond=Server->createCondition();
	exit=false;
	exit_now=false;
	written=free_space_lim;
}

ServerFileBufferWriter::~ServerFileBufferWriter(void)
{
	while(!fb_queue.empty())
	{
		parent->freeFile(fb_queue.front());
		fb_queue.pop();
	}
	Server->destroy(mutex);
	Server->destroy(cond);
}

void ServerFileBufferWriter::operator()(void)
{
	char *blockbuf=new char[blocksize];
	unsigned int blockbuf_size=blocksize;

	while(!exit_now)
	{
		IFile* tmp;
		bool has_item=false;
		{
			IScopedLock lock(mutex);
			while(fb_queue.empty() && exit==false)
			{
				cond->wait(&lock);
			}
			if(!fb_queue.empty())
			{
				has_item=true;
				tmp=fb_queue.front();
				fb_queue.pop();
			}
		}

		if(has_item)
		{
			tmp->Seek(0);
			uint64 tpos=0;
			uint64 tsize=tmp->Size();
			while(tpos<tsize)
			{
				if(!parent->hasError())
				{
					FileBufferVHDItem item;
					if(tmp->Read((char*)&item, sizeof(FileBufferVHDItem))!=sizeof(FileBufferVHDItem))
					{
						Server->Log("Error reading FileBufferVHDItem", LL_ERROR);
					}
					tpos+=sizeof(FileBufferVHDItem);
					unsigned int tw=item.bsize;
					if(tpos+tw>tsize)
					{
						Server->Log("Size field is wrong", LL_ERROR);
					}
					if(tw>blockbuf_size)
					{
						delete []blockbuf;
						blockbuf=new char[tw];
						blockbuf_size=tw;
					}
					if(tmp->Read(blockbuf, tw)!=tw)
					{
						Server->Log("Error reading from tmp.f", LL_ERROR);
					}
					parent->writeVHD(item.pos, blockbuf, tw);
					written+=tw;

					tpos+=item.bsize;

					if( written>=free_space_lim/2)
					{
						written=0;
						parent->checkFreeSpaceAndCleanup();
					}
				}
				else
				{
					break;
				}
			}
			parent->freeFile(tmp);
		}
		else if(exit)
		{
			break;
		}
	}

	delete []blockbuf;
}

void ServerFileBufferWriter::doExit(void)
{
	exit=true;
	cond->notify_all();
}

void ServerFileBufferWriter::doExitNow(void)
{
	exit_now=true;
	exit=true;
	cond->notify_all();
}

void ServerFileBufferWriter::writeBuffer(IFile *buf)
{
	IScopedLock lock(mutex);
	fb_queue.push(buf);
	cond->notify_all();
}

#endif //CLIENT_ONLY<commit_msg>Sped up imaging<commit_after>/*************************************************************************
*    UrBackup - Client/Server backup system
*    Copyright (C) 2011  Martin Raiber
*
*    This program is free software: you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/

#ifndef CLIENT_ONLY

#include "server_writer.h"
#include "../Interface/Mutex.h"
#include "../Interface/Condition.h"
#include "../Interface/Server.h"
#include "../fsimageplugin/IVHDFile.h"
#include "../fsimageplugin/IFSImageFactory.h"
#include "../stringtools.h"
#include "os_functions.h"
#include "server_log.h"
#include "server_cleanup.h"

extern IFSImageFactory *image_fak;
const size_t free_space_lim=1000*1024*1024; //1000MB
const uint64 filebuf_lim=1000*1024*1024; //1000MB

ServerVHDWriter::ServerVHDWriter(IVHDFile *pVHD, unsigned int blocksize, unsigned int nbufs, int pClientid)
{
	filebuffer=true;

	clientid=pClientid;
	vhd=pVHD;
	if(filebuffer)
	{
		bufmgr=new CBufMgr2(nbufs, sizeof(FileBufferVHDItem)+blocksize);
	}
	else
	{
		bufmgr=new CBufMgr2(nbufs, blocksize);
	}

	if(filebuffer)
	{
		filebuf=new CFileBufMgr(500, false);
		filebuf_writer=new ServerFileBufferWriter(this, blocksize);
		filebuf_writer_ticket=Server->getThreadPool()->execute(filebuf_writer);
		currfile=filebuf->getBuffer();
		currfile_size=0;
	}

	mutex=Server->createMutex();
	vhd_mutex=Server->createMutex();
	cond=Server->createCondition();
	exit=false;
	exit_now=false;
	has_error=false;
	written=free_space_lim;
}

ServerVHDWriter::~ServerVHDWriter(void)
{
	delete bufmgr;

	if(filebuffer)
	{
		delete filebuf_writer;
		delete filebuf;
	}

	Server->destroy(mutex);
	Server->destroy(vhd_mutex);
	Server->destroy(cond);
}

void ServerVHDWriter::operator()(void)
{
	{
		while(!exit_now)
		{
			BufferVHDItem item;
			bool has_item=false;
			bool do_exit;
			{
				IScopedLock lock(mutex);
				do_exit=exit;
				if(tqueue.empty() && exit==false)
					cond->wait(&lock);
				if(!tqueue.empty())
				{
					item=tqueue.front();
					tqueue.pop();
					has_item=true;
				}
			}
			if(has_item)
			{
				if(!has_error)
				{
					if(!filebuffer)
					{
						writeVHD(item.pos, item.buf, item.bsize);
					}
					else
					{
						FileBufferVHDItem *fbi=(FileBufferVHDItem*)(item.buf-sizeof(FileBufferVHDItem));
						fbi->pos=item.pos;
						fbi->bsize=item.bsize;
						writeRetry(currfile, (char*)fbi, sizeof(FileBufferVHDItem)+item.bsize);
						currfile_size+=item.bsize+sizeof(FileBufferVHDItem);

						if(currfile_size>filebuf_lim)
						{
							filebuf_writer->writeBuffer(currfile);
							currfile=filebuf->getBuffer();
							currfile_size=0;
						}
					}
				}

				freeBuffer(item.buf);
			}
			else if(do_exit)
			{
				break;
			}

			if(!filebuffer && written>=free_space_lim/2)
			{
				written=0;
				checkFreeSpaceAndCleanup();
			}
		}
	}
	if(filebuf)
	{
		filebuf_writer->writeBuffer(currfile);

		if(!exit_now)
			filebuf_writer->doExit();
		else
			filebuf_writer->doExitNow();

		Server->getThreadPool()->waitFor(filebuf_writer_ticket);
	}

	image_fak->destroyVHDFile(vhd);
}

void ServerVHDWriter::checkFreeSpaceAndCleanup(void)
{
	std::wstring p;
	{
		IScopedLock lock(vhd_mutex);
		p=ExtractFilePath(vhd->getFilename());
	}
	int64 fs=os_free_space(os_file_prefix()+p);
	if(fs!=-1 && fs <= free_space_lim )
	{
		Server->Log("Not enough free space. Waiting for cleanup...");
		if(!cleanupSpace())
		{
			Server->Log("Not enough free space.", LL_WARNING);
		}
	}
}

void ServerVHDWriter::writeVHD(uint64 pos, char *buf, unsigned int bsize)
{
	IScopedLock lock(vhd_mutex);
	vhd->Seek(pos);
	bool b=vhd->Write(buf, bsize);
	written+=bsize;
	if(!b)
	{
		std::wstring p=ExtractFilePath(vhd->getFilename());
		int64 fs=os_free_space(os_file_prefix()+p);
		if(fs!=-1 && fs <= free_space_lim )
		{
			Server->Log("Not enough free space. Waiting for cleanup...");
			if(cleanupSpace())
			{
				if(!vhd->Write(buf, bsize))
				{
					ServerLogger::Log(clientid, "FATAL: Writing failed after cleanup", LL_ERROR);
					has_error=true;
				}
			}
			else
			{
				has_error=true;
				Server->Log("FATAL: NOT ENOUGH free space. Cleanup failed.", LL_ERROR);
			}
		}
		else
		{
			has_error=true;
			ServerLogger::Log(clientid, "FATAL: Error writing to VHD-File.", LL_ERROR);
		}
	}
}

char *ServerVHDWriter::getBuffer(void)
{
	if(filebuffer)
		return bufmgr->getBuffer()+sizeof(FileBufferVHDItem);
	else
		return bufmgr->getBuffer();
}

void ServerVHDWriter::writeBuffer(uint64 pos, char *buf, unsigned int bsize)
{
	IScopedLock lock(mutex);
	BufferVHDItem item;
	item.pos=pos;
	item.buf=buf;
	item.bsize=bsize;
	tqueue.push(item);
	cond->notify_all();
}

void ServerVHDWriter::freeBuffer(char *buf)
{
	if(filebuffer)
		bufmgr->releaseBuffer(buf-sizeof(FileBufferVHDItem));
	else
		bufmgr->releaseBuffer(buf);
}

void ServerVHDWriter::doExit(void)
{
	IScopedLock lock(mutex);
	exit=true;
	finish=true;
	cond->notify_all();
}

void ServerVHDWriter::doExitNow(void)
{
	IScopedLock lock(mutex);
	exit=true;
	exit_now=true;
	finish=true;
	cond->notify_all();
}

void ServerVHDWriter::doFinish(void)
{
	IScopedLock lock(mutex);
	finish=true;
	cond->notify_all();
}

size_t ServerVHDWriter::getQueueSize(void)
{
	IScopedLock lock(mutex);
	return tqueue.size();
}

bool ServerVHDWriter::hasError(void)
{
	return has_error;
}

IMutex * ServerVHDWriter::getVHDMutex(void)
{
	return vhd_mutex;
}

IVHDFile* ServerVHDWriter::getVHD(void)
{
	return vhd;
}

bool ServerVHDWriter::cleanupSpace(void)
{
	ServerLogger::Log(clientid, "Not enough free space. Cleaning up.", LL_INFO);
	ServerCleanupThread cleanup;
	if(!cleanup.do_cleanup(free_space_lim) )
	{
		ServerLogger::Log(clientid, "Could not free space for image. NOT ENOUGH FREE SPACE.", LL_ERROR);
		return false;
	}
	return true;
}

void ServerVHDWriter::freeFile(IFile *buf)
{
	filebuf->releaseBuffer(buf);
}

void ServerVHDWriter::writeRetry(IFile *f, char *buf, unsigned int bsize)
{
	unsigned int off=0;
	while( off<bsize )
	{
		unsigned int r=f->Write(buf+off, bsize-off);
		off+=r;
		if(off<bsize)
		{
			Server->Log("Error writing to file \""+f->getFilename()+"\". Retrying", LL_WARNING);
			Server->wait(10000);
		}
	}
}

//-------------FilebufferWriter-----------------

ServerFileBufferWriter::ServerFileBufferWriter(ServerVHDWriter *pParent, unsigned int pBlocksize) : parent(pParent), blocksize(pBlocksize)
{
	mutex=Server->createMutex();
	cond=Server->createCondition();
	exit=false;
	exit_now=false;
	written=free_space_lim;
}

ServerFileBufferWriter::~ServerFileBufferWriter(void)
{
	while(!fb_queue.empty())
	{
		parent->freeFile(fb_queue.front());
		fb_queue.pop();
	}
	Server->destroy(mutex);
	Server->destroy(cond);
}

void ServerFileBufferWriter::operator()(void)
{
	char *blockbuf=new char[blocksize+sizeof(FileBufferVHDItem)];
	unsigned int blockbuf_size=blocksize+sizeof(FileBufferVHDItem);

	while(!exit_now)
	{
		IFile* tmp;
		bool has_item=false;
		{
			IScopedLock lock(mutex);
			while(fb_queue.empty() && exit==false)
			{
				cond->wait(&lock);
			}
			if(!fb_queue.empty())
			{
				has_item=true;
				tmp=fb_queue.front();
				fb_queue.pop();
			}
		}

		if(has_item)
		{
			tmp->Seek(0);
			uint64 tpos=0;
			uint64 tsize=tmp->Size();
			while(tpos<tsize)
			{
				if(!parent->hasError())
				{
					unsigned int tw=blockbuf_size;
					bool old_method=false;
					if(tw<sizeof(FileBufferVHDItem) || tmp->Read(blockbuf, tw)!=tw)
					{
						old_method=true;
					}
					else
					{
						FileBufferVHDItem *item=(FileBufferVHDItem*)blockbuf;
						if(tw==item->bsize+sizeof(FileBufferVHDItem) )
						{
							parent->writeVHD(item->pos, blockbuf+sizeof(FileBufferVHDItem), item->bsize);
							written+=item->bsize;
							tpos+=item->bsize+sizeof(FileBufferVHDItem);
						}
						else
						{
							old_method=true;
						}
					}

					if(old_method==true)
					{
						tmp->Seek(tpos);
						FileBufferVHDItem item;
						if(tmp->Read((char*)&item, sizeof(FileBufferVHDItem))!=sizeof(FileBufferVHDItem))
						{
							Server->Log("Error reading FileBufferVHDItem", LL_ERROR);
						}
						tpos+=sizeof(FileBufferVHDItem);
						unsigned int tw=item.bsize;
						if(tpos+tw>tsize)
						{
							Server->Log("Size field is wrong", LL_ERROR);
						}
						if(tw>blockbuf_size)
						{
							delete []blockbuf;
							blockbuf=new char[tw+sizeof(FileBufferVHDItem)];
							blockbuf_size=tw+sizeof(FileBufferVHDItem);
						}
						if(tmp->Read(blockbuf, tw)!=tw)
						{
							Server->Log("Error reading from tmp.f", LL_ERROR);
						}
						parent->writeVHD(item.pos, blockbuf, tw);
						written+=tw;
						tpos+=item.bsize;
					}

					if( written>=free_space_lim/2)
					{
						written=0;
						parent->checkFreeSpaceAndCleanup();
					}
				}
				else
				{
					break;
				}
			}
			parent->freeFile(tmp);
		}
		else if(exit)
		{
			break;
		}
	}

	delete []blockbuf;
}

void ServerFileBufferWriter::doExit(void)
{
	exit=true;
	cond->notify_all();
}

void ServerFileBufferWriter::doExitNow(void)
{
	exit_now=true;
	exit=true;
	cond->notify_all();
}

void ServerFileBufferWriter::writeBuffer(IFile *buf)
{
	IScopedLock lock(mutex);
	fb_queue.push(buf);
	cond->notify_all();
}

#endif //CLIENT_ONLY<|endoftext|>


<commit_before>// -*- C++ -*-

// The MIT License (MIT)
//
// Copyright (c) 2021 Alexander Samoilov
//
// 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.

#include "PoissonProblem.hpp"
#include "ChebyshevDifferentiate.hpp"

PoissonProblem::PoissonProblem(size_t M,     size_t N,
                               double x_min, double x_max,
                               double y_min, double y_max,
                               bool verbose)
: verbose_(verbose),
  M_(M), N_(N),
  x_grid_(M + 1),
  y_grid_(N + 1),
  ome_   (M + 1, N + 1),
  psi_   (M + 1, N + 1),
  border_(M, N)
{
    double xa = 0.5*(x_min-x_max);
    double xb = 0.5*(x_min+x_max);
    double ya = 0.5*(y_min-y_max);
    double yb = 0.5*(y_min+y_max);

    for (size_t i = 0; i <= M_; ++i) {
        x_grid_[i] = xa*std::cos(M_PI*i/(double)M_)+xb;
    }

    for (size_t i = 0; i <= N_; ++i) {
        y_grid_[i] = ya*std::cos(M_PI*i/(double)N_)+yb;
    }

    if (verbose_) {
        std::cout << "x_grid: [" << x_grid_ << "]\n";
        std::cout << "y_grid: [" << y_grid_ << "]\n";
    }

    // zero boundary conditions
    border_.left_  = RowVectorXd::Zero(M_ + 1);
    border_.down_  = RowVectorXd::Zero(N_ + 1);
    border_.right_ = RowVectorXd::Zero(M_ + 1);
    border_.up_    = RowVectorXd::Zero(N_ + 1);

    // fill right hand function
    for (size_t i = 0; i <= M_; ++i) {
        for (size_t j = 0; j <= N_; ++j) {
            ome_(i, j) = 32.*M_PI*M_PI * std::sin(4.*M_PI*x_grid_[i])
                                       * std::sin(4.*M_PI*y_grid_[j]);
        }
    }
}

void PoissonProblem::generate_matrix(size_t n, Eigen::Ref<RowMatrixXd> ma)
{
    // TODO use MatrixXd::Identity(rows,cols)
    for (size_t i=0; i<=n; ++i) {
        for (size_t j=0; j<=n; ++j) {
            ma(i, j) = detail::id(i, j);
        }
    }
    laplacian(n, ma, ma);
}

void PoissonProblem::homogeneous_boundary(size_t n,
                                          Eigen::Ref<RowMatrixXd> in,
                                          Eigen::Ref<RowMatrixXd> out)
{
    for (size_t i=0; i<=n; i++) {
        double evens = 0.0, odds = 0.0;
        for (size_t j=1; j<=n-2; j+=2) {
            odds  -= in(i, j);
            evens -= in(i, j+1);
        }

        if (out != in) {
            for (size_t j=0; j<=n-2; j++) {
                out(i, j) = in(i, j);
            }
        }

        out(i, n-1) = odds;
        out(i, n)   = evens-in(i, 0);
/*
        out(i, 0) = out(i, 1) = 0.0;
*/
    }
}

void PoissonProblem::laplacian(size_t n,
                               Eigen::Ref<RowMatrixXd> in,
                               Eigen::Ref<RowMatrixXd> out)
{
    homogeneous_boundary(n, in, out);
    for (size_t i = 0; i <= n; ++i) {
    }
}
<commit_msg>implemented laplacian in spectral space.<commit_after>// -*- C++ -*-

// The MIT License (MIT)
//
// Copyright (c) 2021 Alexander Samoilov
//
// 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.

#include "PoissonProblem.hpp"
#include "ChebyshevDifferentiate.hpp"

PoissonProblem::PoissonProblem(size_t M,     size_t N,
                               double x_min, double x_max,
                               double y_min, double y_max,
                               bool verbose)
: verbose_(verbose),
  M_(M), N_(N),
  x_grid_(M + 1),
  y_grid_(N + 1),
  ome_   (M + 1, N + 1),
  psi_   (M + 1, N + 1),
  border_(M, N)
{
    double xa = 0.5*(x_min-x_max);
    double xb = 0.5*(x_min+x_max);
    double ya = 0.5*(y_min-y_max);
    double yb = 0.5*(y_min+y_max);

    for (size_t i = 0; i <= M_; ++i) {
        x_grid_[i] = xa*std::cos(M_PI*i/(double)M_)+xb;
    }

    for (size_t i = 0; i <= N_; ++i) {
        y_grid_[i] = ya*std::cos(M_PI*i/(double)N_)+yb;
    }

    if (verbose_) {
        std::cout << "x_grid: [" << x_grid_ << "]\n";
        std::cout << "y_grid: [" << y_grid_ << "]\n";
    }

    // zero boundary conditions
    border_.left_  = RowVectorXd::Zero(M_ + 1);
    border_.down_  = RowVectorXd::Zero(N_ + 1);
    border_.right_ = RowVectorXd::Zero(M_ + 1);
    border_.up_    = RowVectorXd::Zero(N_ + 1);

    // fill right hand function
    for (size_t i = 0; i <= M_; ++i) {
        for (size_t j = 0; j <= N_; ++j) {
            ome_(i, j) = 32.*M_PI*M_PI * std::sin(4.*M_PI*x_grid_[i])
                                       * std::sin(4.*M_PI*y_grid_[j]);
        }
    }
}

void PoissonProblem::generate_matrix(size_t n, Eigen::Ref<RowMatrixXd> ma)
{
    // TODO use MatrixXd::Identity(rows,cols)
    for (size_t i=0; i<=n; ++i) {
        for (size_t j=0; j<=n; ++j) {
            ma(i, j) = detail::id(i, j);
        }
    }
    laplacian(n, ma, ma);
}

void PoissonProblem::homogeneous_boundary(size_t n,
                                          Eigen::Ref<RowMatrixXd> in,
                                          Eigen::Ref<RowMatrixXd> out)
{
    for (size_t i=0; i<=n; i++) {
        double evens = 0.0, odds = 0.0;
        for (size_t j=1; j<=n-2; j+=2) {
            odds  -= in(i, j);
            evens -= in(i, j+1);
        }

        if (out != in) {
            for (size_t j=0; j<=n-2; j++) {
                out(i, j) = in(i, j);
            }
        }

        out(i, n-1) = odds;
        out(i, n)   = evens-in(i, 0);
/*
        out(i, 0) = out(i, 1) = 0.0;
*/
    }
}

void PoissonProblem::laplacian(size_t n,
                               Eigen::Ref<RowMatrixXd> in,
                               Eigen::Ref<RowMatrixXd> out)
{
    homogeneous_boundary(n, in, out);
    for (size_t i = 0; i <= n; ++i) {
        spectral_differentiate(n, out.row(i), out.row(i));
        spectral_differentiate(n, out.row(i), out.row(i));
    }
}
<|endoftext|>


<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.

#include "chrome/browser/extensions/extensions_service.h"

#include "base/file_util.h"
#include "base/values.h"
#include "base/string_util.h"
#include "base/thread.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/json_value_serializer.h"

// ExtensionsService

const FilePath::CharType* ExtensionsService::kInstallDirectoryName =
    FILE_PATH_LITERAL("Extensions");

ExtensionsService::ExtensionsService(const FilePath& profile_directory)
    : message_loop_(MessageLoop::current()),
      backend_(new ExtensionsServiceBackend),
      install_directory_(profile_directory.Append(kInstallDirectoryName)) {
}

ExtensionsService::~ExtensionsService() {
  for (ExtensionList::iterator iter = extensions_.begin();
       iter != extensions_.end(); ++iter) {
    delete *iter;
  }
}

bool ExtensionsService::Init() {
  // TODO(aa): This message loop should probably come from a backend
  // interface, similar to how the message loop for the frontend comes
  // from the frontend interface.
  g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
      NewRunnableMethod(backend_.get(),
          &ExtensionsServiceBackend::LoadExtensionsFromDirectory,
          install_directory_,
          scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
  // TODO(aa): Load extensions from other registered directories.

  return true;
}

MessageLoop* ExtensionsService::GetMessageLoop() {
  return message_loop_;
}

void ExtensionsService::OnExtensionsLoadedFromDirectory(
    ExtensionList* new_extensions) {
  extensions_.insert(extensions_.end(), new_extensions->begin(),
                     new_extensions->end());
  delete new_extensions;

  // TODO(aa): Notify extensions are loaded.
}

void ExtensionsService::OnExtensionLoadError(const std::string& error) {
  // TODO(aa): Print the error message out somewhere better. I think we are
  // going to need some sort of 'extension inspector'.
  LOG(WARNING) << error;
}

// ExtensionsServicesBackend

bool ExtensionsServiceBackend::LoadExtensionsFromDirectory(
    const FilePath& path,
    scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
  // Find all child directories in the install directory and load their
  // manifests. Post errors and results to the frontend.
  scoped_ptr<ExtensionList> extensions(new ExtensionList);
  file_util::FileEnumerator enumerator(path.ToWStringHack(),
                                       false, // not recursive
                                       file_util::FileEnumerator::DIRECTORIES);
  for (std::wstring child_path = enumerator.Next(); !child_path.empty();
       child_path = enumerator.Next()) {
     FilePath manifest_path = FilePath::FromWStringHack(child_path).Append(
         Extension::kManifestFilename);
    if (!file_util::PathExists(manifest_path)) {
      ReportExtensionLoadError(frontend.get(), child_path,
                               Extension::kInvalidManifestError);
      continue;
    }

    JSONFileValueSerializer serializer(manifest_path.ToWStringHack());
    Value* root = NULL;
    std::string error;
    if (!serializer.Deserialize(&root, &error)) {
      ReportExtensionLoadError(frontend.get(), child_path, error);
      continue;
    }

    scoped_ptr<Value> scoped_root(root);
    if (!root->IsType(Value::TYPE_DICTIONARY)) {
      ReportExtensionLoadError(frontend.get(), child_path,
                               Extension::kInvalidManifestError);
      continue;
    }

    scoped_ptr<Extension> extension(new Extension());
    if (!extension->InitFromValue(*static_cast<DictionaryValue*>(root),
                                  &error)) {
      ReportExtensionLoadError(frontend.get(), child_path, error);
      continue;
    }

    extensions->push_back(extension.release());
    delete root;
  }

  ReportExtensionsLoaded(frontend.get(), extensions.release());
  return true;
}

void ExtensionsServiceBackend::ReportExtensionLoadError(
    ExtensionsServiceFrontendInterface *frontend, const std::wstring& path,
    const std::string &error) {
  std::string message = StringPrintf("Could not load extension from '%s'. %s",
                                     WideToASCII(path).c_str(), error.c_str());
  frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
      frontend, &ExtensionsServiceFrontendInterface::OnExtensionLoadError,
      message));
}

void ExtensionsServiceBackend::ReportExtensionsLoaded(
    ExtensionsServiceFrontendInterface *frontend, ExtensionList* extensions) {
  frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
      frontend,
      &ExtensionsServiceFrontendInterface::OnExtensionsLoadedFromDirectory,
      extensions));
}
<commit_msg>bad merge, no need to delete a scoped_ptr<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.

#include "chrome/browser/extensions/extensions_service.h"

#include "base/file_util.h"
#include "base/values.h"
#include "base/string_util.h"
#include "base/thread.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/json_value_serializer.h"

// ExtensionsService

const FilePath::CharType* ExtensionsService::kInstallDirectoryName =
    FILE_PATH_LITERAL("Extensions");

ExtensionsService::ExtensionsService(const FilePath& profile_directory)
    : message_loop_(MessageLoop::current()),
      backend_(new ExtensionsServiceBackend),
      install_directory_(profile_directory.Append(kInstallDirectoryName)) {
}

ExtensionsService::~ExtensionsService() {
  for (ExtensionList::iterator iter = extensions_.begin();
       iter != extensions_.end(); ++iter) {
    delete *iter;
  }
}

bool ExtensionsService::Init() {
  // TODO(aa): This message loop should probably come from a backend
  // interface, similar to how the message loop for the frontend comes
  // from the frontend interface.
  g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
      NewRunnableMethod(backend_.get(),
          &ExtensionsServiceBackend::LoadExtensionsFromDirectory,
          install_directory_,
          scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
  // TODO(aa): Load extensions from other registered directories.

  return true;
}

MessageLoop* ExtensionsService::GetMessageLoop() {
  return message_loop_;
}

void ExtensionsService::OnExtensionsLoadedFromDirectory(
    ExtensionList* new_extensions) {
  extensions_.insert(extensions_.end(), new_extensions->begin(),
                     new_extensions->end());
  delete new_extensions;

  // TODO(aa): Notify extensions are loaded.
}

void ExtensionsService::OnExtensionLoadError(const std::string& error) {
  // TODO(aa): Print the error message out somewhere better. I think we are
  // going to need some sort of 'extension inspector'.
  LOG(WARNING) << error;
}

// ExtensionsServicesBackend

bool ExtensionsServiceBackend::LoadExtensionsFromDirectory(
    const FilePath& path,
    scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
  // Find all child directories in the install directory and load their
  // manifests. Post errors and results to the frontend.
  scoped_ptr<ExtensionList> extensions(new ExtensionList);
  file_util::FileEnumerator enumerator(path.ToWStringHack(),
                                       false, // not recursive
                                       file_util::FileEnumerator::DIRECTORIES);
  for (std::wstring child_path = enumerator.Next(); !child_path.empty();
       child_path = enumerator.Next()) {
     FilePath manifest_path = FilePath::FromWStringHack(child_path).Append(
         Extension::kManifestFilename);
    if (!file_util::PathExists(manifest_path)) {
      ReportExtensionLoadError(frontend.get(), child_path,
                               Extension::kInvalidManifestError);
      continue;
    }

    JSONFileValueSerializer serializer(manifest_path.ToWStringHack());
    Value* root = NULL;
    std::string error;
    if (!serializer.Deserialize(&root, &error)) {
      ReportExtensionLoadError(frontend.get(), child_path, error);
      continue;
    }

    scoped_ptr<Value> scoped_root(root);
    if (!root->IsType(Value::TYPE_DICTIONARY)) {
      ReportExtensionLoadError(frontend.get(), child_path,
                               Extension::kInvalidManifestError);
      continue;
    }

    scoped_ptr<Extension> extension(new Extension());
    if (!extension->InitFromValue(*static_cast<DictionaryValue*>(root),
                                  &error)) {
      ReportExtensionLoadError(frontend.get(), child_path, error);
      continue;
    }

    extensions->push_back(extension.release());
  }

  ReportExtensionsLoaded(frontend.get(), extensions.release());
  return true;
}

void ExtensionsServiceBackend::ReportExtensionLoadError(
    ExtensionsServiceFrontendInterface *frontend, const std::wstring& path,
    const std::string &error) {
  std::string message = StringPrintf("Could not load extension from '%s'. %s",
                                     WideToASCII(path).c_str(), error.c_str());
  frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
      frontend, &ExtensionsServiceFrontendInterface::OnExtensionLoadError,
      message));
}

void ExtensionsServiceBackend::ReportExtensionsLoaded(
    ExtensionsServiceFrontendInterface *frontend, ExtensionList* extensions) {
  frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
      frontend,
      &ExtensionsServiceFrontendInterface::OnExtensionsLoadedFromDirectory,
      extensions));
}
<|endoftext|>


<commit_before>/********************************************************************************
 ** The MIT License (MIT)
 **
 ** Copyright (c) 2013 Sascha Ludwig Häusler
 **
 ** 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.
 *********************************************************************************/

#include "AbstractModel.h"
#include "AbstractModel_p.h"

namespace PublicServerSystem
{
namespace Web
{
namespace Model
{

AbstractModel::AbstractModel(QObject *parent) :
    AbstractModel(new AbstractModelPrivate, parent)
{
}

AbstractModel::AbstractModel(arangodb::Document *doc, QObject *parent) :
    AbstractModel(doc, new AbstractModelPrivate, parent)
{
}

AbstractModel::AbstractModel(const AbstractModel &mo) :
    AbstractModel(new AbstractModelPrivate, mo.parent())
{
    Q_D(AbstractModel);
    d->doc = mo.d_ptr->doc;
}

AbstractModel::~AbstractModel()
{
    if (! d_ptr->hasDocumentDestroyed) delete d_ptr->doc;
    delete d_ptr;
}

void AbstractModel::save()
{
    Q_D(AbstractModel);

    if ( d->doc->save() ) {
        d->doc->waitForResult();
    }
}

void AbstractModel::saveAndDelete()
{
    Q_D(AbstractModel);

    d->hasDocumentDestroyed = true;

    QObject::connect( d->doc, &arangodb::Document::destroyed,
                      this, &AbstractModel::deleteLater
                      );

    d->doc->save();
    d->doc->deleteAfterFinished();
}

QString AbstractModel::dbCollectionKey() const
{
    Q_D(const AbstractModel);
    return d->doc->key();
}

AbstractModel::AbstractModel(arangodb::Document *doc, AbstractModelPrivate *ptr, QObject *parent) :
    AbstractModel(ptr, parent)
{
    Q_D(AbstractModel);
    d->doc = doc;
}

AbstractModel::AbstractModel(AbstractModelPrivate *ptr, QObject *parent) :
    QObject(parent),
    d_ptr(ptr)
{
}

QVariant AbstractModel::get(const QString &name) const
{
    Q_D(const AbstractModel);

    return d->doc->get(name);
}

void AbstractModel::set(const QString &name, QVariant val)
{
    Q_D(AbstractModel);

    d->doc->set(name, val);
}

Form::AbstractFormField *AbstractModel::field(const QString &referencingPropertyName, const QMetaObject &fieldClassObj, const QString &description)
{
    Q_D(AbstractModel);

    Form::AbstractFormField * thisField;

    // If the field has already been created
    if (d->fields.contains(referencingPropertyName)) {
            thisField = d->fields.value(referencingPropertyName);
        }
    else {
        thisField = qobject_cast<Form::AbstractFormField *>(fieldClassObj.newInstance(
                                                                Q_ARG(QString, referencingPropertyName),
                                                                Q_ARG(QString, description),
                                                                Q_ARG(QObject *, 0)
                                                            ));
        d->fields.insert(referencingPropertyName, thisField);
    }

    // Get current value for the field
    thisField->setValue(d->doc->get(referencingPropertyName));

    return thisField;
}

}
}
}
<commit_msg>Objects are only saved if they haven't been created<commit_after>/********************************************************************************
 ** The MIT License (MIT)
 **
 ** Copyright (c) 2013 Sascha Ludwig Häusler
 **
 ** 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.
 *********************************************************************************/

#include "AbstractModel.h"
#include "AbstractModel_p.h"

namespace PublicServerSystem
{
namespace Web
{
namespace Model
{

AbstractModel::AbstractModel(QObject *parent) :
    AbstractModel(new AbstractModelPrivate, parent)
{
}

AbstractModel::AbstractModel(arangodb::Document *doc, QObject *parent) :
    AbstractModel(doc, new AbstractModelPrivate, parent)
{
}

AbstractModel::AbstractModel(const AbstractModel &mo) :
    AbstractModel(new AbstractModelPrivate, mo.parent())
{
    Q_D(AbstractModel);
    d->doc = mo.d_ptr->doc;
}

AbstractModel::~AbstractModel()
{
    if (! d_ptr->hasDocumentDestroyed) delete d_ptr->doc;
    delete d_ptr;
}

void AbstractModel::save()
{
    Q_D(AbstractModel);

    if ( d->doc->isCreated() ) {
        d->doc->update();
        d->doc->waitForResult();
    }
    else if ( d->doc->save() ) {
        d->doc->waitForResult();
    }
}

void AbstractModel::saveAndDelete()
{
    Q_D(AbstractModel);

    d->hasDocumentDestroyed = true;

    QObject::connect( d->doc, &arangodb::Document::destroyed,
                      this, &AbstractModel::deleteLater
                      );

    d->doc->save();
    d->doc->deleteAfterFinished();
}

QString AbstractModel::dbCollectionKey() const
{
    Q_D(const AbstractModel);
    return d->doc->key();
}

AbstractModel::AbstractModel(arangodb::Document *doc, AbstractModelPrivate *ptr, QObject *parent) :
    AbstractModel(ptr, parent)
{
    Q_D(AbstractModel);
    d->doc = doc;
}

AbstractModel::AbstractModel(AbstractModelPrivate *ptr, QObject *parent) :
    QObject(parent),
    d_ptr(ptr)
{
}

QVariant AbstractModel::get(const QString &name) const
{
    Q_D(const AbstractModel);

    return d->doc->get(name);
}

void AbstractModel::set(const QString &name, QVariant val)
{
    Q_D(AbstractModel);

    d->doc->set(name, val);
}

Form::AbstractFormField *AbstractModel::field(const QString &referencingPropertyName, const QMetaObject &fieldClassObj, const QString &description)
{
    Q_D(AbstractModel);

    Form::AbstractFormField * thisField;

    // If the field has already been created
    if (d->fields.contains(referencingPropertyName)) {
            thisField = d->fields.value(referencingPropertyName);
        }
    else {
        thisField = qobject_cast<Form::AbstractFormField *>(fieldClassObj.newInstance(
                                                                Q_ARG(QString, referencingPropertyName),
                                                                Q_ARG(QString, description),
                                                                Q_ARG(QObject *, 0)
                                                            ));
        d->fields.insert(referencingPropertyName, thisField);
    }

    // Get current value for the field
    thisField->setValue(d->doc->get(referencingPropertyName));

    return thisField;
}

}
}
}
<|endoftext|>