<commit_before>// $Id$
//
// Copyright (C) 2003-2008 Greg Landrum and Rational Discovery LLC
//
//  @@ All Rights Reserved @@
//  This file is part of the RDKit.
//  The contents are covered by the terms of the BSD license
//  which is included in the file license.txt, found at the root
//  of the RDKit source tree.
//
#include "Dict.h"

#include <boost/shared_array.hpp>
#include <boost/cstdint.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/utility.hpp>
#include <vector>
#include <list>
#include <iostream>
#include <sstream>
#include <RDGeneral/LocaleSwitcher.h>

namespace RDKit {
namespace {
template <class T>
std::string vectToString(const boost::any &val) {
  const std::vector<T> &tv = boost::any_cast<std::vector<T> >(val);
  Utils::LocaleSwitcher ls;
  std::ostringstream sstr;
  sstr << "[";
  std::copy(tv.begin(), tv.end(), std::ostream_iterator<T>(sstr, ","));
  sstr << "]";
  return sstr.str();
}
}

void Dict::getVal(const std::string &what, std::string &res) const {
  //
  //  We're going to try and be somewhat crafty about this getVal stuff to make
  //  these
  //  containers a little bit more generic.  The normal behavior here is that
  //  the
  //  value being queried must be directly castable to type T.  We'll robustify
  //  a
  //  little bit by trying that and, if the cast fails, attempting a couple of
  //  other casts, which will then be lexically cast to type T.
  //
  if (!hasVal(what)) throw KeyErrorException(what);
  const boost::any &val = _data.find(what)->second;
  Utils::LocaleSwitcher ls;
  try {
    res = boost::any_cast<std::string>(val);
  } catch (const boost::bad_any_cast &) {
    if (val.type() == typeid(int)) {
      res = boost::lexical_cast<std::string>(boost::any_cast<int>(val));
    } else if (val.type() == typeid(unsigned int)) {
      res =
          boost::lexical_cast<std::string>(boost::any_cast<unsigned int>(val));
    } else if (val.type() == typeid(long)) {
      res = boost::lexical_cast<std::string>(boost::any_cast<long>(val));
    } else if (val.type() == typeid(unsigned long)) {
      res =
          boost::lexical_cast<std::string>(boost::any_cast<unsigned long>(val));
    } else if (val.type() == typeid(float)) {
      res = boost::lexical_cast<std::string>(boost::any_cast<float>(val));
    } else if (val.type() == typeid(double)) {
      res = boost::lexical_cast<std::string>(boost::any_cast<double>(val));
    } else if (val.type() == typeid(const char *)) {
      res = std::string(boost::any_cast<const char *>(val));
    } else if (val.type() == typeid(std::vector<unsigned int>)) {
      res = vectToString<unsigned int>(val);
    } else if (val.type() == typeid(std::vector<int>)) {
      res = vectToString<int>(val);
    } else {
      throw;
    }
  }
};

bool Dict::getValIfPresent(const std::string &what, std::string &res) const {
  //
  //  We're going to try and be somewhat crafty about this getVal stuff to make
  //  these
  //  containers a little bit more generic.  The normal behavior here is that
  //  the
  //  value being queried must be directly castable to type T.  We'll robustify
  //  a
  //  little bit by trying that and, if the cast fails, attempting a couple of
  //  other casts, which will then be lexically cast to type T.
  //
  DataType::const_iterator pos = _data.find(what);
  if (pos == _data.end()) return false;
  const boost::any &val = pos->second;
  Utils::LocaleSwitcher ls;
  try {
    res = boost::any_cast<std::string>(val);
  } catch (const boost::bad_any_cast &) {
    if (val.type() == typeid(int)) {
      res = boost::lexical_cast<std::string>(boost::any_cast<int>(val));
    } else if (val.type() == typeid(unsigned int)) {
      res =
          boost::lexical_cast<std::string>(boost::any_cast<unsigned int>(val));
    } else if (val.type() == typeid(long)) {
      res = boost::lexical_cast<std::string>(boost::any_cast<long>(val));
    } else if (val.type() == typeid(unsigned long)) {
      res =
          boost::lexical_cast<std::string>(boost::any_cast<unsigned long>(val));
    } else if (val.type() == typeid(float)) {
      res = boost::lexical_cast<std::string>(boost::any_cast<float>(val));
    } else if (val.type() == typeid(double)) {
      res = boost::lexical_cast<std::string>(boost::any_cast<double>(val));
    } else if (val.type() == typeid(const char *)) {
      res = std::string(boost::any_cast<const char *>(val));
    } else if (val.type() == typeid(std::vector<unsigned int>)) {
      res = vectToString<unsigned int>(val);
    } else if (val.type() == typeid(std::vector<int>)) {
      res = vectToString<int>(val);
    } else {
      return false;
    }
  }
  return true;
};

namespace {
template <class T>
typename boost::enable_if<boost::is_arithmetic<T>, T>::type _fromany(
    const boost::any &arg) {
  T res;
  if (arg.type() == typeid(std::string) || arg.type() == typeid(const char *)) {
    Utils::LocaleSwitcher ls;
    try {
      res = boost::any_cast<T>(arg);
    } catch (const boost::bad_any_cast &exc) {
      try {
        res = boost::lexical_cast<T>(boost::any_cast<std::string>(arg));
      } catch (...) {
        throw exc;
      }
    }
  } else {
    res = boost::any_cast<T>(arg);
  }
  return res;
}
template <class T>
typename boost::disable_if<boost::is_arithmetic<T>, T>::type _fromany(
    const boost::any &arg) {
  return boost::any_cast<T>(arg);
}
}
template <typename T>
T Dict::fromany(const boost::any &arg) const {
  return _fromany<T>(arg);
};
template <typename T>
boost::any Dict::toany(T arg) const {
  return boost::any(arg);
};

#define ANY_FORCE(T)                                        \
  template T Dict::fromany<T>(const boost::any &arg) const; \
  template boost::any Dict::toany<T>(T arg) const;

ANY_FORCE(bool);
ANY_FORCE(boost::shared_array<double>);
ANY_FORCE(boost::shared_array<int>);
ANY_FORCE(double);
ANY_FORCE(int);
ANY_FORCE(std::list<int>);
ANY_FORCE(std::string);
ANY_FORCE(std::vector<boost::shared_array<double> >);
ANY_FORCE(std::vector<boost::shared_array<int> >);
ANY_FORCE(std::vector<double>);
ANY_FORCE(std::vector<int>);
ANY_FORCE(std::vector<std::list<int> >);
ANY_FORCE(std::vector<std::string>);
ANY_FORCE(std::vector<std::vector<double> >);
ANY_FORCE(std::vector<std::vector<int> >);
ANY_FORCE(std::vector<unsigned int>);
ANY_FORCE(std::vector<unsigned long long>);
ANY_FORCE(unsigned int);

template const std::string &Dict::fromany<const std::string &>(
    const boost::any &arg) const;

typedef boost::tuples::tuple<boost::uint32_t, boost::uint32_t, boost::uint32_t>
    uint32_t_tuple;
typedef boost::tuples::tuple<double, double, double> double_tuple;

template uint32_t_tuple Dict::fromany<uint32_t_tuple>(
    const boost::any &arg) const;
template boost::any Dict::toany<uint32_t_tuple>(uint32_t_tuple arg) const;
template double_tuple Dict::fromany<double_tuple>(const boost::any &arg) const;
template boost::any Dict::toany<double_tuple>(double_tuple arg) const;
}
<commit_msg>Fixes #703<commit_after>// $Id$
//
// Copyright (C) 2003-2008 Greg Landrum and Rational Discovery LLC
//
//  @@ All Rights Reserved @@
//  This file is part of the RDKit.
//  The contents are covered by the terms of the BSD license
//  which is included in the file license.txt, found at the root
//  of the RDKit source tree.
//
#include "Dict.h"

#include <boost/shared_array.hpp>
#include <boost/cstdint.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/utility.hpp>
#include <vector>
#include <list>
#include <iostream>
#include <sstream>
#include <RDGeneral/LocaleSwitcher.h>

