<commit_before>#include "ctkDicomExampleHost.h"
#include <QProcess>
#include <QtDebug>

ctkDicomExampleHost::ctkDicomExampleHost() : ctkDicomAbstractHost(8080)
{
}

int ctkDicomExampleHost::StartApplication(QString AppPath, QString App_URL, int port){
    QStringList l;
    l.append("--hostURL");
    l.append("localhost:"+this->getPort());
    l.append("--applicationURL");
    l.append(App_URL+":"+QString(port));
    if (!QProcess::startDetached (
            AppPath,l))
    {
        qCritical() << "application failed to start!";
    }
}

QRect ctkDicomExampleHost::getAvailableScreen(const QRect& preferredScreen){
    qDebug()<< "set screen from preferredScreen:"<< preferredScreen;
    QRect rect (preferredScreen);
    emit giveAvailableScreen(rect);
    return rect;
}

void ctkDicomExampleHost::notifyStateChanged(ctkDicomWG23::State state){
    qDebug()<< "new state received:"<< state;
    emit stateChangedReceived(state);
}

void ctkDicomExampleHost::notifyStatus(const ctkDicomWG23::Status& status){
    qDebug()<< "new status received:"<<status.codeMeaning;
    emit statusReceived(status);;
}
<commit_msg>COMP: return value required<commit_after>#include "ctkDicomExampleHost.h"
#include <QProcess>
#include <QtDebug>

ctkDicomExampleHost::ctkDicomExampleHost() : ctkDicomAbstractHost(8080)
{
}

int ctkDicomExampleHost::StartApplication(QString AppPath, QString App_URL, int port){
    QStringList l;
    l.append("--hostURL");
    l.append("localhost:"+this->getPort());
    l.append("--applicationURL");
    l.append(App_URL+":"+QString(port));
    if (!QProcess::startDetached (
            AppPath,l))
    {
        qCritical() << "application failed to start!";
        return 0;
    }
    return 1;
}

QRect ctkDicomExampleHost::getAvailableScreen(const QRect& preferredScreen){
    qDebug()<< "set screen from preferredScreen:"<< preferredScreen;
    QRect rect (preferredScreen);
    emit giveAvailableScreen(rect);
    return rect;
}

void ctkDicomExampleHost::notifyStateChanged(ctkDicomWG23::State state){
    qDebug()<< "new state received:"<< state;
    emit stateChangedReceived(state);
}

void ctkDicomExampleHost::notifyStatus(const ctkDicomWG23::Status& status){
    qDebug()<< "new status received:"<<status.codeMeaning;
    emit statusReceived(status);;
}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2013, The Mineserver Project
   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 The Mineserver 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 THE COPYRIGHT HOLDER 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.
*/

#ifdef _WIN32
#include <cstdlib>
#include <winsock2.h>
typedef int socklen_t;
#else
#include <netdb.h>   // for gethostbyname()
#include <netinet/tcp.h> // for TCP constants
#endif

#include <string>

#include <sys/stat.h>
#include <fstream>

#include <cerrno>
#include <sstream>
#include <algorithm>

#include <array>

#include "sockets.h"
#include "tools.h"
#include "logger.h"
#include "constants.h"
#include "user.h"
#include "map.h"
#include "chat.h"
#include "nbt.h"
#include "mineserver.h"
#include "packets.h"
#include "config.h"
#include "extern.h"

extern int setnonblock(int fd);

#ifndef WIN32
#define SOCKET_ERROR -1
#endif

static const size_t BUFSIZE = 2048;
static std::array<uint8_t, BUFSIZE> BUF;
static std::array<uint8_t, BUFSIZE> BUFCRYPT;
static char* const cpBUF = reinterpret_cast<char*>(BUF.data());
static uint8_t* const upBUF = BUF.data();

static char* cpBUFCRYPT = reinterpret_cast<char*>(BUFCRYPT.data());

bool client_write(User *user)
{
  if (user->deleting) return false;
  //If there is data in the output buffer, crypt it before writing
  if (!user->buffer.getWriteEmpty())
  {
    std::vector<char> buf;

    user->buffer.getWriteData(buf);

    //More glue - Fador
    if(user->crypted)
    {
      //We might have to write some data uncrypted ToDo: fix
      if(user->uncryptedLeft)
      {
        user->bufferCrypted.addToWrite((uint8_t *)buf.data(),user->uncryptedLeft);
      }
      int p_len = buf.size()-user->uncryptedLeft, f_len = 0;
      if(p_len)
      {
        uint8_t *buffer = (uint8_t *)malloc(p_len+1);
        EVP_EncryptUpdate(&user->en, (uint8_t *)buffer, &p_len, (const uint8_t *)buf.data()+user->uncryptedLeft, buf.size()-user->uncryptedLeft);
        int written = p_len + f_len;
        user->bufferCrypted.addToWrite((uint8_t *)buffer,written);
        free(buffer);
      }
      user->uncryptedLeft = 0;
    }
    else
    {
      user->bufferCrypted.addToWrite((uint8_t *)buf.data(),buf.size());
      user->uncryptedLeft = 0;
    }

    //free(outBuf);
    user->buffer.clearWrite(buf.size());
  }

  //We have crypted data ready to be written
  if(!user->bufferCrypted.getWriteEmpty())
  {
    std::vector<char> buf;
    user->bufferCrypted.getWriteData(buf);

    //Try to write the whole buffer
    const int written = send(user->fd, buf.data(), buf.size(), 0);

    //Handle errors
    if (written == SOCKET_ERROR)
    {
    #ifdef WIN32
    #define ERROR_NUMBER WSAGetLastError()
      if ((ERROR_NUMBER != WSATRY_AGAIN && ERROR_NUMBER != WSAEINTR && ERROR_NUMBER != WSAEWOULDBLOCK))
    #else
    #define ERROR_NUMBER errno
      if ((errno != EAGAIN && errno != EINTR))
    #endif
      {
        LOG2(ERROR, "Error writing to client, tried to write " + dtos(buf.size()) + " bytes, code: " + dtos(ERROR_NUMBER));
        user->deleting = true;
        ServerInstance->usersToRemove().insert(user);
        return false;
      }
    }
    else
    {
      //Remove written amount from the buffer
      user->bufferCrypted.clearWrite(written);
    }

    //If we couldn't write everything at once, add EV_WRITE event calling this function again..
    if (!user->bufferCrypted.getWriteEmpty())
    {
      event_add(user->getWriteEvent(), NULL);
      return false;
    }
  }
  return true;
}

extern "C" void client_callback(int fd, short ev, void* arg)
{
  User* user = reinterpret_cast<User*>(arg);
  if (user->deleting) return;

  if (ev & EV_READ)
  {
    int read = 1;

    //Data must be decrypted if we are in crypted mode
    if(user->crypted)
    {
      read = recv(fd, cpBUFCRYPT, BUFSIZE, 0);
      int p_len = read, f_len = 0;
      EVP_DecryptUpdate(&user->de, upBUF, &p_len, (const uint8_t *)cpBUFCRYPT, read);
      read = p_len + f_len;
    }
    else
    {
      read = recv(fd, cpBUF, BUFSIZE, 0);
    }

    if (read == 0)
    {
      if(user->nick.size())
      {
        LOG2(INFO, "User "+ user->nick + " disconnected by closing socket");
      }
      else
      {
        LOG2(INFO, "Socket closed");
      }
      delete user;
      return;
    }

    if (read == SOCKET_ERROR)
    {
      LOG2(INFO, "Socket error");
      delete user;
      return;
    }

    //Keep track on incoming data, can timeout inactive users
    user->lastData = std::time(NULL);

    user->buffer.addToRead(upBUF, read);
    user->buffer.reset();
    MS_VarInt packetLen;
    uint32_t varint_len;
    uint32_t buffer_pos = 0;

    // Handle exceptions caused by varint reading
    try
    {
      while (user->buffer >> (MS_VarInt&)packetLen)
      {
        varint_len = user->buffer.m_readPos-buffer_pos;

        // Check if we have the data in buffer
        if (!user->buffer.haveData(static_cast<int64_t>(packetLen)))
        {
          user->waitForData = true;
          event_add(user->getReadEvent(), NULL);
          return;
        }
        // Packet data has been received, call the function
        else
        {
          // Store packetlen to be used in the parsing functions (replaced later if compression used..)
          user->packetLen = (int32_t)packetLen;
          // Handle compressed incoming data
          if (user->compression)
          {
            MS_VarInt uncompressed_size;
            int32_t cur_pos = user->buffer.m_readPos;
            user->buffer >> (MS_VarInt&)uncompressed_size;

            // Hopefully a rare occasion when the packet size exceeds the compression threshold
            if (uncompressed_size != 0)
            {
              uLongf written = uncompressed_size;
              uLong sourceLen = (int)packetLen.val-(user->buffer.m_readPos-cur_pos);
              uint8_t* inbuffer = new uint8_t[sourceLen];
              uint8_t* buffer = new uint8_t[uncompressed_size+1];

              user->buffer.getData(inbuffer,sourceLen);

              user->buffer.removePacketLen((uint32_t)packetLen+varint_len);
              int ret = uncompress(buffer, &written, inbuffer, sourceLen);
              if (ret < Z_OK || written != uncompressed_size) {
                LOG2(DEBUG, "Uncompress error");
                buffer_pos = user->buffer.m_readPos;
                continue;
              }

              user->buffer.addToReadBegin(buffer, written);
              packetLen.val = written;
              varint_len = 0;
              delete[] inbuffer;
              delete[] buffer;

              // Replace the current packet length
              user->packetLen = (int32_t)uncompressed_size;
            }
          }
          MS_VarInt action;
          user->buffer >> (MS_VarInt&)action;
          user->action = (uint8_t)static_cast<int64_t>(action);

          if (ServerInstance->packetHandler()->packets[user->gameState][user->action].len == PACKET_DOES_NOT_EXIST) {
            std::ostringstream str;
            str << "Unknown packet: 0x" << std::hex << (unsigned int)(user->action);
            LOG2(DEBUG, str.str());
            user->buffer.removePacketLen((uint32_t)packetLen+varint_len);

            buffer_pos = user->buffer.m_readPos;
            continue;
          }
          user->packetsPerSecond++;
  #ifdef DEBUG
          printf("Packet from %s, state = 0x%hx, id = 0x%hx \n", user->nick.c_str(), user->gameState, user->action);
  #endif

          //Call specific function
          ServerInstance->packetHandler()->packets[user->gameState][user->action].function(user);

          const bool disconnecting = user->action == 0xFF;

          if (disconnecting) // disconnect -- player gone
          {
            if (user->nick.size()) {
              LOG2(INFO, "User " + user->nick + " disconnected normally");
            }
            delete user;
            return;
          }
        }
        user->buffer.removePacketLen((uint32_t)packetLen+varint_len);
        buffer_pos = user->buffer.m_readPos;
      } // while(user->buffer)
    } catch (std::logic_error &e)
    {
      user->waitForData = true;
      event_add(user->getReadEvent(), NULL);
    }
  } //End reading

  //Write data to user socket
  if(!client_write(user))
  {
    return;
  }

  //Add EV_READ event again
  event_add(user->getReadEvent(), NULL);
}

extern "C" void accept_callback(int fd, short ev, void* arg)
{
  sockaddr_in client_addr;
  socklen_t client_len = sizeof(client_addr);

  const int client_fd = accept(fd, reinterpret_cast<sockaddr*>(&client_addr), &client_len);

  if (client_fd < 0)
  {
    LOGLF("Client: accept() failed");
    return;
  }

  User* const client = new User(client_fd, Mineserver::generateEID());
  setnonblock(client_fd);

  //Keep delay minimum: more (smaller) packets -> less lag
  int one = 1;
  setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&one, sizeof(int));

  client->setReadEvent(event_new(ServerInstance->getEventBase(), client_fd, EV_READ, client_callback, client));
  client->setWriteEvent(event_new(ServerInstance->getEventBase(), client_fd, EV_WRITE, client_callback, client));
  event_add(client->getReadEvent(), NULL);
}

