<commit_before>// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.

#include "caf/openssl/session.hpp"

CAF_PUSH_WARNINGS
#include <openssl/err.h>
CAF_POP_WARNINGS

#include "caf/actor_system_config.hpp"

#include "caf/io/network/default_multiplexer.hpp"

#include "caf/openssl/manager.hpp"

// On Linux we need to block SIGPIPE whenever we access OpenSSL functions.
// Unfortunately there's no sane way to configure OpenSSL properly.
#ifdef CAF_LINUX

#  include "caf/detail/scope_guard.hpp"
#  include <signal.h>

#  define CAF_BLOCK_SIGPIPE()                                                  \
    sigset_t sigpipe_mask;                                                     \
    sigemptyset(&sigpipe_mask);                                                \
    sigaddset(&sigpipe_mask, SIGPIPE);                                         \
    sigset_t saved_mask;                                                       \
    if (pthread_sigmask(SIG_BLOCK, &sigpipe_mask, &saved_mask) == -1) {        \
      perror("pthread_sigmask");                                               \
      exit(1);                                                                 \
    }                                                                          \
    auto sigpipe_restore_guard = ::caf::detail::make_scope_guard([&] {         \
      struct timespec zerotime = {};                                           \
      sigtimedwait(&sigpipe_mask, 0, &zerotime);                               \
      if (pthread_sigmask(SIG_SETMASK, &saved_mask, 0) == -1) {                \
        perror("pthread_sigmask");                                             \
        exit(1);                                                               \
      }                                                                        \
    })

#else

#  define CAF_BLOCK_SIGPIPE() static_cast<void>(0)

#endif // CAF_LINUX

namespace caf::openssl {

namespace {

int pem_passwd_cb(char* buf, int size, int, void* ptr) {
  auto passphrase = reinterpret_cast<session*>(ptr)->openssl_passphrase();
  strncpy(buf, passphrase, static_cast<size_t>(size));
  buf[size - 1] = '\0';
  return static_cast<int>(strlen(buf));
}

} // namespace

session::session(actor_system& sys)
  : sys_(sys),
    ctx_(nullptr),
    ssl_(nullptr),
    connecting_(false),
    accepting_(false) {
  // nop
}

bool session::init() {
  CAF_LOG_TRACE("");
  ctx_ = create_ssl_context();
  ssl_ = SSL_new(ctx_);
  if (ssl_ == nullptr) {
    CAF_LOG_ERROR("cannot create SSL session");
    return false;
  }
  return true;
}

session::~session() {
  SSL_free(ssl_);
  SSL_CTX_free(ctx_);
}

rw_state session::do_some(int (*f)(SSL*, void*, int), size_t& result, void* buf,
                          size_t len, const char* debug_name) {
  CAF_BLOCK_SIGPIPE();
  auto check_ssl_res = [&](int res) -> rw_state {
    result = 0;
    switch (SSL_get_error(ssl_, res)) {
      default:
        CAF_LOG_INFO("SSL error:" << get_ssl_error());
        return rw_state::failure;
      case SSL_ERROR_WANT_READ:
        CAF_LOG_DEBUG("SSL_ERROR_WANT_READ reported");
        return rw_state::want_read;
      case SSL_ERROR_WANT_WRITE:
        CAF_LOG_DEBUG("SSL_ERROR_WANT_WRITE reported");
        // Report success to poll on this socket.
        return rw_state::success;
    }
  };
  CAF_LOG_TRACE(CAF_ARG(len) << CAF_ARG(debug_name));
  CAF_IGNORE_UNUSED(debug_name);
  if (connecting_) {
    CAF_LOG_DEBUG(debug_name << ": connecting");
    auto res = SSL_connect(ssl_);
    if (res == 1) {
      CAF_LOG_DEBUG("SSL connection established");
      connecting_ = false;
    } else {
      result = 0;
      return check_ssl_res(res);
    }
  }
  if (accepting_) {
    CAF_LOG_DEBUG(debug_name << ": accepting");
    auto res = SSL_accept(ssl_);
    if (res == 1) {
      CAF_LOG_DEBUG("SSL connection accepted");
      accepting_ = false;
    } else {
      result = 0;
      return check_ssl_res(res);
    }
  }
  CAF_LOG_DEBUG(debug_name << ": calling SSL_write or SSL_read");
  if (len == 0) {
    result = 0;
    return rw_state::indeterminate;
  }
  auto ret = f(ssl_, buf, static_cast<int>(len));
  if (ret > 0) {
    result = static_cast<size_t>(ret);
    return rw_state::success;
  }
  result = 0;
  return handle_ssl_result(ret) ? rw_state::success : rw_state::failure;
}

rw_state
session::read_some(size_t& result, native_socket, void* buf, size_t len) {
  CAF_LOG_TRACE(CAF_ARG(len));
  return do_some(SSL_read, result, buf, len, "read_some");
}

rw_state session::write_some(size_t& result, native_socket, const void* buf,
                             size_t len) {
  CAF_LOG_TRACE(CAF_ARG(len));
  auto wr_fun = [](SSL* sptr, void* vptr, int ptr_size) {
    return SSL_write(sptr, vptr, ptr_size);
  };
  return do_some(wr_fun, result, const_cast<void*>(buf), len, "write_some");
}

bool session::try_connect(native_socket fd) {
  CAF_LOG_TRACE(CAF_ARG(fd));
  CAF_BLOCK_SIGPIPE();
  SSL_set_fd(ssl_, fd);
  SSL_set_connect_state(ssl_);
  auto ret = SSL_connect(ssl_);
  if (ret == 1)
    return true;
  connecting_ = true;
  return handle_ssl_result(ret);
}

bool session::try_accept(native_socket fd) {
  CAF_LOG_TRACE(CAF_ARG(fd));
  CAF_BLOCK_SIGPIPE();
  SSL_set_fd(ssl_, fd);
  SSL_set_accept_state(ssl_);
  auto ret = SSL_accept(ssl_);
  if (ret == 1)
    return true;
  accepting_ = true;
  return handle_ssl_result(ret);
}

bool session::must_read_more(native_socket, size_t threshold) {
  return static_cast<size_t>(SSL_pending(ssl_)) >= threshold;
}

const char* session::openssl_passphrase() {
  return openssl_passphrase_.c_str();
}

SSL_CTX* session::create_ssl_context() {
  CAF_BLOCK_SIGPIPE();
#ifdef CAF_SSL_HAS_NON_VERSIONED_TLS_FUN
  auto ctx = SSL_CTX_new(TLS_method());
#else
  auto ctx = SSL_CTX_new(TLSv1_2_method());
#endif
  if (!ctx)
    CAF_RAISE_ERROR("cannot create OpenSSL context");
  if (sys_.openssl_manager().authentication_enabled()) {
    // Require valid certificates on both sides.
    auto& cfg = sys_.config();
    if (!cfg.openssl_certificate.empty()
        && SSL_CTX_use_certificate_chain_file(ctx,
                                              cfg.openssl_certificate.c_str())
             != 1)
      CAF_RAISE_ERROR("cannot load certificate");
    if (!cfg.openssl_passphrase.empty()) {
      openssl_passphrase_ = cfg.openssl_passphrase;
      SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb);
      SSL_CTX_set_default_passwd_cb_userdata(ctx, this);
    }
    if (!cfg.openssl_key.empty()
        && SSL_CTX_use_PrivateKey_file(ctx, cfg.openssl_key.c_str(),
                                       SSL_FILETYPE_PEM)
             != 1)
      CAF_RAISE_ERROR("cannot load private key");
    auto cafile = (!cfg.openssl_cafile.empty() ? cfg.openssl_cafile.c_str()
                                               : nullptr);
    auto capath = (!cfg.openssl_capath.empty() ? cfg.openssl_capath.c_str()
                                               : nullptr);
    if (cafile || capath) {
      if (SSL_CTX_load_verify_locations(ctx, cafile, capath) != 1)
        CAF_RAISE_ERROR("cannot load trusted CA certificates");
    }
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
                       nullptr);
    if (SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL:!MD5") != 1)
      CAF_RAISE_ERROR("cannot set cipher list");
  } else {
    // No authentication.
    SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr);
#if defined(CAF_SSL_HAS_ECDH_AUTO) && (OPENSSL_VERSION_NUMBER < 0x10100000L)
    SSL_CTX_set_ecdh_auto(ctx, 1);
#else
    auto ecdh = EC_KEY_new_by_curve_name(NID_secp384r1);
    if (!ecdh)
      CAF_RAISE_ERROR("cannot get ECDH curve");
    CAF_PUSH_WARNINGS
    SSL_CTX_set_tmp_ecdh(ctx, ecdh);
    EC_KEY_free(ecdh);
    CAF_POP_WARNINGS
#endif
#ifdef CAF_SSL_HAS_SECURITY_LEVEL
    const char* cipher = "AECDH-AES256-SHA@SECLEVEL=0";
#else
    const char* cipher = "AECDH-AES256-SHA";
#endif
    if (SSL_CTX_set_cipher_list(ctx, cipher) != 1)
      CAF_RAISE_ERROR("cannot set anonymous cipher");
  }
  return ctx;
}

std::string session::get_ssl_error() {
  std::string msg = "";
  while (auto err = ERR_get_error()) {
    if (!msg.empty())
      msg += " ";
    char buf[256];
    ERR_error_string_n(err, buf, sizeof(buf));
    msg += buf;
  }
  return msg;
}

bool session::handle_ssl_result(int ret) {
  auto err = SSL_get_error(ssl_, ret);
  switch (err) {
    case SSL_ERROR_WANT_READ:
      CAF_LOG_DEBUG("Nonblocking call to SSL returned want_read");
      return true;
    case SSL_ERROR_WANT_WRITE:
      CAF_LOG_DEBUG("Nonblocking call to SSL returned want_write");
      return true;
    case SSL_ERROR_ZERO_RETURN: // Regular remote connection shutdown.
    case SSL_ERROR_SYSCALL:     // Socket connection closed.
      return false;
    default: // Other error
      CAF_LOG_INFO("SSL call failed:" << get_ssl_error());
      return false;
  }
}

session_ptr
make_session(actor_system& sys, native_socket fd, bool from_accepted_socket) {
  session_ptr ptr{new session(sys)};
  if (!ptr->init())
    return nullptr;
  if (from_accepted_socket) {
    if (!ptr->try_accept(fd))
      return nullptr;
  } else {
    if (!ptr->try_connect(fd))
      return nullptr;
  }
  return ptr;
}

} // namespace caf::openssl
<commit_msg>Fix OpenSSL 3.0 warning by switching to newer EC-curve API<commit_after>// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.

#include "caf/openssl/session.hpp"

CAF_PUSH_WARNINGS
#include <openssl/err.h>
CAF_POP_WARNINGS

#include "caf/actor_system_config.hpp"

#include "caf/io/network/default_multiplexer.hpp"

#include "caf/openssl/manager.hpp"

// On Linux we need to block SIGPIPE whenever we access OpenSSL functions.
// Unfortunately there's no sane way to configure OpenSSL properly.
#ifdef CAF_LINUX

#  include "caf/detail/scope_guard.hpp"
#  include <signal.h>

