<commit_before>#include <uri.hpp>
#include <iostream>

using namespace uri;

///////////////////////////////////////////////////////////////////////////////
const URI::Span_t URI::zero_span_;

///////////////////////////////////////////////////////////////////////////////
URI::URI(const char* uri)
  : URI{std::string{uri}}
{}

///////////////////////////////////////////////////////////////////////////////
URI::URI(const std::string& uri)
  : uri_str_{uri}
  , port_{}
{
  parse(uri_str_);
  port_ = port_str_.begin ? static_cast<uint16_t>(std::stoi(port_str())) : 0;
}

///////////////////////////////////////////////////////////////////////////////
std::string URI::userinfo() const {
  return uri_str_.substr(userinfo_.begin, userinfo_.end);
}

///////////////////////////////////////////////////////////////////////////////
std::string URI::host() const {
  return uri_str_.substr(host_.begin, host_.end);
}

///////////////////////////////////////////////////////////////////////////////
std::string URI::port_str() const {
  return uri_str_.substr(port_str_.begin, port_str_.end);
}

///////////////////////////////////////////////////////////////////////////////
uint16_t URI::port() const noexcept {
  return port_;
}

///////////////////////////////////////////////////////////////////////////////
std::string URI::path() const {
  return uri_str_.substr(path_.begin, path_.end);
}

///////////////////////////////////////////////////////////////////////////////
std::string URI::query() const {
  return uri_str_.substr(query_.begin, query_.end);
}

///////////////////////////////////////////////////////////////////////////////
std::string URI::fragment() const {
  return uri_str_.substr(fragment_.begin, fragment_.end);
}

///////////////////////////////////////////////////////////////////////////////
std::string URI::to_string() const{
  return uri_str_;
}

///////////////////////////////////////////////////////////////////////////////
URI::operator std::string () const {
  return uri_str_;
}

///////////////////////////////////////////////////////////////////////////////
void URI::parse(const std::string& uri) {
  static const std::regex uri_pattern_matcher
  {
    "^([\\w]+)?(\\://)?"        //< scheme
    "(([^:@]+)(\\:([^@]+))?@)?" //< username && password
    "([^/:?#]+)?(\\:(\\d+))?"   //< hostname && port
    "([^?#]+)"                  //< path
    "(\\?([^#]*))?"             //< query
    "(#(.*))?$"                 //< fragment
  };

  std::smatch uri_parts;

  if (std::regex_match(uri, uri_parts, uri_pattern_matcher)) {
    path_     = Span_t(uri_parts.position(10), uri_parts.length(10));

    userinfo_ = uri_parts.length(3)  ? Span_t(uri_parts.position(3),  uri_parts.length(3))  : zero_span_;
    host_     = uri_parts.length(7)  ? Span_t(uri_parts.position(7),  uri_parts.length(7))  : zero_span_;
    port_str_ = uri_parts.length(9)  ? Span_t(uri_parts.position(9),  uri_parts.length(9))  : zero_span_;
    query_    = uri_parts.length(11) ? Span_t(uri_parts.position(11), uri_parts.length(11)) : zero_span_;
    fragment_ = uri_parts.length(12) ? Span_t(uri_parts.position(12), uri_parts.length(12)) : zero_span_;
  }
}

///////////////////////////////////////////////////////////////////////////////
std::ostream& uri::operator<< (std::ostream& out, const URI& uri) {
  return out << uri.to_string();
}
<commit_msg>Added include for regex<commit_after>#include <regex>
#include <iostream>

#include <uri.hpp>

using namespace uri;

///////////////////////////////////////////////////////////////////////////////
const URI::Span_t URI::zero_span_;

///////////////////////////////////////////////////////////////////////////////
URI::URI(const char* uri)
  : URI{std::string{uri}}
{}

///////////////////////////////////////////////////////////////////////////////
URI::URI(const std::string& uri)
  : uri_str_{uri}
  , port_{}
{
  parse(uri_str_);
  port_ = port_str_.begin ? static_cast<uint16_t>(std::stoi(port_str())) : 0;
}

///////////////////////////////////////////////////////////////////////////////
std::string URI::userinfo() const {
  return uri_str_.substr(userinfo_.begin, userinfo_.end);
}

///////////////////////////////////////////////////////////////////////////////
std::string URI::host() const {
  return uri_str_.substr(host_.begin, host_.end);
}

///////////////////////////////////////////////////////////////////////////////
std::string URI::port_str() const {
  return uri_str_.substr(port_str_.begin, port_str_.end);
}

///////////////////////////////////////////////////////////////////////////////
uint16_t URI::port() const noexcept {
  return port_;
}

///////////////////////////////////////////////////////////////////////////////
std::string URI::path() const {
  return uri_str_.substr(path_.begin, path_.end);
}

///////////////////////////////////////////////////////////////////////////////
std::string URI::query() const {
  return uri_str_.substr(query_.begin, query_.end);
}

///////////////////////////////////////////////////////////////////////////////
std::string URI::fragment() const {
  return uri_str_.substr(fragment_.begin, fragment_.end);
}

///////////////////////////////////////////////////////////////////////////////
std::string URI::to_string() const{
  return uri_str_;
}

///////////////////////////////////////////////////////////////////////////////
URI::operator std::string () const {
  return uri_str_;
}

///////////////////////////////////////////////////////////////////////////////
void URI::parse(const std::string& uri) {
  static const std::regex uri_pattern_matcher
  {
    "^([\\w]+)?(\\://)?"        //< scheme
    "(([^:@]+)(\\:([^@]+))?@)?" //< username && password
    "([^/:?#]+)?(\\:(\\d+))?"   //< hostname && port
    "([^?#]+)"                  //< path
    "(\\?([^#]*))?"             //< query
    "(#(.*))?$"                 //< fragment
  };

  std::smatch uri_parts;

  if (std::regex_match(uri, uri_parts, uri_pattern_matcher)) {
    path_     = Span_t(uri_parts.position(10), uri_parts.length(10));

    userinfo_ = uri_parts.length(3)  ? Span_t(uri_parts.position(3),  uri_parts.length(3))  : zero_span_;
    host_     = uri_parts.length(7)  ? Span_t(uri_parts.position(7),  uri_parts.length(7))  : zero_span_;
    port_str_ = uri_parts.length(9)  ? Span_t(uri_parts.position(9),  uri_parts.length(9))  : zero_span_;
    query_    = uri_parts.length(11) ? Span_t(uri_parts.position(11), uri_parts.length(11)) : zero_span_;
    fragment_ = uri_parts.length(12) ? Span_t(uri_parts.position(12), uri_parts.length(12)) : zero_span_;
  }
}