//Opens a socket and connects to the host/port
int socket_connect(char* host, int port)
{
  struct hostent* hp;
  struct sockaddr_in addr;
  int on = 1, sock;

  if ((hp = gethostbyname(host)) == NULL)
  {
    return 0;
  }

  memmove(&addr.sin_addr, hp->h_addr, hp->h_length);
  addr.sin_port = htons(port);
  addr.sin_family = AF_INET;
  sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

  //Set 5s timeout
  struct timeval tv;
  tv.tv_sec = 5;
  setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(struct timeval));
  setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(struct timeval));
  setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(int));

  if (sock == -1 || connect(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) == -1)
  {
    return 0;
  }

  return sock;
}
<commit_msg>Move all user deletions to timed function (again) since it's still causing issues  - Sometimes when deleting the user in libevent read cb, the write cb is called after deletion causing problems<commit_after>/*
   Copyright (c) 2013, The Mineserver Project
   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 The Mineserver 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 THE COPYRIGHT HOLDER 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.
*/

#ifdef _WIN32
#include <cstdlib>
#include <winsock2.h>
typedef int socklen_t;
#else
#include <netdb.h>   // for gethostbyname()
#include <netinet/tcp.h> // for TCP constants
#endif

#include <string>

#include <sys/stat.h>
#include <fstream>

#include <cerrno>
#include <sstream>
#include <algorithm>

#include <array>

#include "sockets.h"
#include "tools.h"
#include "logger.h"
#include "constants.h"
#include "user.h"
#include "map.h"
#include "chat.h"
#include "nbt.h"
#include "mineserver.h"
#include "packets.h"
#include "config.h"
#include "extern.h"

extern int setnonblock(int fd);

#ifndef WIN32
#define SOCKET_ERROR -1
#endif

static const size_t BUFSIZE = 2048;
static std::array<uint8_t, BUFSIZE> BUF;
static std::array<uint8_t, BUFSIZE> BUFCRYPT;
static char* const cpBUF = reinterpret_cast<char*>(BUF.data());
static uint8_t* const upBUF = BUF.data();

static char* cpBUFCRYPT = reinterpret_cast<char*>(BUFCRYPT.data());

bool client_write(User *user)
{
  if (user->deleting) return false;
  //If there is data in the output buffer, crypt it before writing
  if (!user->buffer.getWriteEmpty())
  {
    std::vector<char> buf;

    user->buffer.getWriteData(buf);

    //More glue - Fador
    if(user->crypted)
    {
      //We might have to write some data uncrypted ToDo: fix
      if(user->uncryptedLeft)
      {
        user->bufferCrypted.addToWrite((uint8_t *)buf.data(),user->uncryptedLeft);
      }
      int p_len = buf.size()-user->uncryptedLeft, f_len = 0;
      if(p_len)
      {
        uint8_t *buffer = (uint8_t *)malloc(p_len+1);
        EVP_EncryptUpdate(&user->en, (uint8_t *)buffer, &p_len, (const uint8_t *)buf.data()+user->uncryptedLeft, buf.size()-user->uncryptedLeft);
        int written = p_len + f_len;
        user->bufferCrypted.addToWrite((uint8_t *)buffer,written);
        free(buffer);
      }
      user->uncryptedLeft = 0;
    }
    else
    {
      user->bufferCrypted.addToWrite((uint8_t *)buf.data(),buf.size());
      user->uncryptedLeft = 0;
    }

    //free(outBuf);
    user->buffer.clearWrite(buf.size());
  }

  //We have crypted data ready to be written
  if(!user->bufferCrypted.getWriteEmpty())
  {
    std::vector<char> buf;
    user->bufferCrypted.getWriteData(buf);

    //Try to write the whole buffer
    const int written = send(user->fd, buf.data(), buf.size(), 0);

    //Handle errors
    if (written == SOCKET_ERROR)
    {
    #ifdef WIN32
    #define ERROR_NUMBER WSAGetLastError()
      if ((ERROR_NUMBER != WSATRY_AGAIN && ERROR_NUMBER != WSAEINTR && ERROR_NUMBER != WSAEWOULDBLOCK))
    #else
    #define ERROR_NUMBER errno
      if ((errno != EAGAIN && errno != EINTR))
    #endif
      {
        LOG2(ERROR, "Error writing to client, tried to write " + dtos(buf.size()) + " bytes, code: " + dtos(ERROR_NUMBER));
        user->deleting = true;
        ServerInstance->usersToRemove().insert(user);
        return false;
      }
    }
    else
    {
      //Remove written amount from the buffer
      user->bufferCrypted.clearWrite(written);
    }

    //If we couldn't write everything at once, add EV_WRITE event calling this function again..
    if (!user->bufferCrypted.getWriteEmpty())
    {
      event_add(user->getWriteEvent(), NULL);
      return false;
    }
  }
  return true;
}

extern "C" void client_callback(int fd, short ev, void* arg)
{
  User* user = reinterpret_cast<User*>(arg);
  if (user->deleting) return;

  if (ev & EV_READ)
  {
    int read = 1;

    //Data must be decrypted if we are in crypted mode
    if(user->crypted)
    {
      read = recv(fd, cpBUFCRYPT, BUFSIZE, 0);
      int p_len = read, f_len = 0;
      EVP_DecryptUpdate(&user->de, upBUF, &p_len, (const uint8_t *)cpBUFCRYPT, read);
      read = p_len + f_len;
    }
    else
    {
      read = recv(fd, cpBUF, BUFSIZE, 0);
    }

    if (read == 0)
    {
      if(user->nick.size())
      {
        LOG2(INFO, "User "+ user->nick + " disconnected by closing socket");
      }
      else
      {
        LOG2(INFO, "Socket closed");
      }
      user->deleting = true;
      ServerInstance->usersToRemove().insert(user);
      return;
    }

    if (read == SOCKET_ERROR)
    {
      LOG2(INFO, "Socket error");
      user->deleting = true;
      ServerInstance->usersToRemove().insert(user);
      return;
    }

    //Keep track on incoming data, can timeout inactive users
    user->lastData = std::time(NULL);

    user->buffer.addToRead(upBUF, read);
    user->buffer.reset();
    MS_VarInt packetLen;
    uint32_t varint_len;
    uint32_t buffer_pos = 0;

    // Handle exceptions caused by varint reading
    try
    {
      while (user->buffer >> (MS_VarInt&)packetLen)
      {
        varint_len = user->buffer.m_readPos-buffer_pos;

        // Check if we have the data in buffer
        if (!user->buffer.haveData(static_cast<int64_t>(packetLen)))
        {
          user->waitForData = true;
          event_add(user->getReadEvent(), NULL);
          return;
        }
        // Packet data has been received, call the function
        else
        {
          // Store packetlen to be used in the parsing functions (replaced later if compression used..)
          user->packetLen = (int32_t)packetLen;
          // Handle compressed incoming data
          if (user->compression)
          {
            MS_VarInt uncompressed_size;
            int32_t cur_pos = user->buffer.m_readPos;
            user->buffer >> (MS_VarInt&)uncompressed_size;

            // Hopefully a rare occasion when the packet size exceeds the compression threshold
            if (uncompressed_size != 0)
            {
              uLongf written = uncompressed_size;
              uLong sourceLen = (int)packetLen.val-(user->buffer.m_readPos-cur_pos);
              uint8_t* inbuffer = new uint8_t[sourceLen];
              uint8_t* buffer = new uint8_t[uncompressed_size+1];

              user->buffer.getData(inbuffer,sourceLen);

              user->buffer.removePacketLen((uint32_t)packetLen+varint_len);
              int ret = uncompress(buffer, &written, inbuffer, sourceLen);
              if (ret < Z_OK || written != uncompressed_size) {
                LOG2(DEBUG, "Uncompress error");
                buffer_pos = user->buffer.m_readPos;
                continue;
              }

              user->buffer.addToReadBegin(buffer, written);
              packetLen.val = written;
              varint_len = 0;
              delete[] inbuffer;
              delete[] buffer;

              // Replace the current packet length
              user->packetLen = (int32_t)uncompressed_size;
            }
          }
          MS_VarInt action;
          user->buffer >> (MS_VarInt&)action;
          user->action = (uint8_t)static_cast<int64_t>(action);

          if (ServerInstance->packetHandler()->packets[user->gameState][user->action].len == PACKET_DOES_NOT_EXIST) {
            std::ostringstream str;
            str << "Unknown packet: 0x" << std::hex << (unsigned int)(user->action);
            LOG2(DEBUG, str.str());
            user->buffer.removePacketLen((uint32_t)packetLen+varint_len);

            buffer_pos = user->buffer.m_readPos;
            continue;
          }
          user->packetsPerSecond++;
  #ifdef DEBUG
          printf("Packet from %s, state = 0x%hx, id = 0x%hx \n", user->nick.c_str(), user->gameState, user->action);
  #endif

          //Call specific function
          ServerInstance->packetHandler()->packets[user->gameState][user->action].function(user);

          const bool disconnecting = user->action == 0xFF;

          if (disconnecting) // disconnect -- player gone
          {
            if (user->nick.size()) {
              LOG2(INFO, "User " + user->nick + " disconnected normally");
            }
            user->deleting = true;
            ServerInstance->usersToRemove().insert(user);
            return;
          }
        }
        user->buffer.removePacketLen((uint32_t)packetLen+varint_len);
        buffer_pos = user->buffer.m_readPos;
      } // while(user->buffer)
    } catch (std::logic_error &e)
    {
      user->waitForData = true;
      event_add(user->getReadEvent(), NULL);
    }
  } //End reading

  //Write data to user socket
  if(!client_write(user))
  {
    return;
  }

  //Add EV_READ event again
  event_add(user->getReadEvent(), NULL);
}

extern "C" void accept_callback(int fd, short ev, void* arg)
{
  sockaddr_in client_addr;
  socklen_t client_len = sizeof(client_addr);

  const int client_fd = accept(fd, reinterpret_cast<sockaddr*>(&client_addr), &client_len);

  if (client_fd < 0)
  {
    LOGLF("Client: accept() failed");
    return;
  }

  User* const client = new User(client_fd, Mineserver::generateEID());
  setnonblock(client_fd);

  //Keep delay minimum: more (smaller) packets -> less lag
  int one = 1;
  setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&one, sizeof(int));

  client->setReadEvent(event_new(ServerInstance->getEventBase(), client_fd, EV_READ, client_callback, client));
  client->setWriteEvent(event_new(ServerInstance->getEventBase(), client_fd, EV_WRITE, client_callback, client));
  event_add(client->getReadEvent(), NULL);
}

//Opens a socket and connects to the host/port
int socket_connect(char* host, int port)
{
  struct hostent* hp;
  struct sockaddr_in addr;
  int on = 1, sock;

  if ((hp = gethostbyname(host)) == NULL)
  {
    return 0;
  }

  memmove(&addr.sin_addr, hp->h_addr, hp->h_length);
  addr.sin_port = htons(port);
  addr.sin_family = AF_INET;
  sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

  //Set 5s timeout
  struct timeval tv;
  tv.tv_sec = 5;
  setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(struct timeval));
  setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(struct timeval));
  setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(int));

  if (sock == -1 || connect(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) == -1)
  {
    return 0;
  }

  return sock;
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Andrew Sutton
// All rights reserved

#ifndef LINGO_TOKEN_HPP
#define LINGO_TOKEN_HPP

#include "lingo/location.hpp"
#include "lingo/buffer.hpp"
#include "lingo/error.hpp"
#include "lingo/symbol.hpp"
#include "lingo/string.hpp"
#include "lingo/integer.hpp"
#include "lingo/print.hpp"
#include "lingo/debug.hpp"

#include <cstdint>
#include <vector>
#include <tuple>

namespace lingo
{

struct Symbol;

// -------------------------------------------------------------------------- //
//                              Management

void install_token(int, char const*, char const*);

char const* get_token_name(int);
char const* get_token_spelling(int);

// -------------------------------------------------------------------------- //
//                            Token class

// The Token class represents the occurrence of a lexeme  within a
// source file. It associates the class of the the lexeme with its
// associated value (if any) and its location in the source file.
//
// Each token indexes an entry in the symbol table, which stores
// additional attributes associated with the token  (e.g. scope
// bindings, numeric interpretation of values, etc.).
//
// Note that -1 is reserved as a special token kind, indicating an
// error.
class Token
{
public:
  // Construct an error token.
  Token()
    : loc_(), sym_(nullptr)
  { }

  Token(Location, char const*, int);
  Token(Location, char const*, char const*);
  Token(Location, int, char const*, int);
  Token(Location, int, char const*, char const*);
  Token(Location, Symbol&);

  explicit operator bool() const { return sym_ && kind() != unknown_tok; }

  // Observers
  char const* token_name() const { return get_token_name(kind()); }

  // Source location and span.
  Location  location() const { return loc_; }
  Span      span() const;

  int kind() const { return sym_->kind; }