#  define CAF_BLOCK_SIGPIPE()                                                  \
    sigset_t sigpipe_mask;                                                     \
    sigemptyset(&sigpipe_mask);                                                \
    sigaddset(&sigpipe_mask, SIGPIPE);                                         \
    sigset_t saved_mask;                                                       \
    if (pthread_sigmask(SIG_BLOCK, &sigpipe_mask, &saved_mask) == -1) {        \
      perror("pthread_sigmask");                                               \
      exit(1);                                                                 \
    }                                                                          \
    auto sigpipe_restore_guard = ::caf::detail::make_scope_guard([&] {         \
      struct timespec zerotime = {};                                           \
      sigtimedwait(&sigpipe_mask, 0, &zerotime);                               \
      if (pthread_sigmask(SIG_SETMASK, &saved_mask, 0) == -1) {                \
        perror("pthread_sigmask");                                             \
        exit(1);                                                               \
      }                                                                        \
    })

#else

#  define CAF_BLOCK_SIGPIPE() static_cast<void>(0)

#endif // CAF_LINUX

namespace caf::openssl {

namespace {

int pem_passwd_cb(char* buf, int size, int, void* ptr) {
  auto passphrase = reinterpret_cast<session*>(ptr)->openssl_passphrase();
  strncpy(buf, passphrase, static_cast<size_t>(size));
  buf[size - 1] = '\0';
  return static_cast<int>(strlen(buf));
}

} // namespace

session::session(actor_system& sys)
  : sys_(sys),
    ctx_(nullptr),
    ssl_(nullptr),
    connecting_(false),
    accepting_(false) {
  // nop
}

bool session::init() {
  CAF_LOG_TRACE("");
  ctx_ = create_ssl_context();
  ssl_ = SSL_new(ctx_);
  if (ssl_ == nullptr) {
    CAF_LOG_ERROR("cannot create SSL session");
    return false;
  }
  return true;
}

session::~session() {
  SSL_free(ssl_);
  SSL_CTX_free(ctx_);
}

rw_state session::do_some(int (*f)(SSL*, void*, int), size_t& result, void* buf,
                          size_t len, const char* debug_name) {
  CAF_BLOCK_SIGPIPE();
  auto check_ssl_res = [&](int res) -> rw_state {
    result = 0;
    switch (SSL_get_error(ssl_, res)) {
      default:
        CAF_LOG_INFO("SSL error:" << get_ssl_error());
        return rw_state::failure;
      case SSL_ERROR_WANT_READ:
        CAF_LOG_DEBUG("SSL_ERROR_WANT_READ reported");
        return rw_state::want_read;
      case SSL_ERROR_WANT_WRITE:
        CAF_LOG_DEBUG("SSL_ERROR_WANT_WRITE reported");
        // Report success to poll on this socket.
        return rw_state::success;
    }
  };
  CAF_LOG_TRACE(CAF_ARG(len) << CAF_ARG(debug_name));
  CAF_IGNORE_UNUSED(debug_name);
  if (connecting_) {
    CAF_LOG_DEBUG(debug_name << ": connecting");
    auto res = SSL_connect(ssl_);
    if (res == 1) {
      CAF_LOG_DEBUG("SSL connection established");
      connecting_ = false;
    } else {
      result = 0;
      return check_ssl_res(res);
    }
  }
  if (accepting_) {
    CAF_LOG_DEBUG(debug_name << ": accepting");
    auto res = SSL_accept(ssl_);
    if (res == 1) {
      CAF_LOG_DEBUG("SSL connection accepted");
      accepting_ = false;
    } else {
      result = 0;
      return check_ssl_res(res);
    }
  }
  CAF_LOG_DEBUG(debug_name << ": calling SSL_write or SSL_read");
  if (len == 0) {
    result = 0;
    return rw_state::indeterminate;
  }
  auto ret = f(ssl_, buf, static_cast<int>(len));
  if (ret > 0) {
    result = static_cast<size_t>(ret);
    return rw_state::success;
  }
  result = 0;
  return handle_ssl_result(ret) ? rw_state::success : rw_state::failure;
}

rw_state
session::read_some(size_t& result, native_socket, void* buf, size_t len) {
  CAF_LOG_TRACE(CAF_ARG(len));
  return do_some(SSL_read, result, buf, len, "read_some");
}

rw_state session::write_some(size_t& result, native_socket, const void* buf,
                             size_t len) {
  CAF_LOG_TRACE(CAF_ARG(len));
  auto wr_fun = [](SSL* sptr, void* vptr, int ptr_size) {
    return SSL_write(sptr, vptr, ptr_size);
  };
  return do_some(wr_fun, result, const_cast<void*>(buf), len, "write_some");
}

bool session::try_connect(native_socket fd) {
  CAF_LOG_TRACE(CAF_ARG(fd));
  CAF_BLOCK_SIGPIPE();
  SSL_set_fd(ssl_, fd);
  SSL_set_connect_state(ssl_);
  auto ret = SSL_connect(ssl_);
  if (ret == 1)
    return true;
  connecting_ = true;
  return handle_ssl_result(ret);
}

bool session::try_accept(native_socket fd) {
  CAF_LOG_TRACE(CAF_ARG(fd));
  CAF_BLOCK_SIGPIPE();
  SSL_set_fd(ssl_, fd);
  SSL_set_accept_state(ssl_);
  auto ret = SSL_accept(ssl_);
  if (ret == 1)
    return true;
  accepting_ = true;
  return handle_ssl_result(ret);
}

bool session::must_read_more(native_socket, size_t threshold) {
  return static_cast<size_t>(SSL_pending(ssl_)) >= threshold;
}

const char* session::openssl_passphrase() {
  return openssl_passphrase_.c_str();
}

SSL_CTX* session::create_ssl_context() {
  CAF_BLOCK_SIGPIPE();
#ifdef CAF_SSL_HAS_NON_VERSIONED_TLS_FUN
  auto ctx = SSL_CTX_new(TLS_method());
#else
  auto ctx = SSL_CTX_new(TLSv1_2_method());
#endif
  if (!ctx)
    CAF_RAISE_ERROR("cannot create OpenSSL context");
  if (sys_.openssl_manager().authentication_enabled()) {
    // Require valid certificates on both sides.
    auto& cfg = sys_.config();
    if (!cfg.openssl_certificate.empty()
        && SSL_CTX_use_certificate_chain_file(ctx,
                                              cfg.openssl_certificate.c_str())
             != 1)
      CAF_RAISE_ERROR("cannot load certificate");
    if (!cfg.openssl_passphrase.empty()) {
      openssl_passphrase_ = cfg.openssl_passphrase;
      SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb);
      SSL_CTX_set_default_passwd_cb_userdata(ctx, this);
    }
    if (!cfg.openssl_key.empty()
        && SSL_CTX_use_PrivateKey_file(ctx, cfg.openssl_key.c_str(),
                                       SSL_FILETYPE_PEM)
             != 1)
      CAF_RAISE_ERROR("cannot load private key");
    auto cafile = (!cfg.openssl_cafile.empty() ? cfg.openssl_cafile.c_str()
                                               : nullptr);
    auto capath = (!cfg.openssl_capath.empty() ? cfg.openssl_capath.c_str()
                                               : nullptr);
    if (cafile || capath) {
      if (SSL_CTX_load_verify_locations(ctx, cafile, capath) != 1)
        CAF_RAISE_ERROR("cannot load trusted CA certificates");
    }
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
                       nullptr);
    if (SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL:!MD5") != 1)
      CAF_RAISE_ERROR("cannot set cipher list");
  } else {
    // No authentication.
    SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr);
#if defined(CAF_SSL_HAS_ECDH_AUTO) && (OPENSSL_VERSION_NUMBER < 0x10100000L)
    SSL_CTX_set_ecdh_auto(ctx, 1);
#else
    SSL_CTX_set1_groups_list(ctx, "P-384");
#endif
#ifdef CAF_SSL_HAS_SECURITY_LEVEL
    const char* cipher = "AECDH-AES256-SHA@SECLEVEL=0";
#else
    const char* cipher = "AECDH-AES256-SHA";
#endif
    if (SSL_CTX_set_cipher_list(ctx, cipher) != 1)
      CAF_RAISE_ERROR("cannot set anonymous cipher");
  }
  return ctx;
}

std::string session::get_ssl_error() {
  std::string msg = "";
  while (auto err = ERR_get_error()) {
    if (!msg.empty())
      msg += " ";
    char buf[256];
    ERR_error_string_n(err, buf, sizeof(buf));
    msg += buf;
  }
  return msg;
}

bool session::handle_ssl_result(int ret) {
  auto err = SSL_get_error(ssl_, ret);
  switch (err) {
    case SSL_ERROR_WANT_READ:
      CAF_LOG_DEBUG("Nonblocking call to SSL returned want_read");
      return true;
    case SSL_ERROR_WANT_WRITE:
      CAF_LOG_DEBUG("Nonblocking call to SSL returned want_write");
      return true;
    case SSL_ERROR_ZERO_RETURN: // Regular remote connection shutdown.
    case SSL_ERROR_SYSCALL:     // Socket connection closed.
      return false;
    default: // Other error
      CAF_LOG_INFO("SSL call failed:" << get_ssl_error());
      return false;
  }
}

session_ptr
make_session(actor_system& sys, native_socket fd, bool from_accepted_socket) {
  session_ptr ptr{new session(sys)};
  if (!ptr->init())
    return nullptr;
  if (from_accepted_socket) {
    if (!ptr->try_accept(fd))
      return nullptr;
  } else {
    if (!ptr->try_connect(fd))
      return nullptr;
  }
  return ptr;
}

} // namespace caf::openssl
<|endoftext|>


<commit_before>/*
Highly Optimized Object-oriented Many-particle Dynamics -- Blue Edition
(HOOMD-blue) Open Source Software License Copyright 2008, 2009 Ames Laboratory
Iowa State University and The Regents of the University of Michigan All rights
reserved.

HOOMD-blue may contain modifications ("Contributions") provided, and to which
copyright is held, by various Contributors who have granted The Regents of the
University of Michigan the right to modify and/or distribute such Contributions.

Redistribution and use of HOOMD-blue, in source and binary forms, with or
without modification, are permitted, provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions, and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions, and the following disclaimer in the documentation and/or
other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of HOOMD-blue's
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.

Disclaimer

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND/OR
ANY WARRANTIES THAT THIS SOFTWARE IS FREE OF INFRINGEMENT ARE DISCLAIMED.

IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// $Id$
// $URL$
// Maintainer: joaander

#ifdef WIN32
#pragma warning( push )
#pragma warning( disable : 4244 )
#endif

#include <boost/python.hpp>
using namespace boost::python;
#include <boost/bind.hpp>
using namespace boost;

#include "TwoStepNPTGPU.h"
#include "TwoStepNPTGPU.cuh"
#include "TwoStepNVTGPU.cuh"

/*! \file TwoStepNPTGPU.h
    \brief Contains code for the TwoStepNPTGPU class
*/

/*! \param sysdef SystemDefinition this method will act on. Must not be NULL.
    \param group The group of particles this integration method is to work on
    \param thermo_group ComputeThermo to compute thermo properties of the integrated \a group
    \param thermo_all ComputeThermo to compute the pressure of the entire system
    \param tau NPT temperature period
    \param tauP NPT pressure period
    \param T Temperature set point
    \param P Pressure set point
*/
TwoStepNPTGPU::TwoStepNPTGPU(boost::shared_ptr<SystemDefinition> sysdef,
                             boost::shared_ptr<ParticleGroup> group,
                             boost::shared_ptr<ComputeThermo> thermo_group,
                             boost::shared_ptr<ComputeThermo> thermo_all,
                             Scalar tau,
                             Scalar tauP,
                             boost::shared_ptr<Variant> T,
                             boost::shared_ptr<Variant> P)
    : TwoStepNPT(sysdef, group, thermo_group, thermo_all, tau, tauP, T, P)
    {
    // only one GPU is supported
    if (!exec_conf->isCUDAEnabled())
        {
        cerr << endl << "***Error! Creating a TwoStepNPTGPU with CUDA disabled" << endl << endl;
        throw std::runtime_error("Error initializing TwoStepNVEGPU");
        }
    }

