<commit_before>//===-- SocketTest.cpp ------------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include <cstdio>
#include <functional>
#include <thread>

#include "gtest/gtest.h"

#include "lldb/Host/Config.h"
#include "lldb/Host/Socket.h"
#include "lldb/Host/common/TCPSocket.h"
#include "lldb/Host/common/UDPSocket.h"

#ifndef LLDB_DISABLE_POSIX
#include "lldb/Host/posix/DomainSocket.h"
#endif

using namespace lldb_private;

class SocketTest : public testing::Test {
public:
  void SetUp() override {
#if defined(_MSC_VER)
    WSADATA data;
    ::WSAStartup(MAKEWORD(2, 2), &data);
#endif
  }

  void TearDown() override {
#if defined(_MSC_VER)
    ::WSACleanup();
#endif
  }

protected:
  static void AcceptThread(Socket *listen_socket,
                           const char *listen_remote_address,
                           bool child_processes_inherit, Socket **accept_socket,
                           Error *error) {
    *error = listen_socket->Accept(listen_remote_address,
                                   child_processes_inherit, *accept_socket);
  }

  template <typename SocketType>
  void CreateConnectedSockets(
      const char *listen_remote_address,
      const std::function<std::string(const SocketType &)> &get_connect_addr,
      std::unique_ptr<SocketType> *a_up, std::unique_ptr<SocketType> *b_up) {
    bool child_processes_inherit = false;
    Error error;
    std::unique_ptr<SocketType> listen_socket_up(
        new SocketType(child_processes_inherit, error));
    EXPECT_FALSE(error.Fail());
    error = listen_socket_up->Listen(listen_remote_address, 5);
    EXPECT_FALSE(error.Fail());
    EXPECT_TRUE(listen_socket_up->IsValid());

    Error accept_error;
    Socket *accept_socket;
    std::thread accept_thread(AcceptThread, listen_socket_up.get(),
                              listen_remote_address, child_processes_inherit,
                              &accept_socket, &accept_error);

    std::string connect_remote_address = get_connect_addr(*listen_socket_up);
    std::unique_ptr<SocketType> connect_socket_up(
        new SocketType(child_processes_inherit, error));
    EXPECT_FALSE(error.Fail());
    error = connect_socket_up->Connect(connect_remote_address);
    EXPECT_FALSE(error.Fail());
    EXPECT_TRUE(connect_socket_up->IsValid());

    a_up->swap(connect_socket_up);
    EXPECT_TRUE(error.Success());
    EXPECT_NE(nullptr, a_up->get());
    EXPECT_TRUE((*a_up)->IsValid());

    accept_thread.join();
    b_up->reset(static_cast<SocketType *>(accept_socket));
    EXPECT_TRUE(accept_error.Success());
    EXPECT_NE(nullptr, b_up->get());
    EXPECT_TRUE((*b_up)->IsValid());

    listen_socket_up.reset();
  }
};

TEST_F(SocketTest, DecodeHostAndPort) {
  std::string host_str;
  std::string port_str;
  int32_t port;
  Error error;
  EXPECT_TRUE(Socket::DecodeHostAndPort("localhost:1138", host_str, port_str,
                                        port, &error));
  EXPECT_STREQ("localhost", host_str.c_str());
  EXPECT_STREQ("1138", port_str.c_str());
  EXPECT_EQ(1138, port);
  EXPECT_TRUE(error.Success());

  EXPECT_FALSE(Socket::DecodeHostAndPort("google.com:65536", host_str, port_str,
                                         port, &error));
  EXPECT_TRUE(error.Fail());
  EXPECT_STREQ("invalid host:port specification: 'google.com:65536'",
               error.AsCString());

  EXPECT_FALSE(Socket::DecodeHostAndPort("google.com:-1138", host_str, port_str,
                                         port, &error));
  EXPECT_TRUE(error.Fail());
  EXPECT_STREQ("invalid host:port specification: 'google.com:-1138'",
               error.AsCString());

  EXPECT_FALSE(Socket::DecodeHostAndPort("google.com:65536", host_str, port_str,
                                         port, &error));
  EXPECT_TRUE(error.Fail());
  EXPECT_STREQ("invalid host:port specification: 'google.com:65536'",
               error.AsCString());

  EXPECT_TRUE(
      Socket::DecodeHostAndPort("12345", host_str, port_str, port, &error));
  EXPECT_STREQ("", host_str.c_str());
  EXPECT_STREQ("12345", port_str.c_str());
  EXPECT_EQ(12345, port);
  EXPECT_TRUE(error.Success());

  EXPECT_TRUE(
      Socket::DecodeHostAndPort("*:0", host_str, port_str, port, &error));
  EXPECT_STREQ("*", host_str.c_str());
  EXPECT_STREQ("0", port_str.c_str());
  EXPECT_EQ(0, port);
  EXPECT_TRUE(error.Success());

  EXPECT_TRUE(
      Socket::DecodeHostAndPort("*:65535", host_str, port_str, port, &error));
  EXPECT_STREQ("*", host_str.c_str());
  EXPECT_STREQ("65535", port_str.c_str());
  EXPECT_EQ(65535, port);
  EXPECT_TRUE(error.Success());
}

#ifndef LLDB_DISABLE_POSIX
TEST_F(SocketTest, DomainListenConnectAccept) {
  char *file_name_str = tempnam(nullptr, nullptr);
  EXPECT_NE(nullptr, file_name_str);
  const std::string file_name(file_name_str);
  free(file_name_str);

  std::unique_ptr<DomainSocket> socket_a_up;
  std::unique_ptr<DomainSocket> socket_b_up;
  CreateConnectedSockets<DomainSocket>(
      file_name.c_str(), [=](const DomainSocket &) { return file_name; },
      &socket_a_up, &socket_b_up);
}
#endif

TEST_F(SocketTest, TCPListen0ConnectAccept) {
  std::unique_ptr<TCPSocket> socket_a_up;
  std::unique_ptr<TCPSocket> socket_b_up;
  CreateConnectedSockets<TCPSocket>(
      "127.0.0.1:0",
      [=](const TCPSocket &s) {
        char connect_remote_address[64];
        snprintf(connect_remote_address, sizeof(connect_remote_address),
                 "localhost:%u", s.GetLocalPortNumber());
        return std::string(connect_remote_address);
      },
      &socket_a_up, &socket_b_up);
}

TEST_F(SocketTest, TCPGetAddress) {
  std::unique_ptr<TCPSocket> socket_a_up;
  std::unique_ptr<TCPSocket> socket_b_up;
  CreateConnectedSockets<TCPSocket>(
      "127.0.0.1:0",
      [=](const TCPSocket &s) {
        char connect_remote_address[64];
        snprintf(connect_remote_address, sizeof(connect_remote_address),
                 "localhost:%u", s.GetLocalPortNumber());
        return std::string(connect_remote_address);
      },
      &socket_a_up, &socket_b_up);

  EXPECT_EQ(socket_a_up->GetLocalPortNumber(),
            socket_b_up->GetRemotePortNumber());
  EXPECT_EQ(socket_b_up->GetLocalPortNumber(),
            socket_a_up->GetRemotePortNumber());
  EXPECT_NE(socket_a_up->GetLocalPortNumber(),
            socket_b_up->GetLocalPortNumber());
  EXPECT_STREQ("127.0.0.1", socket_a_up->GetRemoteIPAddress().c_str());
  EXPECT_STREQ("127.0.0.1", socket_b_up->GetRemoteIPAddress().c_str());
}

TEST_F(SocketTest, UDPConnect) {
  Socket *socket_a;
  Socket *socket_b;

  bool child_processes_inherit = false;
  auto error = UDPSocket::Connect("127.0.0.1:0", child_processes_inherit,
                                  socket_a, socket_b);

  std::unique_ptr<Socket> a_up(socket_a);
  std::unique_ptr<Socket> b_up(socket_b);

  EXPECT_TRUE(error.Success());
  EXPECT_TRUE(a_up->IsValid());
  EXPECT_TRUE(b_up->IsValid());
}
<commit_msg>Update unittests/Host/SocketTest.cpp to also use the new one-socket API.<commit_after>//===-- SocketTest.cpp ------------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include <cstdio>
#include <functional>
#include <thread>

#include "gtest/gtest.h"

#include "lldb/Host/Config.h"
#include "lldb/Host/Socket.h"
#include "lldb/Host/common/TCPSocket.h"
#include "lldb/Host/common/UDPSocket.h"

#ifndef LLDB_DISABLE_POSIX
#include "lldb/Host/posix/DomainSocket.h"
#endif

using namespace lldb_private;

class SocketTest : public testing::Test {
public:
  void SetUp() override {
#if defined(_MSC_VER)
    WSADATA data;
    ::WSAStartup(MAKEWORD(2, 2), &data);
#endif
  }