namespace RDKit {
namespace {
template <class T>
std::string vectToString(const boost::any &val) {
  const std::vector<T> &tv = boost::any_cast<std::vector<T> >(val);
  std::ostringstream sstr;
  sstr << "[";
  std::copy(tv.begin(), tv.end(), std::ostream_iterator<T>(sstr, ","));
  sstr << "]";
  return sstr.str();
}
std::string intVectToString(const boost::any &val) {
  return vectToString<int>(val);
}
std::string uintVectToString(const boost::any &val) {
  return vectToString<unsigned int>(val);
}
std::string doubleVectToString(const boost::any &val) {
  // there is a performance cost associated with the
  // LocaleSwitcher, so make sure we only instantiate it when
  // it's really necessary (this was github #703)
  Utils::LocaleSwitcher ls;
  return vectToString<double>(val);
}
}

void Dict::getVal(const std::string &what, std::string &res) const {
  //
  //  We're going to try and be somewhat crafty about this getVal stuff to make
  //  these
  //  containers a little bit more generic.  The normal behavior here is that
  //  the
  //  value being queried must be directly castable to type T.  We'll robustify
  //  a
  //  little bit by trying that and, if the cast fails, attempting a couple of
  //  other casts, which will then be lexically cast to type T.
  //
  if (!hasVal(what)) throw KeyErrorException(what);
  const boost::any &val = _data.find(what)->second;
  try {
    res = boost::any_cast<std::string>(val);
  } catch (const boost::bad_any_cast &) {
    if (val.type() == typeid(int)) {
      res = boost::lexical_cast<std::string>(boost::any_cast<int>(val));
    } else if (val.type() == typeid(unsigned int)) {
      res =
          boost::lexical_cast<std::string>(boost::any_cast<unsigned int>(val));
    } else if (val.type() == typeid(long)) {
      res = boost::lexical_cast<std::string>(boost::any_cast<long>(val));
    } else if (val.type() == typeid(unsigned long)) {
      res =
          boost::lexical_cast<std::string>(boost::any_cast<unsigned long>(val));
    } else if (val.type() == typeid(float)) {
      Utils::LocaleSwitcher ls;  // late instantiation of LocaleSwitcher
      res = boost::lexical_cast<std::string>(boost::any_cast<float>(val));
    } else if (val.type() == typeid(double)) {
      Utils::LocaleSwitcher ls;  // late instantiation of LocaleSwitcher
      res = boost::lexical_cast<std::string>(boost::any_cast<double>(val));
    } else if (val.type() == typeid(const char *)) {
      res = std::string(boost::any_cast<const char *>(val));
    } else if (val.type() == typeid(std::vector<unsigned int>)) {
      res = uintVectToString(val);
    } else if (val.type() == typeid(std::vector<int>)) {
      res = intVectToString(val);
    } else {
      throw;
    }
  }
};

bool Dict::getValIfPresent(const std::string &what, std::string &res) const {
  //
  //  We're going to try and be somewhat crafty about this getVal stuff to make
  //  these
  //  containers a little bit more generic.  The normal behavior here is that
  //  the
  //  value being queried must be directly castable to type T.  We'll robustify
  //  a
  //  little bit by trying that and, if the cast fails, attempting a couple of
  //  other casts, which will then be lexically cast to type T.
  //
  DataType::const_iterator pos = _data.find(what);
  if (pos == _data.end()) return false;
  const boost::any &val = pos->second;
  try {
    res = boost::any_cast<std::string>(val);
  } catch (const boost::bad_any_cast &) {
    if (val.type() == typeid(int)) {
      res = boost::lexical_cast<std::string>(boost::any_cast<int>(val));
    } else if (val.type() == typeid(unsigned int)) {
      res =
          boost::lexical_cast<std::string>(boost::any_cast<unsigned int>(val));
    } else if (val.type() == typeid(long)) {
      res = boost::lexical_cast<std::string>(boost::any_cast<long>(val));
    } else if (val.type() == typeid(unsigned long)) {
      res =
          boost::lexical_cast<std::string>(boost::any_cast<unsigned long>(val));
    } else if (val.type() == typeid(float)) {
      Utils::LocaleSwitcher ls;  // late instantiation of LocaleSwitcher
      res = boost::lexical_cast<std::string>(boost::any_cast<float>(val));
    } else if (val.type() == typeid(double)) {
      Utils::LocaleSwitcher ls;  // late instantiation of LocaleSwitcher
      res = boost::lexical_cast<std::string>(boost::any_cast<double>(val));
    } else if (val.type() == typeid(const char *)) {
      res = std::string(boost::any_cast<const char *>(val));
    } else if (val.type() == typeid(std::vector<unsigned int>)) {
      res = uintVectToString(val);
    } else if (val.type() == typeid(std::vector<int>)) {
      res = intVectToString(val);
    } else {
      return false;
    }
  }
  return true;
};

namespace {
template <class T>
typename boost::enable_if<boost::is_floating_point<T>, T>::type _fromany(
    const boost::any &arg) {
  T res;
  if (arg.type() == typeid(std::string) || arg.type() == typeid(const char *)) {
    Utils::LocaleSwitcher ls;  // late instantiation of LocaleSwitcher
    try {
      res = boost::any_cast<T>(arg);
    } catch (const boost::bad_any_cast &exc) {
      try {
        res = boost::lexical_cast<T>(boost::any_cast<std::string>(arg));
      } catch (...) {
        throw exc;
      }
    }
  } else {
    res = boost::any_cast<T>(arg);
  }
  return res;
}
template <class T>
typename boost::enable_if<boost::is_integral<T>, T>::type _fromany(
    const boost::any &arg) {
  T res;
  if (arg.type() == typeid(std::string) || arg.type() == typeid(const char *)) {
    try {
      res = boost::any_cast<T>(arg);
    } catch (const boost::bad_any_cast &exc) {
      try {
        res = boost::lexical_cast<T>(boost::any_cast<std::string>(arg));
      } catch (...) {
        throw exc;
      }
    }
  } else {
    res = boost::any_cast<T>(arg);
  }
  return res;
}
template <class T>
typename boost::disable_if<boost::is_arithmetic<T>, T>::type _fromany(
    const boost::any &arg) {
  return boost::any_cast<T>(arg);
}
}
template <typename T>
T Dict::fromany(const boost::any &arg) const {
  return _fromany<T>(arg);
};
template <typename T>
boost::any Dict::toany(T arg) const {
  return boost::any(arg);
};

#define ANY_FORCE(T)                                        \
  template T Dict::fromany<T>(const boost::any &arg) const; \
  template boost::any Dict::toany<T>(T arg) const;

ANY_FORCE(bool);
ANY_FORCE(boost::shared_array<double>);
ANY_FORCE(boost::shared_array<int>);
ANY_FORCE(double);
ANY_FORCE(int);
ANY_FORCE(std::list<int>);
ANY_FORCE(std::string);
ANY_FORCE(std::vector<boost::shared_array<double> >);
ANY_FORCE(std::vector<boost::shared_array<int> >);
ANY_FORCE(std::vector<double>);
ANY_FORCE(std::vector<int>);
ANY_FORCE(std::vector<std::list<int> >);
ANY_FORCE(std::vector<std::string>);
ANY_FORCE(std::vector<std::vector<double> >);
ANY_FORCE(std::vector<std::vector<int> >);
ANY_FORCE(std::vector<unsigned int>);
ANY_FORCE(std::vector<unsigned long long>);
ANY_FORCE(unsigned int);

template const std::string &Dict::fromany<const std::string &>(
    const boost::any &arg) const;

typedef boost::tuples::tuple<boost::uint32_t, boost::uint32_t, boost::uint32_t>
    uint32_t_tuple;
typedef boost::tuples::tuple<double, double, double> double_tuple;

template uint32_t_tuple Dict::fromany<uint32_t_tuple>(
    const boost::any &arg) const;
template boost::any Dict::toany<uint32_t_tuple>(uint32_t_tuple arg) const;
template double_tuple Dict::fromany<double_tuple>(const boost::any &arg) const;
template boost::any Dict::toany<double_tuple>(double_tuple arg) const;
}
<|endoftext|>


<commit_before>#include "./connector.h"
#include "./errors.h"
#include "./uuid.h"

#include "../message/errors.h"

// TODO(ale): include logging library and uncomment logging macros

#include <boost/date_time/posix_time/posix_time.hpp>

#include <cstdio>
#include <iostream>

namespace CthunClient {

//
// Constants
//

static const uint CONNECTION_CHECK_INTERVAL { 15 };  // [s]
static const int DEFAULT_MSG_TIMEOUT { 10 };  // [s]

//
// Utility functions
//

// TODO(ale): move this to leatherman
std::string getISO8601Time(unsigned int modifier_in_seconds) {
    boost::posix_time::ptime t = boost::posix_time::microsec_clock::universal_time()
                                 + boost::posix_time::seconds(modifier_in_seconds);
    return boost::posix_time::to_iso_extended_string(t) + "Z";
}

//
// Public api
//

Connector::Connector(const std::string& server_url,
                     const std::string& type,
                     const std::string& ca_crt_path,
                     const std::string& client_crt_path,
                     const std::string& client_key_path)
        : server_url_ { server_url },
          client_metadata_ { type, ca_crt_path, client_crt_path, client_key_path },
          connection_ptr_ { nullptr },
          validator_ {},
          schema_callback_pairs_ {},
          monitor_task_ {},
          mutex_ {},
          cond_var_ {},
          is_destructing_ { false },
          is_monitoring_ { false },
          monitor_timer_ {} {
    addEnvelopeSchemaToValidator_();
}

Connector::~Connector() {
    if (connection_ptr_ != nullptr) {
        // reset callbacks to avoid breaking the Connection instance
        // due to callbacks having an invalid reference context
        // LOG_INFO("Resetting the WebSocket event callbacks");
        connection_ptr_->resetCallbacks();
    }

    {
        std::lock_guard<std::mutex> the_lock { mutex_ };
        is_destructing_ = true;
        cond_var_.notify_one();
    }

    if (monitor_task_.joinable()) {
        monitor_task_.join();
    }
}

// Register schemas and onMessage callbacks

void Connector::registerMessageCallback(const Schema schema,
                                        MessageCallback callback) {
    validator_.registerSchema(schema);
    auto p = std::pair<std::string, MessageCallback>(schema.getName(), callback);
    schema_callback_pairs_.insert(p);
}

// Manage the connection state

void Connector::connect(int max_connect_attempts) {
    if (connection_ptr_ == nullptr) {
        // Initialize the WebSocket connection
        connection_ptr_.reset(new Connection(server_url_, client_metadata_));

        connection_ptr_->setOnMessageCallback(
            [this](std::string message) {
                processMessage_(message);
            });
    }

    try {
        // Open the WebSocket connection
        connection_ptr_->connect(max_connect_attempts);
        // Send the login message
        sendLogin_();
    } catch (connection_processing_error& e) {
        // NB: connection_fatal_errors are propagated whereas
        //     connection_processing_errors are converted to
        //     connection_config_errors (they can be thrown after
        //     websocketpp::Endpoint::connect() or ::send() failures)

        // LOG_ERROR("Failed to connect: %1%", e.what());
        throw connection_config_error { e.what() };
    }
}

bool Connector::isConnected() const {
    // TODO(ale): make this consistent with the login transaction as
    // specified in the protocol specs (perhaps with a logged-in flag)

    return connection_ptr_ != nullptr
           && connection_ptr_->getConnectionState() == ConnectionStateValues::open;
}

void Connector::enablePersistence(int max_connect_attempts) {
    checkConnectionInitialization_();

    if (!is_monitoring_) {
        if (monitor_task_.joinable()) {
            // Detach the old task
            monitor_task_.detach();
        }

        is_monitoring_ = true;
        monitor_task_ = std::move(
            std::thread(&Connector::monitorConnectionTask_,
                        this, max_connect_attempts));
    } else {
        // LOG_WARNING("The monitoring task that enables persistence "
        //             "is running")
    }
}

// Send messages

void Connector::send(const Message& msg) {
    checkConnectionInitialization_();
    auto serialized_msg = msg.getSerialized();
    // LOG_DEBUG("Sending message of %1% bytes:\n%2%",
    //           serialized_msg.size(), serialized_msg);
    connection_ptr_->send(&serialized_msg[0], serialized_msg.size());
}

void Connector::send(const std::vector<std::string>& endpoints,
                     const std::string& data_schema,
                     unsigned int timeout,
                     const DataContainer& data_json,
                     const std::vector<DataContainer>& debug) {
    sendMessage_(endpoints,
                 data_schema,
                 timeout,
                 data_json.toString(),
                 debug);
}

void Connector::send(const std::vector<std::string>& endpoints,
                     const std::string& data_schema,
                     unsigned int timeout,
                     const std::string& data_binary,
                     const std::vector<DataContainer>& debug) {
    sendMessage_(endpoints,
                 data_schema,
                 timeout,
                 data_binary,
                 debug);
}

//
// Private interface
//

// Utility functions

void Connector::checkConnectionInitialization_() {
    if (connection_ptr_ == nullptr) {
        throw connection_not_init_error { "connection not initialized" };
    }
}

void Connector::addEnvelopeSchemaToValidator_() {
    Schema schema { ENVELOPE_SCHEMA_NAME, ContentType::Json };

    schema.addConstraint("id", TypeConstraint::String, true);
    schema.addConstraint("expires", TypeConstraint::String, true);
    schema.addConstraint("sender", TypeConstraint::String, true);
    schema.addConstraint("endpoints", TypeConstraint::Array, true);
    schema.addConstraint("data_schema", TypeConstraint::String, true);
    schema.addConstraint("destination_report", TypeConstraint::Bool, false);

    validator_.registerSchema(schema);
}

MessageChunk Connector::createEnvelope_(const std::vector<std::string>& endpoints,
                                        const std::string& data_schema,
                                        unsigned int timeout) {
    auto msg_id = UUID::getUUID();
    auto expires = getISO8601Time(timeout);
    // LOG_INFO("Creating message with id %1% for %2%" receiver%3%",
    //          msg_id, endpoints.size(), StringUtils::plural(endpoints.size()));

    DataContainer envelope_content {};

    envelope_content.set<std::string>("id", msg_id);
    envelope_content.set<std::string>("expires", expires);
    envelope_content.set<std::string>("sender", client_metadata_.id);
    envelope_content.set<std::string>("data_schema", data_schema);
    envelope_content.set<std::vector<std::string>>("endpoints", endpoints);

    return MessageChunk { ChunkDescriptor::ENVELOPE, envelope_content.toString() };
}

void Connector::sendMessage_(const std::vector<std::string>& endpoints,
                             const std::string& data_schema,
                             unsigned int timeout,
                             const std::string& data_txt,
                             const std::vector<DataContainer>& debug) {
    auto envelope_chunk = createEnvelope_(endpoints, data_schema, timeout);
    MessageChunk data_chunk { ChunkDescriptor::DATA, data_txt };
    Message msg { envelope_chunk, data_chunk };

    for (auto debug_content : debug) {
        MessageChunk d_c { ChunkDescriptor::DEBUG, debug_content.toString() };
        msg.addDebugChunk(d_c);
    }

    send(msg);
}

// Login

void Connector::sendLogin_() {
    // Envelope
    auto envelope = createEnvelope_(std::vector<std::string> { "cth://server" },
                                    CTHUN_LOGIN_SCHEMA_NAME,
                                    DEFAULT_MSG_TIMEOUT);

    // Data
    DataContainer data_entries {};
    data_entries.set<std::string>("type", client_metadata_.type);
    MessageChunk data { ChunkDescriptor::DATA, data_entries.toString() };

    // Create and send message
    Message msg { envelope, data };
    // LOG_INFO("Sending login message with id: %1%",
    //          envelope_entries.get<std::string>("id"));
    // LOG_DEBUG("Login message data: %1%", data.content);
    send(msg);
}

// WebSocket onMessage callback

void Connector::processMessage_(const std::string& msg_txt) {
    // LOG_DEBUG("Received message of %1% bytes:\n%2%", msg_txt.size(), msg_txt);

    // Deserialize the incoming message
    std::unique_ptr<Message> msg_ptr;
    try {
        msg_ptr.reset(new Message(msg_txt));
    } catch (message_error& e) {
        // LOG_ERROR("Failed to deserialize message: %1%", e.what());
        return;
    }

    // Parse message chunks
    ParsedChunks parsed_chunks;
    try {
        parsed_chunks = msg_ptr->getParsedChunks(validator_);
    } catch (validator_error& e) {
        // LOG_ERROR("Invalid message: %1%", e.what());
        return;
    }

    // Execute the callback associated with the data schema
    auto schema_name = parsed_chunks.envelope.get<std::string>("data_schema");

    if (schema_callback_pairs_.find(schema_name) != schema_callback_pairs_.end()) {
        auto c_b = schema_callback_pairs_.at(schema_name);
        // LOG_TRACE("Executing callback for a message with '%1%' schema",
        //           schema_name);
        c_b(parsed_chunks);
    } else {
        // LOG_WARNING("No message callback has be registered for '%1%' schema",
        //             schema_name);
    }
}

// Monitor task

void Connector::monitorConnectionTask_(int max_connect_attempts) {
    assert(connection_ptr_ != nullptr);

    while (true) {
        std::unique_lock<std::mutex> the_lock { mutex_ };
        monitor_timer_.reset();

        cond_var_.wait(the_lock,
            [this] {
                return is_destructing_
                       || monitor_timer_.elapsedSeconds() < CONNECTION_CHECK_INTERVAL;
            });

        if (is_destructing_) {
            // The dtor has been invoked
            // LOG_INFO("Stopping the monitor task (persistence)");
            return;
        }

        try {
            if (connection_ptr_->getConnectionState() != ConnectionStateValues::open) {
                // LOG_WARNING("Connection to Cthun server lost; retrying");
                connection_ptr_->connect(max_connect_attempts);
            } else {
                // LOG_DEBUG("Sending heartbeat ping");
                connection_ptr_->ping();
            }
        } catch (connection_processing_error& e) {
            // Connection::connect_() failure - keep trying
            // LOG_ERROR("Monitoring task (persistence) failure: %1%", e.what());
        } catch (connection_fatal_error& e) {
            // Failed to reconnect after max_connect_attempts - stop
            // LOG_ERROR("The monitoring task (persistence) will stop - "
            //           "failure: %1%", e.what());

            // TODO(ale): evaluate exposing is_monitoring_ - update docs

            is_monitoring_ = false;
            the_lock.unlock();
            return;
        }

        the_lock.unlock();
    }
}

}  // namespace CthunClient
<commit_msg>(maint) Fix Connector::monitor_thread_<commit_after>#include "./connector.h"
#include "./errors.h"
#include "./uuid.h"