/*! \param timestep Current time step
    \post Particle positions are moved forward to timestep+1 and velocities to timestep+1/2 per the NPT method
*/
void TwoStepNPTGPU::integrateStepOne(unsigned int timestep)
    {
    unsigned int group_size = m_group->getNumMembers();
    if (group_size == 0)
        return;

    // profile this step
    if (m_prof)
        m_prof->push(exec_conf, "NPT step 1");

    IntegratorVariables v = getIntegratorVariables();
    Scalar& xi = v.variable[0];
    Scalar& eta = v.variable[1];

    // access all the needed data
    gpu_pdata_arrays& d_pdata = m_pdata->acquireReadWriteGPU();
    gpu_boxsize box = m_pdata->getBoxGPU();
    ArrayHandle< unsigned int > d_index_array(m_group->getIndexArray(), access_location::device, access_mode::read);

    // advance thermostat(m_Xi) half a time step
    xi += Scalar(1.0/2.0)/(m_tau*m_tau)*(m_curr_group_T/m_T->getValue(timestep) - Scalar(1.0))*m_deltaT;

    // advance barostat (m_Eta) half time step
    Scalar N = Scalar(m_group->getNumMembers());
    eta += Scalar(1.0/2.0)/(m_tauP*m_tauP)*m_V/(N*m_T->getValue(timestep))
            *(m_curr_P - m_P->getValue(timestep))*m_deltaT;

    // perform the particle update on the GPU
    gpu_npt_step_one(d_pdata,
                     d_index_array.data,
                     group_size,
                     m_partial_scale,
                     xi,
                     eta,
                     m_deltaT);

    if (exec_conf->isCUDAErrorCheckingEnabled())
        CHECK_CUDA_ERROR();

    // advance volume
    m_V *= exp(Scalar(3.0)*eta*m_deltaT);

    // get the scaling factor for the box (V_new/V_old)^(1/3)
    Scalar box_len_scale = exp(eta*m_deltaT);
    m_Lx *= box_len_scale;
    m_Ly *= box_len_scale;
    m_Lz *= box_len_scale;

    // two things are done here
    // 1. particles may have been moved slightly outside the box by the above steps, wrap them back into place
    // 2. all particles in the box are rescaled to fit in the new box
    gpu_npt_boxscale(d_pdata,
                     box,
                     m_partial_scale,
                     eta,
                     m_deltaT);

    if (exec_conf->isCUDAErrorCheckingEnabled())
        CHECK_CUDA_ERROR();

    // done profiling
    if (m_prof)
        m_prof->pop(exec_conf);

    m_pdata->release();
    m_pdata->setBox(BoxDim(m_Lx, m_Ly, m_Lz));
    }

/*! \param timestep Current time step
    \post particle velocities are moved forward to timestep+1 on the GPU
*/
void TwoStepNPTGPU::integrateStepTwo(unsigned int timestep)
    {
    unsigned int group_size = m_group->getNumMembers();
    if (group_size == 0)
        return;

    const GPUArray< Scalar4 >& net_force = m_pdata->getNetForce();

    // compute the current thermodynamic properties
    m_thermo_group->compute(timestep+1);
    m_thermo_all->compute(timestep+1);

    // compute temperature for the next half time step
    m_curr_group_T = m_thermo_group->getTemperature();
    // compute pressure for the next half time step
    m_curr_P = m_thermo_all->getPressure();

    // profile this step
    if (m_prof)
        m_prof->push(exec_conf, "NPT step 2");

    IntegratorVariables v = getIntegratorVariables();
    Scalar& xi = v.variable[0];
    Scalar& eta = v.variable[1];

    gpu_pdata_arrays& d_pdata = m_pdata->acquireReadWriteGPU();
    ArrayHandle<Scalar4> d_net_force(net_force, access_location::device, access_mode::read);
    ArrayHandle< unsigned int > d_index_array(m_group->getIndexArray(), access_location::device, access_mode::read);

    // perform the update on the GPU
    gpu_npt_step_two(d_pdata,
                     d_index_array.data,
                     group_size,
                     d_net_force.data,
                     xi,
                     eta,
                     m_deltaT);

    if (exec_conf->isCUDAErrorCheckingEnabled())
        CHECK_CUDA_ERROR();

    // Update state variables
    Scalar N = Scalar(m_group->getNumMembers());
    eta += Scalar(1.0/2.0)/(m_tauP*m_tauP)*m_V/(N*m_T->getValue(timestep))
                            *(m_curr_P - m_P->getValue(timestep))*m_deltaT;
    xi += Scalar(1.0/2.0)/(m_tau*m_tau)*(m_curr_group_T/m_T->getValue(timestep) - Scalar(1.0))*m_deltaT;

    m_pdata->release();
    setIntegratorVariables(v);

    // done profiling
    if (m_prof)
        m_prof->pop(exec_conf);
    }

void export_TwoStepNPTGPU()
    {
    class_<TwoStepNPTGPU, boost::shared_ptr<TwoStepNPTGPU>, bases<TwoStepNPT>, boost::noncopyable>
        ("TwoStepNPTGPU", init< boost::shared_ptr<SystemDefinition>,
                          boost::shared_ptr<ParticleGroup>,
                          boost::shared_ptr<ComputeThermo>,
                          boost::shared_ptr<ComputeThermo>,
                          Scalar,
                          Scalar,
                          boost::shared_ptr<Variant>,
                          boost::shared_ptr<Variant> >())
        ;
    }

#ifdef WIN32
#pragma warning( pop )
#endif

<commit_msg>Fixed a major bug in the NPT integrator on the GPU.<commit_after>/*
Highly Optimized Object-oriented Many-particle Dynamics -- Blue Edition
(HOOMD-blue) Open Source Software License Copyright 2008, 2009 Ames Laboratory
Iowa State University and The Regents of the University of Michigan All rights
reserved.

HOOMD-blue may contain modifications ("Contributions") provided, and to which
copyright is held, by various Contributors who have granted The Regents of the
University of Michigan the right to modify and/or distribute such Contributions.

Redistribution and use of HOOMD-blue, in source and binary forms, with or
without modification, are permitted, provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions, and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions, and the following disclaimer in the documentation and/or
other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of HOOMD-blue's
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.

Disclaimer

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND/OR
ANY WARRANTIES THAT THIS SOFTWARE IS FREE OF INFRINGEMENT ARE DISCLAIMED.

IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// $Id$
// $URL$
// Maintainer: joaander

#ifdef WIN32
#pragma warning( push )
#pragma warning( disable : 4244 )
#endif

#include <boost/python.hpp>
using namespace boost::python;
#include <boost/bind.hpp>
using namespace boost;

#include "TwoStepNPTGPU.h"
#include "TwoStepNPTGPU.cuh"
#include "TwoStepNVTGPU.cuh"

/*! \file TwoStepNPTGPU.h
    \brief Contains code for the TwoStepNPTGPU class
*/

/*! \param sysdef SystemDefinition this method will act on. Must not be NULL.
    \param group The group of particles this integration method is to work on
    \param thermo_group ComputeThermo to compute thermo properties of the integrated \a group
    \param thermo_all ComputeThermo to compute the pressure of the entire system
    \param tau NPT temperature period
    \param tauP NPT pressure period
    \param T Temperature set point
    \param P Pressure set point
*/
TwoStepNPTGPU::TwoStepNPTGPU(boost::shared_ptr<SystemDefinition> sysdef,
                             boost::shared_ptr<ParticleGroup> group,
                             boost::shared_ptr<ComputeThermo> thermo_group,
                             boost::shared_ptr<ComputeThermo> thermo_all,
                             Scalar tau,
                             Scalar tauP,
                             boost::shared_ptr<Variant> T,
                             boost::shared_ptr<Variant> P)
    : TwoStepNPT(sysdef, group, thermo_group, thermo_all, tau, tauP, T, P)
    {
    // only one GPU is supported
    if (!exec_conf->isCUDAEnabled())
        {
        cerr << endl << "***Error! Creating a TwoStepNPTGPU with CUDA disabled" << endl << endl;
        throw std::runtime_error("Error initializing TwoStepNVEGPU");
        }
    }

/*! \param timestep Current time step
    \post Particle positions are moved forward to timestep+1 and velocities to timestep+1/2 per the NPT method
*/
void TwoStepNPTGPU::integrateStepOne(unsigned int timestep)
    {
    unsigned int group_size = m_group->getNumMembers();
    if (group_size == 0)
        return;

    // profile this step
    if (m_prof)
        m_prof->push(exec_conf, "NPT step 1");

    IntegratorVariables v = getIntegratorVariables();
    Scalar& xi = v.variable[0];
    Scalar& eta = v.variable[1];

    // access all the needed data
    gpu_pdata_arrays& d_pdata = m_pdata->acquireReadWriteGPU();
    gpu_boxsize box = m_pdata->getBoxGPU();
    ArrayHandle< unsigned int > d_index_array(m_group->getIndexArray(), access_location::device, access_mode::read);

    // advance thermostat(m_Xi) half a time step
    xi += Scalar(1.0/2.0)/(m_tau*m_tau)*(m_curr_group_T/m_T->getValue(timestep) - Scalar(1.0))*m_deltaT;

    // advance barostat (m_Eta) half time step
    Scalar N = Scalar(m_group->getNumMembers());
    eta += Scalar(1.0/2.0)/(m_tauP*m_tauP)*m_V/(N*m_T->getValue(timestep))
            *(m_curr_P - m_P->getValue(timestep))*m_deltaT;

    // perform the particle update on the GPU
    gpu_npt_step_one(d_pdata,
                     d_index_array.data,
                     group_size,
                     m_partial_scale,
                     xi,
                     eta,
                     m_deltaT);

    if (exec_conf->isCUDAErrorCheckingEnabled())
        CHECK_CUDA_ERROR();

    // advance volume
    m_V *= exp(Scalar(3.0)*eta*m_deltaT);

    // get the scaling factor for the box (V_new/V_old)^(1/3)
    Scalar box_len_scale = exp(eta*m_deltaT);
    m_Lx *= box_len_scale;
    m_Ly *= box_len_scale;
    m_Lz *= box_len_scale;

    // two things are done here
    // 1. particles may have been moved slightly outside the box by the above steps, wrap them back into place
    // 2. all particles in the box are rescaled to fit in the new box
    gpu_npt_boxscale(d_pdata,
                     box,
                     m_partial_scale,
                     eta,
                     m_deltaT);

    if (exec_conf->isCUDAErrorCheckingEnabled())
        CHECK_CUDA_ERROR();

    // done profiling
    if (m_prof)
        m_prof->pop(exec_conf);

    m_pdata->release();
    m_pdata->setBox(BoxDim(m_Lx, m_Ly, m_Lz));
    setIntegratorVariables(v);
    }