  void TearDown() override {
#if defined(_MSC_VER)
    ::WSACleanup();
#endif
  }

protected:
  static void AcceptThread(Socket *listen_socket,
                           const char *listen_remote_address,
                           bool child_processes_inherit, Socket **accept_socket,
                           Error *error) {
    *error = listen_socket->Accept(listen_remote_address,
                                   child_processes_inherit, *accept_socket);
  }

  template <typename SocketType>
  void CreateConnectedSockets(
      const char *listen_remote_address,
      const std::function<std::string(const SocketType &)> &get_connect_addr,
      std::unique_ptr<SocketType> *a_up, std::unique_ptr<SocketType> *b_up) {
    bool child_processes_inherit = false;
    Error error;
    std::unique_ptr<SocketType> listen_socket_up(
        new SocketType(child_processes_inherit, error));
    EXPECT_FALSE(error.Fail());
    error = listen_socket_up->Listen(listen_remote_address, 5);
    EXPECT_FALSE(error.Fail());
    EXPECT_TRUE(listen_socket_up->IsValid());

    Error accept_error;
    Socket *accept_socket;
    std::thread accept_thread(AcceptThread, listen_socket_up.get(),
                              listen_remote_address, child_processes_inherit,
                              &accept_socket, &accept_error);

    std::string connect_remote_address = get_connect_addr(*listen_socket_up);
    std::unique_ptr<SocketType> connect_socket_up(
        new SocketType(child_processes_inherit, error));
    EXPECT_FALSE(error.Fail());
    error = connect_socket_up->Connect(connect_remote_address);
    EXPECT_FALSE(error.Fail());
    EXPECT_TRUE(connect_socket_up->IsValid());

    a_up->swap(connect_socket_up);
    EXPECT_TRUE(error.Success());
    EXPECT_NE(nullptr, a_up->get());
    EXPECT_TRUE((*a_up)->IsValid());

    accept_thread.join();
    b_up->reset(static_cast<SocketType *>(accept_socket));
    EXPECT_TRUE(accept_error.Success());
    EXPECT_NE(nullptr, b_up->get());
    EXPECT_TRUE((*b_up)->IsValid());

    listen_socket_up.reset();
  }
};

TEST_F(SocketTest, DecodeHostAndPort) {
  std::string host_str;
  std::string port_str;
  int32_t port;
  Error error;
  EXPECT_TRUE(Socket::DecodeHostAndPort("localhost:1138", host_str, port_str,
                                        port, &error));
  EXPECT_STREQ("localhost", host_str.c_str());
  EXPECT_STREQ("1138", port_str.c_str());
  EXPECT_EQ(1138, port);
  EXPECT_TRUE(error.Success());

  EXPECT_FALSE(Socket::DecodeHostAndPort("google.com:65536", host_str, port_str,
                                         port, &error));
  EXPECT_TRUE(error.Fail());
  EXPECT_STREQ("invalid host:port specification: 'google.com:65536'",
               error.AsCString());

  EXPECT_FALSE(Socket::DecodeHostAndPort("google.com:-1138", host_str, port_str,
                                         port, &error));
  EXPECT_TRUE(error.Fail());
  EXPECT_STREQ("invalid host:port specification: 'google.com:-1138'",
               error.AsCString());

  EXPECT_FALSE(Socket::DecodeHostAndPort("google.com:65536", host_str, port_str,
                                         port, &error));
  EXPECT_TRUE(error.Fail());
  EXPECT_STREQ("invalid host:port specification: 'google.com:65536'",
               error.AsCString());

  EXPECT_TRUE(
      Socket::DecodeHostAndPort("12345", host_str, port_str, port, &error));
  EXPECT_STREQ("", host_str.c_str());
  EXPECT_STREQ("12345", port_str.c_str());
  EXPECT_EQ(12345, port);
  EXPECT_TRUE(error.Success());

  EXPECT_TRUE(
      Socket::DecodeHostAndPort("*:0", host_str, port_str, port, &error));
  EXPECT_STREQ("*", host_str.c_str());
  EXPECT_STREQ("0", port_str.c_str());
  EXPECT_EQ(0, port);
  EXPECT_TRUE(error.Success());

  EXPECT_TRUE(
      Socket::DecodeHostAndPort("*:65535", host_str, port_str, port, &error));
  EXPECT_STREQ("*", host_str.c_str());
  EXPECT_STREQ("65535", port_str.c_str());
  EXPECT_EQ(65535, port);
  EXPECT_TRUE(error.Success());
}

#ifndef LLDB_DISABLE_POSIX
TEST_F(SocketTest, DomainListenConnectAccept) {
  char *file_name_str = tempnam(nullptr, nullptr);
  EXPECT_NE(nullptr, file_name_str);
  const std::string file_name(file_name_str);
  free(file_name_str);

  std::unique_ptr<DomainSocket> socket_a_up;
  std::unique_ptr<DomainSocket> socket_b_up;
  CreateConnectedSockets<DomainSocket>(
      file_name.c_str(), [=](const DomainSocket &) { return file_name; },
      &socket_a_up, &socket_b_up);
}
#endif

TEST_F(SocketTest, TCPListen0ConnectAccept) {
  std::unique_ptr<TCPSocket> socket_a_up;
  std::unique_ptr<TCPSocket> socket_b_up;
  CreateConnectedSockets<TCPSocket>(
      "127.0.0.1:0",
      [=](const TCPSocket &s) {
        char connect_remote_address[64];
        snprintf(connect_remote_address, sizeof(connect_remote_address),
                 "localhost:%u", s.GetLocalPortNumber());
        return std::string(connect_remote_address);
      },
      &socket_a_up, &socket_b_up);
}

TEST_F(SocketTest, TCPGetAddress) {
  std::unique_ptr<TCPSocket> socket_a_up;
  std::unique_ptr<TCPSocket> socket_b_up;
  CreateConnectedSockets<TCPSocket>(
      "127.0.0.1:0",
      [=](const TCPSocket &s) {
        char connect_remote_address[64];
        snprintf(connect_remote_address, sizeof(connect_remote_address),
                 "localhost:%u", s.GetLocalPortNumber());
        return std::string(connect_remote_address);
      },
      &socket_a_up, &socket_b_up);

  EXPECT_EQ(socket_a_up->GetLocalPortNumber(),
            socket_b_up->GetRemotePortNumber());
  EXPECT_EQ(socket_b_up->GetLocalPortNumber(),
            socket_a_up->GetRemotePortNumber());
  EXPECT_NE(socket_a_up->GetLocalPortNumber(),
            socket_b_up->GetLocalPortNumber());
  EXPECT_STREQ("127.0.0.1", socket_a_up->GetRemoteIPAddress().c_str());
  EXPECT_STREQ("127.0.0.1", socket_b_up->GetRemoteIPAddress().c_str());
}

TEST_F(SocketTest, UDPConnect) {
  Socket *socket;

  bool child_processes_inherit = false;
  auto error = UDPSocket::Connect("127.0.0.1:0", child_processes_inherit,
                                  socket);

  std::unique_ptr<Socket> socket_up(socket);

  EXPECT_TRUE(error.Success());
  EXPECT_TRUE(socket_up->IsValid());
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: atom.cxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: pl $ $Date: 2001-12-12 13:57:09 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  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.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include <unotools/atom.hxx>

using namespace utl;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::util;
#define NMSP_UTIL ::com::sun::star::util

AtomProvider::AtomProvider()
{
    m_nAtoms = 1;
}

AtomProvider::~AtomProvider()
{
}

int AtomProvider::getAtom( const ::rtl::OUString& rString, sal_Bool bCreate )
{
    ::std::hash_map< ::rtl::OUString, int, ::rtl::OUStringHash >::iterator it = m_aAtomMap.find( rString );
    if( it != m_aAtomMap.end() )
        return it->second;
    if( ! bCreate )
        return INVALID_ATOM;
    m_aAtomMap[ rString ] = m_nAtoms;
    m_aStringMap[ m_nAtoms ] = rString;
    m_nAtoms++;
    return m_nAtoms-1;
}

void AtomProvider::getAll( ::std::list< ::utl::AtomDescription >& atoms )
{
    atoms.clear();
    ::std::hash_map< ::rtl::OUString, int, ::rtl::OUStringHash >::const_iterator it = m_aAtomMap.begin();

    ::utl::AtomDescription aDesc;
    while( it != m_aAtomMap.end() )
    {
        aDesc.atom          = it->second;
        aDesc.description   = it->first;
        atoms.push_back( aDesc );
        ++it;
    }
}

void AtomProvider::getRecent( int atom, ::std::list< ::utl::AtomDescription >& atoms )
{
    atoms.clear();

    ::std::hash_map< ::rtl::OUString, int, ::rtl::OUStringHash >::const_iterator it = m_aAtomMap.begin();

    ::utl::AtomDescription aDesc;
    while( it != m_aAtomMap.end() )
    {
        if( it->second > atom )
        {
            aDesc.atom          = it->second;
            aDesc.description   = it->first;
            atoms.push_back( aDesc );
        }
        ++it;
    }
}

const ::rtl::OUString& AtomProvider::getString( int nAtom ) const
{
    static ::rtl::OUString aEmpty;
    ::std::hash_map< int, ::rtl::OUString, ::std::hash< int > >::const_iterator it = m_aStringMap.find( nAtom );

    return it == m_aStringMap.end() ? aEmpty : it->second;
}

void AtomProvider::overrideAtom( int atom, const ::rtl::OUString& description )
{
    m_aAtomMap[ description ] = atom;
    m_aStringMap[ atom ] = description;
    if( m_nAtoms <= atom )
        m_nAtoms=atom+1;
}

sal_Bool AtomProvider::hasAtom( int atom ) const
{
    return m_aStringMap.find( atom ) != m_aStringMap.end() ? sal_True : sal_False;
}

// -----------------------------------------------------------------------

MultiAtomProvider::MultiAtomProvider()
{
}

MultiAtomProvider::~MultiAtomProvider()
{
    for( ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::iterator it = m_aAtomLists.begin(); it != m_aAtomLists.end(); ++it )
        delete it->second;
}

sal_Bool MultiAtomProvider::insertAtomClass( int atomClass )
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::iterator it =
          m_aAtomLists.find( atomClass );
    if( it != m_aAtomLists.end() )
        return sal_False;
    m_aAtomLists[ atomClass ] = new AtomProvider();
    return sal_True;
}