///////////////////////////////////////////////////////////////////////////////
std::ostream& uri::operator<< (std::ostream& out, const URI& uri) {
  return out << uri.to_string();
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011, Peter Thorson. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the WebSocket++ Project 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 PETER THORSON 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 "uri.hpp"

#include <sstream>
#include <iostream>

#include <boost/regex.hpp>

using websocketpp::uri;

uri::uri(const std::string& uri) {
    // temporary testing non-regex implimentation.
    /*enum state {
        BEGIN = 0,
        HOST_BEGIN = 1,
        READ_IPV6_LITERAL = 2,
        READ_HOSTNAME = 3,
        BEGIN_PORT = 4,
        READ_PORT = 5,
        READ_RESOURCE = 6,
    };

    state the_state = BEGIN;
    std::string temp;

    for (std::string::const_iterator it = uri.begin(); it != uri.end(); it++) {
        switch (the_state) {
            case BEGIN:
                // we are looking for a ws:// or wss://
                if (temp.size() < 6) {
                    temp.append(1,*it);
                } else {
                    throw websocketpp::uri_exception("Scheme is too long");
                }

                if (temp == "ws://") {
                    m_secure = false;
                    the_state = HOST_BEGIN;
                    temp.clear();
                } else if (temp == "wss://") {
                    m_secure = true;
                    the_state = HOST_BEGIN;
                    temp.clear();
                }
                break;
            case HOST_BEGIN:
                // if character is [ go into IPv6 literal state
                // otherwise go into read hostname state
                if (*it == '[') {
                    the_state = READ_IPV6_LITERAL;
                } else {
                    *it--;
                    the_state = READ_HOSTNAME;
                }
                break;
            case READ_IPV6_LITERAL:
                // read 0-9a-fA-F:. until ] or 45 characters have been read
                if (*it == ']') {
                    the_state = BEGIN_PORT;
                    break;
                }

                if (m_host.size() > 45) {
                    throw websocketpp::uri_exception("IPv6 literal is too long");
                }

                if (*it == '.' ||
                    *it == ':' ||
                    (*it >= '0' && *it <= '9') ||
                    (*it >= 'a' && *it <= 'f') ||
                    (*it >= 'A' && *it <= 'F'))
                {
                    m_host.append(1,*it);
                }
                break;
            case READ_HOSTNAME:
                //
                if (*it == ':') {
                    it--;
                    the_state = BEGIN_PORT;
                    break;
                }

                if (*it == '/') {
                    it--;
                    the_state = BEGIN_PORT;
                    break;
                }

                // TODO: max url length?

                // TODO: check valid characters?
                if (1) {
                    m_host.append(1,*it);
                }
                break;
            case BEGIN_PORT:
                if (*it == ':') {
                    the_state = READ_PORT;
                } else if (*it == '/') {
                    m_port = get_port_from_string("");
                     *it--;
                    the_state = READ_RESOURCE;
                } else {
                    throw websocketpp::uri_exception("Error parsing WebSocket URI");
                }
                break;
            case READ_PORT:
                if (*it >= '0' && *it <= '9') {
                    if (temp.size() < 5) {
                        temp.append(1,*it);
                    } else {
                        throw websocketpp::uri_exception("Port is too long");
                    }
                } else if (*it == '/') {
                    m_port = get_port_from_string(temp);
                    temp.clear();
                     *it--;
                    the_state = READ_RESOURCE;

                } else {
                    throw websocketpp::uri_exception("Error parsing WebSocket URI");
                }
                break;
            case READ_RESOURCE:
                // max length check?

                if (*it == '#') {
                    throw websocketpp::uri_exception("WebSocket URIs cannot have fragments");
                } else {
                    m_resource.append(1,*it);
                }
                break;
        }
    }

    switch (the_state) {
        case READ_PORT:
            m_port = get_port_from_string(temp);
            break;
        default:
            break;
    }

    if (m_resource == "") {
        m_resource = "/";
    }*/

    boost::cmatch matches;
    static const boost::regex expression("(ws|wss)://([^/:\\[]+|\\[[0-9a-fA-F:.]+\\])(:\\d{1,5})?(/[^#]*)?");

    // TODO: should this split resource into path/query?

    if (boost::regex_match(uri.c_str(), matches, expression)) {
        m_secure = (matches[1] == "wss");
        m_host = matches[2];

        // strip brackets from IPv6 literal URIs
        if (m_host[0] == '[') {
            m_host = m_host.substr(1,m_host.size()-2);
        }

        std::string port(matches[3]);

        if (port != "") {
            // strip off the :
            // this could probably be done with a better regex.
            port = port.substr(1);
        }

        m_port = get_port_from_string(port);

        m_resource = matches[4];

        if (m_resource == "") {
            m_resource = "/";
        }

        return;
    }

    throw websocketpp::uri_exception("Error parsing WebSocket URI");

}

uri::uri(bool secure, const std::string& host, uint16_t port, const std::string& resource)
 : m_secure(secure),
   m_host(host),
   m_port(port),
   m_resource(resource == "" ? "/" : resource) {}

uri::uri(bool secure, const std::string& host, const std::string& resource)
: m_secure(secure),
  m_host(host),
  m_port(m_secure ? URI_DEFAULT_SECURE_PORT : URI_DEFAULT_PORT),
  m_resource(resource == "" ? "/" : resource) {}

uri::uri(bool secure,
         const std::string& host,
         const std::string& port,
         const std::string& resource)
 : m_secure(secure),
   m_host(host),
   m_port(get_port_from_string(port)),
   m_resource(resource == "" ? "/" : resource) {}

/* Slightly cleaner C++11 delegated constructor method

uri::uri(bool secure,
         const std::string& host,
         uint16_t port,
         const std::string& resource)
 : m_secure(secure),
   m_host(host),
   m_port(port),
   m_resource(resource == "" ? "/" : resource)
{
    if (m_port > 65535) {
        throw websocketpp::uri_exception("Port must be less than 65535");
    }
}

uri::uri(bool secure,
         const std::string& host,
         const std::string& port,
         const std::string& resource)
 : uri(secure, host, get_port_from_string(port), resource) {}

*/

bool uri::get_secure() const {
    return m_secure;
}

std::string uri::get_host() const {
    return m_host;
}

std::string uri::get_host_port() const {
    if (m_port == (m_secure ? URI_DEFAULT_SECURE_PORT : URI_DEFAULT_PORT)) {
         return m_host;
    } else {
        std::stringstream p;
        p << m_host << ":" << m_port;
        return p.str();
    }

}

uint16_t uri::get_port() const {
    return m_port;
}

std::string uri::get_port_str() const {
    std::stringstream p;
    p << m_port;
    return p.str();
}

std::string uri::get_resource() const {
    return m_resource;
}

std::string uri::str() const {
    std::stringstream s;

    s << "ws" << (m_secure ? "s" : "") << "://" << m_host;

    if (m_port != (m_secure ? URI_DEFAULT_SECURE_PORT : URI_DEFAULT_PORT)) {
        s << ":" << m_port;
    }

    s << m_resource;
    return s.str();
}

uint16_t uri::get_port_from_string(const std::string& port) const {
    if (port == "") {
        return (m_secure ? URI_DEFAULT_SECURE_PORT : URI_DEFAULT_PORT);
    }

    unsigned int t_port = atoi(port.c_str());

    if (t_port > 65535) {
        throw websocketpp::uri_exception("Port must be less than 65535");
    }

    if (t_port == 0) {
        throw websocketpp::uri_exception("Error parsing port string: "+port);
    }

    return t_port;
}<commit_msg>regex is no longer static, allows multi-threaded uri parsing. Fixes #54<commit_after>/*
 * Copyright (c) 2011, Peter Thorson. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the WebSocket++ Project 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 PETER THORSON 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 "uri.hpp"

#include <sstream>
#include <iostream>

#include <boost/regex.hpp>

using websocketpp::uri;

uri::uri(const std::string& uri) {
    // temporary testing non-regex implimentation.
    /*enum state {
        BEGIN = 0,
        HOST_BEGIN = 1,
        READ_IPV6_LITERAL = 2,
        READ_HOSTNAME = 3,
        BEGIN_PORT = 4,
        READ_PORT = 5,
        READ_RESOURCE = 6,
    };

    state the_state = BEGIN;
    std::string temp;

    for (std::string::const_iterator it = uri.begin(); it != uri.end(); it++) {
        switch (the_state) {
            case BEGIN:
                // we are looking for a ws:// or wss://
                if (temp.size() < 6) {
                    temp.append(1,*it);
                } else {
                    throw websocketpp::uri_exception("Scheme is too long");
                }

                if (temp == "ws://") {
                    m_secure = false;
                    the_state = HOST_BEGIN;
                    temp.clear();
                } else if (temp == "wss://") {
                    m_secure = true;
                    the_state = HOST_BEGIN;
                    temp.clear();
                }
                break;
            case HOST_BEGIN:
                // if character is [ go into IPv6 literal state
                // otherwise go into read hostname state
                if (*it == '[') {
                    the_state = READ_IPV6_LITERAL;
                } else {
                    *it--;
                    the_state = READ_HOSTNAME;
                }
                break;
            case READ_IPV6_LITERAL:
                // read 0-9a-fA-F:. until ] or 45 characters have been read
                if (*it == ']') {
                    the_state = BEGIN_PORT;
                    break;
                }

                if (m_host.size() > 45) {
                    throw websocketpp::uri_exception("IPv6 literal is too long");
                }

                if (*it == '.' ||
                    *it == ':' ||
                    (*it >= '0' && *it <= '9') ||
                    (*it >= 'a' && *it <= 'f') ||
                    (*it >= 'A' && *it <= 'F'))
                {
                    m_host.append(1,*it);
                }
                break;
            case READ_HOSTNAME:
                //
                if (*it == ':') {
                    it--;
                    the_state = BEGIN_PORT;
                    break;
                }

                if (*it == '/') {
                    it--;
                    the_state = BEGIN_PORT;
                    break;
                }

                // TODO: max url length?

                // TODO: check valid characters?
                if (1) {
                    m_host.append(1,*it);
                }
                break;
            case BEGIN_PORT:
                if (*it == ':') {
                    the_state = READ_PORT;
                } else if (*it == '/') {
                    m_port = get_port_from_string("");
                     *it--;
                    the_state = READ_RESOURCE;
                } else {
                    throw websocketpp::uri_exception("Error parsing WebSocket URI");
                }
                break;
            case READ_PORT:
                if (*it >= '0' && *it <= '9') {
                    if (temp.size() < 5) {
                        temp.append(1,*it);
                    } else {
                        throw websocketpp::uri_exception("Port is too long");
                    }
                } else if (*it == '/') {
                    m_port = get_port_from_string(temp);
                    temp.clear();
                     *it--;
                    the_state = READ_RESOURCE;

                } else {
                    throw websocketpp::uri_exception("Error parsing WebSocket URI");
                }
                break;
            case READ_RESOURCE:
                // max length check?

                if (*it == '#') {
                    throw websocketpp::uri_exception("WebSocket URIs cannot have fragments");
                } else {
                    m_resource.append(1,*it);
                }
                break;
        }
    }

    switch (the_state) {
        case READ_PORT:
            m_port = get_port_from_string(temp);
            break;
        default:
            break;
    }

    if (m_resource == "") {
        m_resource = "/";
    }*/

    boost::cmatch matches;
    const boost::regex expression("(ws|wss)://([^/:\\[]+|\\[[0-9a-fA-F:.]+\\])(:\\d{1,5})?(/[^#]*)?");

    // TODO: should this split resource into path/query?

    if (boost::regex_match(uri.c_str(), matches, expression)) {
        m_secure = (matches[1] == "wss");
        m_host = matches[2];

        // strip brackets from IPv6 literal URIs
        if (m_host[0] == '[') {
            m_host = m_host.substr(1,m_host.size()-2);
        }

        std::string port(matches[3]);

        if (port != "") {
            // strip off the :
            // this could probably be done with a better regex.
            port = port.substr(1);
        }

        m_port = get_port_from_string(port);

        m_resource = matches[4];

        if (m_resource == "") {
            m_resource = "/";
        }

        return;
    }

    throw websocketpp::uri_exception("Error parsing WebSocket URI");

}

uri::uri(bool secure, const std::string& host, uint16_t port, const std::string& resource)
 : m_secure(secure),
   m_host(host),
   m_port(port),
   m_resource(resource == "" ? "/" : resource) {}

uri::uri(bool secure, const std::string& host, const std::string& resource)
: m_secure(secure),
  m_host(host),
  m_port(m_secure ? URI_DEFAULT_SECURE_PORT : URI_DEFAULT_PORT),
  m_resource(resource == "" ? "/" : resource) {}

uri::uri(bool secure,
         const std::string& host,
         const std::string& port,
         const std::string& resource)
 : m_secure(secure),
   m_host(host),
   m_port(get_port_from_string(port)),
   m_resource(resource == "" ? "/" : resource) {}

/* Slightly cleaner C++11 delegated constructor method

uri::uri(bool secure,
         const std::string& host,
         uint16_t port,
         const std::string& resource)
 : m_secure(secure),
   m_host(host),
   m_port(port),
   m_resource(resource == "" ? "/" : resource)
{
    if (m_port > 65535) {
        throw websocketpp::uri_exception("Port must be less than 65535");
    }
}

uri::uri(bool secure,
         const std::string& host,
         const std::string& port,
         const std::string& resource)
 : uri(secure, host, get_port_from_string(port), resource) {}

*/

bool uri::get_secure() const {
    return m_secure;
}

std::string uri::get_host() const {
    return m_host;
}

std::string uri::get_host_port() const {
    if (m_port == (m_secure ? URI_DEFAULT_SECURE_PORT : URI_DEFAULT_PORT)) {
         return m_host;
    } else {
        std::stringstream p;
        p << m_host << ":" << m_port;
        return p.str();
    }

}

uint16_t uri::get_port() const {
    return m_port;
}

std::string uri::get_port_str() const {
    std::stringstream p;
    p << m_port;
    return p.str();
}

std::string uri::get_resource() const {
    return m_resource;
}

std::string uri::str() const {
    std::stringstream s;

    s << "ws" << (m_secure ? "s" : "") << "://" << m_host;

    if (m_port != (m_secure ? URI_DEFAULT_SECURE_PORT : URI_DEFAULT_PORT)) {
        s << ":" << m_port;
    }

    s << m_resource;
    return s.str();
}

uint16_t uri::get_port_from_string(const std::string& port) const {
    if (port == "") {
        return (m_secure ? URI_DEFAULT_SECURE_PORT : URI_DEFAULT_PORT);
    }

    unsigned int t_port = atoi(port.c_str());

    if (t_port > 65535) {
        throw websocketpp::uri_exception("Port must be less than 65535");
    }

    if (t_port == 0) {
        throw websocketpp::uri_exception("Error parsing port string: "+port);
    }

    return t_port;
}<|endoftext|>


<commit_before>#pragma once

#include <memory>
#include <utility>
#include <type_traits>
#include "signal.hpp"

namespace eventpp {

namespace details {

template<class E>
struct ETag { using type = E; };

template<int N, int M>
struct Choice: public Choice<N+1, M> { };

template<int N>
struct Choice<N, N> { };

template<typename... T>
using VoidType = void;

template<typename, typename, typename = VoidType<>>
struct HasReceiveMember: std::false_type { };

template<typename C, typename E>
struct HasReceiveMember
<
    C, E,
    VoidType<decltype(std::declval<C>().receive(std::declval<E>()))>
>: std::true_type { };

template<typename C, typename E>
constexpr bool HasReceiveMemberValue = HasReceiveMember<C, E>::value;

}

template<class D>
struct Receiver: public std::enable_shared_from_this<D>
{ };

template<int S, class... T>
class BusBase;

template<int S, class E, class... O>
class BusBase<S, E, O...>: public BusBase<S, O...> {
    using Base = BusBase<S, O...>;

protected:
    using Base::get;
    using Base::reg;
    using Base::unreg;

    Signal<E>& get(details::ETag<E>) {
        return signal;
    }

    template<class C>
    std::enable_if_t<details::HasReceiveMemberValue<C, E>>
    reg(details::Choice<S-(sizeof...(O)+1), S>, std::weak_ptr<C> ptr) {
        signal.template add<C, &C::receive>(ptr);
        Base::reg(details::Choice<S-sizeof...(O), S>{}, ptr);
    }

    template<class C>
    std::enable_if_t<details::HasReceiveMemberValue<C, E>>
    unreg(details::Choice<S-(sizeof...(O)+1), S>, std::weak_ptr<C> ptr) {
        signal.template remove<C, &C::receive>(ptr);
        Base::unreg(details::Choice<S-sizeof...(O), S>{}, ptr);
    }

    std::size_t size() const noexcept {
        return signal.size() + Base::size();
    }

private:
    Signal<E> signal;
};

template<int S>
class BusBase<S> {
protected:
    virtual ~BusBase() { }
    void get();
    void reg(details::Choice<S, S>, std::weak_ptr<void>) { }
    void unreg(details::Choice<S, S>, std::weak_ptr<void>) { }
    std::size_t size() const noexcept { return 0; }
};

template<class... T>
class Bus: public BusBase<sizeof...(T), T...> {
    using Base = BusBase<sizeof...(T), T...>;

public:
    using Base::size;

    template<class C, template<typename> class P>
    std::enable_if_t<std::is_convertible<P<C>, std::weak_ptr<C>>::value>
    reg(P<C> &ptr) {
        auto wptr = static_cast<std::weak_ptr<C>>(ptr);
        Base::reg(details::Choice<0, sizeof...(T)>{}, wptr);
    }

    template<class C, template<typename> class P>
    std::enable_if_t<std::is_convertible<P<C>, std::weak_ptr<C>>::value>
    unreg(P<C> &ptr) {
        auto wptr = static_cast<std::weak_ptr<C>>(ptr);
        Base::unreg(details::Choice<0, sizeof...(T)>{}, wptr);
    }

    template<class E, void(*F)(const E &)>
    void add() {
        Signal<E> &signal = Base::get(details::ETag<E>{});
        signal.template add<F>();
    }

    template<class E, class C, void(C::*M)(const E &) = &C::receive, template<typename> class P>
    std::enable_if_t<std::is_convertible<P<C>, std::weak_ptr<C>>::value, void>
    add(P<C> &ptr) {
        Signal<E> &signal = Base::get(details::ETag<E>{});
        auto wptr = static_cast<std::weak_ptr<C>>(ptr);
        signal.template add<C, M>(wptr);
    }

    template<class E, void(*F)(const E &)>
    void remove() {
        Signal<E> &signal = Base::get(details::ETag<E>{});
        signal.template remove<F>();
    }

    template<class E, class C, void(C::*M)(const E &) = &C::receive, template<typename> class P>
    std::enable_if_t<std::is_convertible<P<C>, std::weak_ptr<C>>::value, void>
    remove(P<C> &ptr) {
        Signal<E> &signal = Base::get(details::ETag<E>{});
        auto wptr = static_cast<std::weak_ptr<C>>(ptr);
        signal.template remove<C, M>(wptr);
    }

    template<class E, class... A>
    void publish(A&&... args) {
        Signal<E> &signal = Base::get(details::ETag<E>{});
        E e(std::forward<A>(args)...);
        signal.publish(e);
    }
};

}
<commit_msg>minor changes<commit_after>#pragma once

#include <memory>
#include <utility>
#include <type_traits>
#include "signal.hpp"

namespace eventpp {

namespace details {

template<class E>
struct ETag { using type = E; };

template<int N, int M>
struct Choice: public Choice<N+1, M> { };

template<int N>
struct Choice<N, N> { };

template<typename... T>
using VoidType = void;

template<typename, typename, typename = VoidType<>>
struct HasReceiveMember: std::false_type { };

template<typename C, typename E>
struct HasReceiveMember
<
    C, E,
    VoidType<decltype(std::declval<C>().receive(std::declval<E>()))>
>: std::true_type { };

template<typename C, typename E>
constexpr bool HasReceiveMemberValue = HasReceiveMember<C, E>::value;

}

template<class D>
struct Receiver: public std::enable_shared_from_this<D>
{ };

template<int S, class... T>
class BusBase;

template<int S, class E, class... O>
class BusBase<S, E, O...>: public BusBase<S, O...> {
    using Base = BusBase<S, O...>;

protected:
    using Base::get;
    using Base::reg;
    using Base::unreg;

    Signal<E>& get(details::ETag<E>) {
        return signal;
    }

    template<class C>
    std::enable_if_t<details::HasReceiveMemberValue<C, E>>
    reg(details::Choice<S-(sizeof...(O)+1), S>, std::weak_ptr<C> ptr) {
        signal.template add<C, &C::receive>(ptr);
        Base::reg(details::Choice<S-sizeof...(O), S>{}, ptr);
    }

    template<class C>
    std::enable_if_t<details::HasReceiveMemberValue<C, E>>
    unreg(details::Choice<S-(sizeof...(O)+1), S>, std::weak_ptr<C> ptr) {
        signal.template remove<C, &C::receive>(ptr);
        Base::unreg(details::Choice<S-sizeof...(O), S>{}, ptr);
    }

    std::size_t size() const noexcept {
        return signal.size() + Base::size();
    }

private:
    Signal<E> signal;
};

template<int S>
class BusBase<S> {
protected:
    virtual ~BusBase() { }
    void get();
    void reg(details::Choice<S, S>, std::weak_ptr<void>) { }
    void unreg(details::Choice<S, S>, std::weak_ptr<void>) { }
    std::size_t size() const noexcept { return 0; }
};

template<class... T>
class Bus: public BusBase<sizeof...(T), T...> {
    using Base = BusBase<sizeof...(T), T...>;

public:
    using Base::size;

    template<class C, template<typename> class P>
    std::enable_if_t<std::is_convertible<P<C>, std::weak_ptr<C>>::value>
    reg(P<C> &ptr) {
        auto wptr = static_cast<std::weak_ptr<C>>(ptr);
        Base::reg(details::Choice<0, sizeof...(T)>{}, wptr);
    }

    template<class C, template<typename> class P>
    std::enable_if_t<std::is_convertible<P<C>, std::weak_ptr<C>>::value>
    unreg(P<C> &ptr) {
        auto wptr = static_cast<std::weak_ptr<C>>(ptr);
        Base::unreg(details::Choice<0, sizeof...(T)>{}, wptr);
    }

    template<class E, void(*F)(const E &)>
    void add() {
        Signal<E> &signal = Base::get(details::ETag<E>{});
        signal.template add<F>();
    }

    template<class E, class C, void(C::*M)(const E &) = &C::receive, template<typename> class P>
    std::enable_if_t<std::is_convertible<P<C>, std::weak_ptr<C>>::value>
    add(P<C> &ptr) {
        Signal<E> &signal = Base::get(details::ETag<E>{});
        auto wptr = static_cast<std::weak_ptr<C>>(ptr);
        signal.template add<C, M>(wptr);
    }

    template<class E, void(*F)(const E &)>
    void remove() {
        Signal<E> &signal = Base::get(details::ETag<E>{});
        signal.template remove<F>();
    }

    template<class E, class C, void(C::*M)(const E &) = &C::receive, template<typename> class P>
    std::enable_if_t<std::is_convertible<P<C>, std::weak_ptr<C>>::value>
    remove(P<C> &ptr) {
        Signal<E> &signal = Base::get(details::ETag<E>{});
        auto wptr = static_cast<std::weak_ptr<C>>(ptr);
        signal.template remove<C, M>(wptr);
    }

    template<class E, class... A>
    void publish(A&&... args) {
        Signal<E> &signal = Base::get(details::ETag<E>{});
        E e(std::forward<A>(args)...);
        signal.publish(e);
    }
};

}
<|endoftext|>


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

#ifndef __PROCESS_MUTEX_HPP__
#define __PROCESS_MUTEX_HPP__

#include <atomic>
#include <memory>
#include <queue>

#include <process/future.hpp>
#include <process/owned.hpp>

#include <stout/nothing.hpp>
#include <stout/synchronized.hpp>

namespace process {

class Mutex
{
public:
  Mutex() : data(new Data()) {}

  Future<Nothing> lock()
  {
    Future<Nothing> future = Nothing();

    synchronized (data->lock) {
      if (!data->locked) {
        data->locked = true;
      } else {
        Owned<Promise<Nothing>> promise(new Promise<Nothing>());
        data->promises.push(promise);
        future = promise->future();
      }
    }

    return future;
  }

  void unlock()
  {
    // NOTE: We need to grab the promise 'date->promises.front()' but
    // set it outside of the critical section because setting it might
    // trigger callbacks that try to reacquire the lock.
    Owned<Promise<Nothing>> promise;

    synchronized (data->lock) {
      if (!data->promises.empty()) {
        // TODO(benh): Skip a future that has been discarded?
        promise = data->promises.front();
        data->promises.pop();
      } else {
        data->locked = false;
      }
    }

    if (promise.get() != nullptr) {
      promise->set(Nothing());
    }
  }

private:
  struct Data
  {
    Data() : locked(false) {}

    ~Data()
    {
      // TODO(benh): Fail promises?
    }

    // Rather than use a process to serialize access to the mutex's
    // internal data we use a 'std::atomic_flag'.
    std::atomic_flag lock = ATOMIC_FLAG_INIT;

    // Describes the state of this mutex.
    bool locked;

    // Represents "waiters" for this lock.
    std::queue<Owned<Promise<Nothing>>> promises;
  };

  std::shared_ptr<Data> data;
};

} // namespace process {

#endif // __PROCESS_MUTEX_HPP__
<commit_msg>Eliminated unnecssary copying within process::Mutex.<commit_after>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License

#ifndef __PROCESS_MUTEX_HPP__
#define __PROCESS_MUTEX_HPP__

#include <atomic>
#include <memory>
#include <queue>

#include <process/future.hpp>
#include <process/owned.hpp>

#include <stout/nothing.hpp>
#include <stout/synchronized.hpp>

namespace process {

class Mutex
{
public:
  Mutex() : data(new Data()) {}

  Future<Nothing> lock()
  {
    Future<Nothing> future = Nothing();

    synchronized (data->lock) {
      if (!data->locked) {
        data->locked = true;
      } else {
        Promise<Nothing> waiter;
        future = waiter.future();
        data->waiters.push(std::move(waiter));
      }
    }

    return future;
  }

  void unlock()
  {
    // NOTE: We need to grab the promise 'date->promises.front()' but
    // set it outside of the critical section because setting it might
    // trigger callbacks that try to reacquire the lock.
    Option<Promise<Nothing>> waiter;

    synchronized (data->lock) {
      if (!data->waiters.empty()) {
        // TODO(benh): Skip a future that has been discarded?
        waiter = std::move(data->waiters.front());
        data->waiters.pop();
      } else {
        data->locked = false;
      }
    }

    if (waiter.isSome()) {
      waiter->set(Nothing());
    }
  }

private:
  struct Data
  {
    Data() : locked(false) {}

    ~Data()
    {
      // TODO(benh): Fail promises?
    }

    // Rather than use a process to serialize access to the mutex's
    // internal data we use a 'std::atomic_flag'.
    std::atomic_flag lock = ATOMIC_FLAG_INIT;

    // Describes the state of this mutex.
    bool locked;

    // Represents "waiters" for this lock.
    std::queue<Promise<Nothing>> waiters;
  };

  std::shared_ptr<Data> data;
};

} // namespace process {

#endif // __PROCESS_MUTEX_HPP__
<|endoftext|>


<commit_before>/**
 * \file DMS.cpp
 * \brief Implementation for GeographicLib::DMS class
 *
 * Copyright (c) Charles Karney (2008, 2009, 2010, 2011) <charles@karney.com>
 * and licensed under the LGPL.  For more information, see
 * http://geographiclib.sourceforge.net/
 **********************************************************************/

#include <GeographicLib/DMS.hpp>
#include <algorithm>

#define GEOGRAPHICLIB_DMS_CPP "$Id: 9bf63d5aa00ac72e470dd6fb7d9a4af40989be25 $"

RCSID_DECL(GEOGRAPHICLIB_DMS_CPP)
RCSID_DECL(GEOGRAPHICLIB_DMS_HPP)

namespace GeographicLib {

  using namespace std;

  const string DMS::hemispheres_ = "SNWE";
  const string DMS::signs_ = "-+";
  const string DMS::digits_ = "0123456789";
  const string DMS::dmsindicators_ = "D'\"";
  const string DMS::components_[] = {"degrees", "minutes", "seconds"};

  Math::real DMS::Decode(const std::string& dms, flag& ind) {
    string errormsg;
    do {                       // Executed once (provides the ability to break)
      int sign = 1;
      unsigned
        beg = 0,
        end = unsigned(dms.size());
      while (beg < end && isspace(dms[beg]))
        ++beg;
      while (beg < end && isspace(dms[end - 1]))
        --end;
      flag ind1 = NONE;
      int k = -1;
      if (end > beg && (k = lookup(hemispheres_, dms[beg])) >= 0) {
        ind1 = (k / 2) ? LONGITUDE : LATITUDE;
        sign = k % 2 ? 1 : -1;
        ++beg;
      }
      if (end > beg && (k = lookup(hemispheres_, dms[end-1])) >= 0) {
        if (k >= 0) {
          if (ind1 != NONE) {
            if (toupper(dms[beg - 1]) == toupper(dms[end - 1]))
              errormsg = "Repeated hemisphere indicators " + str(dms[beg - 1])
                + " in " + dms.substr(beg - 1, end - beg + 1);
            else
              errormsg = "Contradictory hemisphere indicators "
                + str(dms[beg - 1]) + " and " + str(dms[end - 1]) + " in "
                + dms.substr(beg - 1, end - beg + 1);
            break;
          }
          ind1 = (k / 2) ? LONGITUDE : LATITUDE;
          sign = k % 2 ? 1 : -1;
          --end;
        }
      }
      if (end > beg && (k = lookup(signs_, dms[beg])) >= 0) {
        if (k >= 0) {
          sign *= k ? 1 : -1;
          ++beg;
        }
      }
      if (end == beg) {
        errormsg = "Empty or incomplete DMS string " + dms;
        break;
      }
      real ipieces[] = {0, 0, 0};
      real fpieces[] = {0, 0, 0};
      unsigned npiece = 0;
      real icurrent = 0;
      real fcurrent = 0;
      unsigned ncurrent = 0, p = beg;
      bool pointseen = false;
      unsigned digcount = 0;
      while (p < end) {
        char x = dms[p++];
        if ((k = lookup(digits_, x)) >= 0) {
          ++ncurrent;
          if (digcount > 0)
            ++digcount;           // Count of decimal digits_
          else
            icurrent = 10 * icurrent + k;
        } else if (x == '.') {
          if (pointseen) {
            errormsg = "Multiple decimal points in "
              + dms.substr(beg, end - beg);
            break;
          }
          pointseen = true;
          digcount = 1;
        } else if ((k = lookup(dmsindicators_, x)) >= 0) {
          if (unsigned(k) == npiece - 1) {
            errormsg = "Repeated " + components_[k] +
              " component in " + dms.substr(beg, end - beg);
            break;
          } else if (unsigned(k) < npiece) {
            errormsg = components_[k] + " component follows "
              + components_[npiece - 1] + " component in "
              + dms.substr(beg, end - beg);
            break;
          }
          if (ncurrent == 0) {
            errormsg = "Missing numbers in " + components_[k] +
              " component of " + dms.substr(beg, end - beg);
            break;
          }
          if (digcount > 1) {
            istringstream s(dms.substr(p - digcount - 1, digcount));
            s >> fcurrent;
          }
          ipieces[k] = icurrent;
          fpieces[k] = icurrent + fcurrent;
          if (p < end) {
            npiece = k + 1;
            icurrent = fcurrent = 0;
            ncurrent = digcount = 0;
          }
        } else if (lookup(signs_, x) >= 0) {
          errormsg = "Internal sign in DMS string "
            + dms.substr(beg, end - beg);
          break;
        } else {
          errormsg = "Illegal character " + str(x) + " in DMS string "
            + dms.substr(beg, end - beg);
          break;
        }
      }
      if (!errormsg.empty())
        break;
      if (lookup(dmsindicators_, dms[p - 1]) < 0) {
        if (npiece >= 3) {
          errormsg = "Extra text following seconds in DMS string "
            + dms.substr(beg, end - beg);
          break;
        }
        if (ncurrent == 0) {
          errormsg = "Missing numbers in trailing component of "
            + dms.substr(beg, end - beg);
          break;
        }
        if (digcount > 1) {
          istringstream s(dms.substr(p - digcount, digcount));
          s >> fcurrent;
        }
        ipieces[npiece] = icurrent;
        fpieces[npiece] = icurrent + fcurrent;
      }
      if (pointseen && digcount == 0) {
        errormsg = "Decimal point in non-terminal component of "
          + dms.substr(beg, end - beg);
        break;
      }
      // Note that we accept 59.999999... even though it rounds to 60.
      if (ipieces[1] >= 60) {
        errormsg = "Minutes " + str(fpieces[1]) + " not in range [0, 60)";
        break;
      }
      if (ipieces[2] >= 60) {
        errormsg = "Seconds " + str(fpieces[2]) + " not in range [0, 60)";
        break;
      }
      ind = ind1;
      // Assume check on range of result is made by calling routine (which
      // might be able to offer a better diagnostic).
      return real(sign) * (fpieces[0] + (fpieces[1] + fpieces[2] / 60) / 60);
    } while (false);
    real val = NumMatch(dms);
    if (val == 0)
      throw GeographicErr(errormsg);
    else
      ind = NONE;
    return val;
  }

  Math::real DMS::NumMatch(const std::string& s) {
    if (s.length() < 3)
      return 0;
    string t;
    t.resize(s.length());
    for (size_t i = s.length(); i--;)
      t[i] = toupper(s[i]);
    int sign = t[0] == '-' ? -1 : 1;
    string::size_type p0 = t[0] == '-' || t[0] == '+' ? 1 : 0;
    string::size_type p1 = t.find_last_not_of('0');
    if (p1 == string::npos || p1 + 1 < p0 + 3)
      return 0;
    // Strip off sign and trailing 0s
    t = t.substr(p0, p1 + 1 - p0);  // Length at least 3
    if (t == "NAN" || t == "1.#QNAN" || t == "1.#SNAN" || t == "1.#IND")
      return sign * Math::NaN();
    else if (t == "INF" || t == "1.#INF")
      return sign * Math::infinity();
    return 0;
  }

  Math::real DMS::Decode(const std::string& str) {
    istringstream is(str);
    string errormsg;
    real num;
    do {                       // Executed once (provides the ability to break)
      if (!(is >> num)) {
        errormsg = "Could not read number: " + str;
        break;
      }
      // On some platforms, is >> num gobbles final E in 1234E, so look for
      // last character which is legal as the final character in a number
      // (digit or period).
      int pos = min(int(is.tellg()), int(str.find_last_of("0123456789.")) + 1);
      if (pos != int(str.size())) {
        errormsg = "Extra text " + str.substr(pos) + " in number " + str;
        break;
      }
      return num;
    } while (false);
    num = NumMatch(str);
    if (num == 0)
      throw GeographicErr(errormsg);
    return num;
  }

  void DMS::DecodeLatLon(const std::string& stra, const std::string& strb,
                         real& lat, real& lon) {
      real a, b;
      flag ia, ib;
      a = Decode(stra, ia);
      b = Decode(strb, ib);
      if (ia == NONE && ib == NONE) {
        // Default to lat, long
        ia = LATITUDE;
        ib = LONGITUDE;
      } else if (ia == NONE)
        ia = flag(LATITUDE + LONGITUDE - ib);
      else if (ib == NONE)
        ib = flag(LATITUDE + LONGITUDE - ia);
      if (ia == ib)
        throw GeographicErr("Both " + stra + " and "
                            + strb + " interpreted as "
                            + (ia == LATITUDE ? "latitudes" : "longitudes"));
      real
        lat1 = ia == LATITUDE ? a : b,
        lon1 = ia == LATITUDE ? b : a;
      if (lat1 < -90 || lat1 > 90)
        throw GeographicErr("Latitude " + str(lat1) + "d not in [-90d, 90d]");
      if (lon1 < -180 || lon1 > 360)
        throw GeographicErr("Latitude " + str(lon1)
                            + "d not in [-180d, 360d]");
      if (lon1 >= 180)
        lon1 -= 360;
      lat = lat1;
      lon = lon1;
  }

  Math::real DMS::DecodeAngle(const std::string& angstr) {
    DMS::flag ind;
    real ang = Decode(angstr, ind);
    if (ind != DMS::NONE)
      throw GeographicErr("Arc angle " + angstr
                          + " includes a hemisphere, N/E/W/S");
    return ang;
  }

  Math::real DMS::DecodeAzimuth(const std::string& azistr) {
    DMS::flag ind;
    real azi = Decode(azistr, ind);
    if (ind == DMS::LATITUDE)
      throw GeographicErr("Azimuth " + azistr
                          + " has a latitude hemisphere, N/S");
    if (azi < -180 || azi > 360)
      throw GeographicErr("Azimuth " + azistr + " not in range [-180d,360d]");
    if (azi >= 180) azi -= 360;
    return azi;
  }

  string DMS::Encode(real angle, component trailing, unsigned prec, flag ind) {
    // Assume check on range of input angle has been made by calling
    // routine (which might be able to offer a better diagnostic).
    if (!Math::isfinite(angle))
      return angle < 0 ? string("-inf") :
        (angle > 0 ? string("inf") : string("nan"));

    // 15 - 2 * trailing = ceiling(log10(2^53/90/60^trailing)).
    // This suffices to give full real precision for numbers in [-90,90]
    prec = min(15 - 2 * unsigned(trailing), prec);
    real scale = 1;
    for (unsigned i = 0; i < unsigned(trailing); ++i)
      scale *= 60;
    for (unsigned i = 0; i < prec; ++i)
      scale *= 10;
    if (ind == AZIMUTH)
      angle -= floor(angle/360) * 360;
    int sign = angle < 0 ? -1 : 1;
    angle *= sign;

    // Break off integer part to preserve precision in manipulation of
    // fractional part.
    real
      idegree = floor(angle),
      fdegree = floor((angle - idegree) * scale + real(0.5)) / scale;
    if (fdegree >= 1) {
      idegree += 1;
      fdegree -= 1;
    }
    real pieces[3] = {fdegree, 0, 0};
    for (unsigned i = 1; i <= unsigned(trailing); ++i) {
      real
        ip = floor(pieces[i - 1]),
        fp = pieces[i - 1] - ip;
      pieces[i] = fp * 60;
      pieces[i - 1] = ip;
    }
    pieces[0] += idegree;
    ostringstream s;
    s << fixed  << setfill('0');
    if (ind == NONE && sign < 0)
      s << '-';
    switch (trailing) {
    case DEGREE:
      if (ind != NONE)
        s << setw(1 + min(int(ind), 2) + prec + (prec ? 1 : 0));
      s << setprecision(prec) << pieces[0];
      // Don't include degree designator (d) if it is the trailing component.
      break;
    default:
      if (ind != NONE)
        s << setw(1 + min(int(ind), 2));
      s << setprecision(0) << pieces[0] << char(tolower(dmsindicators_[0]));
      switch (trailing) {
      case MINUTE:
        s << setw(2 + prec + (prec ? 1 : 0)) << setprecision(prec)
          << pieces[1] <<  char(tolower(dmsindicators_[1]));
        break;
      case SECOND:
        s << setw(2) << pieces[1] <<  char(tolower(dmsindicators_[1]))
          << setw(2 + prec + (prec ? 1 : 0)) << setprecision(prec)
          << pieces[2] <<  char(tolower(dmsindicators_[2]));
        break;
      default:
        break;
      }
    }
    if (ind != NONE && ind != AZIMUTH)
      s << hemispheres_[(ind == LATITUDE ? 0 : 2) + (sign < 0 ? 0 : 1)];
    return s.str();
  }

} // namespace GeographicLib
<commit_msg>FIX BUG: accommodate tellg() returning -1 at end of string.<commit_after>/**
 * \file DMS.cpp
 * \brief Implementation for GeographicLib::DMS class
 *
 * Copyright (c) Charles Karney (2008, 2009, 2010, 2011) <charles@karney.com>
 * and licensed under the LGPL.  For more information, see
 * http://geographiclib.sourceforge.net/
 **********************************************************************/

#include <GeographicLib/DMS.hpp>
#include <algorithm>

#define GEOGRAPHICLIB_DMS_CPP "$Id: d9e140041598b867448f2a3b47cf5a5cc3ec8ab7 $"

RCSID_DECL(GEOGRAPHICLIB_DMS_CPP)
RCSID_DECL(GEOGRAPHICLIB_DMS_HPP)

namespace GeographicLib {

  using namespace std;

  const string DMS::hemispheres_ = "SNWE";
  const string DMS::signs_ = "-+";
  const string DMS::digits_ = "0123456789";
  const string DMS::dmsindicators_ = "D'\"";
  const string DMS::components_[] = {"degrees", "minutes", "seconds"};

  Math::real DMS::Decode(const std::string& dms, flag& ind) {
    string errormsg;
    do {                       // Executed once (provides the ability to break)
      int sign = 1;
      unsigned
        beg = 0,
        end = unsigned(dms.size());
      while (beg < end && isspace(dms[beg]))
        ++beg;
      while (beg < end && isspace(dms[end - 1]))
        --end;
      flag ind1 = NONE;
      int k = -1;
      if (end > beg && (k = lookup(hemispheres_, dms[beg])) >= 0) {
        ind1 = (k / 2) ? LONGITUDE : LATITUDE;
        sign = k % 2 ? 1 : -1;
        ++beg;
      }
      if (end > beg && (k = lookup(hemispheres_, dms[end-1])) >= 0) {
        if (k >= 0) {
          if (ind1 != NONE) {
            if (toupper(dms[beg - 1]) == toupper(dms[end - 1]))
              errormsg = "Repeated hemisphere indicators " + str(dms[beg - 1])
                + " in " + dms.substr(beg - 1, end - beg + 1);
            else
              errormsg = "Contradictory hemisphere indicators "
                + str(dms[beg - 1]) + " and " + str(dms[end - 1]) + " in "
                + dms.substr(beg - 1, end - beg + 1);
            break;
          }
          ind1 = (k / 2) ? LONGITUDE : LATITUDE;
          sign = k % 2 ? 1 : -1;
          --end;
        }
      }
      if (end > beg && (k = lookup(signs_, dms[beg])) >= 0) {
        if (k >= 0) {
          sign *= k ? 1 : -1;
          ++beg;
        }
      }
      if (end == beg) {
        errormsg = "Empty or incomplete DMS string " + dms;
        break;
      }
      real ipieces[] = {0, 0, 0};
      real fpieces[] = {0, 0, 0};
      unsigned npiece = 0;
      real icurrent = 0;
      real fcurrent = 0;
      unsigned ncurrent = 0, p = beg;
      bool pointseen = false;
      unsigned digcount = 0;
      while (p < end) {
        char x = dms[p++];
        if ((k = lookup(digits_, x)) >= 0) {
          ++ncurrent;
          if (digcount > 0)
            ++digcount;           // Count of decimal digits_
          else
            icurrent = 10 * icurrent + k;
        } else if (x == '.') {
          if (pointseen) {
            errormsg = "Multiple decimal points in "
              + dms.substr(beg, end - beg);
            break;
          }
          pointseen = true;
          digcount = 1;
        } else if ((k = lookup(dmsindicators_, x)) >= 0) {
          if (unsigned(k) == npiece - 1) {
            errormsg = "Repeated " + components_[k] +
              " component in " + dms.substr(beg, end - beg);
            break;
          } else if (unsigned(k) < npiece) {
            errormsg = components_[k] + " component follows "
              + components_[npiece - 1] + " component in "
              + dms.substr(beg, end - beg);
            break;
          }
          if (ncurrent == 0) {
            errormsg = "Missing numbers in " + components_[k] +
              " component of " + dms.substr(beg, end - beg);
            break;
          }
          if (digcount > 1) {
            istringstream s(dms.substr(p - digcount - 1, digcount));
            s >> fcurrent;
          }
          ipieces[k] = icurrent;
          fpieces[k] = icurrent + fcurrent;
          if (p < end) {
            npiece = k + 1;
            icurrent = fcurrent = 0;
            ncurrent = digcount = 0;
          }
        } else if (lookup(signs_, x) >= 0) {
          errormsg = "Internal sign in DMS string "
            + dms.substr(beg, end - beg);
          break;
        } else {
          errormsg = "Illegal character " + str(x) + " in DMS string "
            + dms.substr(beg, end - beg);
          break;
        }
      }
      if (!errormsg.empty())
        break;
      if (lookup(dmsindicators_, dms[p - 1]) < 0) {
        if (npiece >= 3) {
          errormsg = "Extra text following seconds in DMS string "
            + dms.substr(beg, end - beg);
          break;
        }
        if (ncurrent == 0) {
          errormsg = "Missing numbers in trailing component of "
            + dms.substr(beg, end - beg);
          break;
        }
        if (digcount > 1) {
          istringstream s(dms.substr(p - digcount, digcount));
          s >> fcurrent;
        }
        ipieces[npiece] = icurrent;
        fpieces[npiece] = icurrent + fcurrent;
      }
      if (pointseen && digcount == 0) {
        errormsg = "Decimal point in non-terminal component of "
          + dms.substr(beg, end - beg);
        break;
      }
      // Note that we accept 59.999999... even though it rounds to 60.
      if (ipieces[1] >= 60) {
        errormsg = "Minutes " + str(fpieces[1]) + " not in range [0, 60)";
        break;
      }
      if (ipieces[2] >= 60) {
        errormsg = "Seconds " + str(fpieces[2]) + " not in range [0, 60)";
        break;
      }
      ind = ind1;
      // Assume check on range of result is made by calling routine (which
      // might be able to offer a better diagnostic).
      return real(sign) * (fpieces[0] + (fpieces[1] + fpieces[2] / 60) / 60);
    } while (false);
    real val = NumMatch(dms);
    if (val == 0)
      throw GeographicErr(errormsg);
    else
      ind = NONE;
    return val;
  }

  Math::real DMS::NumMatch(const std::string& s) {
    if (s.length() < 3)
      return 0;
    string t;
    t.resize(s.length());
    for (size_t i = s.length(); i--;)
      t[i] = toupper(s[i]);
    int sign = t[0] == '-' ? -1 : 1;
    string::size_type p0 = t[0] == '-' || t[0] == '+' ? 1 : 0;
    string::size_type p1 = t.find_last_not_of('0');
    if (p1 == string::npos || p1 + 1 < p0 + 3)
      return 0;
    // Strip off sign and trailing 0s
    t = t.substr(p0, p1 + 1 - p0);  // Length at least 3
    if (t == "NAN" || t == "1.#QNAN" || t == "1.#SNAN" || t == "1.#IND")
      return sign * Math::NaN();
    else if (t == "INF" || t == "1.#INF")
      return sign * Math::infinity();
    return 0;
  }

  Math::real DMS::Decode(const std::string& str) {
    istringstream is(str);
    string errormsg;
    real num;
    do {                       // Executed once (provides the ability to break)
      if (!(is >> num)) {
        errormsg = "Could not read number: " + str;
        break;
      }
      // On some platforms, is >> num gobbles final E in 1234E, so look for
      // last character which is legal as the final character in a number
      // (digit or period).
      int pos = int(is.tellg()); // Returns -1 at end of string?
      if (pos < 0)
        pos = int(str.size());
      pos = min(pos, int(str.find_last_of("0123456789.")) + 1);
      if (pos != int(str.size())) {
        errormsg = "Extra text " + str.substr(pos) + " in number " + str;
        break;
      }
      return num;
    } while (false);
    num = NumMatch(str);
    if (num == 0)
      throw GeographicErr(errormsg);
    return num;
  }

  void DMS::DecodeLatLon(const std::string& stra, const std::string& strb,
                         real& lat, real& lon) {
      real a, b;
      flag ia, ib;
      a = Decode(stra, ia);
      b = Decode(strb, ib);
      if (ia == NONE && ib == NONE) {
        // Default to lat, long
        ia = LATITUDE;
        ib = LONGITUDE;
      } else if (ia == NONE)
        ia = flag(LATITUDE + LONGITUDE - ib);
      else if (ib == NONE)
        ib = flag(LATITUDE + LONGITUDE - ia);
      if (ia == ib)
        throw GeographicErr("Both " + stra + " and "
                            + strb + " interpreted as "
                            + (ia == LATITUDE ? "latitudes" : "longitudes"));
      real
        lat1 = ia == LATITUDE ? a : b,
        lon1 = ia == LATITUDE ? b : a;
      if (lat1 < -90 || lat1 > 90)
        throw GeographicErr("Latitude " + str(lat1) + "d not in [-90d, 90d]");
      if (lon1 < -180 || lon1 > 360)
        throw GeographicErr("Latitude " + str(lon1)
                            + "d not in [-180d, 360d]");
      if (lon1 >= 180)
        lon1 -= 360;
      lat = lat1;
      lon = lon1;
  }

  Math::real DMS::DecodeAngle(const std::string& angstr) {
    DMS::flag ind;
    real ang = Decode(angstr, ind);
    if (ind != DMS::NONE)
      throw GeographicErr("Arc angle " + angstr
                          + " includes a hemisphere, N/E/W/S");
    return ang;
  }

  Math::real DMS::DecodeAzimuth(const std::string& azistr) {
    DMS::flag ind;
    real azi = Decode(azistr, ind);
    if (ind == DMS::LATITUDE)
      throw GeographicErr("Azimuth " + azistr
                          + " has a latitude hemisphere, N/S");
    if (azi < -180 || azi > 360)
      throw GeographicErr("Azimuth " + azistr + " not in range [-180d,360d]");
    if (azi >= 180) azi -= 360;
    return azi;
  }

  string DMS::Encode(real angle, component trailing, unsigned prec, flag ind) {
    // Assume check on range of input angle has been made by calling
    // routine (which might be able to offer a better diagnostic).
    if (!Math::isfinite(angle))
      return angle < 0 ? string("-inf") :
        (angle > 0 ? string("inf") : string("nan"));

    // 15 - 2 * trailing = ceiling(log10(2^53/90/60^trailing)).
    // This suffices to give full real precision for numbers in [-90,90]
    prec = min(15 - 2 * unsigned(trailing), prec);
    real scale = 1;
    for (unsigned i = 0; i < unsigned(trailing); ++i)
      scale *= 60;
    for (unsigned i = 0; i < prec; ++i)
      scale *= 10;
    if (ind == AZIMUTH)
      angle -= floor(angle/360) * 360;
    int sign = angle < 0 ? -1 : 1;
    angle *= sign;

    // Break off integer part to preserve precision in manipulation of
    // fractional part.
    real
      idegree = floor(angle),
      fdegree = floor((angle - idegree) * scale + real(0.5)) / scale;
    if (fdegree >= 1) {
      idegree += 1;
      fdegree -= 1;
    }
    real pieces[3] = {fdegree, 0, 0};
    for (unsigned i = 1; i <= unsigned(trailing); ++i) {
      real
        ip = floor(pieces[i - 1]),
        fp = pieces[i - 1] - ip;
      pieces[i] = fp * 60;
      pieces[i - 1] = ip;
    }
    pieces[0] += idegree;
    ostringstream s;
    s << fixed  << setfill('0');
    if (ind == NONE && sign < 0)
      s << '-';
    switch (trailing) {
    case DEGREE:
      if (ind != NONE)
        s << setw(1 + min(int(ind), 2) + prec + (prec ? 1 : 0));
      s << setprecision(prec) << pieces[0];
      // Don't include degree designator (d) if it is the trailing component.
      break;
    default:
      if (ind != NONE)
        s << setw(1 + min(int(ind), 2));
      s << setprecision(0) << pieces[0] << char(tolower(dmsindicators_[0]));
      switch (trailing) {
      case MINUTE:
        s << setw(2 + prec + (prec ? 1 : 0)) << setprecision(prec)
          << pieces[1] <<  char(tolower(dmsindicators_[1]));
        break;
      case SECOND:
        s << setw(2) << pieces[1] <<  char(tolower(dmsindicators_[1]))
          << setw(2 + prec + (prec ? 1 : 0)) << setprecision(prec)
          << pieces[2] <<  char(tolower(dmsindicators_[2]));
        break;
      default:
        break;
      }
    }
    if (ind != NONE && ind != AZIMUTH)
      s << hemispheres_[(ind == LATITUDE ? 0 : 2) + (sign < 0 ? 0 : 1)];
    return s.str();
  }

} // namespace GeographicLib
<|endoftext|>


<commit_before>#include <boost/python.hpp>
#include <boost/optional/optional.hpp>

#include <nix.hpp>
#include <transmorgify.hpp>
#include <accessors.hpp>

#include <PyEntity.hpp>

using namespace boost::python;
using namespace nix;
using namespace base;
using namespace nixpy;

BOOST_PYTHON_MODULE(core)
{
    // set options for doc strings
    // show user defined / show py signatures / don't show cpp signatures
    docstring_options local_docstring_options(true, true, false);

    PyFile::do_export();

    PySection::do_export();
    PyProperty::do_export();
    PyValue::do_export();

    PyBlock::do_export();
    PySource::do_export();
    PyDataArray::do_export();
    PyDimensions::do_export();
    PyFeature::do_export();
    PySimpleTag::do_export();
    PyDataTag::do_export();

    to_python_converter<boost::optional<std::string>, option_transmogrify<std::string>>();
    option_transmogrify<std::string>::register_from_python();

    to_python_converter<std::vector<std::string>, vector_transmogrify<std::string>>();
    vector_transmogrify<std::string>::register_from_python();

    to_python_converter<std::vector<double>, vector_transmogrify<double>>();
    vector_transmogrify<double>::register_from_python();

    to_python_converter<boost::optional<double>, option_transmogrify<double>>();
    option_transmogrify<double>::register_from_python();

    to_python_converter<NDSize, ndsize_transmogrify>();
    ndsize_transmogrify::register_from_python();
}
<commit_msg>[core] add transmogrify for vector<int><commit_after>#include <boost/python.hpp>
#include <boost/optional/optional.hpp>

#include <nix.hpp>
#include <transmorgify.hpp>
#include <accessors.hpp>

#include <PyEntity.hpp>

using namespace boost::python;
using namespace nix;
using namespace base;
using namespace nixpy;

BOOST_PYTHON_MODULE(core)
{
    // set options for doc strings
    // show user defined / show py signatures / don't show cpp signatures
    docstring_options local_docstring_options(true, true, false);

    PyFile::do_export();

    PySection::do_export();
    PyProperty::do_export();
    PyValue::do_export();

    PyBlock::do_export();
    PySource::do_export();
    PyDataArray::do_export();
    PyDimensions::do_export();
    PyFeature::do_export();
    PySimpleTag::do_export();
    PyDataTag::do_export();

    to_python_converter<boost::optional<std::string>, option_transmogrify<std::string>>();
    option_transmogrify<std::string>::register_from_python();

    to_python_converter<std::vector<std::string>, vector_transmogrify<std::string>>();
    vector_transmogrify<std::string>::register_from_python();

    to_python_converter<std::vector<double>, vector_transmogrify<double>>();
    vector_transmogrify<double>::register_from_python();

    to_python_converter<std::vector<int>, vector_transmogrify<int>>();
    vector_transmogrify<int>::register_from_python();

    to_python_converter<boost::optional<double>, option_transmogrify<double>>();
    option_transmogrify<double>::register_from_python();

    to_python_converter<NDSize, ndsize_transmogrify>();
    ndsize_transmogrify::register_from_python();
}
<|endoftext|>


<commit_before>#include <boost/python.hpp>
#include <boost/optional/optional.hpp>

#include <nix.hpp>
#include <transmorgify.hpp>
#include <entity.hpp>

using namespace boost::python;
using namespace nix;
using namespace base;
using namespace nixpy;

static File
nix_file_open ( std::string path ) {
    return File::open ( path );
}

static std::vector<Block>
nix_file_blocks ( const File &f ) {
    return f.blocks();
}

static std::vector<DataArray>
nix_block_data_arrays ( const Block &b ) {
    return b.dataArrays();
}

static void
nix_data_array_label_setter ( DataArray &da, const boost::optional<std::string> &lbl ) {
    if ( lbl == boost::none ) {
        da.label ( boost::none );
    } else {
        da.label ( *lbl );
    }
}

BOOST_PYTHON_MODULE(core)
{

    class_<File>("File")
        .add_property("version", &File::version)
        .def("open", nix_file_open)
        .def("blocks", nix_file_blocks)
        .def("create_block", &File::createBlock)
        .def(self == other<File>())
        .staticmethod("open")
        ;

    named_entity<IBlock>::do_export("IBlock");
    class_<Block, bases<NamedEntity<IBlock>>>("Block")
        .def("create_data_array", &Block::createDataArray)
        .def("data_array_count", &Block::dataArrayCount)
        .def("data_arrays", nix_block_data_arrays)
        .def(self == self)
        ;

    class_<DataArray>("DataArray")
        .add_property("label", static_cast<boost::optional<std::string>(DataArray::*)() const>(&DataArray::label),
                      nix_data_array_label_setter)
        .def("has_data", &DataArray::hasData)
        ;

    to_python_converter<std::vector<Block>, vector_transmogrify<Block>>();
    to_python_converter<std::vector<DataArray>, vector_transmogrify<DataArray>>();
    to_python_converter<boost::optional<std::string>, option_transmogrify<std::string>>();
    option_transmogrify<std::string>::register_from_python();
}
<commit_msg>core: Add stubs for most important class bindings<commit_after>#include <boost/python.hpp>
#include <boost/optional/optional.hpp>

#include <nix.hpp>
#include <transmorgify.hpp>
#include <entity.hpp>

using namespace boost::python;
using namespace nix;
using namespace base;
using namespace nixpy;

static File
nix_file_open ( std::string path ) {
    return File::open ( path );
}

static std::vector<Block>
nix_file_blocks ( const File &f ) {
    return f.blocks();
}

static std::vector<DataArray>
nix_block_data_arrays ( const Block &b ) {
    return b.dataArrays();
}

static void
nix_data_array_label_setter ( DataArray &da, const boost::optional<std::string> &lbl ) {
    if ( lbl == boost::none ) {
        da.label ( boost::none );
    } else {
        da.label ( *lbl );
    }
}

BOOST_PYTHON_MODULE(core)
{

    class_<File>("File")
        .add_property("version", &File::version)
        .def("open", nix_file_open)
        .def("blocks", nix_file_blocks)
        .def("create_block", &File::createBlock)
        .def(self == other<File>())
        .staticmethod("open")
        ;

    // TODO enum classes for Implementation and FileMode

    class_<Value>("Value");

    class_<Property>("Property");

    class_<Section>("Section");

    named_entity<IBlock>::do_export("IBlock");
    class_<Block, bases<NamedEntity<IBlock>>>("Block")
        .def("create_data_array", &Block::createDataArray)
        .def("data_array_count", &Block::dataArrayCount)
        .def("data_arrays", nix_block_data_arrays)
        .def(self == self)
        ;

    class_<Source>("Source");

    class_<DataArray>("DataArray")
        .add_property("label", static_cast<boost::optional<std::string>(DataArray::*)() const>(&DataArray::label),
                      nix_data_array_label_setter)
        .def("has_data", &DataArray::hasData)
        ;

    // TODO enum class DataType

    class_<Dimension>("Dimension");

    class_<SampledDimension>("SampledDimension");

    class_<RangeDimension>("RangeDimension");

    class_<SetDimension>("SetDimension");

    class_<SimpleTag>("SimpleTag");

    class_<DataTag>("DataTag");

    class_<Feature>("Feature");

    // TODO enum class LinkType

    to_python_converter<std::vector<Block>, vector_transmogrify<Block>>();
    to_python_converter<std::vector<DataArray>, vector_transmogrify<DataArray>>();
    to_python_converter<boost::optional<std::string>, option_transmogrify<std::string>>();
    option_transmogrify<std::string>::register_from_python();
}
<|endoftext|>


<commit_before>// AudioEndPointLibrary.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"
#include "AudioEndPointLibrary.h"
#include "MMNotificationClient.h"
#include <DefSoundEndpointColl.h>
#include <algorithm>
#include <mutex>

namespace AudioEndPoint {

    struct CAudioEndPointLibrary::AudioEndPointLibraryImpl
    {
        IMMDeviceEnumeratorPtr m_DeviceEnumerator;
        IMMNotificationClientPtr m_notif_client;
    };

    struct CAudioEndPointLibrary::AudioEndPointLibraryDevicesImpl
    {
        AudioDeviceList m_playback;
        AudioDeviceList m_recording;
        bool m_need_update = true;
        std::mutex m_lists_mutex;
    };

    // This is the constructor of a class that has been exported.
    // see AudioEndPointLibrary.h for the class definition
    CAudioEndPointLibrary::CAudioEndPointLibrary() : m_container(new AudioEndPointLibraryImpl()),
        m_signals(new AudioEndPointLibrarySignals()),
        m_devices_lists(new AudioEndPointLibraryDevicesImpl())
    {
        m_devices_lists->m_need_update = true;
        this->RegisterNotificationClient();
    }

    CAudioEndPointLibrary::~CAudioEndPointLibrary()
    {
        delete m_signals;
        delete m_devices_lists;
        this->UnRegisterNotificationClient();
        delete m_container;
    }

    HRESULT CAudioEndPointLibrary::OnDeviceStateChanged(LPCWSTR pwstr_device_id, DWORD dw_new_state) const
    {
        auto m_playback = this->GetPlaybackDevices(DefSound::EDeviceState::All);
        auto m_recording = this->GetRecordingDevices(DefSound::EDeviceState::All);
        auto audio_device = find_if(m_playback.begin(), m_playback.end(),[pwstr_device_id](AudioDevicePtr device) {
            return wcscmp(device->ID, pwstr_device_id) == 0;
        });

        auto e_device_state = static_cast<DefSound::EDeviceState>(dw_new_state);
        if(audio_device != m_playback.end())
        {
            auto prevState = (*audio_device)->GetEndPoint().m_State.state;
            (*audio_device)->GetEndPoint().m_State.state = e_device_state;
            Signals->DeviceStateChanged.Notify((*audio_device), prevState, e_device_state);
        }

        audio_device = find_if(m_recording.begin(), m_recording.end(), [pwstr_device_id](AudioDevicePtr device) {
            return wcscmp(device->ID, pwstr_device_id) == 0;
        });

        if (audio_device != m_recording.end())
        {
            auto prevState = (*audio_device)->GetEndPoint().m_State.state;
            (*audio_device)->GetEndPoint().m_State.state = e_device_state;
            Signals->DeviceStateChanged.Notify((*audio_device), prevState, e_device_state);
        }
        return S_OK;
    }

    HRESULT CAudioEndPointLibrary::OnDeviceRemoved(LPCWSTR pwstr_device_id) const
    {
        m_devices_lists->m_need_update = true;
        return S_OK;
    }

    HRESULT CAudioEndPointLibrary::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstr_default_device_id) const
    {
        AudioDeviceList list;
        if(flow == ::eRender)
        {
            list = this->GetPlaybackDevices(DefSound::EDeviceState::All);
        }
        else if(flow == ::eCapture)
        {
            list = this->GetRecordingDevices(DefSound::EDeviceState::All);
        } else
        {
            return S_FALSE;
        }

        AudioDevicePtr deviceFound = nullptr;
        for (auto& device : list)
        {
            if (wcscmp(device->ID, pwstr_default_device_id) == 0)
            {
                device->GetEndPoint().m_IsDefault[role] = true;
                deviceFound = device;
            }
            else
            {
                device->GetEndPoint().m_IsDefault[role] = false;
            }
        }
        Signals->DeviceDefaultChanged.Notify(deviceFound, role);

        return S_OK;
    }

    HRESULT CAudioEndPointLibrary::OnDeviceAdded(LPCWSTR pwstr_device_id) const
    {
        m_devices_lists->m_need_update = true;
        auto m_playback = this->GetPlaybackDevices(DefSound::EDeviceState::All);
        auto m_recording = this->GetRecordingDevices(DefSound::EDeviceState::All);
        auto audio_device = find_if(m_playback.begin(), m_playback.end(), [pwstr_device_id](AudioDevicePtr device) {
            return wcscmp(device->ID, pwstr_device_id) == 0;
        });

        if (audio_device != m_playback.end())
        {
            Signals->DeviceAdded.Notify(*audio_device);
        }

        audio_device = find_if(m_recording.begin(), m_recording.end(), [pwstr_device_id](AudioDevicePtr device) {
            return wcscmp(device->ID, pwstr_device_id) == 0;
        });

        if (audio_device != m_recording.end())
        {
            Signals->DeviceAdded.Notify(*audio_device);
        }

        return S_OK;
    }

    CAudioEndPointLibrary& CAudioEndPointLibrary::GetInstance()
    {
        static CAudioEndPointLibrary instance;
        return instance;
    }

    AudioDeviceList CAudioEndPointLibrary::GetPlaybackDevices(DefSound::EDeviceState state) const
    {
        if(m_devices_lists->m_need_update)
        {
            Refresh();
        }

        if(state == DefSound::EDeviceState::All)
        {
            return m_devices_lists->m_playback;
        }
        std::lock_guard<std::mutex> lock(m_devices_lists->m_lists_mutex);
        AudioDeviceList list;
        for (auto &endpoint : m_devices_lists->m_playback)
        {
            if (endpoint->GetDeviceState() == state)
                list.push_back(endpoint);
        }

        return list;

    }

    AudioDeviceList CAudioEndPointLibrary::GetRecordingDevices(DefSound::EDeviceState state) const
    {
        if (m_devices_lists->m_need_update)
        {
            Refresh();
        }

        if (state == DefSound::EDeviceState::All)
        {
            return m_devices_lists->m_recording;
        }
        std::lock_guard<std::mutex> lock(m_devices_lists->m_lists_mutex);
        AudioDeviceList list;
        for (auto &endpoint : m_devices_lists->m_recording)
        {
            if (endpoint->GetDeviceState() == state)
                list.push_back(endpoint);
        }

        return list;
    }

    HRESULT CAudioEndPointLibrary::RegisterNotificationClient() const
    {
        HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
        if(!m_container->m_DeviceEnumerator)
        {
            hr = m_container->m_DeviceEnumerator.CreateInstance(__uuidof(MMDeviceEnumerator));
            ReturnIfFailed(hr);
        }

        if (!m_container->m_notif_client) {
            m_container->m_notif_client = new CMMNotificationClient;
            hr = m_container->m_DeviceEnumerator->RegisterEndpointNotificationCallback(m_container->m_notif_client);
            return hr;
        }
        return S_OK;
    }

    HRESULT CAudioEndPointLibrary::UnRegisterNotificationClient() const
    {
        if (m_container->m_notif_client) {
            m_container->m_notif_client->Release();
            HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
            hr = m_container->m_DeviceEnumerator->UnregisterEndpointNotificationCallback(m_container->m_notif_client);
            return hr;
        }
        return S_FALSE;
    }

    void CAudioEndPointLibrary::Refresh() const
    {
        std::lock_guard<std::mutex> lock(m_devices_lists->m_lists_mutex);

        m_devices_lists->m_playback.clear();
        auto collectionPlayback = DefSound::CEndpointCollection(DefSound::All, ::eRender);
        for (auto &endpoint : collectionPlayback.Get())
        {
            m_devices_lists->m_playback.push_back(std::make_shared<AudioDevice>(endpoint, Playback));
        }

        m_devices_lists->m_recording.clear();
        auto collectionRecording = DefSound::CEndpointCollection(DefSound::All, ::eCapture);
        for (auto &endpoint : collectionRecording.Get())
        {
            m_devices_lists->m_recording.push_back(std::make_shared<AudioDevice>(endpoint, Recording));
        }
        m_devices_lists->m_need_update = false;
    }
}
<commit_msg>Check for possible NPE<commit_after>// AudioEndPointLibrary.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"
#include "AudioEndPointLibrary.h"
#include "MMNotificationClient.h"
#include <DefSoundEndpointColl.h>
#include <algorithm>
#include <mutex>