/*! \param timestep Current time step
    \post particle velocities are moved forward to timestep+1 on the GPU
*/
void TwoStepNPTGPU::integrateStepTwo(unsigned int timestep)
    {
    unsigned int group_size = m_group->getNumMembers();
    if (group_size == 0)
        return;

    const GPUArray< Scalar4 >& net_force = m_pdata->getNetForce();

    // compute the current thermodynamic properties
    m_thermo_group->compute(timestep+1);
    m_thermo_all->compute(timestep+1);

    // compute temperature for the next half time step
    m_curr_group_T = m_thermo_group->getTemperature();
    // compute pressure for the next half time step
    m_curr_P = m_thermo_all->getPressure();

    // profile this step
    if (m_prof)
        m_prof->push(exec_conf, "NPT step 2");

    IntegratorVariables v = getIntegratorVariables();
    Scalar& xi = v.variable[0];
    Scalar& eta = v.variable[1];

    gpu_pdata_arrays& d_pdata = m_pdata->acquireReadWriteGPU();
    ArrayHandle<Scalar4> d_net_force(net_force, access_location::device, access_mode::read);
    ArrayHandle< unsigned int > d_index_array(m_group->getIndexArray(), access_location::device, access_mode::read);

    // perform the update on the GPU
    gpu_npt_step_two(d_pdata,
                     d_index_array.data,
                     group_size,
                     d_net_force.data,
                     xi,
                     eta,
                     m_deltaT);

    if (exec_conf->isCUDAErrorCheckingEnabled())
        CHECK_CUDA_ERROR();

    // Update state variables
    Scalar N = Scalar(m_group->getNumMembers());
    eta += Scalar(1.0/2.0)/(m_tauP*m_tauP)*m_V/(N*m_T->getValue(timestep))
                            *(m_curr_P - m_P->getValue(timestep))*m_deltaT;
    xi += Scalar(1.0/2.0)/(m_tau*m_tau)*(m_curr_group_T/m_T->getValue(timestep) - Scalar(1.0))*m_deltaT;

    m_pdata->release();
    setIntegratorVariables(v);

    // done profiling
    if (m_prof)
        m_prof->pop(exec_conf);
    }

void export_TwoStepNPTGPU()
    {
    class_<TwoStepNPTGPU, boost::shared_ptr<TwoStepNPTGPU>, bases<TwoStepNPT>, boost::noncopyable>
        ("TwoStepNPTGPU", init< boost::shared_ptr<SystemDefinition>,
                          boost::shared_ptr<ParticleGroup>,
                          boost::shared_ptr<ComputeThermo>,
                          boost::shared_ptr<ComputeThermo>,
                          Scalar,
                          Scalar,
                          boost::shared_ptr<Variant>,
                          boost::shared_ptr<Variant> >())
        ;
    }

#ifdef WIN32
#pragma warning( pop )
#endif

<|endoftext|>