int MultiAtomProvider::getAtom( int atomClass, const ::rtl::OUString& rString, sal_Bool bCreate )
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::iterator it =
          m_aAtomLists.find( atomClass );
    if( it != m_aAtomLists.end() )
        return it->second->getAtom( rString, bCreate );

    if( bCreate )
    {
        AtomProvider* pNewClass;
        m_aAtomLists[ atomClass ] = pNewClass = new AtomProvider();
        return pNewClass->getAtom( rString, bCreate );
    }
    return INVALID_ATOM;
}

int MultiAtomProvider::getLastAtom( int atomClass ) const
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it =
          m_aAtomLists.find( atomClass );

    return it != m_aAtomLists.end() ? it->second->getLastAtom() : INVALID_ATOM;
}

void MultiAtomProvider::getRecent( int atomClass, int atom, ::std::list< ::utl::AtomDescription >& atoms )
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it =
          m_aAtomLists.find( atomClass );
    if( it != m_aAtomLists.end() )
        it->second->getRecent( atom, atoms );
    else
        atoms.clear();
}

const ::rtl::OUString& MultiAtomProvider::getString( int atomClass, int atom ) const
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it =
          m_aAtomLists.find( atomClass );
    if( it != m_aAtomLists.end() )
        return it->second->getString( atom );

    static ::rtl::OUString aEmpty;
    return aEmpty;
}

sal_Bool MultiAtomProvider::hasAtom( int atomClass, int atom ) const
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it = m_aAtomLists.find( atomClass );
    return it != m_aAtomLists.end() ? it->second->hasAtom( atom ) : sal_False;
}

void MultiAtomProvider::getClass( int atomClass, ::std::list< ::utl::AtomDescription >& atoms) const
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it = m_aAtomLists.find( atomClass );

    if( it != m_aAtomLists.end() )
        it->second->getAll( atoms );
    else
        atoms.clear();
}

void MultiAtomProvider::overrideAtom( int atomClass, int atom, const ::rtl::OUString& description )
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it = m_aAtomLists.find( atomClass );
    if( it == m_aAtomLists.end() )
        m_aAtomLists[ atomClass ] = new AtomProvider();
    m_aAtomLists[ atomClass ]->overrideAtom( atom, description );
}

// -----------------------------------------------------------------------

AtomServer::AtomServer()
{
}

AtomServer::~AtomServer()
{
}

sal_Int32 AtomServer::getAtom( sal_Int32 atomClass, const ::rtl::OUString& description, sal_Bool create ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    return m_aProvider.getAtom( atomClass, description, create );
}

Sequence< Sequence< NMSP_UTIL::AtomDescription > > AtomServer::getClasses( const Sequence< sal_Int32 >& atomClasses ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    Sequence< Sequence< NMSP_UTIL::AtomDescription > > aRet( atomClasses.getLength() );
    for( int i = 0; i < atomClasses.getLength(); i++ )
    {
        aRet.getArray()[i] = getClass( atomClasses.getConstArray()[i] );
    }
    return aRet;
}

Sequence< NMSP_UTIL::AtomDescription > AtomServer::getClass( sal_Int32 atomClass ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    ::std::list< ::utl::AtomDescription > atoms;
    m_aProvider.getClass( atomClass, atoms );

    Sequence< NMSP_UTIL::AtomDescription > aRet( atoms.size() );
    for( int i = aRet.getLength()-1; i >= 0; i-- )
    {
        aRet.getArray()[i].atom         = atoms.back().atom;
        aRet.getArray()[i].description  = atoms.back().description;
        atoms.pop_back();
    }

    return aRet;
}

Sequence< NMSP_UTIL::AtomDescription > AtomServer::getRecentAtoms( sal_Int32 atomClass, sal_Int32 atom ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    ::std::list< ::utl::AtomDescription > atoms;
    m_aProvider.getRecent( atomClass, atom, atoms );

    Sequence< NMSP_UTIL::AtomDescription > aRet( atoms.size() );
    for( int i = aRet.getLength()-1; i >= 0; i-- )
    {
        aRet.getArray()[i].atom         = atoms.back().atom;
        aRet.getArray()[i].description  = atoms.back().description;
        atoms.pop_back();
    }

    return aRet;
}

Sequence< ::rtl::OUString > AtomServer::getAtomDescriptions( const Sequence< AtomClassRequest >& atoms ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    int nStrings = 0, i;
    for( i = 0; i < atoms.getLength(); i++ )
        nStrings += atoms.getConstArray()[ i ].atoms.getLength();
    Sequence< ::rtl::OUString > aRet( nStrings );
    for( i = 0, nStrings = 0; i < atoms.getLength(); i++ )
    {
        const AtomClassRequest& rRequest = atoms.getConstArray()[i];
        for( int n = 0; n < rRequest.atoms.getLength(); n++ )
            aRet.getArray()[ nStrings++ ] = m_aProvider.getString( rRequest.atomClass, rRequest.atoms.getConstArray()[ n ] );
    }
    return aRet;
}

// -----------------------------------------------------------------------

AtomClient::AtomClient( const Reference< XAtomServer >& xServer ) :
        m_xServer( xServer )
{
}

AtomClient::~AtomClient()
{
}

int AtomClient::getAtom( int atomClass, const ::rtl::OUString& description, sal_Bool bCreate )
{
    int nAtom = m_aProvider.getAtom( atomClass, description, sal_False );
    if( nAtom == INVALID_ATOM && bCreate )
    {
        try
        {
            nAtom = m_xServer->getAtom( atomClass, description, bCreate );
        }
        catch( RuntimeException& )
        {
            return INVALID_ATOM;
        }
        if( nAtom != INVALID_ATOM )
            m_aProvider.overrideAtom( atomClass, nAtom, description );
    }
    return nAtom;
}

const ::rtl::OUString& AtomClient::getString( int atomClass, int atom )
{
    static ::rtl::OUString aEmpty;

    if( ! m_aProvider.hasAtom( atomClass, atom ) )
    {
        Sequence< NMSP_UTIL::AtomDescription > aSeq;
        try
        {
            aSeq = m_xServer->getRecentAtoms( atomClass, m_aProvider.getLastAtom( atomClass ) );
        }
        catch( RuntimeException& )
        {
            return aEmpty;
        }
        const NMSP_UTIL::AtomDescription* pDescriptions = aSeq.getConstArray();
        for( int i = 0; i < aSeq.getLength(); i++ )
            m_aProvider.overrideAtom( atomClass,
                                      pDescriptions[i].atom,
                                      pDescriptions[i].description
                                      );

        if( ! m_aProvider.hasAtom( atomClass, atom ) )
        {
            // holes may occur by the above procedure!
            Sequence< AtomClassRequest > aSeq( 1 );
            aSeq.getArray()[0].atomClass = atomClass;
            aSeq.getArray()[0].atoms.realloc( 1 );
            aSeq.getArray()[0].atoms.getArray()[0] = atom;
            Sequence< ::rtl::OUString > aRet;
            try
            {
                aRet = m_xServer->getAtomDescriptions( aSeq );
            }
            catch( RuntimeException& )
            {
                return aEmpty;
            }
            if( aRet.getLength() == 1 )
                m_aProvider.overrideAtom( atomClass, atom, aRet.getConstArray()[0] );
        }
    }
    return m_aProvider.getString( atomClass, atom );
}