  // Symbol/text representation
  Symbol const& symbol() const { return *sym_; }
  String const* str() const    { return &sym_->str; }

private:
  Location   loc_;
  Symbol*    sym_;
};

// Returns the span of the token.
inline Span
Token::span() const
{
  Location start = location();
  Location end(start.offset() + str()->size());
  return {start, end};
}

// A list of tokens.
using Token_list = std::vector<Token>;

// -------------------------------------------------------------------------- //
//                              Printing

void print(Printer&, Token const&);
void debug(Printer&, Token const&);

std::ostream& operator<<(std::ostream&, Token const&);

// -------------------------------------------------------------------------- //
//                              Token stream

// A token stream provides a sequence of tokens and has a very
// simple streaming interface consisting of only 5 functions:
// peek(), get(), and eof(), begin(), and end(). Character streams
// are the input to lexical analyzers.
class Token_stream
{
public:
  using value_type = Token;

  // Construct a token stream over a non-empty range of
  // token pointers. Behavior is undefind if f == l.
  Token_stream(Token const* f, Token const* l)
    : first_(f), last_(l)
  {
    lingo_assert(first_ != last_);
  }

  Token_stream(Token_list const& toks)
    : Token_stream(toks.data(), toks.data() + toks.size())
  { }

  // Stream control
  bool eof() const { return first_ == last_; }
  Token const& peek() const;
  Token const& peek(int) const;
  Token const& get();
  Token const& last() { return *(last_ - 1); }
  Token const& last() const { return *(last_ - 1); }

  // Iterators
  Token const* begin()       { return first_; }
  Token const* begin() const { return first_; }
  Token const* end()       { return last_; }
  Token const* end() const { return last_; }

  // Returns the source location of the the current token.
  Location location() const { return eof() ? Location{} : peek().location(); }

  // Returns the last source location for a token in the buffer.
  Location last_location() const { return last().span().end(); }

  Token const* first_; // Current character pointer
  Token const* last_;  // Past the end of the character buffer
};

// Debug print a token string.
inline void
debug(Printer& p, Token_stream const& toks)
{
  for (const Token& tok : toks) {
    debug(p, tok);
    print(p, " ");
  }
}

} // namespace lingo

#endif
<commit_msg>Allow empty token streams.<commit_after>// Copyright (c) 2015 Andrew Sutton
// All rights reserved

#ifndef LINGO_TOKEN_HPP
#define LINGO_TOKEN_HPP

#include "lingo/location.hpp"
#include "lingo/buffer.hpp"
#include "lingo/error.hpp"
#include "lingo/symbol.hpp"
#include "lingo/string.hpp"
#include "lingo/integer.hpp"
#include "lingo/print.hpp"
#include "lingo/debug.hpp"

#include <cstdint>
#include <vector>
#include <tuple>

namespace lingo
{

struct Symbol;

// -------------------------------------------------------------------------- //
//                              Management

void install_token(int, char const*, char const*);

char const* get_token_name(int);
char const* get_token_spelling(int);

// -------------------------------------------------------------------------- //
//                            Token class

// The Token class represents the occurrence of a lexeme  within a
// source file. It associates the class of the the lexeme with its
// associated value (if any) and its location in the source file.
//
// Each token indexes an entry in the symbol table, which stores
// additional attributes associated with the token  (e.g. scope
// bindings, numeric interpretation of values, etc.).
//
// Note that -1 is reserved as a special token kind, indicating an
// error.
class Token
{
public:
  // Construct an error token.
  Token()
    : loc_(), sym_(nullptr)
  { }

  Token(Location, char const*, int);
  Token(Location, char const*, char const*);
  Token(Location, int, char const*, int);
  Token(Location, int, char const*, char const*);
  Token(Location, Symbol&);

  explicit operator bool() const { return sym_ && kind() != unknown_tok; }

  // Observers
  char const* token_name() const { return get_token_name(kind()); }

  // Source location and span.
  Location  location() const { return loc_; }
  Span      span() const;

  int kind() const { return sym_->kind; }

  // Symbol/text representation
  Symbol const& symbol() const { return *sym_; }
  String const* str() const    { return &sym_->str; }

private:
  Location   loc_;
  Symbol*    sym_;
};

// Returns the span of the token.
inline Span
Token::span() const
{
  Location start = location();
  Location end(start.offset() + str()->size());
  return {start, end};
}

// A list of tokens.
using Token_list = std::vector<Token>;

// -------------------------------------------------------------------------- //
//                              Printing

void print(Printer&, Token const&);
void debug(Printer&, Token const&);

std::ostream& operator<<(std::ostream&, Token const&);

// -------------------------------------------------------------------------- //
//                              Token stream

// A token stream provides a sequence of tokens and has a very
// simple streaming interface consisting of only 5 functions:
// peek(), get(), and eof(), begin(), and end(). Character streams
// are the input to lexical analyzers.
class Token_stream
{
public:
  using value_type = Token;

  // Construct a token stream over a non-empty range of token pointers.
  Token_stream(Token const* f, Token const* l)
    : first_(f), last_(l)
  { }

  Token_stream(Token_list const& toks)
    : Token_stream(toks.data(), toks.data() + toks.size())
  { }

  // Stream control
  bool eof() const { return first_ == last_; }
  Token const& peek() const;
  Token const& peek(int) const;
  Token const& get();
  Token const& last() { return *(last_ - 1); }
  Token const& last() const { return *(last_ - 1); }

  // Iterators
  Token const* begin()       { return first_; }
  Token const* begin() const { return first_; }
  Token const* end()       { return last_; }
  Token const* end() const { return last_; }

  // Returns the source location of the the current token.
  Location location() const { return eof() ? Location{} : peek().location(); }

  // Returns the last source location for a token in the buffer.
  Location last_location() const { return last().span().end(); }

  Token const* first_; // Current character pointer
  Token const* last_;  // Past the end of the character buffer
};

// Debug print a token string.
inline void
debug(Printer& p, Token_stream const& toks)
{
  for (const Token& tok : toks) {
    debug(p, tok);
    print(p, " ");
  }
}

} // namespace lingo

#endif
<|endoftext|>


<commit_before>#include "sfm.h"

using namespace cv;

void StructureFromMotion::newFrame(Mat frame_in)
{
  // this method is to be overwritten by the derived class
  // it is supposed to perform logic based on the frame id to determine if
  // the current frame is a keyframe
  std::cout << "base class method needs to be overwritten!" << std::endl;
}

void StructureFromMotion::featureTracker(Mat frame_in)
{
  // convert to intensity image
  Mat frame_gray;
  cvtColor(frame_in, frame_gray, CV_BGR2GRAY);

  if (features_cur.size()==0)
  {
    // no features have been initialized
    // populate the feature vector

    int max_points = 1000;
    double quality = 0.01;
    double min_dist = 10;
    Mat mask;
    int blockSize = 3;
    bool useHarris = false;
    double k = 0.04;
    std::cout << "here" << std::endl;
    goodFeaturesToTrack(frame_gray, features_cur, max_points, quality, min_dist, mask, blockSize, useHarris, k);

    drawFeatures(frame_in, features_cur);

    // remember the last iteration's frame
    frame_gray_old = frame_gray.clone();

    // REMEMBER TO PUSH BACK ON features_all !!!!!!!!!!!!!!!!!!!!!!!!
  }
  else
  {
    // features have been initialized, perform tracking, remove outliers (from all feature sets of all idx)
    // look into the "keep" command with masking

    // template matching
    int dim_win = 81;
    int dim_tem = 31;
    // delcare a features_new
    // rename features_cur to old
    std::vector<Point2f> features_new;
    for (int k=0; k<features_cur.size(); k++)
    {
      std::cout << "---" << std::endl;
      std::cout << features_cur[k] << std::endl;
      // create a window around features_cur[k], which is Point2f
      int x1 = features_cur[k].x - dim_win/2;
      if (x1 < 0)
      {
        x1 = 0;
      }
      if ((x1+dim_win)>frame_in.cols)
      {
        x1 = frame_in.cols - dim_win;
      }
      int y1 = features_cur[k].y - dim_win/2;
      if (y1 < 0)
      {
        y1 = 0;
      }
      if ((y1+dim_win)>frame_in.rows)
      {
        y1 = frame_in.rows - dim_win;
      }
      // window established in next image
      Mat win = frame_gray(Rect(x1, y1, dim_win, dim_win));

      // create a template around features_cur[k], which is Point2f
      int x2 = features_cur[k].x - dim_tem/2;
      if (x2 < 0)
      {
        x2 = 0;
        features_cur[k].x = x2 + dim_tem/2;
      }
      if ((x2+dim_tem)>frame_in.cols)
      {
        x2 = frame_in.cols - dim_tem;
        features_cur[k].x = x2 + dim_tem/2;
      }
      int y2 = features_cur[k].y - dim_tem/2;
      if (y2 < 0)
      {
        y2 = 0;
        features_cur[k].y = y2 + dim_tem/2;
      }
      if ((y2+dim_tem)>frame_in.rows)
      {
        y2 = frame_in.rows - dim_tem;
        features_cur[k].y = y2 + dim_tem/2;
      }

      // window established in next image
      Mat tem = frame_gray_old(Rect(x2, y2, dim_tem, dim_tem));

      // match the current template of image 1 to window of image 2
      Mat output;
      matchTemplate(win, tem, output, TM_CCORR_NORMED);
      std::cout << output.size() << std::endl;

      // normalize the intensity output
      normalize(output, output, 0, 1, NORM_MINMAX,  -1, Mat());

      // locate the position of highest correlation
      Point max_point;
      minMaxLoc(output, 0, 0, 0, &max_point, Mat());
      std::cout << max_point << std::endl;

      // represent the max point in original image coordinates
      max_point.x = max_point.x + dim_tem/2 + x1;
      max_point.y = max_point.y + dim_tem/2 + y1;
      std::cout << max_point << std::endl;
      features_new.push_back(max_point);

    } // end of looping through features

    drawFeatures(frame_in, features_new);
    features_cur.clear();
    features_cur = features_new;

    frame_gray_old = frame_gray.clone();

    // REMEMBER TO PUSH BACK ON features_all !!!!!!!!!!!!!!!!!!!!!!!!

  } // end of if check

} // end of method

void StructureFromMotion::drawFeatures(Mat img, std::vector<Point2f>& features)
{
  for (int i=0; i < features.size(); i++)
  {
  circle(img, features[i], 2, Scalar(255, 0, 0), 2);
  }
}
<commit_msg>cleanup<commit_after>#include "sfm.h"

using namespace cv;

void StructureFromMotion::newFrame(Mat frame_in)
{
  // this method is to be overwritten by the derived class
  // it is supposed to perform logic based on the frame id to determine if
  // the current frame is a keyframe
  std::cout << "base class method needs to be overwritten!" << std::endl;
}

void StructureFromMotion::featureTracker(Mat frame_in)
{
  // convert to intensity image
  Mat frame_gray;
  cvtColor(frame_in, frame_gray, CV_BGR2GRAY);

  if (features_cur.size()==0)
  {
    // no features have been initialized
    // populate the feature vector

    int max_points = 1000;
    double quality = 0.01;
    double min_dist = 10;
    Mat mask;
    int blockSize = 3;
    bool useHarris = false;
    double k = 0.04;
    goodFeaturesToTrack(frame_gray, features_cur, max_points, quality, min_dist, mask, blockSize, useHarris, k);

    drawFeatures(frame_in, features_cur);

    // remember the last iteration's frame
    frame_gray_old = frame_gray.clone();

    // REMEMBER TO PUSH BACK ON features_all !!!!!!!!!!!!!!!!!!!!!!!!
  }
  else
  {
    // features have been initialized, perform tracking, remove outliers (from all feature sets of all idx)
    // look into the "keep" command with masking

    // template matching
    int dim_win = 81;
    int dim_tem = 31;

    // rename features_cur to old
    std::vector<Point2f> features_new;
    for (int i=0; i<features_cur.size(); i++)
    {

      // create a window around from current image for the current feature
      int x1 = features_cur[i].x - (dim_win-1)/2;
      if (x1 < 0)
      {
        x1 = 0;
      }
      if ((x1+dim_win)>frame_in.cols)
      {
        x1 = frame_in.cols - dim_win;
      }
      int y1 = features_cur[i].y - (dim_win-1)/2;
      if (y1 < 0)
      {
        y1 = 0;
      }
      if ((y1+dim_win)>frame_in.rows)
      {
        y1 = frame_in.rows - dim_win;
      }
      // window established in next image
      Mat win = frame_gray(Rect(x1, y1, dim_win, dim_win));

      // create a template around features_cur[i], which is Point2f
      int x2 = features_cur[i].x - (dim_tem-1)/2;
      if (x2 < 0)
      {
        x2 = 0;
      }
      if ((x2+dim_tem)>frame_in.cols)
      {
        x2 = frame_in.cols - dim_tem;
      }
      int y2 = features_cur[i].y - (dim_tem-1)/2;
      if (y2 < 0)
      {
        y2 = 0;
      }
      if ((y2+dim_tem)>frame_in.rows)
      {
        y2 = frame_in.rows - dim_tem;
      }

      // window established in next image
      Mat tem = frame_gray_old(Rect(x2, y2, dim_tem, dim_tem));

      // match the current template of image 1 to window of image 2
      Mat output;
      matchTemplate(win, tem, output, TM_CCORR_NORMED);

      // normalize the intensity output
      normalize(output, output, 0, 1, NORM_MINMAX,  -1, Mat());

      // locate the position of highest correlation
      Point max_point;
      minMaxLoc(output, 0, 0, 0, &max_point, Mat());

      // represent the max point in original image coordinates
      max_point.x = max_point.x + dim_tem/2 + x1;
      max_point.y = max_point.y + dim_tem/2 + y1;

      features_new.push_back(max_point);

    } // end of looping through features

    drawFeatures(frame_in, features_new);
    features_cur.clear();
    features_cur = features_new;

    frame_gray_old = frame_gray.clone();

    // REMEMBER TO PUSH BACK ON features_all !!!!!!!!!!!!!!!!!!!!!!!!

  } // end of if check

} // end of method