<commit_before>/*
	Nazara Development Kit ("NDK"), also called Nazara Engine - SDK ("Software Development Kit")

	Copyright (C) 2015 Jérôme "Lynix" Leclercq (Lynix680@gmail.com)

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

#ifndef NDK_PREREQUESITES_HPP
#define NDK_PREREQUESITES_HPP

#include <Nazara/Prerequesites.hpp>

// Version of SDK
#define NDK_VERSION_MAJOR 0
#define NDK_VERSION_MINOR 1

// Importation/Exportation of the API
#if defined(NAZARA_STATIC)
  #define #define NDK_API
#else
  #ifdef NDK_BUILD
	  #define NDK_API NAZARA_EXPORT
  #else
	  #define NDK_API NAZARA_IMPORT
	#endif
#endif

namespace Ndk
{
	using ComponentId = Nz::UInt64;
	using ComponentIndex = Nz::UInt32;
	using EntityId = Nz::UInt32;
	using SystemIndex = Nz::UInt32;
}

#endif // NDK_PREREQUESITES_HPP
<commit_msg>SDK/Prerequesites: Remove version informations<commit_after>/*
	Nazara Development Kit ("NDK"), also called Nazara Engine - SDK ("Software Development Kit")

	Copyright (C) 2015 Jérôme "Lynix" Leclercq (Lynix680@gmail.com)

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

#ifndef NDK_PREREQUESITES_HPP
#define NDK_PREREQUESITES_HPP

#include <Nazara/Prerequesites.hpp>

// Importation/Exportation of the API
#if defined(NAZARA_STATIC)
  #define #define NDK_API
#else
  #ifdef NDK_BUILD
	  #define NDK_API NAZARA_EXPORT
  #else
	  #define NDK_API NAZARA_IMPORT
	#endif
#endif

namespace Ndk
{
	using ComponentId = Nz::UInt64;
	using ComponentIndex = Nz::UInt32;
	using EntityId = Nz::UInt32;
	using SystemIndex = Nz::UInt32;
}

#endif // NDK_PREREQUESITES_HPP
<|endoftext|>


<commit_before>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010 Francois Beaune
//
// 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.
//

// Interface header.
#include "appleseed.h"

// Standard headers.
#include <cstdio>

using namespace std;

namespace foundation
{

//
// Appleseed class implementation.
//

const char* Appleseed::get_lib_name()
{
// Windows.
#if defined _WIN32

    return "appleseed.dll";

// Mac OS X.
#elif defined __APPLE__

    return "libappleseed.dylib";

// Other operating system.
#else

    return "appleseed.so";

#endif
}

const char* Appleseed::get_lib_version()
{
    static const char* LibVersionToken = "1.1.0-UNOFFICIAL";
    return LibVersionToken;
}

const char* Appleseed::get_lib_maturity_level()
{
    static const char* LibMaturityLevelToken = "alpha-3";
    return LibMaturityLevelToken;
}

size_t Appleseed::get_lib_build_number()
{
    static const size_t LibBuildNumberToken = 7618;
    return LibBuildNumberToken;
}

const char* Appleseed::get_lib_configuration()
{
#ifdef DEBUG
    return "Debug";
#else
    return "Release";
#endif
}

const char* Appleseed::get_lib_compilation_date()
{
    return __DATE__;
}

const char* Appleseed::get_lib_compilation_time()
{
    return __TIME__;
}

namespace
{
    struct SyntheticVersionString
    {
        char m_value[1024];

        SyntheticVersionString()
        {
            sprintf(
                m_value,
                "%s version %s %s (build " FMT_SIZE_T ")",
                Appleseed::get_lib_name(),
                Appleseed::get_lib_version(),
                Appleseed::get_lib_maturity_level(),
                Appleseed::get_lib_build_number());
        }

    };

    SyntheticVersionString s_synthetic_version_string;
}

const char* Appleseed::get_synthetic_version_string()
{
    return s_synthetic_version_string.m_value;
}

}   // namespace foundation
<commit_msg>bumped maturity level of appleseed to alpha-4.<commit_after>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010 Francois Beaune
//
// 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.
//

// Interface header.
#include "appleseed.h"

// Standard headers.
#include <cstdio>

using namespace std;

namespace foundation
{

//
// Appleseed class implementation.
//

const char* Appleseed::get_lib_name()
{
// Windows.
#if defined _WIN32

    return "appleseed.dll";

// Mac OS X.
#elif defined __APPLE__

    return "libappleseed.dylib";

// Other operating system.
#else

    return "appleseed.so";

#endif
}

const char* Appleseed::get_lib_version()
{
    static const char* LibVersionToken = "1.1.0-UNOFFICIAL";
    return LibVersionToken;
}

const char* Appleseed::get_lib_maturity_level()
{
    static const char* LibMaturityLevelToken = "alpha-4";
    return LibMaturityLevelToken;
}

size_t Appleseed::get_lib_build_number()
{
    static const size_t LibBuildNumberToken = 7618;
    return LibBuildNumberToken;
}

const char* Appleseed::get_lib_configuration()
{
#ifdef DEBUG
    return "Debug";
#else
    return "Release";
#endif
}

const char* Appleseed::get_lib_compilation_date()
{
    return __DATE__;
}

const char* Appleseed::get_lib_compilation_time()
{
    return __TIME__;
}

namespace
{
    struct SyntheticVersionString
    {
        char m_value[1024];

        SyntheticVersionString()
        {
            sprintf(
                m_value,
                "%s version %s %s (build " FMT_SIZE_T ")",
                Appleseed::get_lib_name(),
                Appleseed::get_lib_version(),
                Appleseed::get_lib_maturity_level(),
                Appleseed::get_lib_build_number());
        }

    };

    SyntheticVersionString s_synthetic_version_string;
}

const char* Appleseed::get_synthetic_version_string()
{
    return s_synthetic_version_string.m_value;
}

}   // namespace foundation
<|endoftext|>


<commit_before>/*
 * Copyright 2016-2017 deepstreamHub GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

#include <cstring>

#include <numeric>
#include <random>
#include <vector>

#include <buffer.hpp>
#include <message.hpp>
#include <message_builder.hpp>
#include <parser.hpp>
#include <random.hpp>
#include <scope_guard.hpp>

extern "C" {
#include <lexer.h>
}

// Remarks:
// - Do not use BOOST_CHECK_EQUAL() to compare const char* variables as
//   Boost.Test attempts to be smart by calling std::strcmp()
//   [with Boost 1.53, see boost/test/impl/test_tools.ipp:383, equal_impl()].
//   In this file, const char* variables do not always reference null-terminated
//   strings, e.g., when using std::vector<char*>::data(). If this happens,
//   Valgrind may detect invalid reads.

namespace deepstream {
namespace parser
{

BOOST_AUTO_TEST_CASE(empty_string)
{
	deepstream_parser_state state("", 0);

	int token = state.handle_token(TOKEN_EOF, "\0", 1);
	BOOST_CHECK_EQUAL( token, TOKEN_EOF );

	BOOST_CHECK( state.tokenizing_header_ );
	BOOST_CHECK_EQUAL( state.offset_, 1 );
	BOOST_CHECK( state.messages_.empty() );
	BOOST_CHECK( state.errors_.empty() );
}

BOOST_AUTO_TEST_CASE(simple)
{
	const auto input = Message::from_human_readable("A|A+");
	const auto copy(input);
	const char* matches[] = { &input[0], &input[3], "" };
	std::size_t sizes[] = { 3, 1, 1 };

	const deepstream_token tokens[] = {
		TOKEN_A_A, TOKEN_MESSAGE_SEPARATOR, TOKEN_EOF
	};

	const std::size_t num_tokens = sizeof(tokens) / sizeof(tokens[0]);

	deepstream_parser_state state( &copy[0], copy.size() );

	for(std::size_t i = 0; i < num_tokens; ++i)
	{
		bool tokenizing_header = state.tokenizing_header_;
		BOOST_CHECK( (i==0||i==2) ? tokenizing_header : !tokenizing_header );

		int ret = state.handle_token(tokens[i], matches[i], sizes[i]);

		BOOST_CHECK_EQUAL( ret, tokens[i] );

		BOOST_CHECK_EQUAL( state.messages_.size(), 1 );
		BOOST_CHECK( state.errors_.empty() );

		std::size_t offset = std::accumulate( sizes, sizes+i+1, 0 );
		BOOST_CHECK_EQUAL( state.offset_, offset );
	}

	MessageProxy& msg = state.messages_.front();

	BOOST_CHECK( msg.base_ == copy.data() );
	BOOST_CHECK_EQUAL( msg.offset_, 0 );
	BOOST_CHECK_EQUAL( msg.size_, input.size() );

	BOOST_CHECK_EQUAL( msg.topic(), Topic::AUTH );
	BOOST_CHECK_EQUAL( msg.action(), Action::REQUEST );
	BOOST_CHECK( msg.is_ack() );

	BOOST_CHECK( msg.arguments_.empty() );
}

BOOST_AUTO_TEST_CASE(concatenated_messages)
{
	const char STRING[] = "E|L|listen+E|S|event+";

	const auto input = Message::from_human_readable(STRING);
	const auto copy(input);

	const deepstream_token tokens[] = {
		TOKEN_E_L, TOKEN_PAYLOAD, TOKEN_MESSAGE_SEPARATOR,
		TOKEN_E_S, TOKEN_PAYLOAD, TOKEN_MESSAGE_SEPARATOR,
		TOKEN_EOF
	};

	const std::size_t num_tokens = sizeof(tokens) / sizeof(tokens[0]);

	const std::size_t matchlens[num_tokens] = { 3, 7, 1, 3, 6, 1, 1 };
	const char* matches[num_tokens] = {
		&input[ 0], &input[3], &input[10],
		&input[11], &input[14], &input[20],
		""
	};

	deepstream_parser_state state( &copy[0], copy.size() );

	for(std::size_t i = 0; i < num_tokens; ++i)
	{
		BOOST_CHECK( (i==0||i==3||i==6) ?
			state.tokenizing_header_ :
			!state.tokenizing_header_
		);

		std::size_t offset = std::accumulate( matchlens, matchlens+i, 0 );
		BOOST_CHECK_EQUAL( state.offset_, offset );

		int ret = state.handle_token(tokens[i], matches[i], matchlens[i]);

		BOOST_CHECK_EQUAL( ret, tokens[i] );

		BOOST_CHECK_EQUAL( state.messages_.size(), (i>=3) ? 2 : 1 );
		BOOST_CHECK( state.errors_.empty() );

		BOOST_CHECK_EQUAL( state.offset_, offset+matchlens[i] );
	}

	BOOST_CHECK( state.tokenizing_header_ );

	for(const MessageProxy& msg : state.messages_)
	{
		BOOST_CHECK( msg.base_ == copy.data() );

		BOOST_CHECK_EQUAL( msg.topic(), Topic::EVENT );
		BOOST_CHECK( !msg.is_ack() );

		BOOST_CHECK_EQUAL( msg.arguments_.size(), 1 );
	}

	BOOST_CHECK_EQUAL( state.messages_.size(), 2 );

	const MessageProxy& msg_f = state.messages_.front();
	BOOST_CHECK_EQUAL( msg_f.offset(), 0 );
	BOOST_CHECK_EQUAL( msg_f.size(), 11 );
	BOOST_CHECK_EQUAL( msg_f.topic(), Topic::EVENT );
	BOOST_CHECK_EQUAL( msg_f.action(), Action::LISTEN );

	const Location& arg_f = msg_f.arguments_.front();

	BOOST_CHECK_EQUAL( arg_f.offset_, 4 );
	BOOST_CHECK_EQUAL( arg_f.size_, 6 );
	BOOST_CHECK( !strncmp(&input[arg_f.offset_], "listen", arg_f.size_) );

	const MessageProxy& msg_b = state.messages_.back();
	BOOST_CHECK_EQUAL( msg_b.offset(), 11 );
	BOOST_CHECK_EQUAL( msg_b.size(), 10 );
	BOOST_CHECK_EQUAL( msg_b.topic(), Topic::EVENT );
	BOOST_CHECK_EQUAL( msg_b.action(), Action::SUBSCRIBE );

	const Location& arg_b = msg_b.arguments_.front();

	BOOST_CHECK_EQUAL( arg_b.offset_, 15 );
	BOOST_CHECK_EQUAL( arg_b.size_, 5 );
	BOOST_CHECK( !strncmp(&input[arg_b.offset_], "event", arg_b.size_) );
}

BOOST_AUTO_TEST_CASE(invalid_number_of_arguments)
{
	const char STRING[] = "E|A|L|l+";

	const auto input = Message::from_human_readable(STRING);
	const auto copy(input);

	const deepstream_token TOKENS[] = {
		TOKEN_E_A_L, TOKEN_PAYLOAD, TOKEN_MESSAGE_SEPARATOR, TOKEN_EOF
	};

	const std::size_t NUM_TOKENS = sizeof(TOKENS) / sizeof(TOKENS[0]);

	const std::size_t MATCHLENS[NUM_TOKENS] = { 5, 2, 1, 1 };
	const char* MATCHES[NUM_TOKENS] = { &input[0], &input[5], &input[7], "" };

	deepstream_parser_state state( &copy[0], copy.size() );

	for(std::size_t i = 0; i < NUM_TOKENS; ++i)
	{
		bool tokenizing_header = state.tokenizing_header_;
		BOOST_CHECK( (i==0||i==3) ? tokenizing_header : !tokenizing_header );

		std::size_t offset = std::accumulate( MATCHLENS, MATCHLENS+i, 0 );
		BOOST_CHECK_EQUAL( state.offset_, offset );

		int ret = state.handle_token(TOKENS[i], MATCHES[i], MATCHLENS[i]);

		BOOST_CHECK_EQUAL( ret, TOKENS[i] );

		BOOST_CHECK_EQUAL( state.messages_.size(), (i>=2) ? 0 : 1 );
		BOOST_CHECK_EQUAL( state.errors_.size(), (i>=2) ? 1 : 0 );

		BOOST_CHECK_EQUAL( state.offset_, offset+MATCHLENS[i] );
	}

	const Error& e = state.errors_.front();

	BOOST_CHECK_EQUAL( e.location_.offset_, 0 );
	BOOST_CHECK_EQUAL( e.location_.size_, 8 );
	BOOST_CHECK_EQUAL( e.tag_, Error::INVALID_NUMBER_OF_ARGUMENTS );
}

// lexer, parser integration tests

BOOST_AUTO_TEST_CASE(simple_integration)
{
	const char raw[] = "A|A+ERROR+++E|A|L|p|m+";
	const std::vector<char> input = Message::from_human_readable(raw);

	std::vector<char> lexer_input( input.size()+2 );
	std::copy( input.cbegin(), input.cend(), lexer_input.begin() );

	yyscan_t scanner;
	int ret = yylex_init(&scanner);
	BOOST_REQUIRE_EQUAL(ret, 0);
	DEEPSTREAM_ON_EXIT( [&scanner] () {
		yylex_destroy(scanner);
	} );

	YY_BUFFER_STATE buffer = \
		yy_scan_buffer( lexer_input.data(), lexer_input.size(), scanner );
	yy_switch_to_buffer(buffer, scanner);

	State parser( input.data(), input.size() );
	yyset_extra(&parser, scanner);

	for( int ret = TOKEN_UNKNOWN; ret != TOKEN_EOF; ret = yylex(scanner) );

	BOOST_CHECK( parser.buffer_ == input.data() );
	BOOST_CHECK_EQUAL( parser.buffer_size_, input.size() );

	BOOST_CHECK( parser.tokenizing_header_ );
	BOOST_CHECK_EQUAL( parser.offset_, parser.buffer_size_ + 1 );

	BOOST_CHECK_EQUAL( parser.messages_.size(), 2 );
	BOOST_CHECK_EQUAL( parser.errors_.size(), 1 );

	const MessageProxy& msg_f = parser.messages_.front();
	BOOST_CHECK( msg_f.base() == input.data() );
	BOOST_CHECK_EQUAL( msg_f.offset(), 0 );
	BOOST_CHECK_EQUAL( msg_f.size(), 4 );
	BOOST_CHECK_EQUAL( msg_f.topic(), Topic::AUTH );
	BOOST_CHECK_EQUAL( msg_f.action(), Action::REQUEST );
	BOOST_CHECK( msg_f.is_ack() );
	BOOST_CHECK_EQUAL( msg_f.num_arguments(), 0 );

	const MessageProxy& msg_b = parser.messages_.back();
	BOOST_CHECK( msg_b.base() == input.data() );
	BOOST_CHECK_EQUAL( msg_b.offset(), 12 );
	BOOST_CHECK_EQUAL( msg_b.size(), 10 );
	BOOST_CHECK_EQUAL( msg_b.topic(), Topic::EVENT );
	BOOST_CHECK_EQUAL( msg_b.action(), Action::LISTEN );
	BOOST_CHECK( msg_b.is_ack() );
	BOOST_CHECK_EQUAL( msg_b.num_arguments(), 2 );

	const Error& error = parser.errors_.front();
	BOOST_CHECK_EQUAL( error.tag(), Error::UNEXPECTED_TOKEN );
}

BOOST_AUTO_TEST_CASE(random_messages)
{
	for(std::size_t iteration = 0; iteration < 50; ++iteration)
	{
		random::Engine engine(iteration);

		std::vector<Message::Header> headers;
		Buffer input;

		for(std::size_t i = 0; i < 20; ++i)
		{
			MessageBuilder message_builder = random::make_message(&engine);

			Buffer bin = message_builder.to_binary();
			input.insert( input.end(), bin.cbegin(), bin.cend() );

			headers.push_back( message_builder.header() );
		}

		input.push_back( '\0' );
		input.push_back( '\0' );

		auto ret = execute( input.data(), input.size() );
		const ErrorList& errors = ret.second;

		BOOST_CHECK( errors.empty() );
	}
}

}
}
<commit_msg>Test: make random parser test more strict<commit_after>/*
 * Copyright 2016-2017 deepstreamHub GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

#include <cstring>

#include <numeric>
#include <random>
#include <vector>

#include <buffer.hpp>
#include <message.hpp>
#include <message_builder.hpp>
#include <parser.hpp>
#include <random.hpp>
#include <scope_guard.hpp>

extern "C" {
#include <lexer.h>
}

// Remarks:
// - Do not use BOOST_CHECK_EQUAL() to compare const char* variables as
//   Boost.Test attempts to be smart by calling std::strcmp()
//   [with Boost 1.53, see boost/test/impl/test_tools.ipp:383, equal_impl()].
//   In this file, const char* variables do not always reference null-terminated
//   strings, e.g., when using std::vector<char*>::data(). If this happens,
//   Valgrind may detect invalid reads.

namespace deepstream {
namespace parser
{

BOOST_AUTO_TEST_CASE(empty_string)
{
	deepstream_parser_state state("", 0);

	int token = state.handle_token(TOKEN_EOF, "\0", 1);
	BOOST_CHECK_EQUAL( token, TOKEN_EOF );

	BOOST_CHECK( state.tokenizing_header_ );
	BOOST_CHECK_EQUAL( state.offset_, 1 );
	BOOST_CHECK( state.messages_.empty() );
	BOOST_CHECK( state.errors_.empty() );
}

BOOST_AUTO_TEST_CASE(simple)
{
	const auto input = Message::from_human_readable("A|A+");
	const auto copy(input);
	const char* matches[] = { &input[0], &input[3], "" };
	std::size_t sizes[] = { 3, 1, 1 };

	const deepstream_token tokens[] = {
		TOKEN_A_A, TOKEN_MESSAGE_SEPARATOR, TOKEN_EOF
	};

	const std::size_t num_tokens = sizeof(tokens) / sizeof(tokens[0]);

	deepstream_parser_state state( &copy[0], copy.size() );

	for(std::size_t i = 0; i < num_tokens; ++i)
	{
		bool tokenizing_header = state.tokenizing_header_;
		BOOST_CHECK( (i==0||i==2) ? tokenizing_header : !tokenizing_header );

		int ret = state.handle_token(tokens[i], matches[i], sizes[i]);

		BOOST_CHECK_EQUAL( ret, tokens[i] );

		BOOST_CHECK_EQUAL( state.messages_.size(), 1 );
		BOOST_CHECK( state.errors_.empty() );

		std::size_t offset = std::accumulate( sizes, sizes+i+1, 0 );
		BOOST_CHECK_EQUAL( state.offset_, offset );
	}

	MessageProxy& msg = state.messages_.front();

	BOOST_CHECK( msg.base_ == copy.data() );
	BOOST_CHECK_EQUAL( msg.offset_, 0 );
	BOOST_CHECK_EQUAL( msg.size_, input.size() );

	BOOST_CHECK_EQUAL( msg.topic(), Topic::AUTH );
	BOOST_CHECK_EQUAL( msg.action(), Action::REQUEST );
	BOOST_CHECK( msg.is_ack() );

	BOOST_CHECK( msg.arguments_.empty() );
}

BOOST_AUTO_TEST_CASE(concatenated_messages)
{
	const char STRING[] = "E|L|listen+E|S|event+";

	const auto input = Message::from_human_readable(STRING);
	const auto copy(input);

	const deepstream_token tokens[] = {
		TOKEN_E_L, TOKEN_PAYLOAD, TOKEN_MESSAGE_SEPARATOR,
		TOKEN_E_S, TOKEN_PAYLOAD, TOKEN_MESSAGE_SEPARATOR,
		TOKEN_EOF
	};

	const std::size_t num_tokens = sizeof(tokens) / sizeof(tokens[0]);

	const std::size_t matchlens[num_tokens] = { 3, 7, 1, 3, 6, 1, 1 };
	const char* matches[num_tokens] = {
		&input[ 0], &input[3], &input[10],
		&input[11], &input[14], &input[20],
		""
	};

	deepstream_parser_state state( &copy[0], copy.size() );

	for(std::size_t i = 0; i < num_tokens; ++i)
	{
		BOOST_CHECK( (i==0||i==3||i==6) ?
			state.tokenizing_header_ :
			!state.tokenizing_header_
		);

		std::size_t offset = std::accumulate( matchlens, matchlens+i, 0 );
		BOOST_CHECK_EQUAL( state.offset_, offset );

		int ret = state.handle_token(tokens[i], matches[i], matchlens[i]);

		BOOST_CHECK_EQUAL( ret, tokens[i] );

		BOOST_CHECK_EQUAL( state.messages_.size(), (i>=3) ? 2 : 1 );
		BOOST_CHECK( state.errors_.empty() );

		BOOST_CHECK_EQUAL( state.offset_, offset+matchlens[i] );
	}

	BOOST_CHECK( state.tokenizing_header_ );

	for(const MessageProxy& msg : state.messages_)
	{
		BOOST_CHECK( msg.base_ == copy.data() );

		BOOST_CHECK_EQUAL( msg.topic(), Topic::EVENT );
		BOOST_CHECK( !msg.is_ack() );

		BOOST_CHECK_EQUAL( msg.arguments_.size(), 1 );
	}

	BOOST_CHECK_EQUAL( state.messages_.size(), 2 );

	const MessageProxy& msg_f = state.messages_.front();
	BOOST_CHECK_EQUAL( msg_f.offset(), 0 );
	BOOST_CHECK_EQUAL( msg_f.size(), 11 );
	BOOST_CHECK_EQUAL( msg_f.topic(), Topic::EVENT );
	BOOST_CHECK_EQUAL( msg_f.action(), Action::LISTEN );

	const Location& arg_f = msg_f.arguments_.front();

	BOOST_CHECK_EQUAL( arg_f.offset_, 4 );
	BOOST_CHECK_EQUAL( arg_f.size_, 6 );
	BOOST_CHECK( !strncmp(&input[arg_f.offset_], "listen", arg_f.size_) );

	const MessageProxy& msg_b = state.messages_.back();
	BOOST_CHECK_EQUAL( msg_b.offset(), 11 );
	BOOST_CHECK_EQUAL( msg_b.size(), 10 );
	BOOST_CHECK_EQUAL( msg_b.topic(), Topic::EVENT );
	BOOST_CHECK_EQUAL( msg_b.action(), Action::SUBSCRIBE );

	const Location& arg_b = msg_b.arguments_.front();

	BOOST_CHECK_EQUAL( arg_b.offset_, 15 );
	BOOST_CHECK_EQUAL( arg_b.size_, 5 );
	BOOST_CHECK( !strncmp(&input[arg_b.offset_], "event", arg_b.size_) );
}

BOOST_AUTO_TEST_CASE(invalid_number_of_arguments)
{
	const char STRING[] = "E|A|L|l+";

	const auto input = Message::from_human_readable(STRING);
	const auto copy(input);

	const deepstream_token TOKENS[] = {
		TOKEN_E_A_L, TOKEN_PAYLOAD, TOKEN_MESSAGE_SEPARATOR, TOKEN_EOF
	};

	const std::size_t NUM_TOKENS = sizeof(TOKENS) / sizeof(TOKENS[0]);

	const std::size_t MATCHLENS[NUM_TOKENS] = { 5, 2, 1, 1 };
	const char* MATCHES[NUM_TOKENS] = { &input[0], &input[5], &input[7], "" };

	deepstream_parser_state state( &copy[0], copy.size() );

	for(std::size_t i = 0; i < NUM_TOKENS; ++i)
	{
		bool tokenizing_header = state.tokenizing_header_;
		BOOST_CHECK( (i==0||i==3) ? tokenizing_header : !tokenizing_header );

		std::size_t offset = std::accumulate( MATCHLENS, MATCHLENS+i, 0 );
		BOOST_CHECK_EQUAL( state.offset_, offset );

		int ret = state.handle_token(TOKENS[i], MATCHES[i], MATCHLENS[i]);

		BOOST_CHECK_EQUAL( ret, TOKENS[i] );

		BOOST_CHECK_EQUAL( state.messages_.size(), (i>=2) ? 0 : 1 );
		BOOST_CHECK_EQUAL( state.errors_.size(), (i>=2) ? 1 : 0 );

		BOOST_CHECK_EQUAL( state.offset_, offset+MATCHLENS[i] );
	}

	const Error& e = state.errors_.front();

	BOOST_CHECK_EQUAL( e.location_.offset_, 0 );
	BOOST_CHECK_EQUAL( e.location_.size_, 8 );
	BOOST_CHECK_EQUAL( e.tag_, Error::INVALID_NUMBER_OF_ARGUMENTS );
}

// lexer, parser integration tests

BOOST_AUTO_TEST_CASE(simple_integration)
{
	const char raw[] = "A|A+ERROR+++E|A|L|p|m+";
	const std::vector<char> input = Message::from_human_readable(raw);

	std::vector<char> lexer_input( input.size()+2 );
	std::copy( input.cbegin(), input.cend(), lexer_input.begin() );

	yyscan_t scanner;
	int ret = yylex_init(&scanner);
	BOOST_REQUIRE_EQUAL(ret, 0);
	DEEPSTREAM_ON_EXIT( [&scanner] () {
		yylex_destroy(scanner);
	} );

	YY_BUFFER_STATE buffer = \
		yy_scan_buffer( lexer_input.data(), lexer_input.size(), scanner );
	yy_switch_to_buffer(buffer, scanner);

	State parser( input.data(), input.size() );
	yyset_extra(&parser, scanner);

	for( int ret = TOKEN_UNKNOWN; ret != TOKEN_EOF; ret = yylex(scanner) );

	BOOST_CHECK( parser.buffer_ == input.data() );
	BOOST_CHECK_EQUAL( parser.buffer_size_, input.size() );

	BOOST_CHECK( parser.tokenizing_header_ );
	BOOST_CHECK_EQUAL( parser.offset_, parser.buffer_size_ + 1 );

	BOOST_CHECK_EQUAL( parser.messages_.size(), 2 );
	BOOST_CHECK_EQUAL( parser.errors_.size(), 1 );

	const MessageProxy& msg_f = parser.messages_.front();
	BOOST_CHECK( msg_f.base() == input.data() );
	BOOST_CHECK_EQUAL( msg_f.offset(), 0 );
	BOOST_CHECK_EQUAL( msg_f.size(), 4 );
	BOOST_CHECK_EQUAL( msg_f.topic(), Topic::AUTH );
	BOOST_CHECK_EQUAL( msg_f.action(), Action::REQUEST );
	BOOST_CHECK( msg_f.is_ack() );
	BOOST_CHECK_EQUAL( msg_f.num_arguments(), 0 );

	const MessageProxy& msg_b = parser.messages_.back();
	BOOST_CHECK( msg_b.base() == input.data() );
	BOOST_CHECK_EQUAL( msg_b.offset(), 12 );
	BOOST_CHECK_EQUAL( msg_b.size(), 10 );
	BOOST_CHECK_EQUAL( msg_b.topic(), Topic::EVENT );
	BOOST_CHECK_EQUAL( msg_b.action(), Action::LISTEN );
	BOOST_CHECK( msg_b.is_ack() );
	BOOST_CHECK_EQUAL( msg_b.num_arguments(), 2 );

	const Error& error = parser.errors_.front();
	BOOST_CHECK_EQUAL( error.tag(), Error::UNEXPECTED_TOKEN );
}

BOOST_AUTO_TEST_CASE(random_messages)
{
	for(std::size_t iteration = 0; iteration < 50; ++iteration)
	{
		random::Engine engine(iteration);

		std::vector<Message::Header> headers;
		Buffer input;

		for(std::size_t i = 0; i < 20; ++i)
		{
			MessageBuilder message_builder = random::make_message(&engine);

			Buffer bin = message_builder.to_binary();
			input.insert( input.end(), bin.cbegin(), bin.cend() );

			headers.push_back( message_builder.header() );
		}

		input.push_back( '\0' );
		input.push_back( '\0' );

		auto ret = execute( input.data(), input.size() );

		const ErrorList& errors = ret.second;
		BOOST_CHECK( errors.empty() );

		const MessageList& messages = ret.first;

		BOOST_REQUIRE_EQUAL( messages.size(), headers.size() );

		auto i = headers.cbegin();
		auto j = messages.cbegin();
		for( ; i != headers.cend(); ++i, ++j )
			BOOST_CHECK_EQUAL( *i, j->header() );
	}
}

}
}
<|endoftext|>


<commit_before>/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/

#include "Globals.h"
#include "stdafx.h"
#include "parser.h"
#include "utilities.h"
#include "Logs.h"

#include "scenenodegroups.h"

/*
    MaSzyna EU07 locomotive simulator parser
    Copyright (C) 2003  TOLARIS

*/