void AtomClient::updateAtomClasses( const Sequence< sal_Int32 >& atomClasses )
{
    Sequence< Sequence< NMSP_UTIL::AtomDescription > > aUpdate;
    try
    {
        aUpdate = m_xServer->getClasses( atomClasses );
    }
    catch( RuntimeException& )
    {
        return;
    }
    for( int i = 0; i < atomClasses.getLength(); i++ )
    {
        int nClass = atomClasses.getConstArray()[i];
        const Sequence< NMSP_UTIL::AtomDescription >& rClass = aUpdate.getConstArray()[i];
        const NMSP_UTIL::AtomDescription* pDesc = rClass.getConstArray();
        for( int n = 0; n < rClass.getLength(); n++, pDesc++ )
            m_aProvider.overrideAtom( nClass, pDesc->atom, pDesc->description );
    }
}
<commit_msg>INTEGRATION: CWS ooo19126 (1.6.246); FILE MERGED 2005/09/05 14:01:06 rt 1.6.246.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: atom.cxx,v $
 *
 *  $Revision: 1.7 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 09:46:36 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include <unotools/atom.hxx>

using namespace utl;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::util;
#define NMSP_UTIL ::com::sun::star::util

AtomProvider::AtomProvider()
{
    m_nAtoms = 1;
}

AtomProvider::~AtomProvider()
{
}

int AtomProvider::getAtom( const ::rtl::OUString& rString, sal_Bool bCreate )
{
    ::std::hash_map< ::rtl::OUString, int, ::rtl::OUStringHash >::iterator it = m_aAtomMap.find( rString );
    if( it != m_aAtomMap.end() )
        return it->second;
    if( ! bCreate )
        return INVALID_ATOM;
    m_aAtomMap[ rString ] = m_nAtoms;
    m_aStringMap[ m_nAtoms ] = rString;
    m_nAtoms++;
    return m_nAtoms-1;
}

void AtomProvider::getAll( ::std::list< ::utl::AtomDescription >& atoms )
{
    atoms.clear();
    ::std::hash_map< ::rtl::OUString, int, ::rtl::OUStringHash >::const_iterator it = m_aAtomMap.begin();

    ::utl::AtomDescription aDesc;
    while( it != m_aAtomMap.end() )
    {
        aDesc.atom          = it->second;
        aDesc.description   = it->first;
        atoms.push_back( aDesc );
        ++it;
    }
}

void AtomProvider::getRecent( int atom, ::std::list< ::utl::AtomDescription >& atoms )
{
    atoms.clear();

    ::std::hash_map< ::rtl::OUString, int, ::rtl::OUStringHash >::const_iterator it = m_aAtomMap.begin();

    ::utl::AtomDescription aDesc;
    while( it != m_aAtomMap.end() )
    {
        if( it->second > atom )
        {
            aDesc.atom          = it->second;
            aDesc.description   = it->first;
            atoms.push_back( aDesc );
        }
        ++it;
    }
}

const ::rtl::OUString& AtomProvider::getString( int nAtom ) const
{
    static ::rtl::OUString aEmpty;
    ::std::hash_map< int, ::rtl::OUString, ::std::hash< int > >::const_iterator it = m_aStringMap.find( nAtom );

    return it == m_aStringMap.end() ? aEmpty : it->second;
}

void AtomProvider::overrideAtom( int atom, const ::rtl::OUString& description )
{
    m_aAtomMap[ description ] = atom;
    m_aStringMap[ atom ] = description;
    if( m_nAtoms <= atom )
        m_nAtoms=atom+1;
}

sal_Bool AtomProvider::hasAtom( int atom ) const
{
    return m_aStringMap.find( atom ) != m_aStringMap.end() ? sal_True : sal_False;
}

// -----------------------------------------------------------------------

MultiAtomProvider::MultiAtomProvider()
{
}

MultiAtomProvider::~MultiAtomProvider()
{
    for( ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::iterator it = m_aAtomLists.begin(); it != m_aAtomLists.end(); ++it )
        delete it->second;
}

sal_Bool MultiAtomProvider::insertAtomClass( int atomClass )
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::iterator it =
          m_aAtomLists.find( atomClass );
    if( it != m_aAtomLists.end() )
        return sal_False;
    m_aAtomLists[ atomClass ] = new AtomProvider();
    return sal_True;
}

int MultiAtomProvider::getAtom( int atomClass, const ::rtl::OUString& rString, sal_Bool bCreate )
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::iterator it =
          m_aAtomLists.find( atomClass );
    if( it != m_aAtomLists.end() )
        return it->second->getAtom( rString, bCreate );

    if( bCreate )
    {
        AtomProvider* pNewClass;
        m_aAtomLists[ atomClass ] = pNewClass = new AtomProvider();
        return pNewClass->getAtom( rString, bCreate );
    }
    return INVALID_ATOM;
}

int MultiAtomProvider::getLastAtom( int atomClass ) const
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it =
          m_aAtomLists.find( atomClass );

    return it != m_aAtomLists.end() ? it->second->getLastAtom() : INVALID_ATOM;
}

void MultiAtomProvider::getRecent( int atomClass, int atom, ::std::list< ::utl::AtomDescription >& atoms )
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it =
          m_aAtomLists.find( atomClass );
    if( it != m_aAtomLists.end() )
        it->second->getRecent( atom, atoms );
    else
        atoms.clear();
}

const ::rtl::OUString& MultiAtomProvider::getString( int atomClass, int atom ) const
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it =
          m_aAtomLists.find( atomClass );
    if( it != m_aAtomLists.end() )
        return it->second->getString( atom );

    static ::rtl::OUString aEmpty;
    return aEmpty;
}

sal_Bool MultiAtomProvider::hasAtom( int atomClass, int atom ) const
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it = m_aAtomLists.find( atomClass );
    return it != m_aAtomLists.end() ? it->second->hasAtom( atom ) : sal_False;
}

void MultiAtomProvider::getClass( int atomClass, ::std::list< ::utl::AtomDescription >& atoms) const
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it = m_aAtomLists.find( atomClass );

    if( it != m_aAtomLists.end() )
        it->second->getAll( atoms );
    else
        atoms.clear();
}

void MultiAtomProvider::overrideAtom( int atomClass, int atom, const ::rtl::OUString& description )
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it = m_aAtomLists.find( atomClass );
    if( it == m_aAtomLists.end() )
        m_aAtomLists[ atomClass ] = new AtomProvider();
    m_aAtomLists[ atomClass ]->overrideAtom( atom, description );
}

// -----------------------------------------------------------------------

AtomServer::AtomServer()
{
}

AtomServer::~AtomServer()
{
}

sal_Int32 AtomServer::getAtom( sal_Int32 atomClass, const ::rtl::OUString& description, sal_Bool create ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    return m_aProvider.getAtom( atomClass, description, create );
}

Sequence< Sequence< NMSP_UTIL::AtomDescription > > AtomServer::getClasses( const Sequence< sal_Int32 >& atomClasses ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    Sequence< Sequence< NMSP_UTIL::AtomDescription > > aRet( atomClasses.getLength() );
    for( int i = 0; i < atomClasses.getLength(); i++ )
    {
        aRet.getArray()[i] = getClass( atomClasses.getConstArray()[i] );
    }
    return aRet;
}

Sequence< NMSP_UTIL::AtomDescription > AtomServer::getClass( sal_Int32 atomClass ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    ::std::list< ::utl::AtomDescription > atoms;
    m_aProvider.getClass( atomClass, atoms );

    Sequence< NMSP_UTIL::AtomDescription > aRet( atoms.size() );
    for( int i = aRet.getLength()-1; i >= 0; i-- )
    {
        aRet.getArray()[i].atom         = atoms.back().atom;
        aRet.getArray()[i].description  = atoms.back().description;
        atoms.pop_back();
    }

    return aRet;
}

Sequence< NMSP_UTIL::AtomDescription > AtomServer::getRecentAtoms( sal_Int32 atomClass, sal_Int32 atom ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    ::std::list< ::utl::AtomDescription > atoms;
    m_aProvider.getRecent( atomClass, atom, atoms );

    Sequence< NMSP_UTIL::AtomDescription > aRet( atoms.size() );
    for( int i = aRet.getLength()-1; i >= 0; i-- )
    {
        aRet.getArray()[i].atom         = atoms.back().atom;
        aRet.getArray()[i].description  = atoms.back().description;
        atoms.pop_back();
    }

    return aRet;
}