void StructureFromMotion::drawFeatures(Mat img, std::vector<Point2f>& features)
{
  for (int i=0; i < features.size(); i++)
  {
  circle(img, features[i], 2, Scalar(255, 0, 0), 2);
  }
}
<|endoftext|>


<commit_before>extern "C" {
#include <getopt.h>
}
#include <fstream>
#include <iostream>
#include <iterator>
#include "incline.h"

using namespace std;

static void usage(int argc, char** argv)
{
  printf("Usage: %s --driver=d command\n", argv[0]);
  exit(0);
}

int
main(int argc, char** argv)
{
  string driver_name, source_file, command;
  picojson::value defs;

  { // parse commond
    static struct option longopts[] = {
      { "driver", required_argument, NULL, 'd' },
      { "source", required_argument, NULL, 's' },
      { "help",   no_argument,       NULL, 'h' },
      { NULL,     0,                 NULL, 0 },
    };
    int ch;
    while ((ch = getopt_long(argc, argv, "d:h", longopts, NULL)) != -1) {
      switch (ch) {
      case 'd':
	driver_name = optarg;
	break;
      case 's':
	source_file = optarg;
	break;
      case 'h':
	usage(argc, argv);
	break;
      default:
	exit(1);
	break;
      }
    }
    argc -= optind;
    argv += optind;
    if (argc == 0) {
      fprintf(stderr, "no command\n");
      exit(1);
    }
    command = *argv++;
    argc--;
    if (driver_name.empty()) {
      fprintf(stderr, "--driver not set\n");
      exit(1);
    }
    if (source_file.empty()) {
      fprintf(stderr, "--source not set\n");
      exit(1);
    }
    { // parse source
      string err;
      if (source_file == "-") {
	err = picojson::parse(defs, cin);
      } else {
	ifstream fin;
	fin.open(source_file.c_str(), ios::in);
	if (! fin.is_open()) {
	  fprintf(stderr, "failed to open file:%s\n", source_file.c_str());
	  exit(2);
	}
	err = picojson::parse(defs, fin);
	fin.close();
      }
      if (! err.empty()) {
	fprintf(stderr, "failed to parse: %s, %s\n", source_file.c_str(),
		err.c_str());
	exit(2);
      }
    }
  }

  // create driver
  incline_driver* driver;
  if (driver_name == "standalone") {
    driver = new incline_driver_standalone();
  } else if (driver_name == "async_qtable") {
    driver = new incline_driver_async_qtable();
  } else {
    fprintf(stderr, "unknown driver: %s\n", driver_name.c_str());
    exit(1);
  }

  // create manager and feed the settings
  incline_mgr mgr(driver);
  string err = mgr.parse(defs);
  if (! err.empty()) {
    fprintf(stderr, "failed to parse: %s, %s\n", source_file.c_str(),
	    err.c_str());
    exit(2);
  }

  // handel the commond
  if (command == "print-trigger") {
    vector<string> stmt(mgr.create_trigger_all(true));
    printf("DELIMITER |\n");
    fputs(incline_util::join("|\n", stmt.begin(), stmt.end()).c_str(),
	  stdout);
    printf("\nDELIMTER ;\n");
  } else {
    fprintf(stderr, "unknown command: %s\n", command.c_str());
    exit(1);
  }

  return 0;
}
<commit_msg>fix typo<commit_after>extern "C" {
#include <getopt.h>
}
#include <fstream>
#include <iostream>
#include <iterator>
#include "incline.h"

using namespace std;

static void usage(int argc, char** argv)
{
  printf("Usage: %s --driver=d command\n", argv[0]);
  exit(0);
}

int
main(int argc, char** argv)
{
  string driver_name, source_file, command;
  picojson::value defs;

  { // parse commond
    static struct option longopts[] = {
      { "driver", required_argument, NULL, 'd' },
      { "source", required_argument, NULL, 's' },
      { "help",   no_argument,       NULL, 'h' },
      { NULL,     0,                 NULL, 0 },
    };
    int ch;
    while ((ch = getopt_long(argc, argv, "d:h", longopts, NULL)) != -1) {
      switch (ch) {
      case 'd':
	driver_name = optarg;
	break;
      case 's':
	source_file = optarg;
	break;
      case 'h':
	usage(argc, argv);
	break;
      default:
	exit(1);
	break;
      }
    }
    argc -= optind;
    argv += optind;
    if (argc == 0) {
      fprintf(stderr, "no command\n");
      exit(1);
    }
    command = *argv++;
    argc--;
    if (driver_name.empty()) {
      fprintf(stderr, "--driver not set\n");
      exit(1);
    }
    if (source_file.empty()) {
      fprintf(stderr, "--source not set\n");
      exit(1);
    }
    { // parse source
      string err;
      if (source_file == "-") {
	err = picojson::parse(defs, cin);
      } else {
	ifstream fin;
	fin.open(source_file.c_str(), ios::in);
	if (! fin.is_open()) {
	  fprintf(stderr, "failed to open file:%s\n", source_file.c_str());
	  exit(2);
	}
	err = picojson::parse(defs, fin);
	fin.close();
      }
      if (! err.empty()) {
	fprintf(stderr, "failed to parse: %s, %s\n", source_file.c_str(),
		err.c_str());
	exit(2);
      }
    }
  }

  // create driver
  incline_driver* driver;
  if (driver_name == "standalone") {
    driver = new incline_driver_standalone();
  } else if (driver_name == "async_qtable") {
    driver = new incline_driver_async_qtable();
  } else {
    fprintf(stderr, "unknown driver: %s\n", driver_name.c_str());
    exit(1);
  }

  // create manager and feed the settings
  incline_mgr mgr(driver);
  string err = mgr.parse(defs);
  if (! err.empty()) {
    fprintf(stderr, "failed to parse: %s, %s\n", source_file.c_str(),
	    err.c_str());
    exit(2);
  }

  // handle the command
  if (command == "print-trigger") {
    vector<string> stmt(mgr.create_trigger_all(true));
    printf("DELIMITER |\n");
    fputs(incline_util::join("|\n", stmt.begin(), stmt.end()).c_str(),
	  stdout);
    printf("\nDELIMTER ;\n");
  } else {
    fprintf(stderr, "unknown command: %s\n", command.c_str());
    exit(1);
  }

  return 0;
}
<|endoftext|>


<commit_before>/*=========================================================================
 *
 *  Copyright UMC Utrecht and contributors
 *
 *  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.txt
 *
 *  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.
 *
 *=========================================================================*/
#include "itkTestHelper.h"
#include "itkGenericMultiResolutionPyramidImageFilter.h"

// GPU includes
#include "itkGPUImageFactory.h"
#include "itkGPURecursiveGaussianImageFilterFactory.h"
#include "itkGPUCastImageFilterFactory.h"
#include "itkGPUShrinkImageFilterFactory.h"
#include "itkGPUResampleImageFilterFactory.h"
#include "itkGPUIdentityTransformFactory.h"
#include "itkGPULinearInterpolateImageFunctionFactory.h"

#include "itkOpenCLContextScopeGuard.h"

// ITK include files
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkImageRegionConstIterator.h"
#include "itkMersenneTwisterRandomVariateGenerator.h"
#include "itkTimeProbe.h"

#include <iomanip> // setprecision, etc.
#include <random>  // For mt19937.

//------------------------------------------------------------------------------
// This test compares the CPU with the GPU version of the
// GenericMultiResolutionPyramidImageFilter.
// The filter takes an input image and produces an output image.
// We compare the CPU and GPU output image write RMSE and speed.