namespace AudioEndPoint {

    struct CAudioEndPointLibrary::AudioEndPointLibraryImpl
    {
        IMMDeviceEnumeratorPtr m_DeviceEnumerator;
        IMMNotificationClientPtr m_notif_client;
    };

    struct CAudioEndPointLibrary::AudioEndPointLibraryDevicesImpl
    {
        AudioDeviceList m_playback;
        AudioDeviceList m_recording;
        bool m_need_update = true;
        std::mutex m_lists_mutex;
    };

    // This is the constructor of a class that has been exported.
    // see AudioEndPointLibrary.h for the class definition
    CAudioEndPointLibrary::CAudioEndPointLibrary() : m_container(new AudioEndPointLibraryImpl()),
        m_signals(new AudioEndPointLibrarySignals()),
        m_devices_lists(new AudioEndPointLibraryDevicesImpl())
    {
        m_devices_lists->m_need_update = true;
        this->RegisterNotificationClient();
    }

    CAudioEndPointLibrary::~CAudioEndPointLibrary()
    {
        delete m_signals;
        delete m_devices_lists;
        this->UnRegisterNotificationClient();
        delete m_container;
    }

    HRESULT CAudioEndPointLibrary::OnDeviceStateChanged(LPCWSTR pwstr_device_id, DWORD dw_new_state) const
    {
        auto m_playback = this->GetPlaybackDevices(DefSound::EDeviceState::All);
        auto m_recording = this->GetRecordingDevices(DefSound::EDeviceState::All);
        auto audio_device = find_if(m_playback.begin(), m_playback.end(),[pwstr_device_id](AudioDevicePtr device) {
            return pwstr_device_id != nullptr && wcscmp(device->ID, pwstr_device_id) == 0;
        });

        auto e_device_state = static_cast<DefSound::EDeviceState>(dw_new_state);
        if(audio_device != m_playback.end())
        {
            auto prevState = (*audio_device)->GetEndPoint().m_State.state;
            (*audio_device)->GetEndPoint().m_State.state = e_device_state;
            Signals->DeviceStateChanged.Notify((*audio_device), prevState, e_device_state);
        }

        audio_device = find_if(m_recording.begin(), m_recording.end(), [pwstr_device_id](AudioDevicePtr device) {
            return pwstr_device_id != nullptr && wcscmp(device->ID, pwstr_device_id) == 0;
        });

        if (audio_device != m_recording.end())
        {
            auto prevState = (*audio_device)->GetEndPoint().m_State.state;
            (*audio_device)->GetEndPoint().m_State.state = e_device_state;
            Signals->DeviceStateChanged.Notify((*audio_device), prevState, e_device_state);
        }
        return S_OK;
    }