#include "../message/errors.h"

// TODO(ale): include logging library and uncomment logging macros

#include <boost/date_time/posix_time/posix_time.hpp>

#include <cstdio>
#include <iostream>

namespace CthunClient {

//
// Constants
//

static const uint CONNECTION_CHECK_INTERVAL { 15 };  // [s]
static const int DEFAULT_MSG_TIMEOUT { 10 };  // [s]

//
// Utility functions
//

// TODO(ale): move this to leatherman
std::string getISO8601Time(unsigned int modifier_in_seconds) {
    boost::posix_time::ptime t = boost::posix_time::microsec_clock::universal_time()
                                 + boost::posix_time::seconds(modifier_in_seconds);
    return boost::posix_time::to_iso_extended_string(t) + "Z";
}

//
// Public api
//

Connector::Connector(const std::string& server_url,
                     const std::string& type,
                     const std::string& ca_crt_path,
                     const std::string& client_crt_path,
                     const std::string& client_key_path)
        : server_url_ { server_url },
          client_metadata_ { type, ca_crt_path, client_crt_path, client_key_path },
          connection_ptr_ { nullptr },
          validator_ {},
          schema_callback_pairs_ {},
          monitor_task_ {},
          mutex_ {},
          cond_var_ {},
          is_destructing_ { false },
          is_monitoring_ { false },
          monitor_timer_ {} {
    addEnvelopeSchemaToValidator_();
}

Connector::~Connector() {
    if (connection_ptr_ != nullptr) {
        // reset callbacks to avoid breaking the Connection instance
        // due to callbacks having an invalid reference context
        // LOG_INFO("Resetting the WebSocket event callbacks");
        connection_ptr_->resetCallbacks();
    }

    {
        std::lock_guard<std::mutex> the_lock { mutex_ };
        is_destructing_ = true;
        cond_var_.notify_one();
    }

    if (monitor_task_.joinable()) {
        monitor_task_.join();
    }
}

// Register schemas and onMessage callbacks

void Connector::registerMessageCallback(const Schema schema,
                                        MessageCallback callback) {
    validator_.registerSchema(schema);
    auto p = std::pair<std::string, MessageCallback>(schema.getName(), callback);
    schema_callback_pairs_.insert(p);
}

// Manage the connection state

void Connector::connect(int max_connect_attempts) {
    if (connection_ptr_ == nullptr) {
        // Initialize the WebSocket connection
        connection_ptr_.reset(new Connection(server_url_, client_metadata_));

        connection_ptr_->setOnMessageCallback(
            [this](std::string message) {
                processMessage_(message);
            });
    }

    try {
        // Open the WebSocket connection
        connection_ptr_->connect(max_connect_attempts);
        // Send the login message
        sendLogin_();
    } catch (connection_processing_error& e) {
        // NB: connection_fatal_errors are propagated whereas
        //     connection_processing_errors are converted to
        //     connection_config_errors (they can be thrown after
        //     websocketpp::Endpoint::connect() or ::send() failures)

        // LOG_ERROR("Failed to connect: %1%", e.what());
        throw connection_config_error { e.what() };
    }
}

bool Connector::isConnected() const {
    // TODO(ale): make this consistent with the login transaction as
    // specified in the protocol specs (perhaps with a logged-in flag)

    return connection_ptr_ != nullptr
           && connection_ptr_->getConnectionState() == ConnectionStateValues::open;
}

void Connector::enablePersistence(int max_connect_attempts) {
    checkConnectionInitialization_();

    if (!is_monitoring_) {
        if (monitor_task_.joinable()) {
            // Detach the old task
            monitor_task_.detach();
        }

        is_monitoring_ = true;
        monitor_task_ = std::move(
            std::thread(&Connector::monitorConnectionTask_,
                        this, max_connect_attempts));
    } else {
        // LOG_WARNING("The monitoring task that enables persistence "
        //             "is running")
    }
}

// Send messages

void Connector::send(const Message& msg) {
    checkConnectionInitialization_();
    auto serialized_msg = msg.getSerialized();
    // LOG_DEBUG("Sending message of %1% bytes:\n%2%",
    //           serialized_msg.size(), serialized_msg);
    connection_ptr_->send(&serialized_msg[0], serialized_msg.size());
}

void Connector::send(const std::vector<std::string>& endpoints,
                     const std::string& data_schema,
                     unsigned int timeout,
                     const DataContainer& data_json,
                     const std::vector<DataContainer>& debug) {
    sendMessage_(endpoints,
                 data_schema,
                 timeout,
                 data_json.toString(),
                 debug);
}

void Connector::send(const std::vector<std::string>& endpoints,
                     const std::string& data_schema,
                     unsigned int timeout,
                     const std::string& data_binary,
                     const std::vector<DataContainer>& debug) {
    sendMessage_(endpoints,
                 data_schema,
                 timeout,
                 data_binary,
                 debug);
}

//
// Private interface
//

// Utility functions

void Connector::checkConnectionInitialization_() {
    if (connection_ptr_ == nullptr) {
        throw connection_not_init_error { "connection not initialized" };
    }
}

void Connector::addEnvelopeSchemaToValidator_() {
    Schema schema { ENVELOPE_SCHEMA_NAME, ContentType::Json };

    schema.addConstraint("id", TypeConstraint::String, true);
    schema.addConstraint("expires", TypeConstraint::String, true);
    schema.addConstraint("sender", TypeConstraint::String, true);
    schema.addConstraint("endpoints", TypeConstraint::Array, true);
    schema.addConstraint("data_schema", TypeConstraint::String, true);
    schema.addConstraint("destination_report", TypeConstraint::Bool, false);

    validator_.registerSchema(schema);
}

MessageChunk Connector::createEnvelope_(const std::vector<std::string>& endpoints,
                                        const std::string& data_schema,
                                        unsigned int timeout) {
    auto msg_id = UUID::getUUID();
    auto expires = getISO8601Time(timeout);
    // LOG_INFO("Creating message with id %1% for %2%" receiver%3%",
    //          msg_id, endpoints.size(), StringUtils::plural(endpoints.size()));

    DataContainer envelope_content {};

    envelope_content.set<std::string>("id", msg_id);
    envelope_content.set<std::string>("expires", expires);
    envelope_content.set<std::string>("sender", client_metadata_.id);
    envelope_content.set<std::string>("data_schema", data_schema);
    envelope_content.set<std::vector<std::string>>("endpoints", endpoints);

    return MessageChunk { ChunkDescriptor::ENVELOPE, envelope_content.toString() };
}

void Connector::sendMessage_(const std::vector<std::string>& endpoints,
                             const std::string& data_schema,
                             unsigned int timeout,
                             const std::string& data_txt,
                             const std::vector<DataContainer>& debug) {
    auto envelope_chunk = createEnvelope_(endpoints, data_schema, timeout);
    MessageChunk data_chunk { ChunkDescriptor::DATA, data_txt };
    Message msg { envelope_chunk, data_chunk };

    for (auto debug_content : debug) {
        MessageChunk d_c { ChunkDescriptor::DEBUG, debug_content.toString() };
        msg.addDebugChunk(d_c);
    }

    send(msg);
}

// Login

void Connector::sendLogin_() {
    // Envelope
    auto envelope = createEnvelope_(std::vector<std::string> { "cth://server" },
                                    CTHUN_LOGIN_SCHEMA_NAME,
                                    DEFAULT_MSG_TIMEOUT);

    // Data
    DataContainer data_entries {};
    data_entries.set<std::string>("type", client_metadata_.type);
    MessageChunk data { ChunkDescriptor::DATA, data_entries.toString() };

    // Create and send message
    Message msg { envelope, data };
    // LOG_INFO("Sending login message with id: %1%",
    //          envelope_entries.get<std::string>("id"));
    // LOG_DEBUG("Login message data: %1%", data.content);
    send(msg);
}

// WebSocket onMessage callback

void Connector::processMessage_(const std::string& msg_txt) {
    // LOG_DEBUG("Received message of %1% bytes:\n%2%", msg_txt.size(), msg_txt);

    // Deserialize the incoming message
    std::unique_ptr<Message> msg_ptr;
    try {
        msg_ptr.reset(new Message(msg_txt));
    } catch (message_error& e) {
        // LOG_ERROR("Failed to deserialize message: %1%", e.what());
        return;
    }

    // Parse message chunks
    ParsedChunks parsed_chunks;
    try {
        parsed_chunks = msg_ptr->getParsedChunks(validator_);
    } catch (validator_error& e) {
        // LOG_ERROR("Invalid message: %1%", e.what());
        return;
    }

    // Execute the callback associated with the data schema
    auto schema_name = parsed_chunks.envelope.get<std::string>("data_schema");

    if (schema_callback_pairs_.find(schema_name) != schema_callback_pairs_.end()) {
        auto c_b = schema_callback_pairs_.at(schema_name);
        // LOG_TRACE("Executing callback for a message with '%1%' schema",
        //           schema_name);
        c_b(parsed_chunks);
    } else {
        // LOG_WARNING("No message callback has be registered for '%1%' schema",
        //             schema_name);
    }
}

// Monitor task

void Connector::monitorConnectionTask_(int max_connect_attempts) {
    assert(connection_ptr_ != nullptr);

    while (true) {
        std::unique_lock<std::mutex> the_lock { mutex_ };
        monitor_timer_.reset();

        cond_var_.wait(the_lock,
            [this] {
                return is_destructing_
                       || monitor_timer_.elapsedSeconds() > CONNECTION_CHECK_INTERVAL;
            });

        if (is_destructing_) {
            // The dtor has been invoked
            // LOG_INFO("Stopping the monitor task (persistence)");
            the_lock.unlock();
            return;
        }

        try {
            if (connection_ptr_->getConnectionState() != ConnectionStateValues::open) {
                // LOG_WARNING("Connection to Cthun server lost; retrying");
                connection_ptr_->connect(max_connect_attempts);
            } else {
                // LOG_DEBUG("Sending heartbeat ping");
                connection_ptr_->ping();
            }
        } catch (connection_processing_error& e) {
            // Connection::connect_() failure - keep trying
            // LOG_ERROR("Monitoring task (persistence) failure: %1%", e.what());
        } catch (connection_fatal_error& e) {
            // Failed to reconnect after max_connect_attempts - stop
            // LOG_ERROR("The monitoring task (persistence) will stop - "
            //           "failure: %1%", e.what());

            // TODO(ale): evaluate exposing is_monitoring_ - update docs

            is_monitoring_ = false;
            the_lock.unlock();
            return;
        }

        the_lock.unlock();
    }
}

}  // namespace CthunClient
<|endoftext|>