/////////////////////////////////////////////////////////////////////////////////////////////////////
// cParser -- generic class for parsing text data.

// constructors
cParser::cParser( std::string const &Stream, buffertype const Type, std::string Path, bool const Loadtraction, std::vector<std::string> Parameters ) :
    mPath(Path),
    LoadTraction( Loadtraction ) {
    // store to calculate sub-sequent includes from relative path
    if( Type == buffertype::buffer_FILE ) {
        mFile = Stream;
    }
    // reset pointers and attach proper type of buffer
    switch (Type) {
        case buffer_FILE: {
            Path.append( Stream );
			mStream = std::make_shared<std::ifstream>( Path, std::ios_base::binary );
            // content of *.inc files is potentially grouped together
            if( ( Stream.size() >= 4 )
             && ( ToLower( Stream.substr( Stream.size() - 4 ) ) == ".inc" ) ) {
                mIncFile = true;
                scene::Groups.create();
            }
            break;
        }
        case buffer_TEXT: {
            mStream = std::make_shared<std::istringstream>( Stream );
            break;
        }
        default: {
            break;
        }
    }
    // calculate stream size
    if (mStream)
    {
        if( true == mStream->fail() ) {
            ErrorLog( "Failed to open file \"" + Path + "\"" );
        }
        else {
            mSize = mStream->rdbuf()->pubseekoff( 0, std::ios_base::end );
            mStream->rdbuf()->pubseekoff( 0, std::ios_base::beg );
            mLine = 1;
        }
    }
    // set parameter set if one was provided
    if( false == Parameters.empty() ) {
        parameters.swap( Parameters );
    }
}