int
main(int argc, char * argv[])
{
  // Check arguments for help
  if (argc < 4)
  {
    std::cerr << "ERROR: insufficient command line arguments.\n"
              << "  inputFileName outputNameCPU outputNameGPU" << std::endl;
    return EXIT_FAILURE;
  }

  // Setup for debugging
  itk::SetupForDebugging();

  // Create and check OpenCL context
  if (!itk::CreateContext())
  {
    return EXIT_FAILURE;
  }
  const itk::OpenCLContextScopeGuard openCLContextScopeGuard{};

  /** Get the command line arguments. */
  const std::string  inputFileName = argv[1];
  const std::string  outputFileNameCPU = argv[2];
  const std::string  outputFileNameGPU = argv[3];
  const unsigned int numberOfLevels = 4;
  const bool         useMultiResolutionRescaleSchedule = true;
  const bool         useMultiResolutionSmoothingSchedule = true;
  const bool         useShrinkImageFilter = false;
  const bool         computeOnlyForCurrentLevel = true;
  const double       epsilon = 1e-3;
  const unsigned int runTimes = 5;

  std::cout << std::showpoint << std::setprecision(4);

  // Typedefs.
  const unsigned int Dimension = 3;
  using InputPixelType = float;
  using OutputPixelType = float;
  using InputImageType = itk::Image<InputPixelType, Dimension>;
  using OutputImageType = itk::Image<OutputPixelType, Dimension>;

  // CPU Typedefs
  using FilterType = itk::GenericMultiResolutionPyramidImageFilter<InputImageType, OutputImageType>;
  using ReaderType = itk::ImageFileReader<InputImageType>;

  // Reader
  auto reader = ReaderType::New();
  reader->SetFileName(inputFileName);
  reader->Update();

  // Construct the filter
  auto cpuFilter = FilterType::New();

  using RescaleScheduleType = FilterType::RescaleScheduleType;
  using SmoothingScheduleType = FilterType::SmoothingScheduleType;

  using RandomNumberGeneratorType = itk::Statistics::MersenneTwisterRandomVariateGenerator;
  RandomNumberGeneratorType::Pointer randomNum = RandomNumberGeneratorType::GetInstance();
  randomNum->SetSeed(std::mt19937::default_seed);

  RescaleScheduleType   rescaleSchedule(numberOfLevels, Dimension);
  SmoothingScheduleType smoothingSchedule(numberOfLevels, Dimension);
  double                tmp = 0.0;
  for (unsigned int i = 0; i < numberOfLevels; ++i)
  {
    for (unsigned int j = 0; j < Dimension; ++j)
    {
      tmp = randomNum->GetUniformVariate(0, 8);
      rescaleSchedule[i][j] = static_cast<unsigned int>(tmp);

      tmp = randomNum->GetUniformVariate(0, 4);
      smoothingSchedule[i][j] = tmp;
    }
  }

  cpuFilter->SetNumberOfLevels(numberOfLevels);
  cpuFilter->SetRescaleSchedule(rescaleSchedule);
  cpuFilter->SetSmoothingSchedule(smoothingSchedule);
  if (!useMultiResolutionSmoothingSchedule)
  {
    cpuFilter->SetRescaleScheduleToUnity();
  }
  if (!useMultiResolutionRescaleSchedule)
  {
    cpuFilter->SetSmoothingScheduleToZero();
  }
  cpuFilter->SetUseShrinkImageFilter(useShrinkImageFilter);
  cpuFilter->SetComputeOnlyForCurrentLevel(computeOnlyForCurrentLevel);

  std::cout << "RescaleSchedule:\n" << rescaleSchedule << "\n";
  std::cout << "SmoothingSchedule:\n" << smoothingSchedule << "\n";
  std::cout << "Testing the GenericMultiResolutionPyramidImageFilter, CPU vs GPU:\n";
  std::cout << "CPU/GPU #threads time speedup RMSE\n";
  // std::cout << "CPU/GPU factors sigmas #threads time speedup RMSE\n";

  // Time the filter, run on the CPU
  itk::TimeProbe cputimer;
  cputimer.Start();
  for (unsigned int i = 0; i < runTimes; ++i)
  {
    cpuFilter->SetInput(reader->GetOutput());

    if (!computeOnlyForCurrentLevel)
    {
      try
      {
        cpuFilter->Update();
      }
      catch (itk::ExceptionObject & e)
      {
        std::cerr << "ERROR: " << e << std::endl;
        return EXIT_FAILURE;
      }
      cpuFilter->Modified();
    }
    else
    {
      for (unsigned int j = 0; j < cpuFilter->GetNumberOfLevels(); ++j)
      {
        cpuFilter->SetCurrentLevel(j);
        try
        {
          cpuFilter->Update();
        }
        catch (itk::ExceptionObject & e)
        {
          std::cerr << "ERROR: " << e << std::endl;
          return EXIT_FAILURE;
        }
        // Modify the filter, only not the last iteration
        if (i != runTimes - 1)
        {
          cpuFilter->Modified();
        }
      }
    }
  }
  cputimer.Stop();

  std::cout << "CPU " << cpuFilter->GetNumberOfWorkUnits() << " " << cputimer.GetMean() / runTimes << std::endl;

  /** Write the CPU result. */
  try
  {
    itk::WriteImage(cpuFilter->GetOutput(numberOfLevels - 1), outputFileNameCPU);
  }
  catch (itk::ExceptionObject & e)
  {
    std::cerr << "ERROR: " << e << std::endl;
    return EXIT_FAILURE;
  }

  // Register object factory for GPU image and filter
  // All these filters that are constructed after this point are
  // turned into a GPU filter.
  // Note that we are not registering a
  // GPUGenericMultiResolutionPyramidImageFilter,
  // but the recursive one. We are simply using the original ITK implementation,
  // that internally uses the recursive filter. By registering the recursive
  // filter, we now automatically use it, even if it's usage is hidden by a
  // wrapper.
  using OCLImageTypes = typelist::MakeTypeList<float>::Type;
  itk::GPUImageFactory2<OCLImageTypes, OCLImageDims>::RegisterOneFactory();
  itk::GPURecursiveGaussianImageFilterFactory2<OCLImageTypes, OCLImageTypes, OCLImageDims>::RegisterOneFactory();
  itk::GPUCastImageFilterFactory2<OCLImageTypes, OCLImageTypes, OCLImageDims>::RegisterOneFactory();
  itk::GPUShrinkImageFilterFactory2<OCLImageTypes, OCLImageTypes, OCLImageDims>::RegisterOneFactory();
  itk::GPUResampleImageFilterFactory2<OCLImageTypes, OCLImageTypes, OCLImageDims>::RegisterOneFactory();
  itk::GPUIdentityTransformFactory2<OCLImageDims>::RegisterOneFactory();
  itk::GPULinearInterpolateImageFunctionFactory2<OCLImageTypes, OCLImageDims>::RegisterOneFactory();

  // Construct the filter
  // Use a try/catch, because construction of this filter will trigger
  // OpenCL compilation, which may fail.
  FilterType::Pointer gpuFilter;
  try
  {
    gpuFilter = FilterType::New();
    itk::ITKObjectEnableWarnings(gpuFilter.GetPointer());
  }
  catch (itk::ExceptionObject & e)
  {
    std::cerr << "ERROR: " << e << std::endl;
    return EXIT_FAILURE;
  }
  gpuFilter->SetNumberOfLevels(numberOfLevels);
  gpuFilter->SetRescaleSchedule(rescaleSchedule);
  gpuFilter->SetSmoothingSchedule(smoothingSchedule);
  if (!useMultiResolutionSmoothingSchedule)
  {
    gpuFilter->SetRescaleScheduleToUnity();
  }
  if (!useMultiResolutionRescaleSchedule)
  {
    gpuFilter->SetSmoothingScheduleToZero();
  }
  gpuFilter->SetUseShrinkImageFilter(useShrinkImageFilter);
  gpuFilter->SetComputeOnlyForCurrentLevel(computeOnlyForCurrentLevel);

  // Also need to re-construct the image reader, so that it now
  // reads a GPUImage instead of a normal image.
  // Otherwise, you will get an exception when running the GPU filter:
  // "ERROR: The GPU InputImage is NULL. Filter unable to perform."
  auto gpuReader = ReaderType::New();
  gpuReader->SetFileName(inputFileName);

  // \todo: If the following line is uncommented something goes wrong with
  // the ITK pipeline synchronization.
  // Something is still read in, but I guess it is not properly copied to
  // the GPU. The output of the shrink filter is then bogus.
  // The following line is however not needed in a pure CPU implementation.
  gpuReader->Update();

  // Time the filter, run on the GPU
  itk::TimeProbe gputimer;
  gputimer.Start();
  for (unsigned int i = 0; i < runTimes; ++i)
  {
    gpuFilter->SetInput(gpuReader->GetOutput());

    if (!computeOnlyForCurrentLevel)
    {
      try
      {
        gpuFilter->Update();
      }
      catch (itk::ExceptionObject & e)
      {
        std::cerr << "ERROR: " << e << std::endl;
        return EXIT_FAILURE;
      }
      // Modify the filter, only not the last iteration
      if (i != runTimes - 1)
      {
        gpuFilter->Modified();
      }
    }
    else
    {
      for (unsigned int j = 0; j < gpuFilter->GetNumberOfLevels(); ++j)
      {
        gpuFilter->SetCurrentLevel(j);
        try
        {
          gpuFilter->Update();
        }
        catch (itk::ExceptionObject & e)
        {
          std::cerr << "ERROR: " << e << std::endl;
          return EXIT_FAILURE;
        }
        // Modify the filter, only not the last iteration
        if (i != runTimes - 1)
        {
          gpuFilter->Modified();
        }
      }
    }
  }
  gputimer.Stop();

  std::cout << "GPU x " << gputimer.GetMean() / runTimes << " " << cputimer.GetMean() / gputimer.GetMean();

  /** Write the GPU result. */
  try
  {
    itk::WriteImage(gpuFilter->GetOutput(numberOfLevels - 1), outputFileNameGPU);
  }
  catch (itk::ExceptionObject & e)
  {
    std::cerr << "ERROR: " << e << std::endl;
    return EXIT_FAILURE;
  }

  // Compute RMSE
  double       RMSrelative = 0.0;
  const double RMSerror = itk::ComputeRMSE<double, OutputImageType, OutputImageType>(
    cpuFilter->GetOutput(numberOfLevels - 1), gpuFilter->GetOutput(numberOfLevels - 1), RMSrelative);
  std::cout << " " << RMSerror << std::endl;

  // Check
  if (RMSerror > epsilon)
  {
    std::cerr << "ERROR: RMSE between CPU and GPU result larger than expected" << std::endl;
    return EXIT_FAILURE;
  }

  // End program.
  return EXIT_SUCCESS;
} // end main()
<commit_msg>BUG: Make pyramid test call GPUResampler<commit_after>/*=========================================================================
 *
 *  Copyright UMC Utrecht and contributors
 *
 *  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.txt
 *
 *  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.
 *
 *=========================================================================*/
#include "itkTestHelper.h"
#include "itkGenericMultiResolutionPyramidImageFilter.h"

// GPU includes
#include "itkGPUImageFactory.h"
#include "itkGPURecursiveGaussianImageFilterFactory.h"
#include "itkGPUCastImageFilterFactory.h"
#include "itkGPUShrinkImageFilterFactory.h"
#include "itkGPUResampleImageFilterFactory.h"
#include "itkGPUIdentityTransformFactory.h"
#include "itkGPULinearInterpolateImageFunctionFactory.h"

#include "itkOpenCLContextScopeGuard.h"

// ITK include files
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkImageRegionConstIterator.h"
#include "itkMersenneTwisterRandomVariateGenerator.h"
#include "itkTimeProbe.h"

#include <iomanip> // setprecision, etc.
#include <random>  // For mt19937.

//------------------------------------------------------------------------------
// This test compares the CPU with the GPU version of the
// GenericMultiResolutionPyramidImageFilter.
// The filter takes an input image and produces an output image.
// We compare the CPU and GPU output image write RMSE and speed.