    HRESULT CAudioEndPointLibrary::OnDeviceRemoved(LPCWSTR pwstr_device_id) const
    {
        m_devices_lists->m_need_update = true;
        return S_OK;
    }

    HRESULT CAudioEndPointLibrary::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstr_default_device_id) const
    {
        AudioDeviceList list;
        if(flow == ::eRender)
        {
            list = this->GetPlaybackDevices(DefSound::EDeviceState::All);
        }
        else if(flow == ::eCapture)
        {
            list = this->GetRecordingDevices(DefSound::EDeviceState::All);
        } else
        {
            return S_FALSE;
        }

        AudioDevicePtr deviceFound = nullptr;
        for (auto& device : list)
        {
            if (pwstr_default_device_id != nullptr && wcscmp(device->ID, pwstr_default_device_id) == 0)
            {
                device->GetEndPoint().m_IsDefault[role] = true;
                deviceFound = device;
            }
            else
            {
                device->GetEndPoint().m_IsDefault[role] = false;
            }
        }

        if (deviceFound != nullptr) {
            Signals->DeviceDefaultChanged.Notify(deviceFound, role);
        }

        return S_OK;
    }

    HRESULT CAudioEndPointLibrary::OnDeviceAdded(LPCWSTR pwstr_device_id) const
    {
        m_devices_lists->m_need_update = true;
        auto m_playback = this->GetPlaybackDevices(DefSound::EDeviceState::All);
        auto m_recording = this->GetRecordingDevices(DefSound::EDeviceState::All);
        auto audio_device = find_if(m_playback.begin(), m_playback.end(), [pwstr_device_id](AudioDevicePtr device) {
            return pwstr_device_id != nullptr && wcscmp(device->ID, pwstr_device_id) == 0;
        });

        if (audio_device != m_playback.end())
        {
            Signals->DeviceAdded.Notify(*audio_device);
        }

        audio_device = find_if(m_recording.begin(), m_recording.end(), [pwstr_device_id](AudioDevicePtr device) {
            return pwstr_device_id != nullptr && wcscmp(device->ID, pwstr_device_id) == 0;
        });

        if (audio_device != m_recording.end())
        {
            Signals->DeviceAdded.Notify(*audio_device);
        }

        return S_OK;
    }

    CAudioEndPointLibrary& CAudioEndPointLibrary::GetInstance()
    {
        static CAudioEndPointLibrary instance;
        return instance;
    }

    AudioDeviceList CAudioEndPointLibrary::GetPlaybackDevices(DefSound::EDeviceState state) const
    {
        if(m_devices_lists->m_need_update)
        {
            Refresh();
        }

        if(state == DefSound::EDeviceState::All)
        {
            return m_devices_lists->m_playback;
        }
        std::lock_guard<std::mutex> lock(m_devices_lists->m_lists_mutex);
        AudioDeviceList list;
        for (auto &endpoint : m_devices_lists->m_playback)
        {
            if (endpoint->GetDeviceState() == state)
                list.push_back(endpoint);
        }

        return list;

    }

    AudioDeviceList CAudioEndPointLibrary::GetRecordingDevices(DefSound::EDeviceState state) const
    {
        if (m_devices_lists->m_need_update)
        {
            Refresh();
        }

        if (state == DefSound::EDeviceState::All)
        {
            return m_devices_lists->m_recording;
        }
        std::lock_guard<std::mutex> lock(m_devices_lists->m_lists_mutex);
        AudioDeviceList list;
        for (auto &endpoint : m_devices_lists->m_recording)
        {
            if (endpoint->GetDeviceState() == state)
                list.push_back(endpoint);
        }

        return list;
    }

    HRESULT CAudioEndPointLibrary::RegisterNotificationClient() const
    {
        HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
        if(!m_container->m_DeviceEnumerator)
        {
            hr = m_container->m_DeviceEnumerator.CreateInstance(__uuidof(MMDeviceEnumerator));
            ReturnIfFailed(hr);
        }

        if (!m_container->m_notif_client) {
            m_container->m_notif_client = new CMMNotificationClient;
            hr = m_container->m_DeviceEnumerator->RegisterEndpointNotificationCallback(m_container->m_notif_client);
            return hr;
        }
        return S_OK;
    }

    HRESULT CAudioEndPointLibrary::UnRegisterNotificationClient() const
    {
        if (m_container->m_notif_client) {
            m_container->m_notif_client->Release();
            HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
            hr = m_container->m_DeviceEnumerator->UnregisterEndpointNotificationCallback(m_container->m_notif_client);
            return hr;
        }
        return S_FALSE;
    }

    void CAudioEndPointLibrary::Refresh() const
    {
        std::lock_guard<std::mutex> lock(m_devices_lists->m_lists_mutex);

        m_devices_lists->m_playback.clear();
        auto collectionPlayback = DefSound::CEndpointCollection(DefSound::All, ::eRender);
        for (auto &endpoint : collectionPlayback.Get())
        {
            m_devices_lists->m_playback.push_back(std::make_shared<AudioDevice>(endpoint, Playback));
        }

        m_devices_lists->m_recording.clear();
        auto collectionRecording = DefSound::CEndpointCollection(DefSound::All, ::eCapture);
        for (auto &endpoint : collectionRecording.Get())
        {
            m_devices_lists->m_recording.push_back(std::make_shared<AudioDevice>(endpoint, Recording));
        }
        m_devices_lists->m_need_update = false;
    }
}
<|endoftext|>