// destructor
cParser::~cParser() {

    if( true == mIncFile ) {
        // wrap up the node group holding content of processed file
        scene::Groups.close();
    }
}

template <>
glm::vec3
cParser::getToken( bool const ToLower, char const *Break ) {
    // NOTE: this specialization ignores default arguments
    getTokens( 3, false, "\n\r\t  ,;[]" );
    glm::vec3 output;
    *this
        >> output.x
        >> output.y
        >> output.z;
    return output;
};

template<>
cParser&
cParser::operator>>( std::string &Right ) {

    if( true == this->tokens.empty() ) { return *this; }

    Right = this->tokens.front();
    this->tokens.pop_front();

    return *this;
}

template<>
cParser&
cParser::operator>>( bool &Right ) {

    if( true == this->tokens.empty() ) { return *this; }

    Right = ( ( this->tokens.front() == "true" )
           || ( this->tokens.front() == "yes" )
           || ( this->tokens.front() == "1" ) );
    this->tokens.pop_front();

    return *this;
}

template <>
bool
cParser::getToken<bool>( bool const ToLower, const char *Break ) {

    auto const token = getToken<std::string>( true, Break );
    return ( ( token == "true" )
          || ( token == "yes" )
          || ( token == "1" ) );
}

// methods
cParser &
cParser::autoclear( bool const Autoclear ) {

    m_autoclear = Autoclear;
    if( mIncludeParser ) { mIncludeParser->autoclear( Autoclear ); }

    return *this;
}

bool cParser::getTokens(unsigned int Count, bool ToLower, const char *Break)
{
    if( true == m_autoclear ) {
        // legacy parser behaviour
        tokens.clear();
    }
    /*
     if (LoadTraction==true)
      trtest="niemaproblema"; //wczytywać
     else
      trtest="x"; //nie wczytywać
    */
/*
    int i;
    this->str("");
    this->clear();
*/
    for (unsigned int i = tokens.size(); i < Count; ++i)
    {
        std::string token = readToken(ToLower, Break);
        if( true == token.empty() ) {
            // no more tokens
            break;
        }
        // collect parameters
        tokens.emplace_back( token );
/*
        if (i == 0)
            this->str(token);
        else
        {
            std::string temp = this->str();
            temp.append("\n");
            temp.append(token);
            this->str(temp);
        }
*/
    }
    if (tokens.size() < Count)
        return false;
    else
        return true;
}

std::string cParser::readToken( bool ToLower, const char *Break ) {

    std::string token;
    if( mIncludeParser ) {
        // see if there's include parsing going on. clean up when it's done.
        token = mIncludeParser->readToken( ToLower, Break );
        if( true == token.empty() ) {
            mIncludeParser = nullptr;
        }
    }
    if( true == token.empty() ) {
        // get the token yourself if the delegation attempt failed
        char c { 0 };
        do {
            while( mStream->peek() != EOF && strchr( Break, c = mStream->get() ) == NULL ) {
                if( ToLower )
                    c = tolower( c );
                token += c;
                if( findQuotes( token ) ) // do glue together words enclosed in quotes
                    continue;
                if( trimComments( token ) ) // don't glue together words separated with comment
                    break;
            }
            if( c == '\n' ) {
                // update line counter
                ++mLine;
            }
        } while( token == "" && mStream->peek() != EOF ); // double check in case of consecutive separators
    }
    // check the first token for potential presence of utf bom
    if( mFirstToken ) {
        mFirstToken = false;
        if( token.rfind( "\xef\xbb\xbf", 0 ) == 0 ) {
            token.erase( 0, 3 );
        }
        if( true == token.empty() ) {
            // potentially possible if our first token was standalone utf bom
            token = readToken( ToLower, Break );
        }
    }

    if( false == parameters.empty() ) {
        // if there's parameter list, check the token for potential parameters to replace
        size_t pos; // początek podmienianego ciągu
        while( ( pos = token.find( "(p" ) ) != std::string::npos ) {
            // check if the token is a parameter which should be replaced with stored true value
            auto const parameter{ token.substr( pos + 2, token.find( ")", pos ) - ( pos + 2 ) ) }; // numer parametru
            token.erase( pos, token.find( ")", pos ) - pos + 1 ); // najpierw usunięcie "(pN)"
            size_t nr = atoi( parameter.c_str() ) - 1;
            if( nr < parameters.size() ) {
                token.insert( pos, parameters.at( nr ) ); // wklejenie wartości parametru
                if( ToLower )
                    for( ; pos < parameters.at( nr ).size(); ++pos )
                        token[ pos ] = tolower( token[ pos ] );
            }
            else
                token.insert( pos, "none" ); // zabezpieczenie przed brakiem parametru
        }
    }

    // launch child parser if include directive found.
    // NOTE: parameter collecting uses default set of token separators.
	if( expandIncludes && token == "include" ) {
        std::string includefile = readToken(ToLower); // nazwa pliku
		std::replace(includefile.begin(), includefile.end(), '\\', '/');
        if( ( true == LoadTraction )
         || ( ( false == contains( includefile, "tr/" ) )
           && ( false == contains( includefile, "tra/" ) ) ) ) {
            if (Global.ParserLogIncludes)
                WriteLog("including: " + includefile);
            mIncludeParser = std::make_shared<cParser>( includefile, buffer_FILE, mPath, LoadTraction, readParameters( *this ) );
            mIncludeParser->autoclear( m_autoclear );
            if( mIncludeParser->mSize <= 0 ) {
                ErrorLog( "Bad include: can't open file \"" + includefile + "\"" );
            }
        }
        else {
            while( token != "end" ) {
                token = readToken( true ); // minimize risk of case mismatch on comparison
            }
        }
        token = readToken(ToLower, Break);
    }
    else if( ( std::strcmp( Break, "\n\r" ) == 0 ) && ( token.compare( 0, 7, "include" ) == 0 ) ) {
        // HACK: if the parser reads full lines we expect this line to contain entire include directive, to make parsing easier
        cParser includeparser( token.substr( 7 ) );
        std::string includefile = includeparser.readToken( ToLower ); // nazwa pliku
        if( ( true == LoadTraction )
         || ( ( false == contains( includefile, "tr/" ) )
           && ( false == contains( includefile, "tra/" ) ) ) ) {
            if (Global.ParserLogIncludes)
                WriteLog("including: " + includefile);
            mIncludeParser = std::make_shared<cParser>( includefile, buffer_FILE, mPath, LoadTraction, readParameters( includeparser ) );
            mIncludeParser->autoclear( m_autoclear );
            if( mIncludeParser->mSize <= 0 ) {
                ErrorLog( "Bad include: can't open file \"" + includefile + "\"" );
            }
        }
        token = readToken( ToLower, Break );
    }
    // all done
    return token;
}

std::vector<std::string> cParser::readParameters( cParser &Input ) {

    std::vector<std::string> includeparameters;
    std::string parameter = Input.readToken( false ); // w parametrach nie zmniejszamy
    while( ( parameter.empty() == false )
        && ( parameter != "end" ) ) {
        includeparameters.emplace_back( parameter );
        parameter = Input.readToken( false );
    }
    return includeparameters;
}

std::string cParser::readQuotes(char const Quote) { // read the stream until specified char or stream end
    std::string token = "";
    char c { 0 };
	bool escaped = false;
	while( mStream->peek() != EOF ) { // get all chars until the quote mark
		c = mStream->get();

		if (escaped) {
			escaped = false;
		}
		else {
			if (c == '\\') {
				escaped = true;
				continue;
			}
			else if (c == Quote)
				break;
		}

		if (c == '\n')
			++mLine; // update line counter
		token += c;
    }

	return token;
}

void cParser::skipComment( std::string const &Endmark ) { // pobieranie znaków aż do znalezienia znacznika końca
    std::string input = "";
    char c { 0 };
    auto const endmarksize = Endmark.size();
    while( mStream->peek() != EOF ) {
        // o ile nie koniec pliku
        c = mStream->get(); // pobranie znaku
        if( c == '\n' ) {
            // update line counter
            ++mLine;
        }
        input += c;
        if( input == Endmark ) // szukanie znacznika końca
            break;
        if( input.size() >= endmarksize ) {
            // keep the read text short, to avoid pointless string re-allocations on longer comments
            input = input.substr( 1 );
        }
    }
    return;
}

bool cParser::findQuotes( std::string &String ) {

    if( String.back() == '\"' ) {

        String.pop_back();
        String += readQuotes();
        return true;
    }
    return false;
}

bool cParser::trimComments(std::string &String)
{
    for (auto const &comment : mComments)
    {
        if( String.size() < comment.first.size() ) { continue; }

        if (String.compare( String.size() - comment.first.size(), comment.first.size(), comment.first ) == 0)
        {
            skipComment(comment.second);
            String.resize(String.rfind(comment.first));
            return true;
        }
    }
    return false;
}

void cParser::injectString(const std::string &str)
{
	if (mIncludeParser) {
		mIncludeParser->injectString(str);
	}
	else {
		mIncludeParser = std::make_shared<cParser>( str, buffer_TEXT, "", LoadTraction );
		mIncludeParser->autoclear( m_autoclear );
	}
}

int cParser::getProgress() const
{
    return static_cast<int>( mStream->rdbuf()->pubseekoff(0, std::ios_base::cur) * 100 / mSize );
}

int cParser::getFullProgress() const {

    int progress = getProgress();
    if( mIncludeParser )	return progress + ( ( 100 - progress )*( mIncludeParser->getProgress() ) / 100 );
    else					return progress;
}

std::size_t cParser::countTokens( std::string const &Stream, std::string Path ) {

    return cParser( Stream, buffer_FILE, Path ).count();
}

std::size_t cParser::count() {

    std::string token;
    size_t count { 0 };
    do {
        token = "";
        token = readToken( false );
        ++count;
    } while( false == token.empty() );

    return count - 1;
}

void cParser::addCommentStyle( std::string const &Commentstart, std::string const &Commentend ) {

    mComments.insert( commentmap::value_type(Commentstart, Commentend) );
}

// returns name of currently open file, or empty string for text type stream
std::string
cParser::Name() const {

    if( mIncludeParser ) { return mIncludeParser->Name(); }
    else                 { return mPath + mFile; }
}

// returns number of currently processed line
std::size_t
cParser::Line() const {

    if( mIncludeParser ) { return mIncludeParser->Line(); }
    else                 { return mLine; }
}

int cParser::LineMain() const {
	return mIncludeParser ? -1 : mLine;
}
<commit_msg>remove stray include<commit_after>/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/

#include "stdafx.h"
#include "parser.h"
#include "utilities.h"
#include "Logs.h"

#include "scenenodegroups.h"

/*
    MaSzyna EU07 locomotive simulator parser
    Copyright (C) 2003  TOLARIS

*/

/////////////////////////////////////////////////////////////////////////////////////////////////////
// cParser -- generic class for parsing text data.