int
main(int argc, char * argv[])
{
  // Check arguments for help
  if (argc < 4)
  {
    std::cerr << "ERROR: insufficient command line arguments.\n"
              << "  inputFileName outputNameCPU outputNameGPU" << std::endl;
    return EXIT_FAILURE;
  }

  // Setup for debugging
  itk::SetupForDebugging();

  // Create and check OpenCL context
  if (!itk::CreateContext())
  {
    return EXIT_FAILURE;
  }
  const itk::OpenCLContextScopeGuard openCLContextScopeGuard{};

  /** Get the command line arguments. */
  const std::string  inputFileName = argv[1];
  const std::string  outputFileNameCPU = argv[2];
  const std::string  outputFileNameGPU = argv[3];
  const unsigned int numberOfLevels = 4;
  const bool         useMultiResolutionRescaleSchedule = true;
  const bool         useMultiResolutionSmoothingSchedule = true;
  const bool         useShrinkImageFilter = false;
  const bool         computeOnlyForCurrentLevel = true;
  const double       epsilon = 1e-3;
  const unsigned int runTimes = 5;

  std::cout << std::showpoint << std::setprecision(4);

  // Typedefs.
  const unsigned int Dimension = 3;
  using InputPixelType = float;
  using OutputPixelType = float;
  using InputImageType = itk::Image<InputPixelType, Dimension>;
  using OutputImageType = itk::Image<OutputPixelType, Dimension>;

  // CPU Typedefs
  using PrecisionType = float;
  using FilterType = itk::GenericMultiResolutionPyramidImageFilter<InputImageType, OutputImageType, PrecisionType>;
  using ReaderType = itk::ImageFileReader<InputImageType>;

  // Reader
  auto reader = ReaderType::New();
  reader->SetFileName(inputFileName);
  reader->Update();

  // Construct the filter
  auto cpuFilter = FilterType::New();

  using RescaleScheduleType = FilterType::RescaleScheduleType;
  using SmoothingScheduleType = FilterType::SmoothingScheduleType;

  using RandomNumberGeneratorType = itk::Statistics::MersenneTwisterRandomVariateGenerator;
  RandomNumberGeneratorType::Pointer randomNum = RandomNumberGeneratorType::GetInstance();
  randomNum->SetSeed(std::mt19937::default_seed);

  RescaleScheduleType   rescaleSchedule(numberOfLevels, Dimension);
  SmoothingScheduleType smoothingSchedule(numberOfLevels, Dimension);
  double                tmp = 0.0;
  for (unsigned int i = 0; i < numberOfLevels; ++i)
  {
    for (unsigned int j = 0; j < Dimension; ++j)
    {
      tmp = randomNum->GetUniformVariate(0, 8);
      rescaleSchedule[i][j] = static_cast<unsigned int>(tmp);

      tmp = randomNum->GetUniformVariate(0, 4);
      smoothingSchedule[i][j] = tmp;
    }
  }

  cpuFilter->SetNumberOfLevels(numberOfLevels);
  cpuFilter->SetRescaleSchedule(rescaleSchedule);
  cpuFilter->SetSmoothingSchedule(smoothingSchedule);
  if (!useMultiResolutionSmoothingSchedule)
  {
    cpuFilter->SetRescaleScheduleToUnity();
  }
  if (!useMultiResolutionRescaleSchedule)
  {
    cpuFilter->SetSmoothingScheduleToZero();
  }
  cpuFilter->SetUseShrinkImageFilter(useShrinkImageFilter);
  cpuFilter->SetComputeOnlyForCurrentLevel(computeOnlyForCurrentLevel);

  std::cout << "RescaleSchedule:\n" << rescaleSchedule << "\n";
  std::cout << "SmoothingSchedule:\n" << smoothingSchedule << "\n";
  std::cout << "Testing the GenericMultiResolutionPyramidImageFilter, CPU vs GPU:\n";
  std::cout << "CPU/GPU #threads time speedup RMSE\n";
  // std::cout << "CPU/GPU factors sigmas #threads time speedup RMSE\n";

  // Time the filter, run on the CPU
  itk::TimeProbe cputimer;
  cputimer.Start();
  for (unsigned int i = 0; i < runTimes; ++i)
  {
    cpuFilter->SetInput(reader->GetOutput());

    if (!computeOnlyForCurrentLevel)
    {
      try
      {
        cpuFilter->Update();
      }
      catch (itk::ExceptionObject & e)
      {
        std::cerr << "ERROR: " << e << std::endl;
        return EXIT_FAILURE;
      }
      cpuFilter->Modified();
    }
    else
    {
      for (unsigned int j = 0; j < cpuFilter->GetNumberOfLevels(); ++j)
      {
        cpuFilter->SetCurrentLevel(j);
        try
        {
          cpuFilter->Update();
        }
        catch (itk::ExceptionObject & e)
        {
          std::cerr << "ERROR: " << e << std::endl;
          return EXIT_FAILURE;
        }
        // Modify the filter, only not the last iteration
        if (i != runTimes - 1)
        {
          cpuFilter->Modified();
        }
      }
    }
  }
  cputimer.Stop();

  std::cout << "CPU " << cpuFilter->GetNumberOfWorkUnits() << " " << cputimer.GetMean() / runTimes << std::endl;

  /** Write the CPU result. */
  try
  {
    itk::WriteImage(cpuFilter->GetOutput(numberOfLevels - 1), outputFileNameCPU);
  }
  catch (itk::ExceptionObject & e)
  {
    std::cerr << "ERROR: " << e << std::endl;
    return EXIT_FAILURE;
  }

  // Register object factory for GPU image and filter
  // All these filters that are constructed after this point are
  // turned into a GPU filter.
  // Note that we are not registering a
  // GPUGenericMultiResolutionPyramidImageFilter,
  // but the recursive one. We are simply using the original ITK implementation,
  // that internally uses the recursive filter. By registering the recursive
  // filter, we now automatically use it, even if it's usage is hidden by a
  // wrapper.
  using OCLImageTypes = typelist::MakeTypeList<float>::Type;
  itk::GPUImageFactory2<OCLImageTypes, OCLImageDims>::RegisterOneFactory();
  itk::GPURecursiveGaussianImageFilterFactory2<OCLImageTypes, OCLImageTypes, OCLImageDims>::RegisterOneFactory();
  itk::GPUCastImageFilterFactory2<OCLImageTypes, OCLImageTypes, OCLImageDims>::RegisterOneFactory();
  itk::GPUShrinkImageFilterFactory2<OCLImageTypes, OCLImageTypes, OCLImageDims>::RegisterOneFactory();
  itk::GPUResampleImageFilterFactory2<OCLImageTypes, OCLImageTypes, OCLImageDims>::RegisterOneFactory();
  itk::GPUIdentityTransformFactory2<OCLImageDims>::RegisterOneFactory();
  itk::GPULinearInterpolateImageFunctionFactory2<OCLImageTypes, OCLImageDims>::RegisterOneFactory();

  // Construct the filter
  // Use a try/catch, because construction of this filter will trigger
  // OpenCL compilation, which may fail.
  FilterType::Pointer gpuFilter;
  try
  {
    gpuFilter = FilterType::New();
    itk::ITKObjectEnableWarnings(gpuFilter.GetPointer());
  }
  catch (itk::ExceptionObject & e)
  {
    std::cerr << "ERROR: " << e << std::endl;
    return EXIT_FAILURE;
  }
  gpuFilter->SetNumberOfLevels(numberOfLevels);
  gpuFilter->SetRescaleSchedule(rescaleSchedule);
  gpuFilter->SetSmoothingSchedule(smoothingSchedule);
  if (!useMultiResolutionSmoothingSchedule)
  {
    gpuFilter->SetRescaleScheduleToUnity();
  }
  if (!useMultiResolutionRescaleSchedule)
  {
    gpuFilter->SetSmoothingScheduleToZero();
  }
  gpuFilter->SetUseShrinkImageFilter(useShrinkImageFilter);
  gpuFilter->SetComputeOnlyForCurrentLevel(computeOnlyForCurrentLevel);

  // Also need to re-construct the image reader, so that it now
  // reads a GPUImage instead of a normal image.
  // Otherwise, you will get an exception when running the GPU filter:
  // "ERROR: The GPU InputImage is NULL. Filter unable to perform."
  auto gpuReader = ReaderType::New();
  gpuReader->SetFileName(inputFileName);

  // \todo: If the following line is uncommented something goes wrong with
  // the ITK pipeline synchronization.
  // Something is still read in, but I guess it is not properly copied to
  // the GPU. The output of the shrink filter is then bogus.
  // The following line is however not needed in a pure CPU implementation.
  gpuReader->Update();

  // Time the filter, run on the GPU
  itk::TimeProbe gputimer;
  gputimer.Start();
  for (unsigned int i = 0; i < runTimes; ++i)
  {
    gpuFilter->SetInput(gpuReader->GetOutput());

    if (!computeOnlyForCurrentLevel)
    {
      try
      {
        gpuFilter->Update();
      }
      catch (itk::ExceptionObject & e)
      {
        std::cerr << "ERROR: " << e << std::endl;
        return EXIT_FAILURE;
      }
      // Modify the filter, only not the last iteration
      if (i != runTimes - 1)
      {
        gpuFilter->Modified();
      }
    }
    else
    {
      for (unsigned int j = 0; j < gpuFilter->GetNumberOfLevels(); ++j)
      {
        gpuFilter->SetCurrentLevel(j);
        try
        {
          gpuFilter->Update();
        }
        catch (itk::ExceptionObject & e)
        {
          std::cerr << "ERROR: " << e << std::endl;
          return EXIT_FAILURE;
        }
        // Modify the filter, only not the last iteration
        if (i != runTimes - 1)
        {
          gpuFilter->Modified();
        }
      }
    }
  }
  gputimer.Stop();

  std::cout << "GPU x " << gputimer.GetMean() / runTimes << " " << cputimer.GetMean() / gputimer.GetMean();

  /** Write the GPU result. */
  try
  {
    itk::WriteImage(gpuFilter->GetOutput(numberOfLevels - 1), outputFileNameGPU);
  }
  catch (itk::ExceptionObject & e)
  {
    std::cerr << "ERROR: " << e << std::endl;
    return EXIT_FAILURE;
  }

  // Compute RMSE
  double       RMSrelative = 0.0;
  const double RMSerror = itk::ComputeRMSE<double, OutputImageType, OutputImageType>(
    cpuFilter->GetOutput(numberOfLevels - 1), gpuFilter->GetOutput(numberOfLevels - 1), RMSrelative);
  std::cout << " " << RMSerror << std::endl;

  // Check
  if (RMSerror > epsilon)
  {
    std::cerr << "ERROR: RMSE between CPU and GPU result larger than expected" << std::endl;
    return EXIT_FAILURE;
  }

  // End program.
  return EXIT_SUCCESS;
} // end main()
<|endoftext|>


<commit_before>/*

This file is part of VROOM.

Copyright (c) 2015-2018, Julien Coupey.
All rights reserved (see LICENSE).

*/

#include "problems/vrptw/vrptw.h"
#include "problems/vrptw/heuristics/best_insertion.h"
#include "problems/vrptw/heuristics/solomon.h"
#include "structures/vroom/input/input.h"
#include "structures/vroom/tw_route.h"
#include "utils/helpers.h"

using tw_solution = std::vector<tw_route>;

vrptw::vrptw(const input& input) : vrp(input) {
}

solution vrptw::solve(unsigned exploration_level, unsigned nb_threads) const {
  // tw_solution sol = best_insertion(_input);
  tw_solution sol = solomon(_input, 1);

  return format_solution(_input, sol);
}
<commit_msg>Lots of (multi-threaded) heuristic parameters.<commit_after>/*

This file is part of VROOM.

Copyright (c) 2015-2018, Julien Coupey.
All rights reserved (see LICENSE).

*/

#include <thread>

#include "problems/vrptw/heuristics/best_insertion.h"
#include "problems/vrptw/heuristics/solomon.h"
#include "problems/vrptw/vrptw.h"
#include "structures/vroom/input/input.h"
#include "structures/vroom/tw_route.h"
#include "utils/helpers.h"

using tw_solution = std::vector<tw_route>;

vrptw::vrptw(const input& input) : vrp(input) {
}

solution vrptw::solve(unsigned exploration_level, unsigned nb_threads) const {
  struct param {
    INIT_T init;
    float regret_coeff;
  };
  std::vector<param> parameters;

  std::vector<INIT_T> inits({INIT_T::NONE,
                             INIT_T::HIGHER_AMOUNT,
                             INIT_T::EARLIEST_DEADLINE,
                             INIT_T::FURTHEST});
  for (auto init : inits) {
    for (float lambda = 0; lambda <= 3.05; lambda += 0.1) {
      parameters.push_back({init, lambda});
    }
  }

  auto P = parameters.size();
  std::vector<tw_solution> tw_solutions(P);

  // Split the work among threads.
  std::vector<std::vector<std::size_t>>
    thread_ranks(nb_threads, std::vector<std::size_t>());
  for (std::size_t i = 0; i < P; ++i) {
    thread_ranks[i % nb_threads].push_back(i);
  }

  auto run_solve = [&](const std::vector<std::size_t>& param_ranks) {
    for (auto rank : param_ranks) {
      auto& p = parameters[rank];
      tw_solutions[rank] = solomon(_input, p.init, p.regret_coeff);
    }
  };

  std::vector<std::thread> solving_threads;

  for (std::size_t i = 0; i < nb_threads; ++i) {
    solving_threads.emplace_back(run_solve, thread_ranks[i]);
  }

  for (auto& t : solving_threads) {
    t.join();
  }

  std::vector<solution> solutions;
  cost_t best_cost = std::numeric_limits<cost_t>::max();
  std::size_t best_sol_rank;
  for (const auto& tw_sol : tw_solutions) {
    solutions.push_back(format_solution(_input, tw_sol));
    if (solutions.back().summary.cost < best_cost) {
      best_cost = solutions.back().summary.cost;
      best_sol_rank = solutions.size() - 1;
    }
  }

  return solutions[best_sol_rank];
}
<|endoftext|>