Sequence< ::rtl::OUString > AtomServer::getAtomDescriptions( const Sequence< AtomClassRequest >& atoms ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    int nStrings = 0, i;
    for( i = 0; i < atoms.getLength(); i++ )
        nStrings += atoms.getConstArray()[ i ].atoms.getLength();
    Sequence< ::rtl::OUString > aRet( nStrings );
    for( i = 0, nStrings = 0; i < atoms.getLength(); i++ )
    {
        const AtomClassRequest& rRequest = atoms.getConstArray()[i];
        for( int n = 0; n < rRequest.atoms.getLength(); n++ )
            aRet.getArray()[ nStrings++ ] = m_aProvider.getString( rRequest.atomClass, rRequest.atoms.getConstArray()[ n ] );
    }
    return aRet;
}

// -----------------------------------------------------------------------

AtomClient::AtomClient( const Reference< XAtomServer >& xServer ) :
        m_xServer( xServer )
{
}

AtomClient::~AtomClient()
{
}

int AtomClient::getAtom( int atomClass, const ::rtl::OUString& description, sal_Bool bCreate )
{
    int nAtom = m_aProvider.getAtom( atomClass, description, sal_False );
    if( nAtom == INVALID_ATOM && bCreate )
    {
        try
        {
            nAtom = m_xServer->getAtom( atomClass, description, bCreate );
        }
        catch( RuntimeException& )
        {
            return INVALID_ATOM;
        }
        if( nAtom != INVALID_ATOM )
            m_aProvider.overrideAtom( atomClass, nAtom, description );
    }
    return nAtom;
}

const ::rtl::OUString& AtomClient::getString( int atomClass, int atom )
{
    static ::rtl::OUString aEmpty;

    if( ! m_aProvider.hasAtom( atomClass, atom ) )
    {
        Sequence< NMSP_UTIL::AtomDescription > aSeq;
        try
        {
            aSeq = m_xServer->getRecentAtoms( atomClass, m_aProvider.getLastAtom( atomClass ) );
        }
        catch( RuntimeException& )
        {
            return aEmpty;
        }
        const NMSP_UTIL::AtomDescription* pDescriptions = aSeq.getConstArray();
        for( int i = 0; i < aSeq.getLength(); i++ )
            m_aProvider.overrideAtom( atomClass,
                                      pDescriptions[i].atom,
                                      pDescriptions[i].description
                                      );

        if( ! m_aProvider.hasAtom( atomClass, atom ) )
        {
            // holes may occur by the above procedure!
            Sequence< AtomClassRequest > aSeq( 1 );
            aSeq.getArray()[0].atomClass = atomClass;
            aSeq.getArray()[0].atoms.realloc( 1 );
            aSeq.getArray()[0].atoms.getArray()[0] = atom;
            Sequence< ::rtl::OUString > aRet;
            try
            {
                aRet = m_xServer->getAtomDescriptions( aSeq );
            }
            catch( RuntimeException& )
            {
                return aEmpty;
            }
            if( aRet.getLength() == 1 )
                m_aProvider.overrideAtom( atomClass, atom, aRet.getConstArray()[0] );
        }
    }
    return m_aProvider.getString( atomClass, atom );
}

void AtomClient::updateAtomClasses( const Sequence< sal_Int32 >& atomClasses )
{
    Sequence< Sequence< NMSP_UTIL::AtomDescription > > aUpdate;
    try
    {
        aUpdate = m_xServer->getClasses( atomClasses );
    }
    catch( RuntimeException& )
    {
        return;
    }
    for( int i = 0; i < atomClasses.getLength(); i++ )
    {
        int nClass = atomClasses.getConstArray()[i];
        const Sequence< NMSP_UTIL::AtomDescription >& rClass = aUpdate.getConstArray()[i];
        const NMSP_UTIL::AtomDescription* pDesc = rClass.getConstArray();
        for( int n = 0; n < rClass.getLength(); n++, pDesc++ )
            m_aProvider.overrideAtom( nClass, pDesc->atom, pDesc->description );
    }
}
<|endoftext|>


<commit_before>#ifndef ENTT_SIGNAL_DISPATCHER_HPP
#define ENTT_SIGNAL_DISPATCHER_HPP

#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
#include "sigh.hpp"

namespace entt {

/**
 * @brief Basic dispatcher implementation.
 *
 * A dispatcher can be used either to trigger an immediate event or to enqueue
 * events to be published all together once per tick.

 * Listeners are provided in the form of member functions. For each event of
 * type `Event`, listeners are such that they can be invoked with an argument of
 * type `const Event &`, no matter what the return type is.
 *
 * The dispatcher creates instances of the `sigh` class internally. Refer to the
 * documentation of the latter for more details.
 */
class dispatcher {
    struct basic_pool {
        virtual ~basic_pool() = default;
        virtual void publish() = 0;
        virtual void clear() ENTT_NOEXCEPT = 0;
        virtual id_type type_id() const ENTT_NOEXCEPT = 0;
    };

    template<typename Event>
    struct pool_handler final: basic_pool {
        using signal_type = sigh<void(const Event &)>;
        using sink_type = typename signal_type::sink_type;

        void publish() override {
            const auto length = events.size();

            for(std::size_t pos{}; pos < length; ++pos) {
                signal.publish(events[pos]);
            }

            events.erase(events.cbegin(), events.cbegin()+length);
        }

        void clear() ENTT_NOEXCEPT override {
            events.clear();
        }

        sink_type sink() ENTT_NOEXCEPT {
            return entt::sink{signal};
        }

        template<typename... Args>
        void trigger(Args &&... args) {
            signal.publish(Event{std::forward<Args>(args)...});
        }

        template<typename... Args>
        void enqueue(Args &&... args) {
            events.emplace_back(std::forward<Args>(args)...);
        }

        id_type type_id() const ENTT_NOEXCEPT override {
            return type_info<Event>::id();
        }

    private:
        signal_type signal{};
        std::vector<Event> events;
    };

    template<typename Event>
    pool_handler<Event> & assure() {
        static_assert(std::is_same_v<Event, std::decay_t<Event>>);
        const auto index = type_index<Event>::value();

        if(!(index < pools.size())) {
            pools.resize(index+1);
        }

        if(!pools[index]) {
            pools[index].reset(new pool_handler<Event>{});
        }

        return static_cast<pool_handler<Event> &>(*pools[index]);
    }

public:
    /**
     * @brief Returns a sink object for the given event.
     *
     * A sink is an opaque object used to connect listeners to events.
     *
     * The function type for a listener is:
     * @code{.cpp}
     * void(const Event &);
     * @endcode
     *
     * The order of invocation of the listeners isn't guaranteed.
     *
     * @sa sink
     *
     * @tparam Event Type of event of which to get the sink.
     * @return A temporary sink object.
     */
    template<typename Event>
    auto sink() {
        return assure<Event>().sink();
    }

    /**
     * @brief Triggers an immediate event of the given type.
     *
     * All the listeners registered for the given type are immediately notified.
     * The event is discarded after the execution.
     *
     * @tparam Event Type of event to trigger.
     * @tparam Args Types of arguments to use to construct the event.
     * @param args Arguments to use to construct the event.
     */
    template<typename Event, typename... Args>
    void trigger(Args &&... args) {
        assure<Event>().trigger(std::forward<Args>(args)...);
    }

    /**
     * @brief Triggers an immediate event of the given type.
     *
     * All the listeners registered for the given type are immediately notified.
     * The event is discarded after the execution.
     *
     * @tparam Event Type of event to trigger.
     * @param event An instance of the given type of event.
     */
    template<typename Event>
    void trigger(Event &&event) {
        assure<std::decay_t<Event>>().trigger(std::forward<Event>(event));
    }

    /**
     * @brief Enqueues an event of the given type.
     *
     * An event of the given type is queued. No listener is invoked. Use the
     * `update` member function to notify listeners when ready.
     *
     * @tparam Event Type of event to enqueue.
     * @tparam Args Types of arguments to use to construct the event.
     * @param args Arguments to use to construct the event.
     */
    template<typename Event, typename... Args>
    void enqueue(Args &&... args) {
        assure<Event>().enqueue(std::forward<Args>(args)...);
    }

    /**
     * @brief Enqueues an event of the given type.
     *
     * An event of the given type is queued. No listener is invoked. Use the
     * `update` member function to notify listeners when ready.
     *
     * @tparam Event Type of event to enqueue.
     * @param event An instance of the given type of event.
     */
    template<typename Event>
    void enqueue(Event &&event) {
        assure<std::decay_t<Event>>().enqueue(std::forward<Event>(event));
    }

    /**
     * @brief Discards all the events queued so far.
     *
     * If no types are provided, the dispatcher will clear all the existing
     * pools.
     *
     * @tparam Event Type of events to discard.
     */
    template<typename... Event>
    void clear() {
        if constexpr(sizeof...(Event) == 0) {
            for(auto &&cpool: pools) {
                if(cpool) {
                    cpool->clear();
                }
            }
        } else {
            (assure<Event>().clear(), ...);
        }
    }