// constructors
cParser::cParser( std::string const &Stream, buffertype const Type, std::string Path, bool const Loadtraction, std::vector<std::string> Parameters ) :
    mPath(Path),
    LoadTraction( Loadtraction ) {
    // store to calculate sub-sequent includes from relative path
    if( Type == buffertype::buffer_FILE ) {
        mFile = Stream;
    }
    // reset pointers and attach proper type of buffer
    switch (Type) {
        case buffer_FILE: {
            Path.append( Stream );
			mStream = std::make_shared<std::ifstream>( Path, std::ios_base::binary );
            // content of *.inc files is potentially grouped together
            if( ( Stream.size() >= 4 )
             && ( ToLower( Stream.substr( Stream.size() - 4 ) ) == ".inc" ) ) {
                mIncFile = true;
                scene::Groups.create();
            }
            break;
        }
        case buffer_TEXT: {
            mStream = std::make_shared<std::istringstream>( Stream );
            break;
        }
        default: {
            break;
        }
    }
    // calculate stream size
    if (mStream)
    {
        if( true == mStream->fail() ) {
            ErrorLog( "Failed to open file \"" + Path + "\"" );
        }
        else {
            mSize = mStream->rdbuf()->pubseekoff( 0, std::ios_base::end );
            mStream->rdbuf()->pubseekoff( 0, std::ios_base::beg );
            mLine = 1;
        }
    }
    // set parameter set if one was provided
    if( false == Parameters.empty() ) {
        parameters.swap( Parameters );
    }
}

// destructor
cParser::~cParser() {

    if( true == mIncFile ) {
        // wrap up the node group holding content of processed file
        scene::Groups.close();
    }
}

template <>
glm::vec3
cParser::getToken( bool const ToLower, char const *Break ) {
    // NOTE: this specialization ignores default arguments
    getTokens( 3, false, "\n\r\t  ,;[]" );
    glm::vec3 output;
    *this
        >> output.x
        >> output.y
        >> output.z;
    return output;
};

template<>
cParser&
cParser::operator>>( std::string &Right ) {

    if( true == this->tokens.empty() ) { return *this; }

    Right = this->tokens.front();
    this->tokens.pop_front();

    return *this;
}

template<>
cParser&
cParser::operator>>( bool &Right ) {

    if( true == this->tokens.empty() ) { return *this; }

    Right = ( ( this->tokens.front() == "true" )
           || ( this->tokens.front() == "yes" )
           || ( this->tokens.front() == "1" ) );
    this->tokens.pop_front();

    return *this;
}

template <>
bool
cParser::getToken<bool>( bool const ToLower, const char *Break ) {

    auto const token = getToken<std::string>( true, Break );
    return ( ( token == "true" )
          || ( token == "yes" )
          || ( token == "1" ) );
}

// methods
cParser &
cParser::autoclear( bool const Autoclear ) {

    m_autoclear = Autoclear;
    if( mIncludeParser ) { mIncludeParser->autoclear( Autoclear ); }

    return *this;
}

bool cParser::getTokens(unsigned int Count, bool ToLower, const char *Break)
{
    if( true == m_autoclear ) {
        // legacy parser behaviour
        tokens.clear();
    }
    /*
     if (LoadTraction==true)
      trtest="niemaproblema"; //wczytywać
     else
      trtest="x"; //nie wczytywać
    */
/*
    int i;
    this->str("");
    this->clear();
*/
    for (unsigned int i = tokens.size(); i < Count; ++i)
    {
        std::string token = readToken(ToLower, Break);
        if( true == token.empty() ) {
            // no more tokens
            break;
        }
        // collect parameters
        tokens.emplace_back( token );
/*
        if (i == 0)
            this->str(token);
        else
        {
            std::string temp = this->str();
            temp.append("\n");
            temp.append(token);
            this->str(temp);
        }
*/
    }
    if (tokens.size() < Count)
        return false;
    else
        return true;
}

std::string cParser::readToken( bool ToLower, const char *Break ) {

    std::string token;
    if( mIncludeParser ) {
        // see if there's include parsing going on. clean up when it's done.
        token = mIncludeParser->readToken( ToLower, Break );
        if( true == token.empty() ) {
            mIncludeParser = nullptr;
        }
    }
    if( true == token.empty() ) {
        // get the token yourself if the delegation attempt failed
        char c { 0 };
        do {
            while( mStream->peek() != EOF && strchr( Break, c = mStream->get() ) == NULL ) {
                if( ToLower )
                    c = tolower( c );
                token += c;
                if( findQuotes( token ) ) // do glue together words enclosed in quotes
                    continue;
                if( trimComments( token ) ) // don't glue together words separated with comment
                    break;
            }
            if( c == '\n' ) {
                // update line counter
                ++mLine;
            }
        } while( token == "" && mStream->peek() != EOF ); // double check in case of consecutive separators
    }
    // check the first token for potential presence of utf bom
    if( mFirstToken ) {
        mFirstToken = false;
        if( token.rfind( "\xef\xbb\xbf", 0 ) == 0 ) {
            token.erase( 0, 3 );
        }
        if( true == token.empty() ) {
            // potentially possible if our first token was standalone utf bom
            token = readToken( ToLower, Break );
        }
    }

    if( false == parameters.empty() ) {
        // if there's parameter list, check the token for potential parameters to replace
        size_t pos; // początek podmienianego ciągu
        while( ( pos = token.find( "(p" ) ) != std::string::npos ) {
            // check if the token is a parameter which should be replaced with stored true value
            auto const parameter{ token.substr( pos + 2, token.find( ")", pos ) - ( pos + 2 ) ) }; // numer parametru
            token.erase( pos, token.find( ")", pos ) - pos + 1 ); // najpierw usunięcie "(pN)"
            size_t nr = atoi( parameter.c_str() ) - 1;
            if( nr < parameters.size() ) {
                token.insert( pos, parameters.at( nr ) ); // wklejenie wartości parametru
                if( ToLower )
                    for( ; pos < parameters.at( nr ).size(); ++pos )
                        token[ pos ] = tolower( token[ pos ] );
            }
            else
                token.insert( pos, "none" ); // zabezpieczenie przed brakiem parametru
        }
    }

    // launch child parser if include directive found.
    // NOTE: parameter collecting uses default set of token separators.
	if( expandIncludes && token == "include" ) {
        std::string includefile = readToken(ToLower); // nazwa pliku
		std::replace(includefile.begin(), includefile.end(), '\\', '/');
        if( ( true == LoadTraction )
         || ( ( false == contains( includefile, "tr/" ) )
           && ( false == contains( includefile, "tra/" ) ) ) ) {
            if (Global.ParserLogIncludes)
                WriteLog("including: " + includefile);
            mIncludeParser = std::make_shared<cParser>( includefile, buffer_FILE, mPath, LoadTraction, readParameters( *this ) );
            mIncludeParser->autoclear( m_autoclear );
            if( mIncludeParser->mSize <= 0 ) {
                ErrorLog( "Bad include: can't open file \"" + includefile + "\"" );
            }
        }
        else {
            while( token != "end" ) {
                token = readToken( true ); // minimize risk of case mismatch on comparison
            }
        }
        token = readToken(ToLower, Break);
    }
    else if( ( std::strcmp( Break, "\n\r" ) == 0 ) && ( token.compare( 0, 7, "include" ) == 0 ) ) {
        // HACK: if the parser reads full lines we expect this line to contain entire include directive, to make parsing easier
        cParser includeparser( token.substr( 7 ) );
        std::string includefile = includeparser.readToken( ToLower ); // nazwa pliku
        if( ( true == LoadTraction )
         || ( ( false == contains( includefile, "tr/" ) )
           && ( false == contains( includefile, "tra/" ) ) ) ) {
            if (Global.ParserLogIncludes)
                WriteLog("including: " + includefile);
            mIncludeParser = std::make_shared<cParser>( includefile, buffer_FILE, mPath, LoadTraction, readParameters( includeparser ) );
            mIncludeParser->autoclear( m_autoclear );
            if( mIncludeParser->mSize <= 0 ) {
                ErrorLog( "Bad include: can't open file \"" + includefile + "\"" );
            }
        }
        token = readToken( ToLower, Break );
    }
    // all done
    return token;
}

std::vector<std::string> cParser::readParameters( cParser &Input ) {

    std::vector<std::string> includeparameters;
    std::string parameter = Input.readToken( false ); // w parametrach nie zmniejszamy
    while( ( parameter.empty() == false )
        && ( parameter != "end" ) ) {
        includeparameters.emplace_back( parameter );
        parameter = Input.readToken( false );
    }
    return includeparameters;
}

std::string cParser::readQuotes(char const Quote) { // read the stream until specified char or stream end
    std::string token = "";
    char c { 0 };
	bool escaped = false;
	while( mStream->peek() != EOF ) { // get all chars until the quote mark
		c = mStream->get();

		if (escaped) {
			escaped = false;
		}
		else {
			if (c == '\\') {
				escaped = true;
				continue;
			}
			else if (c == Quote)
				break;
		}

		if (c == '\n')
			++mLine; // update line counter
		token += c;
    }

	return token;
}

void cParser::skipComment( std::string const &Endmark ) { // pobieranie znaków aż do znalezienia znacznika końca
    std::string input = "";
    char c { 0 };
    auto const endmarksize = Endmark.size();
    while( mStream->peek() != EOF ) {
        // o ile nie koniec pliku
        c = mStream->get(); // pobranie znaku
        if( c == '\n' ) {
            // update line counter
            ++mLine;
        }
        input += c;
        if( input == Endmark ) // szukanie znacznika końca
            break;
        if( input.size() >= endmarksize ) {
            // keep the read text short, to avoid pointless string re-allocations on longer comments
            input = input.substr( 1 );
        }
    }
    return;
}

bool cParser::findQuotes( std::string &String ) {

    if( String.back() == '\"' ) {

        String.pop_back();
        String += readQuotes();
        return true;
    }
    return false;
}

bool cParser::trimComments(std::string &String)
{
    for (auto const &comment : mComments)
    {
        if( String.size() < comment.first.size() ) { continue; }

        if (String.compare( String.size() - comment.first.size(), comment.first.size(), comment.first ) == 0)
        {
            skipComment(comment.second);
            String.resize(String.rfind(comment.first));
            return true;
        }
    }
    return false;
}

void cParser::injectString(const std::string &str)
{
	if (mIncludeParser) {
		mIncludeParser->injectString(str);
	}
	else {
		mIncludeParser = std::make_shared<cParser>( str, buffer_TEXT, "", LoadTraction );
		mIncludeParser->autoclear( m_autoclear );
	}
}

int cParser::getProgress() const
{
    return static_cast<int>( mStream->rdbuf()->pubseekoff(0, std::ios_base::cur) * 100 / mSize );
}

int cParser::getFullProgress() const {

    int progress = getProgress();
    if( mIncludeParser )	return progress + ( ( 100 - progress )*( mIncludeParser->getProgress() ) / 100 );
    else					return progress;
}

std::size_t cParser::countTokens( std::string const &Stream, std::string Path ) {

    return cParser( Stream, buffer_FILE, Path ).count();
}

std::size_t cParser::count() {

    std::string token;
    size_t count { 0 };
    do {
        token = "";
        token = readToken( false );
        ++count;
    } while( false == token.empty() );

    return count - 1;
}

void cParser::addCommentStyle( std::string const &Commentstart, std::string const &Commentend ) {

    mComments.insert( commentmap::value_type(Commentstart, Commentend) );
}

// returns name of currently open file, or empty string for text type stream
std::string
cParser::Name() const {

    if( mIncludeParser ) { return mIncludeParser->Name(); }
    else                 { return mPath + mFile; }
}

// returns number of currently processed line
std::size_t
cParser::Line() const {

    if( mIncludeParser ) { return mIncludeParser->Line(); }
    else                 { return mLine; }
}

int cParser::LineMain() const {
	return mIncludeParser ? -1 : mLine;
}
<|endoftext|>