<commit_before>/**
 * The Seeks proxy and plugin framework are part of the SEEKS project.
 * Copyright (C) 2009 Emmanuel Benazera, juban@free.fr
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

#include "plugin_element.h"
#include "mem_utils.h"
#include "loaders.h"
#include "urlmatch.h"
#include "errlog.h"

#include <iostream>

#include <assert.h>

namespace sp
{

  plugin_element::plugin_element(const std::vector<url_spec*> &pos_patterns,
				 const std::vector<url_spec*> &neg_patterns,
				 plugin *parent)
    : _pos_patterns(pos_patterns),_neg_patterns(neg_patterns),_parent(parent),
      _pcrs(false),_perl(false),
      _joblist(NULL), _is_dynamic(false),
      _pattern_filename(NULL),
      _code_filename(NULL)
   {
   }

  plugin_element::plugin_element(const std::vector<std::string> &pos_patterns,
				 const std::vector<std::string> &neg_patterns,
				 plugin *parent)
    : _parent(parent),
      _pcrs(false), _perl(false),
      _joblist(NULL), _is_dynamic(false),
      _pattern_filename(NULL),_code_filename(NULL)
  {
     compile_patterns(pos_patterns,_pos_patterns);
     compile_patterns(neg_patterns,_neg_patterns);
  }

  plugin_element::plugin_element(const char *filename,
				 plugin *parent)
    :_parent(parent),
     _pcrs(false), _perl(false),
     _joblist(NULL), _is_dynamic(false),
     _pattern_filename(strdup(filename)),
     _code_filename(NULL)
  {
     // TODO: error handling !
     load_pattern_file();
  }

   plugin_element::plugin_element(const std::vector<std::string> &pos_patterns,
				  const std::vector<std::string> &neg_patterns,
				  const char *code_filename,
				  const bool &pcrs, const bool &perl,
				  plugin *parent)
     : _parent(parent),
       _pcrs(pcrs),_perl(perl),
       _joblist(NULL), _is_dynamic(false),
       _pattern_filename(NULL),
       _code_filename(strdup(code_filename))
     {
	compile_patterns(pos_patterns,_pos_patterns);
	compile_patterns(neg_patterns,_neg_patterns);
	load_code_file(); // TODO: error handling !
     }

   plugin_element::plugin_element(const char *pattern_filename,
				  const char *code_filename,
				  const bool &pcrs, const bool &perl,
				  plugin *parent)
     :_parent(parent),
      _pcrs(pcrs),_perl(perl),
      _joblist(NULL), _is_dynamic(false),
      _pattern_filename(strdup(pattern_filename)),
      _code_filename(strdup(code_filename))
     {
	//TODO error handling !
	load_pattern_file();
	load_code_file();
     }

  plugin_element::~plugin_element()
  {
     clear_patterns();
     freez(_pattern_filename);
     freez(_code_filename);

#if defined(HAVE_PERL) && !defined(NO_PERL)
     if (_perl)
       {
	  if (_perl_interpreter)
	    {
	       perl_destruct(_perl_interpreter);
	       perl_free(_perl_interpreter);
	    }
       }
#endif
  }

   void plugin_element::clear_patterns()
     {
	std::vector<url_spec*>::iterator vit;
	for (vit=_pos_patterns.begin();vit!=_pos_patterns.end();++vit)
	  {
	     delete (*vit);
	  }
	_pos_patterns.clear();

	for (vit=_neg_patterns.begin();vit!=_neg_patterns.end();++vit)
	  {
	     delete (*vit);
	  }
	_neg_patterns.clear();
     }

   sp_err plugin_element::load_pattern_file()
     {
	clear_patterns();

	return loaders::load_pattern_file(_pattern_filename,_pos_patterns,_neg_patterns);
     }

   sp_err plugin_element::load_code_file()
     {
	if (_code_filename)
	  {
	     if (_pcrs)
	       return pcrs_load_code_file();
#if defined(HAVE_PERL) && !defined(NO_PERL)
	     else if (_perl)
	       return perl_load_code_file();
#endif
	     return SP_ERR_FILE;
	  }
	return SP_ERR_OK;
     }

   sp_err plugin_element::reload()
     {
	sp_err p = load_pattern_file();
	sp_err c = load_code_file();
	return (p == SP_ERR_OK && c == SP_ERR_OK);
     }

   void plugin_element::compile_patterns(const std::vector<std::string> &patterns,
					 std::vector<url_spec*> &c_patterns)
     {
	c_patterns.reserve(patterns.size());
	std::vector<std::string>::const_iterator vit;
	for (vit=patterns.begin();vit!=patterns.end();++vit)
	  {
	     url_spec *usp = NULL;
	     sp_err err = url_spec::create_url_spec(usp,(char*)(*vit).c_str()); // beware of casting...
	     if (err != SP_ERR_OK)
	       {
		  // signal and skip bad pattern.
		  errlog::log_error(LOG_LEVEL_ERROR,
				    "cannot create URL pattern from: %s", (*vit).c_str());
	       }
	     else c_patterns.push_back(usp);
	  }
     }

  bool plugin_element::match_url(const http_request *http)
  {
    // check whether at least a pattern matches the request.
     /* std::cout << "[Debug]:plugin_element::match_url: pos_patterns size: "
       << _pos_patterns.size()
	 << " -- neg_patterns size: " << _neg_patterns.size()
	 << std::endl; */

     int count = 0;
     std::vector<url_spec*>::const_iterator vit;
     for(vit=_neg_patterns.begin();vit!=_neg_patterns.end();++vit)
       {
	  if (urlmatch::url_match((*vit),http))
	 {
	    return false;
	 }
       }

     count = 0;
     for(vit=_pos_patterns.begin();vit!=_pos_patterns.end();++vit)
       {
	  if (urlmatch::url_match((*vit),http))
	    {
	       return true;
	    }
	  count++;
       }
     return false;
  }

// two paths: pcrs or full Perl interpreter. */
// 1- pcrs.

void plugin_element::pcrs_load_code(char *buf,
				    pcrs_job *lastjob)
{
   _job_patterns.push_back(buf);
   if (_is_dynamic || pcrs::pcrs_job_is_dynamic(buf))
     {
	_is_dynamic = true;
	if (_joblist)
	  {
	     pcrs_job::pcrs_free_joblist(_joblist);
	     _joblist = NULL;
	  }
	return;
     }

   int error;
   pcrs_job *job = NULL;
   if ((job = pcrs::pcrs_compile_command(buf, &error)) == NULL)
     {
	errlog::log_error(LOG_LEVEL_ERROR,
			  "Compiling plugin job \'%s\' failed with error %d.", buf, error);
	return;
     }
   else
     {
	if (_joblist == NULL)
	  {
	     _joblist = job;
	  }
	else if (NULL != lastjob)
	  {
	     lastjob->_next = job;
	  }
	lastjob = job;
	errlog::log_error(LOG_LEVEL_RE_FILTER, "Compiling plugin job \'%s\' succeeded.", buf);
     }
}

int plugin_element::pcrs_load_code_file()
{
   FILE *fp;

   if ((fp = fopen(_code_filename, "r")) == NULL)
     {
	errlog::log_error(LOG_LEVEL_FATAL, "can't load plugin file '%s': %E",
			  _code_filename);
	return -1;
     }

   _is_dynamic = false;

   pcrs_job *lastjob = NULL;
   unsigned long linenum = 0;
   char  buf[BUFFER_SIZE];
   while(loaders::read_config_line(buf, sizeof(buf), fp, &linenum) != NULL)
     {
	pcrs_load_code(buf,lastjob);
     } // end while.
   fclose(fp);

   return 0;
}

pcrs_job* plugin_element::compile_dynamic_pcrs_job_list(const client_state *csp)
{
   pcrs_job *job_list = NULL;
   pcrs_job *dummy = NULL;
   pcrs_job *lastjob = NULL;
   int error = 0;

   const pcrs_variable variables[] =
     {
	pcrs_variable("url",csp->_http._url,1),
	pcrs_variable("path",csp->_http._path,1),
	pcrs_variable("host",csp->_http._host,1),
	pcrs_variable("origin",csp->_ip_addr_str,1),
	pcrs_variable(NULL,NULL,1)
     };

   assert(!_job_patterns.empty());

   std::list<const char*>::const_iterator lit = _job_patterns.begin();
   while(lit!=_job_patterns.end())
     {
	assert((*lit) != NULL);
	const char *str = (*lit);
	++lit;

	dummy = pcrs::pcrs_compile_dynamic_command((char*)str, variables, &error);
	if (NULL == dummy)
	  {
	     assert(error < 0);
	     errlog::log_error(LOG_LEVEL_ERROR,
			       "Compiling plugin job \'%s\' failed: %s",
			       str, pcrs::pcrs_strerror(error));
	     continue;
	  }
	else
	  {
	     if (error == PCRS_WARN_TRUNCATION)
	       {
		  errlog::log_error(LOG_LEVEL_ERROR,
				    "At least one of the variables in \'%s\' had to "
				    "be truncated before compilation", str);
	       }
	     if (job_list == NULL)
	       {
		  job_list = dummy;
	       }
	     else
	       {
		  lastjob->_next = dummy;
	       }
	     lastjob = dummy;
	  }
     }

   return job_list;
}

   char* plugin_element::pcrs_plugin_response(client_state *csp,
					      char *old)
     {
	// compile and execute jobs as needed.
	if (_is_dynamic)
	  _joblist = compile_dynamic_pcrs_job_list(csp);

	if (!_joblist)
	  return NULL;

	size_t size = 0, prev_size = 0;
	int job_number = 0;
	int hits = 0;
	char *newstr = NULL;
	pcrs_job *job;
	for (job = _joblist; NULL != job; job = job->_next)
	  {
	     job_number++;
	     size = strlen(old)+1;
	     int job_hits = pcrs::pcrs_execute(job, old, size, &newstr, &size);

	     if (job_hits >= 0)
	       {
		  /*
		   * That went well. Continue filtering
		   * and use the result of this job as
		   * input for the next one.
		   */
		  hits += job_hits;
		  if (old != csp->_iob._cur)
		    {
		       freez(old);
		    }
		  old = newstr;
	       }
	     else
	       {
		  /*
		   * This job caused an unexpected error. Inform the user
		   * and skip the rest of the jobs in this filter. We could
		   * continue with the next job, but usually the jobs
		   * depend on each other or are similar enough to
		   * fail for the same reason.
		   *
		   * At the moment our pcrs expects the error codes of pcre 3.4,
		   * but newer pcre versions can return additional error codes.
		   * As a result pcrs_strerror()'s error message might be
		   * "Unknown error ...", therefore we print the numerical value
		   * as well.
		   */
		  errlog::log_error(LOG_LEVEL_ERROR, "Skipped plugin job %u: %s (%d)",
				    job_number, pcrs::pcrs_strerror(job_hits), job_hits);
		  break;
	       }
	  }
	if (_is_dynamic) pcrs_job::pcrs_free_joblist(_joblist);

	errlog::log_error(LOG_LEVEL_RE_FILTER,
			  "filtering %s%s (size %d) with \'%s\' produced %d hits (new size %d).",
			  csp->_http._hostport, csp->_http._path, prev_size, _parent->get_name_cstr(),
			  hits, size);

	/* std::cout << "filtering has procuded #" << hits << "hits!\n";
	 std::cout << "newstr: " << newstr << std::endl; */

	/*
	 * If there were no hits, destroy our copy and let
	 * chat() use the original in csp->iob
	 */

	if (!hits)
	  {
	     freez(newstr);
	     return(NULL);
	  }

	csp->_flags |= CSP_FLAG_MODIFIED;
	csp->_content_length = size;
	IOB_RESET(csp);

	return(newstr);
     }

#if defined(HAVE_PERL) && !defined(NO_PERL)
   // 2- Perl
   EXTERN_C void xs_init(pTHX);

   int plugin_element::perl_load_code_file()
     {
	char *command_line[2] = { (char*)"", (char*)NULL };
	command_line[1] = _code_filename;

	_perl_interpreter = perl_alloc();
	perl_construct(_perl_interpreter);

	if (perl_parse(_perl_interpreter, xs_init, 2, command_line, (char**)NULL))
	  return 1; // failed to parse.

	std::cout << "Loaded Perl code file " << command_line << std::endl;

	return 0;
     }

   char** plugin_element::perl_execute_subroutine(const std::string &subr_name,
						  char **str)
     {
//	seeks_proxy::mutex_lock(&_ple_mutex);

	PerlInterpreter *my_perl = _perl_interpreter;

	dSP; // Perl macro to get access to the stack (of the interpreter pointed by 'my_perl').
	ENTER;
	SAVETMPS;
	PUSHMARK(SP);

	// only use the first string, array is not supported here.
	XPUSHs(sv_2mortal(newSVpv(str[0],0)));

	PUTBACK;

	int count = call_pv(subr_name.c_str(), G_ARRAY);

	SPAGAIN;

	if (SvTRUE(ERRSV))
	  {
	     errlog::log_error(LOG_LEVEL_ERROR,
			       "An error occurred in the Perl preprocessor: %s",
			       SvPV_nolen(ERRSV));
	     return str; // beware: return the original string.
	  }

	char **output = (char**)malloc((count+1)*sizeof(char*));
	output[count] = NULL;
	int i=count;
	while(i>0)
	  output[--i] = savepv(SvPV_nolen(POPs)); // stack comes in reverse order...

	FREETMPS;
	LEAVE;

//	seeks_proxy::mutex_unlock(&_ple_mutex);

	return output;
     }