<commit_before>/*
Copyright (c) 2014, Sam Schetterer, Nathan Kutz, University of Washington
Authors: Sam Schetterer
All rights reserved.

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

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

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

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 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.

*/
#include "engineimp.h"
#include "writer/writer.h"
#include "utils/item_heads.hpp"
#include <ostream>
static size_t write_data(std::shared_ptr<writer> dat, std::ostream& wfile){
    size_t dat_size = dat->write(wfile);
    wfile << "&&\n";
    return dat_size + sizeof("&&\n");
}
//only call with empty ostream, and then append that ostream onto a bigger one
static size_t write_individual_dat(const std::list<std::shared_ptr<writer>>& dats, size_t ind,
        std::ostream& out_stream){
    out_stream << "index: " << ind << "\n";
    //this may be slightly innaccurate, but is easier than trying to figure out how many characters
    //the stream will use to represent ind
    size_t dat_size=sizeof("index: ") + sizeof("\n") + 1;
    size_t ind_cpy=ind;
    while(ind_cpy > 9){
        ind_cpy/=10;
        dat_size++;
    }
    for(auto& dat : dats){
        dat_size += write_data(dat, out_stream);
    }
    out_stream << "&\n";
    //std::cout << out_stream << std::endl;
    return dat_size + sizeof("&\n");
}
static size_t write_dat(std::map<size_t, std::list<std::shared_ptr<writer>>>& writers, std::ostream& out_stream){
    size_t total_size=0;
    for(auto& writes : writers){
        total_size += write_individual_dat(writes.second, writes.first, out_stream);
    }
    writers.clear();
    return total_size;
}

//!Adds a new writer to the engine
void engineimp::add_writer(const std::shared_ptr<writer>& wval){
    writers[index].push_back(wval);
}
//OS still seems to block entire process during io...
//both threads run simultaneously, but the
//!Writes data stored in writer to a buffer and writes to disk asynchronously every 50mb. Will block while > ~500mb of data is waiting to be written
void engineimp::_write_dat(bool force){
    //easier than dealing with orders of 1024
    constexpr size_t max_size = 5e8; //won't add more data while ~500mb of data is waiting to be held
    constexpr size_t write_size=2e8; //write every 50mb
    while(data_size > max_size - write_size){
        //nothing is writing at 2.5gb a second, this might clear a hundred mb if we are lucky!!!
        //Better to let this thread sleep instead of waste cpu cycles
        //Also, we wait on max_size - write_size since we may also have write_size worth of data in the engine
        std::chrono::milliseconds sleep_len(200);
        std::this_thread::sleep_for(sleep_len);
    }
    cur_blob->size += ::write_dat(writers, cur_blob->dat);
    if(force || cur_blob->size >= write_size){
        std::unique_lock<std::mutex> dat_lock(data_lock);
        datas_queued++;
        data_size += cur_blob->size;
        async_write_queue.push_back(cur_blob);
        dat_lock.unlock();
        cur_blob = std::make_shared<data_blob>();
        cur_blob->size=0;
        std::unique_lock<std::mutex> lock(condition_lock);
        write_cond.notify_all();
        lock.unlock();
    }
}
void engineimp::write_dat(){
    _write_dat(false);
};
<commit_msg>Fixed bug in asynchronous output, added limitations on memory waiting for write<commit_after>/*
Copyright (c) 2014, Sam Schetterer, Nathan Kutz, University of Washington
Authors: Sam Schetterer
All rights reserved.

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

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

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

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 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.

*/
#include "engineimp.h"
#include "writer/writer.h"
#include "utils/item_heads.hpp"
#include <ostream>
static size_t write_data(std::shared_ptr<writer> dat, std::ostream& wfile){
    size_t dat_size = dat->write(wfile);
    wfile << "&&\n";
    return dat_size + sizeof("&&\n");
}
//only call with empty ostream, and then append that ostream onto a bigger one
static size_t write_individual_dat(const std::list<std::shared_ptr<writer>>& dats, size_t ind,
        std::ostream& out_stream){
    out_stream << "index: " << ind << "\n";
    //this may be slightly innaccurate, but is easier than trying to figure out how many characters
    //the stream will use to represent ind
    size_t dat_size=sizeof("index: ") + sizeof("\n") + 1;
    size_t ind_cpy=ind;
    while(ind_cpy > 9){
        ind_cpy/=10;
        dat_size++;
    }
    for(auto& dat : dats){
        dat_size += write_data(dat, out_stream);
    }
    out_stream << "&\n";
    //std::cout << out_stream << std::endl;
    return dat_size + sizeof("&\n");
}
static size_t write_dat(std::map<size_t, std::list<std::shared_ptr<writer>>>& writers, std::ostream& out_stream){
    size_t total_size=0;
    for(auto& writes : writers){
        total_size += write_individual_dat(writes.second, writes.first, out_stream);
    }
    writers.clear();
    return total_size;
}

//!Adds a new writer to the engine
void engineimp::add_writer(const std::shared_ptr<writer>& wval){
    writers[index].push_back(wval);
}
//OS still seems to block entire process during io...
//both threads run simultaneously, but the
//!Writes data stored in writer to a buffer and writes to disk asynchronously every 50mb. Will block while > ~500mb of data is waiting to be written
void engineimp::_write_dat(bool force){
    //easier than dealing with orders of 1024
    constexpr size_t max_size = 5e8; //won't add more data while ~500mb of data is waiting to be held
    constexpr size_t write_size=5e7; //write every 50mb
    while(data_size > max_size - write_size){
        //nothing is writing at 2.5gb a second, this might clear a hundred mb if we are lucky!!!
        //Better to let this thread sleep instead of waste cpu cycles
        //Also, we wait on max_size - write_size since we may also have write_size worth of data in the engine
        std::chrono::milliseconds sleep_len(200);
        std::this_thread::sleep_for(sleep_len);
    }
    cur_blob->size += ::write_dat(writers, cur_blob->dat);
    if(force || cur_blob->size >= write_size){
        std::unique_lock<std::mutex> dat_lock(data_lock);
        datas_queued++;
        data_size += cur_blob->size;
        async_write_queue.push_back(cur_blob);
        dat_lock.unlock();
        cur_blob = std::make_shared<data_blob>();
        cur_blob->size=0;
        std::unique_lock<std::mutex> lock(condition_lock);
        write_cond.notify_all();
        lock.unlock();
    }
}
void engineimp::write_dat(){
    _write_dat(false);
};
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2008-2010 The QXmpp developers
 *
 * Author:
 *  Jeremy Lainé
 *
 * Source:
 *  http://code.google.com/p/qxmpp
 *
 * This file is a part of QXmpp library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 */

#include <QDomElement>

#include "QXmppConstants.h"
#include "QXmppUtils.h"
#include "QXmppVersionIq.h"

/// Returns the name of the software.
///

QString QXmppVersionIq::name() const
{
    return m_name;
}

/// Sets the name of the software.
///
/// \param name

void QXmppVersionIq::setName(const QString &name)
{
    m_name = name;
}

/// Returns the operating system.
///

QString QXmppVersionIq::os() const
{
    return m_os;
}

/// Sets the operating system.
///
/// \param os

void QXmppVersionIq::setOs(const QString &os)
{
    m_os = os;
}

/// Returns the software version.
///

QString QXmppVersionIq::version() const
{
    return m_version;
}

/// Sets the software version.
///
/// \param version

void QXmppVersionIq::setVersion(const QString &version)
{
    m_version = version;
}

bool QXmppVersionIq::isVersionIq(const QDomElement &element)
{
    QDomElement queryElement = element.firstChildElement("query");
    return queryElement.namespaceURI() == ns_version;
}

void QXmppVersionIq::parseElementFromChild(const QDomElement &element)
{
    QDomElement queryElement = element.firstChildElement("query");
    m_name = element.firstChildElement("name").text();
    m_os = element.firstChildElement("os").text();
    m_version = element.firstChildElement("version").text();
}

void QXmppVersionIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
{
    writer->writeStartElement("query");
    helperToXmlAddAttribute(writer, "xmlns", ns_version);

    if (!m_name.isEmpty())
        helperToXmlAddTextElement(writer, "name", m_name);

    if (!m_os.isEmpty())
        helperToXmlAddTextElement(writer, "os", m_os);

    if (!m_version.isEmpty())
        helperToXmlAddTextElement(writer, "version", m_version);

    writer->writeEndElement();
}