    /**
     * @brief Delivers all the pending events of the given type.
     *
     * This method is blocking and it doesn't return until all the events are
     * delivered to the registered listeners. It's responsibility of the users
     * to reduce at a minimum the time spent in the bodies of the listeners.
     *
     * @tparam Event Type of events to send.
     */
    template<typename Event>
    void update() {
        assure<Event>().publish();
    }

    /**
     * @brief Delivers all the pending events.
     *
     * This method is blocking and it doesn't return until all the events are
     * delivered to the registered listeners. It's responsibility of the users
     * to reduce at a minimum the time spent in the bodies of the listeners.
     */
    void update() const {
        for(auto pos = pools.size(); pos; --pos) {
            if(auto &&cpool = pools[pos-1]; cpool) {
                cpool->publish();
            }
        }
    }

private:
    std::vector<std::unique_ptr<basic_pool>> pools;
};

}

#endif
<commit_msg>dispatcher: use fast path if available, standard path otherwise<commit_after>#ifndef ENTT_SIGNAL_DISPATCHER_HPP
#define ENTT_SIGNAL_DISPATCHER_HPP

#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
#include "sigh.hpp"

namespace entt {

/**
 * @brief Basic dispatcher implementation.
 *
 * A dispatcher can be used either to trigger an immediate event or to enqueue
 * events to be published all together once per tick.

 * Listeners are provided in the form of member functions. For each event of
 * type `Event`, listeners are such that they can be invoked with an argument of
 * type `const Event &`, no matter what the return type is.
 *
 * The dispatcher creates instances of the `sigh` class internally. Refer to the
 * documentation of the latter for more details.
 */
class dispatcher {
    struct basic_pool {
        virtual ~basic_pool() = default;
        virtual void publish() = 0;
        virtual void clear() ENTT_NOEXCEPT = 0;
        virtual id_type type_id() const ENTT_NOEXCEPT = 0;
    };

    template<typename Event>
    struct pool_handler final: basic_pool {
        using signal_type = sigh<void(const Event &)>;
        using sink_type = typename signal_type::sink_type;

        void publish() override {
            const auto length = events.size();

            for(std::size_t pos{}; pos < length; ++pos) {
                signal.publish(events[pos]);
            }

            events.erase(events.cbegin(), events.cbegin()+length);
        }

        void clear() ENTT_NOEXCEPT override {
            events.clear();
        }

        sink_type sink() ENTT_NOEXCEPT {
            return entt::sink{signal};
        }

        template<typename... Args>
        void trigger(Args &&... args) {
            signal.publish(Event{std::forward<Args>(args)...});
        }

        template<typename... Args>
        void enqueue(Args &&... args) {
            events.emplace_back(std::forward<Args>(args)...);
        }

        id_type type_id() const ENTT_NOEXCEPT override {
            return type_info<Event>::id();
        }

    private:
        signal_type signal{};
        std::vector<Event> events;
    };

    template<typename Event>
    pool_handler<Event> & assure() {
        static_assert(std::is_same_v<Event, std::decay_t<Event>>);

        if constexpr(has_type_index_v<Event>) {
            const auto index = type_index<Event>::value();

            if(!(index < pools.size())) {
                pools.resize(index+1);
            }

            if(!pools[index]) {
                pools[index].reset(new pool_handler<Event>{});
            }

            return static_cast<pool_handler<Event> &>(*pools[index]);
        } else {
            auto it = std::find_if(pools.begin(), pools.end(), [id = type_info<Event>::id()](const auto &cpool) { return id == cpool->type_id(); });
            return static_cast<pool_handler<Event> &>(it == pools.cend() ? *pools.emplace_back(new pool_handler<Event>{}) : **it);
        }
    }

public:
    /**
     * @brief Returns a sink object for the given event.
     *
     * A sink is an opaque object used to connect listeners to events.
     *
     * The function type for a listener is:
     * @code{.cpp}
     * void(const Event &);
     * @endcode
     *
     * The order of invocation of the listeners isn't guaranteed.
     *
     * @sa sink
     *
     * @tparam Event Type of event of which to get the sink.
     * @return A temporary sink object.
     */
    template<typename Event>
    auto sink() {
        return assure<Event>().sink();
    }

    /**
     * @brief Triggers an immediate event of the given type.
     *
     * All the listeners registered for the given type are immediately notified.
     * The event is discarded after the execution.
     *
     * @tparam Event Type of event to trigger.
     * @tparam Args Types of arguments to use to construct the event.
     * @param args Arguments to use to construct the event.
     */
    template<typename Event, typename... Args>
    void trigger(Args &&... args) {
        assure<Event>().trigger(std::forward<Args>(args)...);
    }

    /**
     * @brief Triggers an immediate event of the given type.
     *
     * All the listeners registered for the given type are immediately notified.
     * The event is discarded after the execution.
     *
     * @tparam Event Type of event to trigger.
     * @param event An instance of the given type of event.
     */
    template<typename Event>
    void trigger(Event &&event) {
        assure<std::decay_t<Event>>().trigger(std::forward<Event>(event));
    }

    /**
     * @brief Enqueues an event of the given type.
     *
     * An event of the given type is queued. No listener is invoked. Use the
     * `update` member function to notify listeners when ready.
     *
     * @tparam Event Type of event to enqueue.
     * @tparam Args Types of arguments to use to construct the event.
     * @param args Arguments to use to construct the event.
     */
    template<typename Event, typename... Args>
    void enqueue(Args &&... args) {
        assure<Event>().enqueue(std::forward<Args>(args)...);
    }

    /**
     * @brief Enqueues an event of the given type.
     *
     * An event of the given type is queued. No listener is invoked. Use the
     * `update` member function to notify listeners when ready.
     *
     * @tparam Event Type of event to enqueue.
     * @param event An instance of the given type of event.
     */
    template<typename Event>
    void enqueue(Event &&event) {
        assure<std::decay_t<Event>>().enqueue(std::forward<Event>(event));
    }

    /**
     * @brief Discards all the events queued so far.
     *
     * If no types are provided, the dispatcher will clear all the existing
     * pools.
     *
     * @tparam Event Type of events to discard.
     */
    template<typename... Event>
    void clear() {
        if constexpr(sizeof...(Event) == 0) {
            for(auto &&cpool: pools) {
                if(cpool) {
                    cpool->clear();
                }
            }
        } else {
            (assure<Event>().clear(), ...);
        }
    }

    /**
     * @brief Delivers all the pending events of the given type.
     *
     * This method is blocking and it doesn't return until all the events are
     * delivered to the registered listeners. It's responsibility of the users
     * to reduce at a minimum the time spent in the bodies of the listeners.
     *
     * @tparam Event Type of events to send.
     */
    template<typename Event>
    void update() {
        assure<Event>().publish();
    }

    /**
     * @brief Delivers all the pending events.
     *
     * This method is blocking and it doesn't return until all the events are
     * delivered to the registered listeners. It's responsibility of the users
     * to reduce at a minimum the time spent in the bodies of the listeners.
     */
    void update() const {
        for(auto pos = pools.size(); pos; --pos) {
            if(auto &&cpool = pools[pos-1]; cpool) {
                cpool->publish();
            }
        }
    }

private:
    std::vector<std::unique_ptr<basic_pool>> pools;
};

}

#endif
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2016 DeepCortex GmbH <legal@eventql.io>
 * Authors:
 *   - Paul Asmuth <paul@eventql.io>
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License ("the license") as
 * published by the Free Software Foundation, either version 3 of the License,
 * or any later version.
 *
 * In accordance with Section 7(e) of the license, the licensing of the Program
 * under the license does not imply a trademark license. Therefore any rights,
 * title and interest in our trademarks remain entirely with us.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the license for more details.
 *
 * You can be released from the requirements of the license by purchasing a
 * commercial license. Buying such a license is mandatory as soon as you develop
 * commercial activities involving this program without disclosing the source
 * code of your own applications
 */
#include <unistd.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <ctime>
#include <sys/time.h>
#include <sys/types.h>
#include <thread>
#ifdef __MACH__
#include <mach/clock.h>
#include <mach/mach.h>
#endif
#include "eventql/util/http/httpserverconnection.h"
#include "eventql/eventql.h"
#include "eventql/server/listener.h"
#include "eventql/server/session.h"
#include "eventql/db/database.h"
#include "eventql/util/return_code.h"
#include "eventql/util/logging.h"
#include "eventql/transport/native/connection_tcp.h"

namespace eventql {

uint64_t getMonoTime() {
#ifdef __MACH__
  clock_serv_t cclock;
  mach_timespec_t mts;
  host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
  clock_get_time(cclock, &mts);
  mach_port_deallocate(mach_task_self(), cclock);
  return std::uint64_t(mts.tv_sec) * 1000000 + mts.tv_nsec / 1000;
#else
  timespec ts;
  if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
    logFatal("clock_gettime(CLOCK_MONOTONIC) failed");
    abort();
  } else {
    return std::uint64_t(ts.tv_sec) * 1000000 + ts.tv_nsec / 1000;
  }
#endif
}