#endif

} /* end of namespace. */
<commit_msg>added pattern filename log to plugin element<commit_after>/**
 * The Seeks proxy and plugin framework are part of the SEEKS project.
 * Copyright (C) 2009 Emmanuel Benazera, juban@free.fr
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

#include "plugin_element.h"
#include "mem_utils.h"
#include "loaders.h"
#include "urlmatch.h"
#include "errlog.h"

#include <iostream>

#include <assert.h>

namespace sp
{

  plugin_element::plugin_element(const std::vector<url_spec*> &pos_patterns,
				 const std::vector<url_spec*> &neg_patterns,
				 plugin *parent)
    : _pos_patterns(pos_patterns),_neg_patterns(neg_patterns),_parent(parent),
      _pcrs(false),_perl(false),
      _joblist(NULL), _is_dynamic(false),
      _pattern_filename(NULL),
      _code_filename(NULL)
   {
   }

  plugin_element::plugin_element(const std::vector<std::string> &pos_patterns,
				 const std::vector<std::string> &neg_patterns,
				 plugin *parent)
    : _parent(parent),
      _pcrs(false), _perl(false),
      _joblist(NULL), _is_dynamic(false),
      _pattern_filename(NULL),_code_filename(NULL)
  {
     compile_patterns(pos_patterns,_pos_patterns);
     compile_patterns(neg_patterns,_neg_patterns);
  }

  plugin_element::plugin_element(const char *filename,
				 plugin *parent)
    :_parent(parent),
     _pcrs(false), _perl(false),
     _joblist(NULL), _is_dynamic(false),
     _pattern_filename(strdup(filename)),
     _code_filename(NULL)
  {
     // TODO: error handling !
     load_pattern_file();
  }

   plugin_element::plugin_element(const std::vector<std::string> &pos_patterns,
				  const std::vector<std::string> &neg_patterns,
				  const char *code_filename,
				  const bool &pcrs, const bool &perl,
				  plugin *parent)
     : _parent(parent),
       _pcrs(pcrs),_perl(perl),
       _joblist(NULL), _is_dynamic(false),
       _pattern_filename(NULL),
       _code_filename(strdup(code_filename))
     {
	compile_patterns(pos_patterns,_pos_patterns);
	compile_patterns(neg_patterns,_neg_patterns);
	load_code_file(); // TODO: error handling !
     }

   plugin_element::plugin_element(const char *pattern_filename,
				  const char *code_filename,
				  const bool &pcrs, const bool &perl,
				  plugin *parent)
     :_parent(parent),
      _pcrs(pcrs),_perl(perl),
      _joblist(NULL), _is_dynamic(false),
      _pattern_filename(strdup(pattern_filename)),
      _code_filename(strdup(code_filename))
     {
	//TODO error handling !
	load_pattern_file();
	load_code_file();
     }

  plugin_element::~plugin_element()
  {
     clear_patterns();
     freez(_pattern_filename);
     freez(_code_filename);

#if defined(HAVE_PERL) && !defined(NO_PERL)
     if (_perl)
       {
	  if (_perl_interpreter)
	    {
	       perl_destruct(_perl_interpreter);
	       perl_free(_perl_interpreter);
	    }
       }
#endif
  }

   void plugin_element::clear_patterns()
     {
	std::vector<url_spec*>::iterator vit;
	for (vit=_pos_patterns.begin();vit!=_pos_patterns.end();++vit)
	  {
	     delete (*vit);
	  }
	_pos_patterns.clear();

	for (vit=_neg_patterns.begin();vit!=_neg_patterns.end();++vit)
	  {
	     delete (*vit);
	  }
	_neg_patterns.clear();
     }

   sp_err plugin_element::load_pattern_file()
     {
	clear_patterns();

	errlog::log_error(LOG_LEVEL_INFO, "Loading pattern file %s",_pattern_filename);

	return loaders::load_pattern_file(_pattern_filename,_pos_patterns,_neg_patterns);
     }

   sp_err plugin_element::load_code_file()
     {
	if (_code_filename)
	  {
	     if (_pcrs)
	       return pcrs_load_code_file();
#if defined(HAVE_PERL) && !defined(NO_PERL)
	     else if (_perl)
	       return perl_load_code_file();
#endif
	     return SP_ERR_FILE;
	  }
	return SP_ERR_OK;
     }

   sp_err plugin_element::reload()
     {
	sp_err p = load_pattern_file();
	sp_err c = load_code_file();
	return (p == SP_ERR_OK && c == SP_ERR_OK);
     }

   void plugin_element::compile_patterns(const std::vector<std::string> &patterns,
					 std::vector<url_spec*> &c_patterns)
     {
	c_patterns.reserve(patterns.size());
	std::vector<std::string>::const_iterator vit;
	for (vit=patterns.begin();vit!=patterns.end();++vit)
	  {
	     url_spec *usp = NULL;
	     sp_err err = url_spec::create_url_spec(usp,(char*)(*vit).c_str()); // beware of casting...
	     if (err != SP_ERR_OK)
	       {
		  // signal and skip bad pattern.
		  errlog::log_error(LOG_LEVEL_ERROR,
				    "cannot create URL pattern from: %s", (*vit).c_str());
	       }
	     else c_patterns.push_back(usp);
	  }
     }

  bool plugin_element::match_url(const http_request *http)
  {
    // check whether at least a pattern matches the request.
     /* std::cout << "[Debug]:plugin_element::match_url: pos_patterns size: "
       << _pos_patterns.size()
	 << " -- neg_patterns size: " << _neg_patterns.size()
	 << std::endl; */

     int count = 0;
     std::vector<url_spec*>::const_iterator vit;
     for(vit=_neg_patterns.begin();vit!=_neg_patterns.end();++vit)
       {
	  if (urlmatch::url_match((*vit),http))
	 {
	    return false;
	 }
       }

     count = 0;
     for(vit=_pos_patterns.begin();vit!=_pos_patterns.end();++vit)
       {
	  if (urlmatch::url_match((*vit),http))
	    {
	       return true;
	    }
	  count++;
       }
     return false;
  }

// two paths: pcrs or full Perl interpreter. */
// 1- pcrs.

void plugin_element::pcrs_load_code(char *buf,
				    pcrs_job *lastjob)
{
   _job_patterns.push_back(buf);
   if (_is_dynamic || pcrs::pcrs_job_is_dynamic(buf))
     {
	_is_dynamic = true;
	if (_joblist)
	  {
	     pcrs_job::pcrs_free_joblist(_joblist);
	     _joblist = NULL;
	  }
	return;
     }

   int error;
   pcrs_job *job = NULL;
   if ((job = pcrs::pcrs_compile_command(buf, &error)) == NULL)
     {
	errlog::log_error(LOG_LEVEL_ERROR,
			  "Compiling plugin job \'%s\' failed with error %d.", buf, error);
	return;
     }
   else
     {
	if (_joblist == NULL)
	  {
	     _joblist = job;
	  }
	else if (NULL != lastjob)
	  {
	     lastjob->_next = job;
	  }
	lastjob = job;
	errlog::log_error(LOG_LEVEL_RE_FILTER, "Compiling plugin job \'%s\' succeeded.", buf);
     }
}

int plugin_element::pcrs_load_code_file()
{
   FILE *fp;

   if ((fp = fopen(_code_filename, "r")) == NULL)
     {
	errlog::log_error(LOG_LEVEL_FATAL, "can't load plugin file '%s': %E",
			  _code_filename);
	return -1;
     }

   _is_dynamic = false;

   pcrs_job *lastjob = NULL;
   unsigned long linenum = 0;
   char  buf[BUFFER_SIZE];
   while(loaders::read_config_line(buf, sizeof(buf), fp, &linenum) != NULL)
     {
	pcrs_load_code(buf,lastjob);
     } // end while.
   fclose(fp);

   return 0;
}

pcrs_job* plugin_element::compile_dynamic_pcrs_job_list(const client_state *csp)
{
   pcrs_job *job_list = NULL;
   pcrs_job *dummy = NULL;
   pcrs_job *lastjob = NULL;
   int error = 0;

   const pcrs_variable variables[] =
     {
	pcrs_variable("url",csp->_http._url,1),
	pcrs_variable("path",csp->_http._path,1),
	pcrs_variable("host",csp->_http._host,1),
	pcrs_variable("origin",csp->_ip_addr_str,1),
	pcrs_variable(NULL,NULL,1)
     };

   assert(!_job_patterns.empty());

   std::list<const char*>::const_iterator lit = _job_patterns.begin();
   while(lit!=_job_patterns.end())
     {
	assert((*lit) != NULL);
	const char *str = (*lit);
	++lit;

	dummy = pcrs::pcrs_compile_dynamic_command((char*)str, variables, &error);
	if (NULL == dummy)
	  {
	     assert(error < 0);
	     errlog::log_error(LOG_LEVEL_ERROR,
			       "Compiling plugin job \'%s\' failed: %s",
			       str, pcrs::pcrs_strerror(error));
	     continue;
	  }
	else
	  {
	     if (error == PCRS_WARN_TRUNCATION)
	       {
		  errlog::log_error(LOG_LEVEL_ERROR,
				    "At least one of the variables in \'%s\' had to "
				    "be truncated before compilation", str);
	       }
	     if (job_list == NULL)
	       {
		  job_list = dummy;
	       }
	     else
	       {
		  lastjob->_next = dummy;
	       }
	     lastjob = dummy;
	  }
     }

   return job_list;
}

   char* plugin_element::pcrs_plugin_response(client_state *csp,
					      char *old)
     {
	// compile and execute jobs as needed.
	if (_is_dynamic)
	  _joblist = compile_dynamic_pcrs_job_list(csp);

	if (!_joblist)
	  return NULL;

	size_t size = 0, prev_size = 0;
	int job_number = 0;
	int hits = 0;
	char *newstr = NULL;
	pcrs_job *job;
	for (job = _joblist; NULL != job; job = job->_next)
	  {
	     job_number++;
	     size = strlen(old)+1;
	     int job_hits = pcrs::pcrs_execute(job, old, size, &newstr, &size);

	     if (job_hits >= 0)
	       {
		  /*
		   * That went well. Continue filtering
		   * and use the result of this job as
		   * input for the next one.
		   */
		  hits += job_hits;
		  if (old != csp->_iob._cur)
		    {
		       freez(old);
		    }
		  old = newstr;
	       }
	     else
	       {
		  /*
		   * This job caused an unexpected error. Inform the user
		   * and skip the rest of the jobs in this filter. We could
		   * continue with the next job, but usually the jobs
		   * depend on each other or are similar enough to
		   * fail for the same reason.
		   *
		   * At the moment our pcrs expects the error codes of pcre 3.4,
		   * but newer pcre versions can return additional error codes.
		   * As a result pcrs_strerror()'s error message might be
		   * "Unknown error ...", therefore we print the numerical value
		   * as well.
		   */
		  errlog::log_error(LOG_LEVEL_ERROR, "Skipped plugin job %u: %s (%d)",
				    job_number, pcrs::pcrs_strerror(job_hits), job_hits);
		  break;
	       }
	  }
	if (_is_dynamic) pcrs_job::pcrs_free_joblist(_joblist);

	errlog::log_error(LOG_LEVEL_RE_FILTER,
			  "filtering %s%s (size %d) with \'%s\' produced %d hits (new size %d).",
			  csp->_http._hostport, csp->_http._path, prev_size, _parent->get_name_cstr(),
			  hits, size);

	/* std::cout << "filtering has procuded #" << hits << "hits!\n";
	 std::cout << "newstr: " << newstr << std::endl; */

	/*
	 * If there were no hits, destroy our copy and let
	 * chat() use the original in csp->iob
	 */

	if (!hits)
	  {
	     freez(newstr);
	     return(NULL);
	  }

	csp->_flags |= CSP_FLAG_MODIFIED;
	csp->_content_length = size;
	IOB_RESET(csp);

	return(newstr);
     }

#if defined(HAVE_PERL) && !defined(NO_PERL)
   // 2- Perl
   EXTERN_C void xs_init(pTHX);

   int plugin_element::perl_load_code_file()
     {
	char *command_line[2] = { (char*)"", (char*)NULL };
	command_line[1] = _code_filename;

	_perl_interpreter = perl_alloc();
	perl_construct(_perl_interpreter);

	if (perl_parse(_perl_interpreter, xs_init, 2, command_line, (char**)NULL))
	  return 1; // failed to parse.

	std::cout << "Loaded Perl code file " << command_line << std::endl;

	return 0;
     }

   char** plugin_element::perl_execute_subroutine(const std::string &subr_name,
						  char **str)
     {
//	seeks_proxy::mutex_lock(&_ple_mutex);

	PerlInterpreter *my_perl = _perl_interpreter;

	dSP; // Perl macro to get access to the stack (of the interpreter pointed by 'my_perl').
	ENTER;
	SAVETMPS;
	PUSHMARK(SP);

	// only use the first string, array is not supported here.
	XPUSHs(sv_2mortal(newSVpv(str[0],0)));

	PUTBACK;

	int count = call_pv(subr_name.c_str(), G_ARRAY);

	SPAGAIN;

	if (SvTRUE(ERRSV))
	  {
	     errlog::log_error(LOG_LEVEL_ERROR,
			       "An error occurred in the Perl preprocessor: %s",
			       SvPV_nolen(ERRSV));
	     return str; // beware: return the original string.
	  }

	char **output = (char**)malloc((count+1)*sizeof(char*));
	output[count] = NULL;
	int i=count;
	while(i>0)
	  output[--i] = savepv(SvPV_nolen(POPs)); // stack comes in reverse order...

	FREETMPS;
	LEAVE;

//	seeks_proxy::mutex_unlock(&_ple_mutex);

	return output;
     }
#endif

} /* end of namespace. */
<|endoftext|>