<commit_msg>bugfix<commit_after>/*
 * Copyright (C) 2008-2010 The QXmpp developers
 *
 * Author:
 *  Jeremy Lainé
 *
 * Source:
 *  http://code.google.com/p/qxmpp
 *
 * This file is a part of QXmpp library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 */

#include <QDomElement>

#include "QXmppConstants.h"
#include "QXmppUtils.h"
#include "QXmppVersionIq.h"

/// Returns the name of the software.
///

QString QXmppVersionIq::name() const
{
    return m_name;
}

/// Sets the name of the software.
///
/// \param name

void QXmppVersionIq::setName(const QString &name)
{
    m_name = name;
}

/// Returns the operating system.
///

QString QXmppVersionIq::os() const
{
    return m_os;
}

/// Sets the operating system.
///
/// \param os

void QXmppVersionIq::setOs(const QString &os)
{
    m_os = os;
}

/// Returns the software version.
///

QString QXmppVersionIq::version() const
{
    return m_version;
}

/// Sets the software version.
///
/// \param version

void QXmppVersionIq::setVersion(const QString &version)
{
    m_version = version;
}

bool QXmppVersionIq::isVersionIq(const QDomElement &element)
{
    QDomElement queryElement = element.firstChildElement("query");
    return queryElement.namespaceURI() == ns_version;
}

void QXmppVersionIq::parseElementFromChild(const QDomElement &element)
{
    QDomElement queryElement = element.firstChildElement("query");
    m_name = queryElement.firstChildElement("name").text();
    m_os = queryElement.firstChildElement("os").text();
    m_version = queryElement.firstChildElement("version").text();
}

void QXmppVersionIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
{
    writer->writeStartElement("query");
    helperToXmlAddAttribute(writer, "xmlns", ns_version);

    if (!m_name.isEmpty())
        helperToXmlAddTextElement(writer, "name", m_name);

    if (!m_os.isEmpty())
        helperToXmlAddTextElement(writer, "os", m_os);

    if (!m_version.isEmpty())
        helperToXmlAddTextElement(writer, "version", m_version);

    writer->writeEndElement();
}

<|endoftext|>


<commit_before>#include "RTAudioWrapper.h"

// constructor
RtAudioWrapper::RtAudioWrapper()
{
    this->audioConfiguration = { 0 };
    streamData = new StreamData();

    #ifdef _WIN32
    semaphore_waitForMainThread = CreateSemaphore(nullptr, 1, 1, "semaphore_waitForMainThread");
    semaphore_waitForWorkerThread = CreateSemaphore(nullptr, 0, 1, "semaphore_waitForWorkerThread");
    #else
            /* TODO, two linux semaphores needed */
    #endif
}

// constructor
RtAudioWrapper::RtAudioWrapper(const AudioConfiguration &audioConfig) : RtAudioWrapper()
{
    this->setConfiguration(audioConfig);
}

// static callbackHelper (calls the callback of the object)
auto RtAudioWrapper::callbackHelper(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *rtAudioWrapperObject) -> int
{
    RtAudioWrapper *rtAudioWrapper = static_cast <RtAudioWrapper*> (rtAudioWrapperObject);
    return rtAudioWrapper->callback(outputBuffer, inputBuffer, nBufferFrames, streamTime, status, nullptr);
}

// callback of the object
auto RtAudioWrapper::callback(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *rtAudioWrapperObject) -> int
{
    if (status == RTAUDIO_INPUT_OVERFLOW)
    {
        // TODO: Create a log. Input data was discarded because of an overflow (data loss)
        std::cout << "Overflow\n";
    }

    if (status == RTAUDIO_OUTPUT_UNDERFLOW)
    {
        // TODO: Create a log. Output buffer ran low, produces a break in the output sound.
        std::cout << "Underflow\n";
    }

    // relevant if playData(..) is invoked
    this->playbackFileData(outputBuffer);

    this->streamData->nBufferFrames = nBufferFrames;
    //streamTime is the number of seconds since start of stream, so we convert to number of microseconds
    this->streamData->streamTime = lround(streamTime * 1000000);

    //in case the two buffer sizes max vary
    this->streamData->maxBufferSize = inputBufferByteSize;
    if (inputBuffer != nullptr)
        this->processAudioInput(inputBuffer, inputBufferByteSize, streamData);

    //in case the two buffer sizes max vary
    this->streamData->maxBufferSize = outputBufferByteSize;
    if (outputBuffer != nullptr)
        this->processAudioOutput(outputBuffer, outputBufferByteSize, streamData);

    return 0;
}

void RtAudioWrapper::playbackFileData(void *outputBuffer)
{
    if (this->BufferAudioOutHasData)
    {
        // critical section starts
        #ifdef _WIN32
        WaitForSingleObject(semaphore_waitForMainThread, INFINITE);
        #endif

        char *buffer = (char *)outputBuffer;
        memcpy(buffer, bufferAudioOutput, outputBufferByteSize);
        this->BufferAudioOutHasData = false;

        #ifdef _WIN32
        ReleaseSemaphore(semaphore_waitForMainThread, 1, nullptr);

        // critical section ends

        // inform that the data has been processed
        ReleaseSemaphore(semaphore_waitForWorkerThread, 1, nullptr);
        #endif
    }
    else
    {
        // If there is no data to be played then clean the buffer (otherwise it will cause a wired
        // sound, since the buffer is used for playback over and over again with the same playback data)

        // TODO: getOutputData() and copy to outputbuffer;
    }
}

// Region: AudioWrapper methods
void RtAudioWrapper::startRecordingMode()
{
    if (this->flagPrepared)
    {
        this->rtaudio.openStream(nullptr, &input, audioConfiguration.audioFormat, audioConfiguration.sampleRate, &audioConfiguration.bufferFrames, &RtAudioWrapper::callbackHelper, this);
        this->rtaudio.startStream();
    }
    else
        std::cout << "Did you forget to call AudioHandler::prepare()?" << std::endl;
}

void RtAudioWrapper::startPlaybackMode()
{
    if (this->flagPrepared)
    {
            this->rtaudio.openStream(&output, nullptr, audioConfiguration.audioFormat, audioConfiguration.sampleRate, &audioConfiguration.bufferFrames, &RtAudioWrapper::callbackHelper, this);
            this->rtaudio.startStream();
    }
    else
            std::cout << "Did you forget to call AudioHandler::prepare()?" << std::endl;
}

void RtAudioWrapper::startDuplexMode()
{
    if (this->flagPrepared)
    {
        this->rtaudio.openStream(&output, &input, audioConfiguration.audioFormat, audioConfiguration.sampleRate, &audioConfiguration.bufferFrames, &RtAudioWrapper::callbackHelper, this);
        this->rtaudio.startStream();
    }
    else
        std::cout << "Did you forget to call AudioHandler::prepare()?" << std::endl;
}

void RtAudioWrapper::setConfiguration(const AudioConfiguration &audioConfig)
{
    this->audioConfiguration = audioConfig;
    this->flagAudioConfigSet = true;
}

void RtAudioWrapper::suspend()
{
    if (this->rtaudio.isStreamRunning())
    {
        #ifdef _WIN32
        CloseHandle(semaphore_waitForMainThread);
        CloseHandle(semaphore_waitForWorkerThread);
        #endif
        this->rtaudio.stopStream();
    }
}

void RtAudioWrapper::stop()
{
    this->suspend();
    this->rtaudio.closeStream();
}

void RtAudioWrapper::resume()
{
    if (this->rtaudio.isStreamOpen() && this->rtaudio.isStreamRunning() == false)
        this->rtaudio.startStream();
}

void RtAudioWrapper::reset()
{
    this->stop();
    this->audioConfiguration = { 0 };
    this->flagAudioConfigSet = false;
    this->flagPrepared = false;
}

void RtAudioWrapper::playData(void *playbackData, unsigned int size)
{
    char *buffer = (char *)playbackData;
    int dataLoops = (int) ceil((double)size / outputBufferByteSize);

    for (int i = 0; i < dataLoops; i++)
    {
        // critical section starts
        #ifdef _WIN32
        WaitForSingleObject(semaphore_waitForMainThread, INFINITE);
        #endif
        this->BufferAudioOutHasData = true;

        /* In the last loop, the size of the data may not be as big as bufferByteSize
        * In this special case, calculate the size of the remaining data
        */
        if (i + 1 == dataLoops && i != 0)
        {
            // calculate the size of  the remaining data
            int dataSizeLastLoop = (int) ceil(size % outputBufferByteSize);

            // first clean the buffer
            //memcpy(bufferAudioOut, 0x0, bufferByteSize);

            // fill the buffer with the remaining data
            memcpy(bufferAudioOutput, (buffer + i * dataSizeLastLoop), dataSizeLastLoop);
        }
        else
            memcpy(bufferAudioOutput, (buffer + i * outputBufferByteSize), outputBufferByteSize);

        #ifdef _WIN32
        ReleaseSemaphore(semaphore_waitForMainThread, 1, nullptr);
        // critical section ends

        // wait until the data is processed
        WaitForSingleObject(semaphore_waitForWorkerThread, INFINITE);
        #endif
    }
}

// Region: private functions
auto RtAudioWrapper::initRtAudioStreamParameters() -> bool
{
    // calculate the input- and outputbuffer sizes
    this->outputBufferByteSize = audioConfiguration.bufferFrames * audioConfiguration.outputDeviceChannels * getAudioFormatByteSize(audioConfiguration.audioFormat);
    this->inputBufferByteSize = audioConfiguration.bufferFrames * audioConfiguration.inputDeviceChannels * getAudioFormatByteSize(audioConfiguration.audioFormat);

    /* internal buffer for playback data */
    this->bufferAudioOutput = new char[this->outputBufferByteSize];

    /* Prepare the StreamParameters */
    this->input.deviceId = audioConfiguration.inputDeviceID;
    this->output.deviceId = audioConfiguration.outputDeviceID;
    this->input.nChannels = audioConfiguration.inputDeviceChannels;
    this->output.nChannels = audioConfiguration.outputDeviceChannels;
    this->input.firstChannel = audioConfiguration.inputDeviceFirstChannel;
    this->output.firstChannel = audioConfiguration.outputDeviceFirstChannel;

    return true;
}

void RtAudioWrapper::setDefaultAudioConfig()
{
    AudioConfiguration audioConfig = { 0 };
    audioConfig.inputDeviceID = this->rtaudio.getDefaultInputDevice();
    audioConfig.outputDeviceID = this->rtaudio.getDefaultOutputDevice();

    //input device
    RtAudio::DeviceInfo inputDeviceInfo = this->rtaudio.getDeviceInfo(audioConfig.inputDeviceID);
    RtAudio::DeviceInfo outputDeviceInfo = this->rtaudio.getDeviceInfo(audioConfig.outputDeviceID);

    audioConfig.inputDeviceChannels = 2;
    audioConfig.outputDeviceChannels = 2;
    RtAudioFormat rtaudioFormat = autoSelectAudioFormat(outputDeviceInfo.nativeFormats);
    audioConfig.audioFormat = getAudioFormatByteSize(rtaudioFormat);
    audioConfig.sampleRate = 44100;
    audioConfig.bufferFrames = 512;

    this->setConfiguration(audioConfig);
}