Listener::Listener(
    Database* database) :
    database_(database),
    io_timeout_(
        std::max(
            database->getConfig()->getInt("server.s2s_io_timeout").get(),
            database->getConfig()->getInt("server.c2s_io_timeout").get())),
    running_(true),
    ssock_(-1),
    http_transport_(database),
    native_server_(database) {}

ReturnCode Listener::bind(int listen_port) {
  ssock_ = ::socket(AF_INET, SOCK_STREAM, 0);
  if (ssock_ == 0) {
    return ReturnCode::error(
        "IOERR",
        "create socket() failed: %s",
        strerror(errno));
  }

  int opt = 1;
  if (::setsockopt(ssock_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
    return ReturnCode::error(
        "IOERR",
        "setsockopt(SO_REUSEADDR) failed: %s",
        strerror(errno));
  }

  int flags = ::fcntl(ssock_, F_GETFL, 0);
  flags = flags | O_NONBLOCK;

  if (::fcntl(ssock_, F_SETFL, flags) != 0) {
    return ReturnCode::error(
        "IOERR",
        "fcntl(F_SETFL, O_NONBLOCK) failed: %s",
        strerror(errno));
  }

  struct sockaddr_in addr;
  memset((char *) &addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
  addr.sin_port = htons(listen_port);
  if (::bind(ssock_, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
    return ReturnCode::error(
        "IOERR",
        "bind() failed: %s",
        strerror(errno));
  }

  if (::listen(ssock_, 1024) == -1) {
    return ReturnCode::error(
        "IOERR",
        "listen() failed: %s",
        strerror(errno));
  }

  logNotice("eventql", "Listening on port $0", listen_port);
  return ReturnCode::success();
}

void Listener::run() {
  http_transport_.startIOThread();

  while (running_.load()) {
    fd_set op_read, op_write, op_error;
    FD_ZERO(&op_read);
    FD_ZERO(&op_write);
    FD_ZERO(&op_error);

    int max_fd = ssock_;
    FD_SET(ssock_, &op_read);

    auto now = getMonoTime();
    while (!connections_.empty()) {
      if (connections_.front().accepted_at + io_timeout_ > now) {
        break;
      }

      logError(
          "eventql",
          "timeout while reading first byte, closing connection");

      close(connections_.front().fd);
      connections_.pop_front();
    }

    uint64_t timeout = 0;
    if (!connections_.empty()) {
      timeout = (connections_.front().accepted_at + io_timeout_) - now;
    }

    for (const auto& c : connections_) {
      if (c.fd > max_fd) {
        max_fd = c.fd;
      }

      FD_SET(c.fd, &op_read);
    }

    int res;
    if (timeout) {
      struct timeval tv;
      tv.tv_sec = timeout / kMicrosPerSecond;
      tv.tv_usec = timeout % kMicrosPerSecond;
      res = select(max_fd + 1, &op_read, &op_write, &op_error, &tv);
    } else {
      res = select(max_fd + 1, &op_read, &op_write, &op_error, NULL);
    }

    switch (res) {
      case 0:
        continue;
      case -1:
        if (errno == EINTR) {
          continue;
        }

        logError("eventql", "select() failed");
        goto exit;
    }

    if (FD_ISSET(ssock_, &op_read)) {
      int conn_fd = ::accept(ssock_, NULL, NULL);
      if (conn_fd < 0) {
        logError("eventql", "accept() failed");
      }

      int flags = ::fcntl(conn_fd, F_GETFL, 0);
      flags = flags | O_NONBLOCK;

      if (::fcntl(conn_fd, F_SETFL, flags) != 0) {
        logError(
            "eventql",
            "fcntl(F_SETFL, O_NONBLOCK) failed, closing connection: $0",
            strerror(errno));

        close(conn_fd);
        continue;
      }

      EstablishingConnection c;
      c.fd = conn_fd;
      c.accepted_at = getMonoTime();
      connections_.emplace_back(c);
    }

    auto iter = connections_.begin();
    while (iter != connections_.end()) {
      if (FD_ISSET(iter->fd, &op_read)) {
        open(iter->fd);
        iter = connections_.erase(iter);
      } else {
        iter++;
      }
    }
  }

exit:
  http_transport_.stopIOThread();
  return;
}

void Listener::shutdown() {
}

void Listener::open(int fd) {
  char first_byte;
  auto res = ::read(fd, &first_byte, 1);
  if (res != 1) {
    logError(
        "eventql",
        "error while reading first byte, closing connection: $0",
        strerror(errno));

    close(fd);
    return;
  }

  int flags = ::fcntl(fd, F_GETFL, 0);
  flags = flags & ~O_NONBLOCK;

  if (::fcntl(fd, F_SETFL, flags) != 0) {
    logError(
        "eventql",
        "fcntl(F_SETFL, O_NONBLOCK) failed, closing connection: $0",
        strerror(errno));

    close(fd);
    return;
  }

  switch (first_byte) {

    // native
   case '^': {
      native_server_.startConnection(
          std::unique_ptr<native_transport::NativeConnection>(
              new native_transport::TCPConnection(
                  fd,
                  io_timeout_,
                  std::string(&first_byte, 1))));
      break;
    }

    // http
    default:
      http_transport_.handleConnection(fd, std::string(&first_byte, 1));
      break;

  }
}

} // namespace eventql
<commit_msg>use MonotonicClock::now in Listener<commit_after>/**
 * Copyright (c) 2016 DeepCortex GmbH <legal@eventql.io>
 * Authors:
 *   - Paul Asmuth <paul@eventql.io>
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License ("the license") as
 * published by the Free Software Foundation, either version 3 of the License,
 * or any later version.
 *
 * In accordance with Section 7(e) of the license, the licensing of the Program
 * under the license does not imply a trademark license. Therefore any rights,
 * title and interest in our trademarks remain entirely with us.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the license for more details.
 *
 * You can be released from the requirements of the license by purchasing a
 * commercial license. Buying such a license is mandatory as soon as you develop
 * commercial activities involving this program without disclosing the source
 * code of your own applications
 */
#include <unistd.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <ctime>
#include <sys/time.h>
#include <sys/types.h>
#include <thread>
#ifdef __MACH__
#include <mach/clock.h>
#include <mach/mach.h>
#endif
#include "eventql/util/http/httpserverconnection.h"
#include "eventql/eventql.h"
#include "eventql/server/listener.h"
#include "eventql/server/session.h"
#include "eventql/db/database.h"
#include "eventql/util/return_code.h"
#include "eventql/util/logging.h"
#include "eventql/transport/native/connection_tcp.h"

namespace eventql {

Listener::Listener(
    Database* database) :
    database_(database),
    io_timeout_(
        std::max(
            database->getConfig()->getInt("server.s2s_io_timeout").get(),
            database->getConfig()->getInt("server.c2s_io_timeout").get())),
    running_(true),
    ssock_(-1),
    http_transport_(database),
    native_server_(database) {}

ReturnCode Listener::bind(int listen_port) {
  ssock_ = ::socket(AF_INET, SOCK_STREAM, 0);
  if (ssock_ == 0) {
    return ReturnCode::error(
        "IOERR",
        "create socket() failed: %s",
        strerror(errno));
  }

  int opt = 1;
  if (::setsockopt(ssock_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
    return ReturnCode::error(
        "IOERR",
        "setsockopt(SO_REUSEADDR) failed: %s",
        strerror(errno));
  }

  int flags = ::fcntl(ssock_, F_GETFL, 0);
  flags = flags | O_NONBLOCK;

  if (::fcntl(ssock_, F_SETFL, flags) != 0) {
    return ReturnCode::error(
        "IOERR",
        "fcntl(F_SETFL, O_NONBLOCK) failed: %s",
        strerror(errno));
  }

  struct sockaddr_in addr;
  memset((char *) &addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
  addr.sin_port = htons(listen_port);
  if (::bind(ssock_, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
    return ReturnCode::error(
        "IOERR",
        "bind() failed: %s",
        strerror(errno));
  }

  if (::listen(ssock_, 1024) == -1) {
    return ReturnCode::error(
        "IOERR",
        "listen() failed: %s",
        strerror(errno));
  }

  logNotice("eventql", "Listening on port $0", listen_port);
  return ReturnCode::success();
}

void Listener::run() {
  http_transport_.startIOThread();

  while (running_.load()) {
    fd_set op_read, op_write, op_error;
    FD_ZERO(&op_read);
    FD_ZERO(&op_write);
    FD_ZERO(&op_error);

    int max_fd = ssock_;
    FD_SET(ssock_, &op_read);

    auto now = MonotonicClock::now();
    while (!connections_.empty()) {
      if (connections_.front().accepted_at + io_timeout_ > now) {
        break;
      }

      logError(
          "eventql",
          "timeout while reading first byte, closing connection");

      close(connections_.front().fd);
      connections_.pop_front();
    }

    uint64_t timeout = 0;
    if (!connections_.empty()) {
      timeout = (connections_.front().accepted_at + io_timeout_) - now;
    }

    for (const auto& c : connections_) {
      if (c.fd > max_fd) {
        max_fd = c.fd;
      }

      FD_SET(c.fd, &op_read);
    }

    int res;
    if (timeout) {
      struct timeval tv;
      tv.tv_sec = timeout / kMicrosPerSecond;
      tv.tv_usec = timeout % kMicrosPerSecond;
      res = select(max_fd + 1, &op_read, &op_write, &op_error, &tv);
    } else {
      res = select(max_fd + 1, &op_read, &op_write, &op_error, NULL);
    }

    switch (res) {
      case 0:
        continue;
      case -1:
        if (errno == EINTR) {
          continue;
        }

        logError("eventql", "select() failed");
        goto exit;
    }

    if (FD_ISSET(ssock_, &op_read)) {
      int conn_fd = ::accept(ssock_, NULL, NULL);
      if (conn_fd < 0) {
        logError("eventql", "accept() failed");
      }

      int flags = ::fcntl(conn_fd, F_GETFL, 0);
      flags = flags | O_NONBLOCK;

      if (::fcntl(conn_fd, F_SETFL, flags) != 0) {
        logError(
            "eventql",
            "fcntl(F_SETFL, O_NONBLOCK) failed, closing connection: $0",
            strerror(errno));

        close(conn_fd);
        continue;
      }

      EstablishingConnection c;
      c.fd = conn_fd;
      c.accepted_at = MonotonicClock::now();
      connections_.emplace_back(c);
    }

    auto iter = connections_.begin();
    while (iter != connections_.end()) {
      if (FD_ISSET(iter->fd, &op_read)) {
        open(iter->fd);
        iter = connections_.erase(iter);
      } else {
        iter++;
      }
    }
  }

exit:
  http_transport_.stopIOThread();
  return;
}

void Listener::shutdown() {
}

void Listener::open(int fd) {
  char first_byte;
  auto res = ::read(fd, &first_byte, 1);
  if (res != 1) {
    logError(
        "eventql",
        "error while reading first byte, closing connection: $0",
        strerror(errno));

    close(fd);
    return;
  }

  int flags = ::fcntl(fd, F_GETFL, 0);
  flags = flags & ~O_NONBLOCK;

  if (::fcntl(fd, F_SETFL, flags) != 0) {
    logError(
        "eventql",
        "fcntl(F_SETFL, O_NONBLOCK) failed, closing connection: $0",
        strerror(errno));

    close(fd);
    return;
  }

  switch (first_byte) {

    // native
   case '^': {
      native_server_.startConnection(
          std::unique_ptr<native_transport::NativeConnection>(
              new native_transport::TCPConnection(
                  fd,
                  io_timeout_,
                  std::string(&first_byte, 1))));
      break;
    }

    // http
    default:
      http_transport_.handleConnection(fd, std::string(&first_byte, 1));
      break;

  }
}

} // namespace eventql
<|endoftext|>


<commit_before>#include "util.hpp"
#include "distributed-node.hpp"

DistributedNode::DistributedNode(std::string new_keyfile)
:status(OBJECT),
ddata(OBJECT),
keyfile(new_keyfile){
	uint16_t port = 30000;
	while(true){
		try{
			this->server = new SymmetricEpollServer(this->keyfile, port, 10);
		}catch(std::runtime_error& e){
			if(errno != 98){
				PRINT("ERROR: " << errno)
				throw e;
			}
			port++;
			continue;
		}
		break;
	}

	this->server->on_read = [&](int fd, const char* data, ssize_t data_length)->ssize_t{
		PRINT(data)
		JsonObject request;
		request.parse(data);
		PRINT(request.stringify(true))
		JsonObject response(OBJECT);

		if(request.HasObj("hash", STRING)){
			PRINT("GIVEN HASH: " << request.GetStr("hash"))
			PRINT("MY HASH: " << this->status.GetStr("hash"))
			if(request.GetStr("hash") == this->status.GetStr("hash")){
				response.objectValues["status"] = new JsonObject("Up to date.");
			}else{
				response.objectValues["status"] = new JsonObject("Out of date.");
			}
		}else{
			response.objectValues["hash"] = new JsonObject(this->status.GetStr("hash"));
		}

		if(this->server->send(fd, response.stringify(false))){
			ERROR("DistributedNode send")
			return -1;
		}

		return data_length;
	};

	// Returns and runs on a single thread.
	this->server->run(true, 1);

	start_thread = std::thread(&DistributedNode::start, this);

	PRINT("DISTRIBUTED NODE INITIALIZED.")
}

void DistributedNode::add_client(const char* ip_address, uint16_t port){
	std::string client = std::string(ip_address) + ':' + std::to_string(port);
	this->clients_mutex.lock();
	this->clients[client] = new SymmetricTcpClient(ip_address, port, this->keyfile);
	this->clients_mutex.unlock();
}

bool DistributedNode::set_ddata(std::string data){
	this->ddata.objectValues.clear();
	this->ddata.parse(data.c_str());
	this->status.objectValues["hash"] = new JsonObject(Util::sha256_hash(this->ddata.stringify(false)));
	return true;
}

void DistributedNode::start(){
	std::string response;
	while(true){
		this->clients_mutex.lock();
		for(auto iter = this->clients.begin(); iter != this->clients.end(); ++iter){
			response = iter->second->communicate(status.stringify(false));
			if(response.empty()){
				PRINT(iter->first << " DISCONNECTED")
			}else{
				PRINT(iter->first << " -> " << response)
			}
		}
		this->clients_mutex.unlock();

		sleep(5);
	}
}
<commit_msg>Improved port selection for distributed node. Hmph.<commit_after>#include "util.hpp"
#include "distributed-node.hpp"

DistributedNode::DistributedNode(std::string new_keyfile)
:status(OBJECT),
ddata(OBJECT),
keyfile(new_keyfile){
	uint16_t port = 30000;
	while(true){
		try{
			this->server = new SymmetricEpollServer(this->keyfile, port, 10);
		}catch(std::runtime_error& e){
			if(errno != 98 && errno != 22){
				PRINT("ERROR: " << errno << ':' << port)
				throw e;
			}
			port++;
			sleep(1);
			continue;
		}
		break;
	}

	this->server->on_read = [&](int fd, const char* data, ssize_t data_length)->ssize_t{
		PRINT(data)
		JsonObject request;
		request.parse(data);
		PRINT(request.stringify(true))
		JsonObject response(OBJECT);

		if(request.HasObj("hash", STRING)){
			PRINT("GIVEN HASH: " << request.GetStr("hash"))
			PRINT("MY HASH: " << this->status.GetStr("hash"))
			if(request.GetStr("hash") == this->status.GetStr("hash")){
				response.objectValues["status"] = new JsonObject("Up to date.");
			}else{
				response.objectValues["status"] = new JsonObject("Out of date.");
			}
		}else{
			response.objectValues["hash"] = new JsonObject(this->status.GetStr("hash"));
		}

		if(this->server->send(fd, response.stringify(false))){
			ERROR("DistributedNode send")
			return -1;
		}

		return data_length;
	};

	// Returns and runs on a single thread.
	this->server->run(true, 1);

	start_thread = std::thread(&DistributedNode::start, this);

	PRINT("DISTRIBUTED NODE INITIALIZED.")
}

void DistributedNode::add_client(const char* ip_address, uint16_t port){
	std::string client = std::string(ip_address) + ':' + std::to_string(port);
	this->clients_mutex.lock();
	this->clients[client] = new SymmetricTcpClient(ip_address, port, this->keyfile);
	this->clients_mutex.unlock();
}

bool DistributedNode::set_ddata(std::string data){
	this->ddata.objectValues.clear();
	this->ddata.parse(data.c_str());
	this->status.objectValues["hash"] = new JsonObject(Util::sha256_hash(this->ddata.stringify(false)));
	return true;
}

void DistributedNode::start(){
	std::string response;
	while(true){
		this->clients_mutex.lock();
		for(auto iter = this->clients.begin(); iter != this->clients.end(); ++iter){
			response = iter->second->communicate(status.stringify(false));
			if(response.empty()){
				PRINT(iter->first << " DISCONNECTED")
			}else{
				PRINT(iter->first << " -> " << response)
			}
		}
		this->clients_mutex.unlock();

		sleep(5);
	}
}
<|endoftext|>