auto RtAudioWrapper::getAudioFormatByteSize(RtAudioFormat rtaudioFormat) -> int
{
    switch (rtaudioFormat)
    {
        case(RTAUDIO_SINT8): return 1;
        case(RTAUDIO_SINT16) : return 2;
        case(RTAUDIO_SINT24) : return 3;
        case(RTAUDIO_SINT32) : return 4;
        case(RTAUDIO_FLOAT32) : return 4;
        case(RTAUDIO_FLOAT64) : return 8;
    }
    return 0;
}

auto RtAudioWrapper::getOutputFrameSize() -> int
{
    if (this->flagAudioConfigSet)
        return getAudioFormatByteSize(this->audioConfiguration.audioFormat) * this->audioConfiguration.outputDeviceChannels;
    return 0;
}

auto RtAudioWrapper::getInputFrameSize() -> int
{
	if (this->flagAudioConfigSet)
		return getAudioFormatByteSize(this->audioConfiguration.audioFormat) * this->audioConfiguration.inputDeviceChannels;
	return 0;
}

auto RtAudioWrapper::autoSelectAudioFormat(RtAudioFormat supportedFormats) -> RtAudioFormat
{
    if ((supportedFormats & RTAUDIO_FLOAT64) == RTAUDIO_FLOAT64)
    {
        return RTAUDIO_FLOAT64;
    }
    if ((supportedFormats & RTAUDIO_FLOAT32) == RTAUDIO_FLOAT32)
    {
        return RTAUDIO_FLOAT32;
    }
    if ((supportedFormats & RTAUDIO_SINT32) == RTAUDIO_SINT32)
    {
        return RTAUDIO_SINT32;
    }
    if ((supportedFormats & RTAUDIO_SINT24) == RTAUDIO_SINT24)
    {
        return RTAUDIO_SINT24;
    }
    if ((supportedFormats & RTAUDIO_SINT16) == RTAUDIO_SINT16)
    {
        return RTAUDIO_SINT16;
    }
    //fall back to worst quality
    return RTAUDIO_SINT8;
}

unsigned int RtAudioWrapper::autoSelectSampleRate(unsigned int supportedRatesFlag)
{
    if((supportedRatesFlag & AudioConfiguration::SAMPLE_RATE_192000) == AudioConfiguration::SAMPLE_RATE_192000)
    {
        return 192000;
    }
    if((supportedRatesFlag & AudioConfiguration::SAMPLE_RATE_96000) == AudioConfiguration::SAMPLE_RATE_96000)
    {
        return 96000;
    }
    if((supportedRatesFlag & AudioConfiguration::SAMPLE_RATE_48000) == AudioConfiguration::SAMPLE_RATE_48000)
    {
        return 48000;
    }
    if((supportedRatesFlag & AudioConfiguration::SAMPLE_RATE_44100) == AudioConfiguration::SAMPLE_RATE_44100)
    {
        return 44100;
    }
    if((supportedRatesFlag & AudioConfiguration::SAMPLE_RATE_32000) == AudioConfiguration::SAMPLE_RATE_32000)
    {
        return 32000;
    }
    if((supportedRatesFlag & AudioConfiguration::SAMPLE_RATE_24000) == AudioConfiguration::SAMPLE_RATE_24000)
    {
        return 24000;
    }
    if((supportedRatesFlag & AudioConfiguration::SAMPLE_RATE_16000) == AudioConfiguration::SAMPLE_RATE_16000)
    {
        return 16000;
    }
    if((supportedRatesFlag & AudioConfiguration::SAMPLE_RATE_12000) == AudioConfiguration::SAMPLE_RATE_12000)
    {
        return 12000;
    }
    //fall back to worst sample-rate
    return 8000;
}

bool RtAudioWrapper::prepare()
{
    /* If there is no config set, then load the default */
    if (this->flagAudioConfigSet == false)
        this->setDefaultAudioConfig();

    //checks if there is a configuration all processors support
    if(!queryProcessorSupport())
    {
        std::cerr << "AudioProcessors could not agree on configuration!" << std::endl;
        return false;
    }

    bool resultA = this->initRtAudioStreamParameters();
    bool resultB = this->configureAudioProcessors();

    if (resultA && resultB) {
        this->flagPrepared = true;
        return true;
    }

    return false;
}

auto RtAudioWrapper::getBufferSize() -> unsigned int
{
    return outputBufferByteSize;
}

bool RtAudioWrapper::queryProcessorSupport()
{
    //preset supported audio-formats with device-supported formats
    unsigned int supportedFormats = this->rtaudio.getDeviceInfo(audioConfiguration.inputDeviceID).nativeFormats;
    //preset supported sample-rates with device-supported rates
    unsigned int supportedSampleRates = mapDeviceSampleRates(this->rtaudio.getDeviceInfo(audioConfiguration.inputDeviceID).sampleRates);
    for (unsigned int i = 0; i < audioProcessors.size(); i++)
    {
        // a & b return all bits set in a AND b -> all flags supported by both
        supportedFormats = supportedFormats & audioProcessors.at(i)->getSupportedAudioFormats();
        supportedSampleRates = supportedSampleRates &audioProcessors.at(i)->getSupportedSampleRates();
    }
    if(supportedFormats == 0)
    {
        //there is no format supported by all processors
        std::cout << "Could not find a single audio-format supported by all processors!" << std::endl;
        return false;
    }
    if(supportedSampleRates == 0)
    {
        //there is no sample-rate supported by all processors
        std::cout << "Could not find a single sample-rate supported by all processors!" << std::endl;
        return false;
    }
    audioConfiguration.audioFormat = autoSelectAudioFormat(supportedFormats);
    audioConfiguration.sampleRate = autoSelectSampleRate(supportedSampleRates);

    return true;
}

unsigned int RtAudioWrapper::mapDeviceSampleRates(std::vector<unsigned int> sampleRates)
{
    unsigned int sampleRate;
    unsigned int sampleRatesFlags = 0;
    for (unsigned int i = 0; i < sampleRates.size(); i++)
    {
        sampleRate = sampleRates.at(i);
        if(sampleRate == 8000)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_8000;
        else if(sampleRate == 12000)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_12000;
        else if(sampleRate == 16000)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_16000;
        else if(sampleRate == 24000)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_24000;
        else if(sampleRate == 32000)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_32000;
        else if(sampleRate == 44100)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_44100;
        else if(sampleRate == 48000)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_48000;
        else if(sampleRate == 96000)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_96000;
        else if(sampleRate == 192000)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_192000;
        else
            std::cout << "Unrecognized sample-rate: " << sampleRate << std::endl;
    }

    return sampleRatesFlags;
}
<commit_msg>RtAudioWrapper now initially supports all audio-formats, because RtAudio guarantees them to be supported Fixes #37, related to #34 Signed-off-by:doe300 <stadeldani@web.de><commit_after>#include "RTAudioWrapper.h"

// constructor
RtAudioWrapper::RtAudioWrapper()
{
    this->audioConfiguration = { 0 };
    streamData = new StreamData();

    #ifdef _WIN32
    semaphore_waitForMainThread = CreateSemaphore(nullptr, 1, 1, "semaphore_waitForMainThread");
    semaphore_waitForWorkerThread = CreateSemaphore(nullptr, 0, 1, "semaphore_waitForWorkerThread");
    #else
            /* TODO, two linux semaphores needed */
    #endif
}

// constructor
RtAudioWrapper::RtAudioWrapper(const AudioConfiguration &audioConfig) : RtAudioWrapper()
{
    this->setConfiguration(audioConfig);
}

// static callbackHelper (calls the callback of the object)
auto RtAudioWrapper::callbackHelper(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *rtAudioWrapperObject) -> int
{
    RtAudioWrapper *rtAudioWrapper = static_cast <RtAudioWrapper*> (rtAudioWrapperObject);
    return rtAudioWrapper->callback(outputBuffer, inputBuffer, nBufferFrames, streamTime, status, nullptr);
}

// callback of the object
auto RtAudioWrapper::callback(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *rtAudioWrapperObject) -> int
{
    if (status == RTAUDIO_INPUT_OVERFLOW)
    {
        // TODO: Create a log. Input data was discarded because of an overflow (data loss)
        std::cout << "Overflow\n";
    }

    if (status == RTAUDIO_OUTPUT_UNDERFLOW)
    {
        // TODO: Create a log. Output buffer ran low, produces a break in the output sound.
        std::cout << "Underflow\n";
    }

    // relevant if playData(..) is invoked
    this->playbackFileData(outputBuffer);

    this->streamData->nBufferFrames = nBufferFrames;
    //streamTime is the number of seconds since start of stream, so we convert to number of microseconds
    this->streamData->streamTime = lround(streamTime * 1000000);

    //in case the two buffer sizes max vary
    this->streamData->maxBufferSize = inputBufferByteSize;
    if (inputBuffer != nullptr)
        this->processAudioInput(inputBuffer, inputBufferByteSize, streamData);

    //in case the two buffer sizes max vary
    this->streamData->maxBufferSize = outputBufferByteSize;
    if (outputBuffer != nullptr)
        this->processAudioOutput(outputBuffer, outputBufferByteSize, streamData);

    return 0;
}

void RtAudioWrapper::playbackFileData(void *outputBuffer)
{
    if (this->BufferAudioOutHasData)
    {
        // critical section starts
        #ifdef _WIN32
        WaitForSingleObject(semaphore_waitForMainThread, INFINITE);
        #endif

        char *buffer = (char *)outputBuffer;
        memcpy(buffer, bufferAudioOutput, outputBufferByteSize);
        this->BufferAudioOutHasData = false;

        #ifdef _WIN32
        ReleaseSemaphore(semaphore_waitForMainThread, 1, nullptr);

        // critical section ends

        // inform that the data has been processed
        ReleaseSemaphore(semaphore_waitForWorkerThread, 1, nullptr);
        #endif
    }
    else
    {
        // If there is no data to be played then clean the buffer (otherwise it will cause a wired
        // sound, since the buffer is used for playback over and over again with the same playback data)

        // TODO: getOutputData() and copy to outputbuffer;
    }
}

// Region: AudioWrapper methods
void RtAudioWrapper::startRecordingMode()
{
    if (this->flagPrepared)
    {
        this->rtaudio.openStream(nullptr, &input, audioConfiguration.audioFormat, audioConfiguration.sampleRate, &audioConfiguration.bufferFrames, &RtAudioWrapper::callbackHelper, this);
        this->rtaudio.startStream();
    }
    else
        std::cout << "Did you forget to call AudioHandler::prepare()?" << std::endl;
}

void RtAudioWrapper::startPlaybackMode()
{
    if (this->flagPrepared)
    {
            this->rtaudio.openStream(&output, nullptr, audioConfiguration.audioFormat, audioConfiguration.sampleRate, &audioConfiguration.bufferFrames, &RtAudioWrapper::callbackHelper, this);
            this->rtaudio.startStream();
    }
    else
            std::cout << "Did you forget to call AudioHandler::prepare()?" << std::endl;
}

void RtAudioWrapper::startDuplexMode()
{
    if (this->flagPrepared)
    {
        this->rtaudio.openStream(&output, &input, audioConfiguration.audioFormat, audioConfiguration.sampleRate, &audioConfiguration.bufferFrames, &RtAudioWrapper::callbackHelper, this);
        this->rtaudio.startStream();
    }
    else
        std::cout << "Did you forget to call AudioHandler::prepare()?" << std::endl;
}

void RtAudioWrapper::setConfiguration(const AudioConfiguration &audioConfig)
{
    this->audioConfiguration = audioConfig;
    this->flagAudioConfigSet = true;
}

void RtAudioWrapper::suspend()
{
    if (this->rtaudio.isStreamRunning())
    {
        #ifdef _WIN32
        CloseHandle(semaphore_waitForMainThread);
        CloseHandle(semaphore_waitForWorkerThread);
        #endif
        this->rtaudio.stopStream();
    }
}

void RtAudioWrapper::stop()
{
    this->suspend();
    this->rtaudio.closeStream();
}

void RtAudioWrapper::resume()
{
    if (this->rtaudio.isStreamOpen() && this->rtaudio.isStreamRunning() == false)
        this->rtaudio.startStream();
}

void RtAudioWrapper::reset()
{
    this->stop();
    this->audioConfiguration = { 0 };
    this->flagAudioConfigSet = false;
    this->flagPrepared = false;
}

void RtAudioWrapper::playData(void *playbackData, unsigned int size)
{
    char *buffer = (char *)playbackData;
    int dataLoops = (int) ceil((double)size / outputBufferByteSize);

    for (int i = 0; i < dataLoops; i++)
    {
        // critical section starts
        #ifdef _WIN32
        WaitForSingleObject(semaphore_waitForMainThread, INFINITE);
        #endif
        this->BufferAudioOutHasData = true;

        /* In the last loop, the size of the data may not be as big as bufferByteSize
        * In this special case, calculate the size of the remaining data
        */
        if (i + 1 == dataLoops && i != 0)
        {
            // calculate the size of  the remaining data
            int dataSizeLastLoop = (int) ceil(size % outputBufferByteSize);

            // first clean the buffer
            //memcpy(bufferAudioOut, 0x0, bufferByteSize);

            // fill the buffer with the remaining data
            memcpy(bufferAudioOutput, (buffer + i * dataSizeLastLoop), dataSizeLastLoop);
        }
        else
            memcpy(bufferAudioOutput, (buffer + i * outputBufferByteSize), outputBufferByteSize);

        #ifdef _WIN32
        ReleaseSemaphore(semaphore_waitForMainThread, 1, nullptr);
        // critical section ends

        // wait until the data is processed
        WaitForSingleObject(semaphore_waitForWorkerThread, INFINITE);
        #endif
    }
}

// Region: private functions
auto RtAudioWrapper::initRtAudioStreamParameters() -> bool
{
    // calculate the input- and outputbuffer sizes
    this->outputBufferByteSize = audioConfiguration.bufferFrames * audioConfiguration.outputDeviceChannels * getAudioFormatByteSize(audioConfiguration.audioFormat);
    this->inputBufferByteSize = audioConfiguration.bufferFrames * audioConfiguration.inputDeviceChannels * getAudioFormatByteSize(audioConfiguration.audioFormat);

    /* internal buffer for playback data */
    this->bufferAudioOutput = new char[this->outputBufferByteSize];

    /* Prepare the StreamParameters */
    this->input.deviceId = audioConfiguration.inputDeviceID;
    this->output.deviceId = audioConfiguration.outputDeviceID;
    this->input.nChannels = audioConfiguration.inputDeviceChannels;
    this->output.nChannels = audioConfiguration.outputDeviceChannels;
    this->input.firstChannel = audioConfiguration.inputDeviceFirstChannel;
    this->output.firstChannel = audioConfiguration.outputDeviceFirstChannel;

    return true;
}

void RtAudioWrapper::setDefaultAudioConfig()
{
    AudioConfiguration audioConfig = { 0 };
    audioConfig.inputDeviceID = this->rtaudio.getDefaultInputDevice();
    audioConfig.outputDeviceID = this->rtaudio.getDefaultOutputDevice();

    //input device
    RtAudio::DeviceInfo inputDeviceInfo = this->rtaudio.getDeviceInfo(audioConfig.inputDeviceID);
    RtAudio::DeviceInfo outputDeviceInfo = this->rtaudio.getDeviceInfo(audioConfig.outputDeviceID);

    audioConfig.inputDeviceChannels = 2;
    audioConfig.outputDeviceChannels = 2;
    RtAudioFormat rtaudioFormat = autoSelectAudioFormat(outputDeviceInfo.nativeFormats);
    audioConfig.audioFormat = getAudioFormatByteSize(rtaudioFormat);
    audioConfig.sampleRate = 44100;
    audioConfig.bufferFrames = 512;

    this->setConfiguration(audioConfig);
}

auto RtAudioWrapper::getAudioFormatByteSize(RtAudioFormat rtaudioFormat) -> int
{
    switch (rtaudioFormat)
    {
        case(RTAUDIO_SINT8): return 1;
        case(RTAUDIO_SINT16) : return 2;
        case(RTAUDIO_SINT24) : return 3;
        case(RTAUDIO_SINT32) : return 4;
        case(RTAUDIO_FLOAT32) : return 4;
        case(RTAUDIO_FLOAT64) : return 8;
    }
    return 0;
}

auto RtAudioWrapper::getOutputFrameSize() -> int
{
    if (this->flagAudioConfigSet)
        return getAudioFormatByteSize(this->audioConfiguration.audioFormat) * this->audioConfiguration.outputDeviceChannels;
    return 0;
}

auto RtAudioWrapper::getInputFrameSize() -> int
{
	if (this->flagAudioConfigSet)
		return getAudioFormatByteSize(this->audioConfiguration.audioFormat) * this->audioConfiguration.inputDeviceChannels;
	return 0;
}

auto RtAudioWrapper::autoSelectAudioFormat(RtAudioFormat supportedFormats) -> RtAudioFormat
{
    if ((supportedFormats & RTAUDIO_FLOAT64) == RTAUDIO_FLOAT64)
    {
        return RTAUDIO_FLOAT64;
    }
    if ((supportedFormats & RTAUDIO_FLOAT32) == RTAUDIO_FLOAT32)
    {
        return RTAUDIO_FLOAT32;
    }
    if ((supportedFormats & RTAUDIO_SINT32) == RTAUDIO_SINT32)
    {
        return RTAUDIO_SINT32;
    }
    if ((supportedFormats & RTAUDIO_SINT24) == RTAUDIO_SINT24)
    {
        return RTAUDIO_SINT24;
    }
    if ((supportedFormats & RTAUDIO_SINT16) == RTAUDIO_SINT16)
    {
        return RTAUDIO_SINT16;
    }
    //fall back to worst quality
    return RTAUDIO_SINT8;
}

unsigned int RtAudioWrapper::autoSelectSampleRate(unsigned int supportedRatesFlag)
{
    if((supportedRatesFlag & AudioConfiguration::SAMPLE_RATE_192000) == AudioConfiguration::SAMPLE_RATE_192000)
    {
        return 192000;
    }
    if((supportedRatesFlag & AudioConfiguration::SAMPLE_RATE_96000) == AudioConfiguration::SAMPLE_RATE_96000)
    {
        return 96000;
    }
    if((supportedRatesFlag & AudioConfiguration::SAMPLE_RATE_48000) == AudioConfiguration::SAMPLE_RATE_48000)
    {
        return 48000;
    }
    if((supportedRatesFlag & AudioConfiguration::SAMPLE_RATE_44100) == AudioConfiguration::SAMPLE_RATE_44100)
    {
        return 44100;
    }
    if((supportedRatesFlag & AudioConfiguration::SAMPLE_RATE_32000) == AudioConfiguration::SAMPLE_RATE_32000)
    {
        return 32000;
    }
    if((supportedRatesFlag & AudioConfiguration::SAMPLE_RATE_24000) == AudioConfiguration::SAMPLE_RATE_24000)
    {
        return 24000;
    }
    if((supportedRatesFlag & AudioConfiguration::SAMPLE_RATE_16000) == AudioConfiguration::SAMPLE_RATE_16000)
    {
        return 16000;
    }
    if((supportedRatesFlag & AudioConfiguration::SAMPLE_RATE_12000) == AudioConfiguration::SAMPLE_RATE_12000)
    {
        return 12000;
    }
    //fall back to worst sample-rate
    return 8000;
}

bool RtAudioWrapper::prepare()
{
    /* If there is no config set, then load the default */
    if (this->flagAudioConfigSet == false)
        this->setDefaultAudioConfig();

    //checks if there is a configuration all processors support
    if(!queryProcessorSupport())
    {
        std::cerr << "AudioProcessors could not agree on configuration!" << std::endl;
        return false;
    }

    bool resultA = this->initRtAudioStreamParameters();
    bool resultB = this->configureAudioProcessors();

    if (resultA && resultB) {
        this->flagPrepared = true;
        return true;
    }

    return false;
}

auto RtAudioWrapper::getBufferSize() -> unsigned int
{
    return outputBufferByteSize;
}

bool RtAudioWrapper::queryProcessorSupport()
{
    //preset supported audio-formats with device-supported formats
    //although there is a value for native audio-formats in the device-info, RtAudio documentation says:
    // "However, RtAudio will automatically provide format conversion if a particular format is not natively supported."
    unsigned int supportedFormats = AudioConfiguration::AUDIO_FORMAT_ALL;
    //preset supported sample-rates with device-supported rates
    unsigned int supportedSampleRates = mapDeviceSampleRates(this->rtaudio.getDeviceInfo(audioConfiguration.inputDeviceID).sampleRates);
    for (unsigned int i = 0; i < audioProcessors.size(); i++)
    {
        // a & b return all bits set in a AND b -> all flags supported by both
        supportedFormats = supportedFormats & audioProcessors.at(i)->getSupportedAudioFormats();
        supportedSampleRates = supportedSampleRates &audioProcessors.at(i)->getSupportedSampleRates();
    }
    if(supportedFormats == 0)
    {
        //there is no format supported by all processors
        std::cout << "Could not find a single audio-format supported by all processors!" << std::endl;
        return false;
    }
    if(supportedSampleRates == 0)
    {
        //there is no sample-rate supported by all processors
        std::cout << "Could not find a single sample-rate supported by all processors!" << std::endl;
        return false;
    }
    audioConfiguration.audioFormat = autoSelectAudioFormat(supportedFormats);
    audioConfiguration.sampleRate = autoSelectSampleRate(supportedSampleRates);

    return true;
}

unsigned int RtAudioWrapper::mapDeviceSampleRates(std::vector<unsigned int> sampleRates)
{
    unsigned int sampleRate;
    unsigned int sampleRatesFlags = 0;
    for (unsigned int i = 0; i < sampleRates.size(); i++)
    {
        sampleRate = sampleRates.at(i);
        if(sampleRate == 8000)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_8000;
        else if(sampleRate == 12000)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_12000;
        else if(sampleRate == 16000)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_16000;
        else if(sampleRate == 24000)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_24000;
        else if(sampleRate == 32000)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_32000;
        else if(sampleRate == 44100)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_44100;
        else if(sampleRate == 48000)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_48000;
        else if(sampleRate == 96000)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_96000;
        else if(sampleRate == 192000)
            sampleRatesFlags |= AudioConfiguration::SAMPLE_RATE_192000;
        else
            std::cout << "Unrecognized sample-rate: " << sampleRate << std::endl;
    }

    return sampleRatesFlags;
}
<|endoftext|>


<commit_before>#include <src/include/battle_room/common/animation_handler.h>
#include "battle_room/game/objects/player.h"
#include "battle_room/game/objects/bullet.h"

#include <cmath>
#include <iostream>

const double MAX_ANGULAR_VEL = 1; // radians per second
const double MAX_BULLET_SPEED = 60; // meters per second
const double MIN_BULLET_SPEED = 20; // meters per second
const double MAX_JUMP_SPEED = 6; // meters per second
const double MIN_JUMP_SPEED = 2; // meters per second

namespace BattleRoom {

    // constructors
    Player::Player(UniqueId id)
            : GameObject(id, ObjectType::Player),
            m_state(PlayerState::Flying) {
                setAnimation(AnimationHandler::getAnimation("man_0"));
            }

    // other functions
    void Player::reactToCollision(Vector2D velocityResult, Vector2D intersectionNormal) {

        velocityResult = velocityResult.times(0.7);

        if (m_state == PlayerState::Frozen) {
            if (velocityResult.magnitude() < 0.01) {
                setVelocity(Vector2D(0, 0));
            } else {
                setVelocity(velocityResult);
            }

        } else {
            m_state = PlayerState::Landed;

            // move to wall after rotating
            const Frame& frame = getAnimation().getFrame(getAnimationState());
            Projection1D rotated = frame.getBoundarySet().projectOnto(intersectionNormal.getRotated(-getRotation()));
            Projection1D upright = frame.getBoundarySet().projectOnto(Vector2D(0, 1));
            meters delta = rotated.getMin() - upright.getMin();
            setPosition(getPosition().plus(intersectionNormal.times(delta)));

            setIsStatic(true);
            setVelocity(Vector2D(0, 0));
            setAngularVelocity(0);

            setUp(intersectionNormal);
        }
    }

    void Player::updateAnimation(seconds timestep) {

        if (m_state != PlayerState::Frozen) {
            // should be dependent on m_state, m_chargingGun, m_chargingJump

            Vector2D delta = m_aim.minus(getPosition());
            radians exactAngle = (PI*0.5 + getRotation()) - std::atan2(delta.y(), delta.x());
            degrees aimAngle = under180(45*round(toDegrees(exactAngle)/45));

            std::string animationName = "man_";
            if (aimAngle < 0) {
                animationName += "n" + std::to_string((int)std::abs(aimAngle));
            } else {
                animationName += std::to_string((int)std::abs(aimAngle));
            }

            setAnimation(AnimationHandler::getAnimation(animationName));
            GameObject::updateAnimation(timestep);
        }
    }

    radians diffOfAngles(radians a, radians b) {
        return fmod(a - b + PI, 2*PI) - PI;

    }

    double calcFlyingAngularVelocity(Vector2D vel, radians angle, double current) {

        if (current > MAX_ANGULAR_VEL) {
            return 0.9*current;
        } else {
            radians desired = std::atan2(vel.x(), -vel.y());
            radians diff = diffOfAngles(desired, angle);

            if (diff*current < 0) {
                diff = diff + (current > 0 ? 2*PI : -2*PI);
            }
            return diff;
        }
    }

    void Player::updateForNext(seconds timestep) {

        if (m_state == PlayerState::Flying) {
            // desired is feet leading
            setAngularVelocity(calcFlyingAngularVelocity(getVelocity(), getRotation(), getAngularVelocity()));
        }

        m_gunCharge = m_chargingGun ? std::min(m_gunCharge + timestep, 1.0) : 0;
        m_jumpCharge = m_chargingJump ? std::min(m_jumpCharge + timestep, 1.0) : 0;
    }

    void Player::shootBullet(Vector2D aim) {
        m_aim = aim;
        Bullet *bullet = new Bullet(UniqueId::generateNewNetworkId());
        Vector2D bulletVelUnit = m_aim.minus(getPosition()).getUnit();

        const Frame& frame = getAnimation().getFrame(getAnimationState());
        meters dist = std::max(frame.getWidth(), frame.getHeight()) / 2;

        double speed = std::max(m_gunCharge*MAX_BULLET_SPEED, MIN_BULLET_SPEED);
        bullet->setVelocity(bulletVelUnit.times(speed).plus(getVelocity()));
        bullet->setRotation(bulletVelUnit.angle());
        bullet->setPosition(getPosition().plus(bulletVelUnit.times(dist)));

        m_gunCharge = 0;
        m_chargingGun = false;

        m_addedObjects.push_back(bullet);
    }

    void Player::jump(Vector2D aim) {
        if (m_state == PlayerState::Landed) {

            setIsStatic(false);
            m_state = PlayerState::Flying;
            double speed = std::max(m_jumpCharge*MAX_JUMP_SPEED, MIN_JUMP_SPEED);
            setVelocity(aim.minus(getPosition()).getUnit().times(speed));

        }
        m_jumpCharge = 0;
        m_chargingJump = false;
    }

    bool Player::interpretCommand(Command& cmd) {
        if (GameObject::interpretCommand(cmd)) {

            if (m_state == PlayerState::Frozen) {
                return true;
            }

            switch (cmd.getType()) {
                case CommandType::Aim:
                    m_aim = cmd.getPoint();
                    break;
                case CommandType::ShootCharge:
                    m_chargingGun = true;
                    break;
                case CommandType::ShootRelease:
                    shootBullet(cmd.getPoint());
                    break;
                case CommandType::JumpCharge:
                    m_chargingJump = true;
                    break;
                case CommandType::JumpRelease:
                    jump(cmd.getPoint());
                    break;
                case CommandType::Freeze:
                    m_state = PlayerState::Frozen;
                    break;
                default:
                    break;

            }
            return true;
        }
        return false;
    }

    std::vector<GameObject*> Player::getAddedObjects() {
        std::vector<GameObject*> objects = m_addedObjects;
        m_addedObjects.clear();
        return objects;
    }

} // BattleRoom namespace

<commit_msg>Fixed frozen bounce rotation.<commit_after>#include <src/include/battle_room/common/animation_handler.h>
#include "battle_room/game/objects/player.h"
#include "battle_room/game/objects/bullet.h"

#include <cmath>
#include <iostream>

const double MAX_ANGULAR_VEL = 1; // radians per second
const double MAX_BULLET_SPEED = 60; // meters per second
const double MIN_BULLET_SPEED = 20; // meters per second
const double MAX_JUMP_SPEED = 6; // meters per second
const double MIN_JUMP_SPEED = 2; // meters per second

namespace BattleRoom {

    // constructors
    Player::Player(UniqueId id)
            : GameObject(id, ObjectType::Player),
            m_state(PlayerState::Flying) {
                setAnimation(AnimationHandler::getAnimation("man_0"));
            }

    // other functions
    void Player::reactToCollision(Vector2D velocityResult, Vector2D intersectionNormal) {

        velocityResult = velocityResult.times(0.7);

        if (m_state == PlayerState::Frozen) {
            if (velocityResult.magnitude() < 0.1) {
                setVelocity(Vector2D(0, 0));
                setAngularVelocity(0);
            } else {
                setVelocity(velocityResult);
                Vector2D temp = Vector2D(-intersectionNormal.y(), intersectionNormal.x());
                setAngularVelocity(velocityResult.dot(temp));
            }

        } else {
            m_state = PlayerState::Landed;

            // move to wall after rotating
            const Frame& frame = getAnimation().getFrame(getAnimationState());
            Projection1D rotated = frame.getBoundarySet().projectOnto(intersectionNormal.getRotated(-getRotation()));
            Projection1D upright = frame.getBoundarySet().projectOnto(Vector2D(0, 1));
            meters delta = rotated.getMin() - upright.getMin();
            setPosition(getPosition().plus(intersectionNormal.times(delta)));

            setIsStatic(true);
            setVelocity(Vector2D(0, 0));
            setAngularVelocity(0);

            setUp(intersectionNormal);
        }
    }

    void Player::updateAnimation(seconds timestep) {

        if (m_state != PlayerState::Frozen) {
            // should be dependent on m_state, m_chargingGun, m_chargingJump

            Vector2D delta = m_aim.minus(getPosition());
            radians exactAngle = (PI*0.5 + getRotation()) - std::atan2(delta.y(), delta.x());
            degrees aimAngle = under180(45*round(toDegrees(exactAngle)/45));

            std::string animationName = "man_";
            if (aimAngle < 0) {
                animationName += "n" + std::to_string((int)std::abs(aimAngle));
            } else {
                animationName += std::to_string((int)std::abs(aimAngle));
            }

            setAnimation(AnimationHandler::getAnimation(animationName));
            GameObject::updateAnimation(timestep);
        }
    }

    radians diffOfAngles(radians a, radians b) {
        return fmod(a - b + PI, 2*PI) - PI;

    }

    double calcFlyingAngularVelocity(Vector2D vel, radians angle, double current) {

        if (current > MAX_ANGULAR_VEL) {
            return 0.9*current;
        } else {
            radians desired = std::atan2(vel.x(), -vel.y());
            radians diff = diffOfAngles(desired, angle);

            if (diff*current < 0) {
                diff = diff + (current > 0 ? 2*PI : -2*PI);
            }
            return diff;
        }
    }

    void Player::updateForNext(seconds timestep) {

        if (m_state == PlayerState::Flying) {
            // desired is feet leading
            setAngularVelocity(calcFlyingAngularVelocity(getVelocity(), getRotation(), getAngularVelocity()));
        }

        m_gunCharge = m_chargingGun ? std::min(m_gunCharge + timestep, 1.0) : 0;
        m_jumpCharge = m_chargingJump ? std::min(m_jumpCharge + timestep, 1.0) : 0;
    }

    void Player::shootBullet(Vector2D aim) {
        m_aim = aim;
        Bullet *bullet = new Bullet(UniqueId::generateNewNetworkId());
        Vector2D bulletVelUnit = m_aim.minus(getPosition()).getUnit();

        const Frame& frame = getAnimation().getFrame(getAnimationState());
        meters dist = std::max(frame.getWidth(), frame.getHeight()) / 2;

        double speed = std::max(m_gunCharge*MAX_BULLET_SPEED, MIN_BULLET_SPEED);
        bullet->setVelocity(bulletVelUnit.times(speed).plus(getVelocity()));
        bullet->setRotation(bulletVelUnit.angle());
        bullet->setPosition(getPosition().plus(bulletVelUnit.times(dist)));

        m_gunCharge = 0;
        m_chargingGun = false;

        m_addedObjects.push_back(bullet);
    }

    void Player::jump(Vector2D aim) {
        if (m_state == PlayerState::Landed) {

            setIsStatic(false);
            m_state = PlayerState::Flying;
            double speed = std::max(m_jumpCharge*MAX_JUMP_SPEED, MIN_JUMP_SPEED);
            setVelocity(aim.minus(getPosition()).getUnit().times(speed));

        }
        m_jumpCharge = 0;
        m_chargingJump = false;
    }

    bool Player::interpretCommand(Command& cmd) {
        if (GameObject::interpretCommand(cmd)) {

            if (m_state == PlayerState::Frozen) {
                return true;
            }

            switch (cmd.getType()) {
                case CommandType::Aim:
                    m_aim = cmd.getPoint();
                    break;
                case CommandType::ShootCharge:
                    m_chargingGun = true;
                    break;
                case CommandType::ShootRelease:
                    shootBullet(cmd.getPoint());
                    break;
                case CommandType::JumpCharge:
                    m_chargingJump = true;
                    break;
                case CommandType::JumpRelease:
                    jump(cmd.getPoint());
                    break;
                case CommandType::Freeze:
                    m_state = PlayerState::Frozen;
                    break;
                default:
                    break;

            }
            return true;
        }
        return false;
    }

    std::vector<GameObject*> Player::getAddedObjects() {
        std::vector<GameObject*> objects = m_addedObjects;
        m_addedObjects.clear();
        return objects;
    }

} // BattleRoom namespace

<|endoftext|>