<commit_before>#include "tags.h"

#include <0mq/context.h>
#include <0mq/socket.h>
#include <0mq/message.h>
#include <0mq/poller.h>

#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>

#include <iostream>
#include <cstddef>
#include <chrono>
#include <thread>
#include <functional>
#include <string>
#include <sstream>
#include <cassert>

typedef std::function<zmq::Message (const zmq::Message&)> jobfunction_type;
typedef std::chrono::time_point<std::chrono::steady_clock> timepoint_type;

class Job {
  public:
    Job(const jobfunction_type& jobFunction, zmq::Context& context)
      : context_(context), jobFunction_(jobFunction), doWork_(true)
    {
    }

    ~Job() {
      doWork_ = false;

      thread_->join();
    }

    void init(const std::string& bindStr) {
      using namespace std::placeholders;
      // create a thread that waits on the job socket for work
      thread_ = std::unique_ptr<std::thread>(new
          std::thread(std::bind(&Job::go, this, bindStr)));
    }

  private:
    void go(const std::string& bindStr) {
      zmq::Socket jobSocket = context_.createSocket(ZMQ_PAIR);
      jobSocket.connect(bindStr.c_str());

      while (doWork_) {
        zmq::Socket::messages_type request = jobSocket.receive();

        if (request.size() != 1) {
          throw std::runtime_error("Job::go: empty data");
        }

        zmq::Message replyMessage = jobFunction_(request.front());

        zmq::Socket::messages_type reply {replyMessage};
        jobSocket.send(reply);
      }
    }

    // do not copy
    Job(const Job&);

    zmq::Context context_;
    const jobfunction_type jobFunction_;
    bool doWork_;
    std::unique_ptr<std::thread> thread_;
};

class WorkerApplication {
  public:
    WorkerApplication(const jobfunction_type& jobFunction)
    : context_(), workerSocket_(), jobSocket_(), job_(jobFunction, context_),
      isBusy_(false), client_(), jobID_()
    {
    }

    ~WorkerApplication() {
    }

    void init() {
      boost::uuids::uuid uuid;

      std::ostringstream bindStr;
      bindStr << "inproc://job_" << uuid;

      jobSocket_ = context_.createSocket(ZMQ_PAIR);
      jobSocket_.bind(bindStr.str().c_str());
      job_.init(bindStr.str());

      connect();
    }

    void go() {
      timepoint_type nextQueueBeat;
      nextQueueBeat = std::chrono::steady_clock::now() +
        std::chrono::milliseconds(queuebeatInterval_);
      std::size_t interval = intervalInit_;

      timepoint_type nextHeartBeat;
      nextHeartBeat = std::chrono::steady_clock::now() +
        std::chrono::milliseconds(heartbeatInterval_);

      while (true) {
        std::vector<zmq::Socket> items = { workerSocket_ };
        if (isBusy_) {
          items.push_back(jobSocket_);
        }

        std::vector<short> state = zmq::poll(items, heartbeatInterval_);

        assert(!isBusy_ || state.size() > 1);

        if (state[0] & ZMQ_POLLIN) {
          handleQueue();

          nextQueueBeat = std::chrono::steady_clock::now() +
            std::chrono::milliseconds(queuebeatInterval_);
        }

        if (isBusy_ && state.size() > 1 && state[1] & ZMQ_POLLIN) {
          handleJobDone();

          // make sure we let the queue know that we are available again
          nextHeartBeat = std::chrono::steady_clock::now() - 2 *
            std::chrono::milliseconds(heartbeatInterval_);
        }

        timepoint_type now =
          std::chrono::steady_clock::now();

        if (now > nextQueueBeat) {
          std::cout << "W: heartbeat failure, can't reach queue" << std::endl;
          std::cout << "W: reconnecting in " << interval << " msec..." <<
            std::endl;

          std::this_thread::sleep_for(std::chrono::milliseconds(interval));

          if (interval < intervalMax_)
            interval *= 2;

          connect();

          nextQueueBeat = std::chrono::steady_clock::now() +
            std::chrono::milliseconds(queuebeatInterval_);
        }

        //  Send heartbeat to queue if it's time
        if (now > nextHeartBeat) {
          sendHeartBeat();

          nextHeartBeat = now + std::chrono::milliseconds(heartbeatInterval_);
        }
      }
    }

  private:
    void handleQueue() {
      zmq::Socket::messages_type messages = workerSocket_.receive();

      const std::size_t messageSize = messages.size();

      // check if we got a valid message
      if (messageSize == 0) {
        throw std::runtime_error("WorkerApplication::go: got interrupted");
      }

      zmq::Socket::messages_type::iterator messagePtr =
        messages.begin();
      const zmq::Message& tag = *messagePtr++;

      if (tag.size() != 1) {
        throw std::runtime_error("WorkerApplication::go: invalid tag size");
      }

      switch (tag.data()[0]) {
        case QUEUE_HEARTBEAT_TAG:
          // do nothing
          if (messageSize != 1) {
            throw std::runtime_error("WorkerApplication::go: invalid heartbeak");
          }
          break;
        case QUEUE_JOB_TAG:
          // got a job
          if (messageSize != 4) {
            throw std::runtime_error("WorkerApplication::go: invalid job request");
          }

          if (!isBusy_)
            handleNewJob(messagePtr);
          else
            std::cout << "W: I am busy but I got a job. I'll ignore it" << std::endl;

          break;
      }
    }

    void sendHeartBeat() {
      zmq::Message queueReplyTag(1);
      zmq::Socket::messages_type heartbeat;
      std::cout << "beat: " << isBusy_ << std::endl;
      if (!isBusy_) {
        queueReplyTag.data()[0] = WORKER_HEARTBEAT_TAG;
        heartbeat.push_back(std::move(queueReplyTag));
        workerSocket_.send(heartbeat);
      } else {
        queueReplyTag.data()[0] = WORKER_UPDATE_TAG;
        heartbeat.push_back(std::move(queueReplyTag));

        heartbeat.push_back(client_);

        zmq::Message jobReplyTag(1);
        jobReplyTag.data()[0] = JOB_BUSY;
        heartbeat.push_back(std::move(jobReplyTag));
        heartbeat.push_back(jobID_);
        workerSocket_.send(heartbeat);
      }
    }

    void handleNewJob(zmq::Socket::messages_type::const_iterator messagePtr) {
      // push the address of the client
      std::cout << "got job" << std::endl;

      client_ = *messagePtr++;
      jobID_ = *messagePtr++;
      isBusy_ = true;

      zmq::Socket::messages_type jobRequest;
      jobRequest.push_back(*messagePtr);
      jobSocket_.send(jobRequest);
    }

    void handleJobDone() {
      std::cout << "done" << std::endl;
      zmq::Socket::messages_type jobReply = jobSocket_.receive();

      zmq::Socket::messages_type reply;
      zmq::Message queueReplyTag(1);
      zmq::Message jobReplyTag(1);

      queueReplyTag.data()[0] = WORKER_UPDATE_TAG;
      reply.push_back(std::move(queueReplyTag));

      // push the address of the client
      reply.push_back(std::move(client_));

      jobReplyTag.data()[0] = JOB_DONE;
      reply.push_back(std::move(jobReplyTag));
      reply.push_back(std::move(jobID_));

      reply.splice(reply.end(), jobReply);

      workerSocket_.send(reply);

      isBusy_ = false;

    }

    // do not copy
    WorkerApplication(const WorkerApplication&);

    void connect() {
      workerSocket_ = context_.createSocket(ZMQ_DEALER);
      workerSocket_.connect("tcp://localhost:5556");

      //  Tell queue we're ready for work
      zmq::Message message(1);
      message.data()[0] = WORKER_HEARTBEAT_TAG;
      zmq::Socket::messages_type messages = {message};
      workerSocket_.send(messages);
    }

    const std::size_t heartbeatInterval_ = 1000;
    const std::size_t queuebeatInterval_ = 3000;
    const std::size_t intervalInit_ = 1000;
    const std::size_t intervalMax_ = 32000;

    zmq::Context context_;
    zmq::Socket workerSocket_, jobSocket_;
    Job job_;

    bool isBusy_;
    zmq::Message client_;
    zmq::Message jobID_;
};

zmq::Message job(const zmq::Message& data) {
  std::this_thread::sleep_for(std::chrono::seconds(10));
  return data;
}

int main(int /*argc*/, const char** /*argv*/) {
  WorkerApplication worker(job);
  worker.init();

  worker.go();

  return 0;
}
<commit_msg>Fixes immediate update.<commit_after>#include "tags.h"

#include <0mq/context.h>
#include <0mq/socket.h>
#include <0mq/message.h>
#include <0mq/poller.h>

#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>

#include <iostream>
#include <cstddef>
#include <chrono>
#include <thread>
#include <functional>
#include <string>
#include <sstream>
#include <cassert>

typedef std::function<zmq::Message (const zmq::Message&)> jobfunction_type;
typedef std::chrono::time_point<std::chrono::steady_clock> timepoint_type;

class Job {
  public:
    Job(const jobfunction_type& jobFunction, zmq::Context& context)
      : context_(context), jobFunction_(jobFunction), doWork_(true)
    {
    }

    ~Job() {
      doWork_ = false;

      thread_->join();
    }

    void init(const std::string& bindStr) {
      using namespace std::placeholders;
      // create a thread that waits on the job socket for work
      thread_ = std::unique_ptr<std::thread>(new
          std::thread(std::bind(&Job::go, this, bindStr)));
    }

  private:
    void go(const std::string& bindStr) {
      zmq::Socket jobSocket = context_.createSocket(ZMQ_PAIR);
      jobSocket.connect(bindStr.c_str());

      while (doWork_) {
        zmq::Socket::messages_type request = jobSocket.receive();

        if (request.size() != 1) {
          throw std::runtime_error("Job::go: empty data");
        }

        zmq::Message replyMessage = jobFunction_(request.front());

        zmq::Socket::messages_type reply {replyMessage};
        jobSocket.send(reply);
      }
    }

    // do not copy
    Job(const Job&);

    zmq::Context context_;
    const jobfunction_type jobFunction_;
    bool doWork_;
    std::unique_ptr<std::thread> thread_;
};

class WorkerApplication {
  public:
    WorkerApplication(const jobfunction_type& jobFunction)
    : context_(), workerSocket_(), jobSocket_(), job_(jobFunction, context_),
      isBusy_(false), client_(), jobID_()
    {
    }

    ~WorkerApplication() {
    }

    void init() {
      boost::uuids::uuid uuid;

      std::ostringstream bindStr;
      bindStr << "inproc://job_" << uuid;

      jobSocket_ = context_.createSocket(ZMQ_PAIR);
      jobSocket_.bind(bindStr.str().c_str());
      job_.init(bindStr.str());

      connect();
    }

    void go() {
      timepoint_type nextQueueBeat;
      nextQueueBeat = std::chrono::steady_clock::now() +
        std::chrono::milliseconds(queuebeatInterval_);
      std::size_t interval = intervalInit_;

      timepoint_type nextHeartBeat;
      nextHeartBeat = std::chrono::steady_clock::now() +
        std::chrono::milliseconds(heartbeatInterval_);

      while (true) {
        bool immediateUpdate = false;

        std::vector<zmq::Socket> items = { workerSocket_ };
        if (isBusy_) {
          items.push_back(jobSocket_);
        }

        std::vector<short> state = zmq::poll(items, heartbeatInterval_);

        assert(!isBusy_ || state.size() > 1);

        if (state[0] & ZMQ_POLLIN) {
          immediateUpdate = immediateUpdate || handleQueue();

          nextQueueBeat = std::chrono::steady_clock::now() +
            std::chrono::milliseconds(queuebeatInterval_);
        }

        if (isBusy_ && state.size() > 1 && state[1] & ZMQ_POLLIN) {
          handleJobDone();

          // make sure we let the queue know that we are available again
          immediateUpdate = true;
        }

        timepoint_type now =
          std::chrono::steady_clock::now();

        if (now > nextQueueBeat) {
          std::cout << "W: heartbeat failure, can't reach queue" << std::endl;
          std::cout << "W: reconnecting in " << interval << " msec..." <<
            std::endl;

          std::this_thread::sleep_for(std::chrono::milliseconds(interval));

          if (interval < intervalMax_)
            interval *= 2;

          connect();

          nextQueueBeat = std::chrono::steady_clock::now() +
            std::chrono::milliseconds(queuebeatInterval_);
        }

        //  Send heartbeat to queue if it's time
        if (immediateUpdate || now > nextHeartBeat) {
          sendHeartBeat();

          nextHeartBeat = now + std::chrono::milliseconds(heartbeatInterval_);
        }
      }
    }

  private:
    bool handleQueue() {
      bool immediateUpdate = false;

      zmq::Socket::messages_type messages = workerSocket_.receive();

      const std::size_t messageSize = messages.size();

      // check if we got a valid message
      if (messageSize == 0) {
        throw std::runtime_error("WorkerApplication::go: got interrupted");
      }

      zmq::Socket::messages_type::iterator messagePtr =
        messages.begin();
      const zmq::Message& tag = *messagePtr++;

      if (tag.size() != 1) {
        throw std::runtime_error("WorkerApplication::go: invalid tag size");
      }

      switch (tag.data()[0]) {
        case QUEUE_HEARTBEAT_TAG:
          // do nothing
          if (messageSize != 1) {
            throw std::runtime_error("WorkerApplication::go: invalid heartbeak");
          }
          break;
        case QUEUE_JOB_TAG:
          // got a job
          if (messageSize != 4) {
            throw std::runtime_error("WorkerApplication::go: invalid job request");
          }

          if (!isBusy_) {
            handleNewJob(messagePtr);
            immediateUpdate = true;
          } else {
            std::cout << "W: I am busy but I got a job. I'll ignore it" << std::endl;
          }

          break;
      }

      return immediateUpdate;
    }

    void sendHeartBeat() {
      zmq::Message queueReplyTag(1);
      zmq::Socket::messages_type heartbeat;
      std::cout << "beat: " << isBusy_ << std::endl;
      if (!isBusy_) {
        queueReplyTag.data()[0] = WORKER_HEARTBEAT_TAG;
        heartbeat.push_back(std::move(queueReplyTag));
        workerSocket_.send(heartbeat);
      } else {
        queueReplyTag.data()[0] = WORKER_UPDATE_TAG;
        heartbeat.push_back(std::move(queueReplyTag));

        heartbeat.push_back(client_);

        zmq::Message jobReplyTag(1);
        jobReplyTag.data()[0] = JOB_BUSY;
        heartbeat.push_back(std::move(jobReplyTag));
        heartbeat.push_back(jobID_);
        workerSocket_.send(heartbeat);
      }
    }

    void handleNewJob(zmq::Socket::messages_type::const_iterator messagePtr) {
      // push the address of the client
      std::cout << "got job" << std::endl;

      client_ = *messagePtr++;
      jobID_ = *messagePtr++;
      isBusy_ = true;

      zmq::Socket::messages_type jobRequest;
      jobRequest.push_back(*messagePtr);
      jobSocket_.send(jobRequest);
    }

    void handleJobDone() {
      std::cout << "done" << std::endl;
      zmq::Socket::messages_type jobReply = jobSocket_.receive();

      zmq::Socket::messages_type reply;
      zmq::Message queueReplyTag(1);
      zmq::Message jobReplyTag(1);

      queueReplyTag.data()[0] = WORKER_UPDATE_TAG;
      reply.push_back(std::move(queueReplyTag));

      // push the address of the client
      reply.push_back(std::move(client_));

      jobReplyTag.data()[0] = JOB_DONE;
      reply.push_back(std::move(jobReplyTag));
      reply.push_back(std::move(jobID_));

      reply.splice(reply.end(), jobReply);

      workerSocket_.send(reply);

      isBusy_ = false;
    }

    // do not copy
    WorkerApplication(const WorkerApplication&);

    void connect() {
      workerSocket_ = context_.createSocket(ZMQ_DEALER);
      workerSocket_.connect("tcp://localhost:5556");

      //  Tell queue we're ready for work
      zmq::Message message(1);
      message.data()[0] = WORKER_HEARTBEAT_TAG;
      zmq::Socket::messages_type messages = {message};
      workerSocket_.send(messages);
    }

    const std::size_t heartbeatInterval_ = 1000;
    const std::size_t queuebeatInterval_ = 3000;
    const std::size_t intervalInit_ = 1000;
    const std::size_t intervalMax_ = 32000;

    zmq::Context context_;
    zmq::Socket workerSocket_, jobSocket_;
    Job job_;

    bool isBusy_;
    zmq::Message client_;
    zmq::Message jobID_;
};

zmq::Message job(const zmq::Message& data) {
  std::this_thread::sleep_for(std::chrono::seconds(10));
  return data;
}

int main(int /*argc*/, const char** /*argv*/) {
  WorkerApplication worker(job);
  worker.init();

  worker.go();

  return 0;
}
<|endoftext|>


<commit_before>#include "clay.hpp"
#include <sstream>

using namespace std;

//
// overload <<
//

static void print(const Object *x, ostream &out);

ostream &operator<<(ostream &out, const Object &obj) {
    print(&obj, out);
    return out;
}

ostream &operator<<(ostream &out, const Object *obj) {
    print(obj, out);
    return out;
}

//
// big vec
//

template <class T>
struct BigVec {
    const vector<T> *v;
    BigVec(const vector<T> &v)
        : v(&v) {}
};

template <class T>
BigVec<T> bigVec(const vector<T> &v) {
    return BigVec<T>(v);
}

static int indent = 0;

template <class T>
ostream &operator<<(ostream &out, const BigVec<T> &v) {
    ++indent;
    out << "[";
    typename vector<T>::const_iterator i, end;
    bool first = true;
    for (i = v.v->begin(), end = v.v->end(); i != end; ++i) {
        if (!first)
            out << ",";
        first = false;
        out << "\n";
        for (int j = 0; j < indent; ++j)
            out << "  ";
        out << *i;
    }
    out << "]";
    --indent;
    return out;
}

//
// print
//

static void print(const Object *x, ostream &out) {
    if (x == NULL) {
        out << "NULL";
        return;
    }

    switch (x->objKind) {

    case SOURCE : {
        Source *y = (Source *)x;
        out << "Source(" << y->fileName << ")";
        break;
    }
    case LOCATION : {
        Location *y = (Location *)x;
        out << "Location(" << y->source << ", " << y->offset << ")";
        break;
    }
    case TOKEN : {
        Token *y = (Token *)x;
        out << "Token(";
        switch (y->tokenKind) {
        case T_SYMBOL :
            out << "T_SYMBOL";
            break;
        case T_KEYWORD :
            out << "T_KEYWORD";
            break;
        case T_IDENTIFIER :
            out << "T_IDENTIFIER";
            break;
        case T_STRING_LITERAL :
            out << "T_STRING_LITERAL";
            break;
        case T_CHAR_LITERAL :
            out << "T_CHAR_LITERAL";
            break;
        case T_INT_LITERAL :
            out << "T_INT_LITERAL";
            break;
        case T_FLOAT_LITERAL :
            out << "T_FLOAT_LITERAL";
            break;
        case T_LITERAL_SUFFIX :
            out << "T_LITERAL_SUFFIX";
            break;
        case T_SPACE :
            out << "T_SPACE";
            break;
        case T_LINE_COMMENT :
            out << "T_LINE_COMMENT";
            break;
        case T_BLOCK_COMMENT :
            out << "T_BLOCK_COMMENT";
            break;
        default :
            assert(false);
        }
        out << ", " << y->str << ")";
        break;
    }

    case IDENTIFIER : {
        Identifier *y = (Identifier *)x;
        out << "Identifier(" << y->str << ")";
        break;
    }
    case DOTTED_NAME : {
        DottedName *y = (DottedName *)x;
        out << "DottedName(" << y->parts << ")";
        break;
    }

    case BOOL_LITERAL : {
        BoolLiteral *y = (BoolLiteral *)x;
        out << "BoolLiteral(" << y->value << ")";
        break;
    }
    case INT_LITERAL : {
        IntLiteral *y = (IntLiteral *)x;
        out << "IntLiteral(" << y->value;
        if (!y->suffix.empty())
            out << ", " << y->suffix;
        out << ")";
        break;
    }
    case FLOAT_LITERAL : {
        FloatLiteral *y = (FloatLiteral *)x;
        out << "FloatLiteral(" << y->value;
        if (!y->suffix.empty())
            out << ", " << y->suffix;
        out << ")";
        break;
    }
    case CHAR_LITERAL : {
        CharLiteral *y = (CharLiteral *)x;
        out << "CharLiteral(" << y->value << ")";
        break;
    }
    case STRING_LITERAL : {
        StringLiteral *y = (StringLiteral *)x;
        out << "StringLiteral(" << y->value << ")";
        break;
    }

    case NAME_REF : {
        NameRef *y = (NameRef *)x;
        out << "NameRef(" << y->name << ")";
        break;
    }
    case TUPLE : {
        Tuple *y = (Tuple *)x;
        out << "Tuple(" << y->args << ")";
        break;
    }
    case ARRAY : {
        Array *y = (Array *)x;
        out << "Array(" << y->args << ")";
        break;
    }
    case INDEXING : {
        Indexing *y = (Indexing *)x;
        out << "Indexing(" << y->expr << ", " << y->args << ")";
        break;
    }
    case CALL : {
        Call *y = (Call *)x;
        out << "Call(" << y->expr << ", " << y->args << ")";
        break;
    }
    case FIELD_REF : {
        FieldRef *y = (FieldRef *)x;
        out << "FieldRef(" << y->expr << ", " << y->name << ")";
        break;
    }
    case TUPLE_REF : {
        TupleRef *y = (TupleRef *)x;
        out << "TupleRef(" << y->expr << ", " << y->index << ")";
        break;
    }
    case UNARY_OP : {
        UnaryOp *y = (UnaryOp *)x;
        out << "UnaryOp(";
        switch (y->op) {
        case DEREFERENCE :
            out << "DEREFERENCE";
            break;
        case ADDRESS_OF :
            out << "ADDRESS_OF";
            break;
        case PLUS :
            out << "PLUS";
            break;
        case MINUS :
            out << "MINUS";
            break;
        case NOT :
            out << "NOT";
            break;
        default :
            assert(false);
        }
        out << ", " << y->expr << ")";
        break;
    }
    case BINARY_OP : {
        BinaryOp *y = (BinaryOp *)x;
        out << "BinaryOp(";
        switch (y->op) {
        case ADD :
            out << "ADD";
            break;
        case SUBTRACT :
            out << "SUBTRACT";
            break;
        case MULTIPLY :
            out << "MULTIPLY";
            break;
        case DIVIDE :
            out << "DIVIDE";
            break;
        case REMAINDER :
            out << "REMAINDER";
            break;
        case EQUALS :
            out << "EQUALS";
            break;
        case NOT_EQUALS :
            out << "NOT_EQUALS";
            break;
        case LESSER :
            out << "LESSER";
            break;
        case LESSER_EQUALS :
            out << "LESSER_EQUALS";
            break;
        case GREATER :
            out << "GREATER";
            break;
        case GREATER_EQUALS :
            out << "GREATER_EQUALS";
            break;
        default :
            assert(false);
        }
        out << ", " << y->expr1 << ", " << y->expr2 << ")";
        break;
    }
    case AND : {
        And *y = (And *)x;
        out << "And(" << y->expr1 << ", " << y->expr2 << ")";
        break;
    }
    case OR : {
        Or *y = (Or *)x;
        out << "Or(" << y->expr1 << ", " << y->expr2 << ")";
        break;
        break;
    }

    case BLOCK : {
        Block *y = (Block *)x;
        out << "Block(" << bigVec(y->statements) << ")";
        break;
    }
    case LABEL : {
        Label *y = (Label *)x;
        out << "Label(" << y->name << ")";
        break;
    }
    case BINDING : {
        Binding *y = (Binding *)x;
        out << "Binding(";
        switch (y->bindingKind) {
        case VAR :
            out << "VAR";
            break;
        case REF :
            out << "REF";
            break;
        case STATIC :
            out << "STATIC";
            break;
        default :
            assert(false);
        }
        out << ", " << y->name << ", " << y->expr << ")";
        break;
    }
    case ASSIGNMENT : {
        Assignment *y = (Assignment *)x;
        out << "Assignment(" << y->left << ", " << y->right << ")";
        break;
    }
    case GOTO : {
        Goto *y = (Goto *)x;
        out << "Goto(" << y->labelName << ")";
        break;
    }
    case RETURN : {
        Return *y = (Return *)x;
        out << "Return(" << y->expr << ")";
        break;
    }
    case RETURN_REF : {
        ReturnRef *y = (ReturnRef *)x;
        out << "ReturnRef(" << y->expr << ")";
        break;
    }
    case IF : {
        If *y = (If *)x;
        out << "If(" << y->condition << ", " << y->thenPart;
        out << ", " << y->elsePart << ")";
        break;
    }
    case EXPR_STATEMENT : {
        ExprStatement *y = (ExprStatement *)x;
        out << "ExprStatement(" << y->expr << ")";
        break;
    }
    case WHILE : {
        While *y = (While *)x;
        out << "While(" << y->condition << ", " << y->body << ")";
        break;
    }
    case BREAK : {
        out << "Break()";
        break;
    }
    case CONTINUE : {
        out << "Continue()";
        break;
    }
    case FOR : {
        For *y = (For *)x;
        out << "For(" << y->variable << ", " << y->expr;
        out << ", " << y->body << ")";
        break;
    }

    case CODE : {
        Code *y = (Code *)x;
        out << "Code(" << y->patternVars << ", " << y->predicate;
        out << ", " << y->formalArgs << ", " << y->body << ")";
        break;
    }
    case VALUE_ARG : {
        ValueArg *y = (ValueArg *)x;
        out << "ValueArg(" << y->name << ", " << y->type << ")";
        break;
    }
    case STATIC_ARG : {
        StaticArg *y = (StaticArg *)x;
        out << "StaticArg(" << y->pattern << ")";
        break;
    }

    case RECORD : {
        Record *y = (Record *)x;
        out << "Record(" << y->name << ", " << y->patternVars;
        out << ", " << y->formalArgs << ")";
        break;
    }
    case PROCEDURE : {
        Procedure *y = (Procedure *)x;
        out << "Procedure(" << y->name << ", " << y->code << ")";
        break;
    }
    case OVERLOAD : {
        Overload *y = (Overload *)x;
        out << "Overload(" << y->name << ", " << y->code << ")";
        break;
    }
    case OVERLOADABLE : {
        Overloadable *y = (Overloadable *)x;
        out << "Overloadable(" << y->name << ")";
        break;
    }
    case EXTERNAL_PROCEDURE : {
        ExternalProcedure *y = (ExternalProcedure *)x;
        out << "ExternalProcedure(" << y->name << ", " << y->args;
        out << ", " << y->returnType << ")";
        break;
    }

    case IMPORT : {
        Import *y = (Import *)x;
        out << "Import(" << y->dottedName << ")";
        break;
    }
    case EXPORT : {
        Export *y = (Export *)x;
        out << "Export(" << y->dottedName << ")";
        break;
    }
    case MODULE : {
        Module *y = (Module *)x;
        out << "Module(" << bigVec(y->imports) << ", " << bigVec(y->exports);
        out << ", " << bigVec(y->topLevelItems) << ")";
        break;
    }

    case TYPE : {
        Type *y = (Type *)x;
        typePrint(y, out);
        break;
    }

    case VALUE : {
        Value *y = (Value *)x;
        valuePrint(y, out);
        break;
    }

    default :
        assert(false);
    }
}
<commit_msg>added support for printing 'SCExpr' and 'ValueExpr'.<commit_after>#include "clay.hpp"
#include <sstream>

using namespace std;

//
// overload <<
//

static void print(const Object *x, ostream &out);

ostream &operator<<(ostream &out, const Object &obj) {
    print(&obj, out);
    return out;
}

ostream &operator<<(ostream &out, const Object *obj) {
    print(obj, out);
    return out;
}

//
// big vec
//

template <class T>
struct BigVec {
    const vector<T> *v;
    BigVec(const vector<T> &v)
        : v(&v) {}
};

template <class T>
BigVec<T> bigVec(const vector<T> &v) {
    return BigVec<T>(v);
}

static int indent = 0;

template <class T>
ostream &operator<<(ostream &out, const BigVec<T> &v) {
    ++indent;
    out << "[";
    typename vector<T>::const_iterator i, end;
    bool first = true;
    for (i = v.v->begin(), end = v.v->end(); i != end; ++i) {
        if (!first)
            out << ",";
        first = false;
        out << "\n";
        for (int j = 0; j < indent; ++j)
            out << "  ";
        out << *i;
    }
    out << "]";
    --indent;
    return out;
}

//
// print
//

static void print(const Object *x, ostream &out) {
    if (x == NULL) {
        out << "NULL";
        return;
    }

    switch (x->objKind) {

    case SOURCE : {
        Source *y = (Source *)x;
        out << "Source(" << y->fileName << ")";
        break;
    }
    case LOCATION : {
        Location *y = (Location *)x;
        out << "Location(" << y->source << ", " << y->offset << ")";
        break;
    }
    case TOKEN : {
        Token *y = (Token *)x;
        out << "Token(";
        switch (y->tokenKind) {
        case T_SYMBOL :
            out << "T_SYMBOL";
            break;
        case T_KEYWORD :
            out << "T_KEYWORD";
            break;
        case T_IDENTIFIER :
            out << "T_IDENTIFIER";
            break;
        case T_STRING_LITERAL :
            out << "T_STRING_LITERAL";
            break;
        case T_CHAR_LITERAL :
            out << "T_CHAR_LITERAL";
            break;
        case T_INT_LITERAL :
            out << "T_INT_LITERAL";
            break;
        case T_FLOAT_LITERAL :
            out << "T_FLOAT_LITERAL";
            break;
        case T_LITERAL_SUFFIX :
            out << "T_LITERAL_SUFFIX";
            break;
        case T_SPACE :
            out << "T_SPACE";
            break;
        case T_LINE_COMMENT :
            out << "T_LINE_COMMENT";
            break;
        case T_BLOCK_COMMENT :
            out << "T_BLOCK_COMMENT";
            break;
        default :
            assert(false);
        }
        out << ", " << y->str << ")";
        break;
    }

    case IDENTIFIER : {
        Identifier *y = (Identifier *)x;
        out << "Identifier(" << y->str << ")";
        break;
    }
    case DOTTED_NAME : {
        DottedName *y = (DottedName *)x;
        out << "DottedName(" << y->parts << ")";
        break;
    }

    case BOOL_LITERAL : {
        BoolLiteral *y = (BoolLiteral *)x;
        out << "BoolLiteral(" << y->value << ")";
        break;
    }
    case INT_LITERAL : {
        IntLiteral *y = (IntLiteral *)x;
        out << "IntLiteral(" << y->value;
        if (!y->suffix.empty())
            out << ", " << y->suffix;
        out << ")";
        break;
    }
    case FLOAT_LITERAL : {
        FloatLiteral *y = (FloatLiteral *)x;
        out << "FloatLiteral(" << y->value;
        if (!y->suffix.empty())
            out << ", " << y->suffix;
        out << ")";
        break;
    }
    case CHAR_LITERAL : {
        CharLiteral *y = (CharLiteral *)x;
        out << "CharLiteral(" << y->value << ")";
        break;
    }
    case STRING_LITERAL : {
        StringLiteral *y = (StringLiteral *)x;
        out << "StringLiteral(" << y->value << ")";
        break;
    }

    case NAME_REF : {
        NameRef *y = (NameRef *)x;
        out << "NameRef(" << y->name << ")";
        break;
    }
    case TUPLE : {
        Tuple *y = (Tuple *)x;
        out << "Tuple(" << y->args << ")";
        break;
    }
    case ARRAY : {
        Array *y = (Array *)x;
        out << "Array(" << y->args << ")";
        break;
    }
    case INDEXING : {
        Indexing *y = (Indexing *)x;
        out << "Indexing(" << y->expr << ", " << y->args << ")";
        break;
    }
    case CALL : {
        Call *y = (Call *)x;
        out << "Call(" << y->expr << ", " << y->args << ")";
        break;
    }
    case FIELD_REF : {
        FieldRef *y = (FieldRef *)x;
        out << "FieldRef(" << y->expr << ", " << y->name << ")";
        break;
    }
    case TUPLE_REF : {
        TupleRef *y = (TupleRef *)x;
        out << "TupleRef(" << y->expr << ", " << y->index << ")";
        break;
    }
    case UNARY_OP : {
        UnaryOp *y = (UnaryOp *)x;
        out << "UnaryOp(";
        switch (y->op) {
        case DEREFERENCE :
            out << "DEREFERENCE";
            break;
        case ADDRESS_OF :
            out << "ADDRESS_OF";
            break;
        case PLUS :
            out << "PLUS";
            break;
        case MINUS :
            out << "MINUS";
            break;
        case NOT :
            out << "NOT";
            break;
        default :
            assert(false);
        }
        out << ", " << y->expr << ")";
        break;
    }
    case BINARY_OP : {
        BinaryOp *y = (BinaryOp *)x;
        out << "BinaryOp(";
        switch (y->op) {
        case ADD :
            out << "ADD";
            break;
        case SUBTRACT :
            out << "SUBTRACT";
            break;
        case MULTIPLY :
            out << "MULTIPLY";
            break;
        case DIVIDE :
            out << "DIVIDE";
            break;
        case REMAINDER :
            out << "REMAINDER";
            break;
        case EQUALS :
            out << "EQUALS";
            break;
        case NOT_EQUALS :
            out << "NOT_EQUALS";
            break;
        case LESSER :
            out << "LESSER";
            break;
        case LESSER_EQUALS :
            out << "LESSER_EQUALS";
            break;
        case GREATER :
            out << "GREATER";
            break;
        case GREATER_EQUALS :
            out << "GREATER_EQUALS";
            break;
        default :
            assert(false);
        }
        out << ", " << y->expr1 << ", " << y->expr2 << ")";
        break;
    }
    case AND : {
        And *y = (And *)x;
        out << "And(" << y->expr1 << ", " << y->expr2 << ")";
        break;
    }
    case OR : {
        Or *y = (Or *)x;
        out << "Or(" << y->expr1 << ", " << y->expr2 << ")";
        break;
    }
    case SC_EXPR : {
        SCExpr *y = (SCExpr *)x;
        out << "SCExpr(" << y->expr << ")";
        break;
    }
    case VALUE_EXPR : {
        ValueExpr *y = (ValueExpr *)x;
        out << "ValueExpr(" << y->value << ")";
        break;
    }

    case BLOCK : {
        Block *y = (Block *)x;
        out << "Block(" << bigVec(y->statements) << ")";
        break;
    }
    case LABEL : {
        Label *y = (Label *)x;
        out << "Label(" << y->name << ")";
        break;
    }
    case BINDING : {
        Binding *y = (Binding *)x;
        out << "Binding(";
        switch (y->bindingKind) {
        case VAR :
            out << "VAR";
            break;
        case REF :
            out << "REF";
            break;
        case STATIC :
            out << "STATIC";
            break;
        default :
            assert(false);
        }
        out << ", " << y->name << ", " << y->expr << ")";
        break;
    }
    case ASSIGNMENT : {
        Assignment *y = (Assignment *)x;
        out << "Assignment(" << y->left << ", " << y->right << ")";
        break;
    }
    case GOTO : {
        Goto *y = (Goto *)x;
        out << "Goto(" << y->labelName << ")";
        break;
    }
    case RETURN : {
        Return *y = (Return *)x;
        out << "Return(" << y->expr << ")";
        break;
    }
    case RETURN_REF : {
        ReturnRef *y = (ReturnRef *)x;
        out << "ReturnRef(" << y->expr << ")";
        break;
    }
    case IF : {
        If *y = (If *)x;
        out << "If(" << y->condition << ", " << y->thenPart;
        out << ", " << y->elsePart << ")";
        break;
    }
    case EXPR_STATEMENT : {
        ExprStatement *y = (ExprStatement *)x;
        out << "ExprStatement(" << y->expr << ")";
        break;
    }
    case WHILE : {
        While *y = (While *)x;
        out << "While(" << y->condition << ", " << y->body << ")";
        break;
    }
    case BREAK : {
        out << "Break()";
        break;
    }
    case CONTINUE : {
        out << "Continue()";
        break;
    }
    case FOR : {
        For *y = (For *)x;
        out << "For(" << y->variable << ", " << y->expr;
        out << ", " << y->body << ")";
        break;
    }

    case CODE : {
        Code *y = (Code *)x;
        out << "Code(" << y->patternVars << ", " << y->predicate;
        out << ", " << y->formalArgs << ", " << y->body << ")";
        break;
    }
    case VALUE_ARG : {
        ValueArg *y = (ValueArg *)x;
        out << "ValueArg(" << y->name << ", " << y->type << ")";
        break;
    }
    case STATIC_ARG : {
        StaticArg *y = (StaticArg *)x;
        out << "StaticArg(" << y->pattern << ")";
        break;
    }

    case RECORD : {
        Record *y = (Record *)x;
        out << "Record(" << y->name << ", " << y->patternVars;
        out << ", " << y->formalArgs << ")";
        break;
    }
    case PROCEDURE : {
        Procedure *y = (Procedure *)x;
        out << "Procedure(" << y->name << ", " << y->code << ")";
        break;
    }
    case OVERLOAD : {
        Overload *y = (Overload *)x;
        out << "Overload(" << y->name << ", " << y->code << ")";
        break;
    }
    case OVERLOADABLE : {
        Overloadable *y = (Overloadable *)x;
        out << "Overloadable(" << y->name << ")";
        break;
    }
    case EXTERNAL_PROCEDURE : {
        ExternalProcedure *y = (ExternalProcedure *)x;
        out << "ExternalProcedure(" << y->name << ", " << y->args;
        out << ", " << y->returnType << ")";
        break;
    }

    case IMPORT : {
        Import *y = (Import *)x;
        out << "Import(" << y->dottedName << ")";
        break;
    }
    case EXPORT : {
        Export *y = (Export *)x;
        out << "Export(" << y->dottedName << ")";
        break;
    }
    case MODULE : {
        Module *y = (Module *)x;
        out << "Module(" << bigVec(y->imports) << ", " << bigVec(y->exports);
        out << ", " << bigVec(y->topLevelItems) << ")";
        break;
    }

    case TYPE : {
        Type *y = (Type *)x;
        typePrint(y, out);
        break;
    }

    case VALUE : {
        Value *y = (Value *)x;
        valuePrint(y, out);
        break;
    }

    default :
        assert(false);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2008-2014 The QXmpp developers
 *
 * Authors:
 *  Jeremy Lainé
 *
 * Source:
 *  https://github.com/qxmpp-project/qxmpp
 *
 * This file is a part of QXmpp library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 */

#include <QBuffer>
#include <QObject>

#include "QXmppClient.h"
#include "QXmppServer.h"
#include "QXmppTransferManager.h"
#include "util.h"

Q_DECLARE_METATYPE(QXmppTransferJob::Method)

class tst_QXmppTransferManager : public QObject
{
    Q_OBJECT

private slots:
    void init();
    void testSendFile_data();
    void testSendFile();

    void acceptFile(QXmppTransferJob *job);

private:
    QBuffer receiverBuffer;
    QXmppTransferJob *receiverJob;
};

void tst_QXmppTransferManager::init()
{
    receiverBuffer.close();
    receiverBuffer.setData(QByteArray());
    receiverJob = 0;
}

void tst_QXmppTransferManager::acceptFile(QXmppTransferJob *job)
{
    receiverJob = job;
    receiverBuffer.open(QIODevice::WriteOnly);
    job->accept(&receiverBuffer);
}

void tst_QXmppTransferManager::testSendFile_data()
{
    QTest::addColumn<QXmppTransferJob::Method>("senderMethods");
    QTest::addColumn<QXmppTransferJob::Method>("receiverMethods");
    QTest::addColumn<bool>("works");

    QTest::newRow("any - any") << QXmppTransferJob::AnyMethod << QXmppTransferJob::AnyMethod << true;
    QTest::newRow("any - inband") << QXmppTransferJob::AnyMethod << QXmppTransferJob::InBandMethod << true;
    QTest::newRow("any - socks") << QXmppTransferJob::AnyMethod << QXmppTransferJob::SocksMethod << true;

    QTest::newRow("inband - any") << QXmppTransferJob::InBandMethod << QXmppTransferJob::AnyMethod << true;
    QTest::newRow("inband - inband") << QXmppTransferJob::InBandMethod << QXmppTransferJob::InBandMethod << true;
    QTest::newRow("inband - socks") << QXmppTransferJob::InBandMethod << QXmppTransferJob::SocksMethod << false;

    QTest::newRow("socks - any") << QXmppTransferJob::SocksMethod << QXmppTransferJob::AnyMethod << true;
    QTest::newRow("socks - inband") << QXmppTransferJob::SocksMethod << QXmppTransferJob::InBandMethod << false;
    QTest::newRow("socks - socks") << QXmppTransferJob::SocksMethod << QXmppTransferJob::SocksMethod << true;
}

void tst_QXmppTransferManager::testSendFile()
{
    QFETCH(QXmppTransferJob::Method, senderMethods);
    QFETCH(QXmppTransferJob::Method, receiverMethods);
    QFETCH(bool, works);

    const QString testDomain("localhost");
    const QHostAddress testHost(QHostAddress::LocalHost);
    const quint16 testPort = 12345;

    QXmppLogger logger;
    //logger.setLoggingType(QXmppLogger::StdoutLogging);

    // prepare server
    TestPasswordChecker passwordChecker;
    passwordChecker.addCredentials("sender", "testpwd");
    passwordChecker.addCredentials("receiver", "testpwd");

    QXmppServer server;
    server.setDomain(testDomain);
    server.setLogger(&logger);
    server.setPasswordChecker(&passwordChecker);
    server.listenForClients(testHost, testPort);

    // prepare sender
    QXmppClient sender;
    QXmppTransferManager *senderManager = new QXmppTransferManager;
    senderManager->setSupportedMethods(senderMethods);
    sender.addExtension(senderManager);
    sender.setLogger(&logger);

    QEventLoop senderLoop;
    connect(&sender, SIGNAL(connected()), &senderLoop, SLOT(quit()));
    connect(&sender, SIGNAL(disconnected()), &senderLoop, SLOT(quit()));

    QXmppConfiguration config;
    config.setDomain(testDomain);
    config.setHost(testHost.toString());
    config.setPort(testPort);
    config.setUser("sender");
    config.setPassword("testpwd");
    sender.connectToServer(config);
    senderLoop.exec();
    QCOMPARE(sender.isConnected(), true);

    // prepare receiver
    QXmppClient receiver;
    QXmppTransferManager *receiverManager = new QXmppTransferManager;
    receiverManager->setSupportedMethods(receiverMethods);
    connect(receiverManager, SIGNAL(fileReceived(QXmppTransferJob*)),
            this, SLOT(acceptFile(QXmppTransferJob*)));
    receiver.addExtension(receiverManager);
    receiver.setLogger(&logger);

    QEventLoop receiverLoop;
    connect(&receiver, SIGNAL(connected()), &receiverLoop, SLOT(quit()));
    connect(&receiver, SIGNAL(disconnected()), &receiverLoop, SLOT(quit()));

    config.setUser("receiver");
    config.setPassword("testpwd");
    receiver.connectToServer(config);
    receiverLoop.exec();
    QCOMPARE(receiver.isConnected(), true);

    // send file
    QEventLoop loop;
    QXmppTransferJob *senderJob = senderManager->sendFile("receiver@localhost/QXmpp", ":/test.svg");
    QVERIFY(senderJob);
    QCOMPARE(senderJob->localFileUrl().toLocalFile(), QString(":/test.svg"));
    connect(senderJob, SIGNAL(finished()), &loop, SLOT(quit()));
    loop.exec();

    if (works) {
        QCOMPARE(senderJob->state(), QXmppTransferJob::FinishedState);
        QCOMPARE(senderJob->error(), QXmppTransferJob::NoError);

        // finish receiving file
        QVERIFY(receiverJob);
        connect(receiverJob, SIGNAL(finished()), &loop, SLOT(quit()));
        loop.exec();

        QCOMPARE(receiverJob->state(), QXmppTransferJob::FinishedState);
        QCOMPARE(receiverJob->error(), QXmppTransferJob::NoError);

        // check received file
        QFile expectedFile(":/test.svg");
        QVERIFY(expectedFile.open(QIODevice::ReadOnly));
        const QByteArray expectedData = expectedFile.readAll();
        QCOMPARE(receiverBuffer.data(), expectedData);
    } else {
        QCOMPARE(senderJob->state(), QXmppTransferJob::FinishedState);
        QCOMPARE(senderJob->error(), QXmppTransferJob::AbortError);

        QVERIFY(!receiverJob);

        QCOMPARE(receiverBuffer.data(), QByteArray());
    }
}

QTEST_MAIN(tst_QXmppTransferManager)
#include "tst_qxmpptransfermanager.moc"
<commit_msg>Change test case for c4f27ae, compare localFileUrl directly<commit_after>/*
 * Copyright (C) 2008-2014 The QXmpp developers
 *
 * Authors:
 *  Jeremy Lainé
 *
 * Source:
 *  https://github.com/qxmpp-project/qxmpp
 *
 * This file is a part of QXmpp library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 */

#include <QBuffer>
#include <QObject>

#include "QXmppClient.h"
#include "QXmppServer.h"
#include "QXmppTransferManager.h"
#include "util.h"

Q_DECLARE_METATYPE(QXmppTransferJob::Method)

class tst_QXmppTransferManager : public QObject
{
    Q_OBJECT

private slots:
    void init();
    void testSendFile_data();
    void testSendFile();

    void acceptFile(QXmppTransferJob *job);

private:
    QBuffer receiverBuffer;
    QXmppTransferJob *receiverJob;
};

void tst_QXmppTransferManager::init()
{
    receiverBuffer.close();
    receiverBuffer.setData(QByteArray());
    receiverJob = 0;
}

void tst_QXmppTransferManager::acceptFile(QXmppTransferJob *job)
{
    receiverJob = job;
    receiverBuffer.open(QIODevice::WriteOnly);
    job->accept(&receiverBuffer);
}

void tst_QXmppTransferManager::testSendFile_data()
{
    QTest::addColumn<QXmppTransferJob::Method>("senderMethods");
    QTest::addColumn<QXmppTransferJob::Method>("receiverMethods");
    QTest::addColumn<bool>("works");

    QTest::newRow("any - any") << QXmppTransferJob::AnyMethod << QXmppTransferJob::AnyMethod << true;
    QTest::newRow("any - inband") << QXmppTransferJob::AnyMethod << QXmppTransferJob::InBandMethod << true;
    QTest::newRow("any - socks") << QXmppTransferJob::AnyMethod << QXmppTransferJob::SocksMethod << true;

    QTest::newRow("inband - any") << QXmppTransferJob::InBandMethod << QXmppTransferJob::AnyMethod << true;
    QTest::newRow("inband - inband") << QXmppTransferJob::InBandMethod << QXmppTransferJob::InBandMethod << true;
    QTest::newRow("inband - socks") << QXmppTransferJob::InBandMethod << QXmppTransferJob::SocksMethod << false;

    QTest::newRow("socks - any") << QXmppTransferJob::SocksMethod << QXmppTransferJob::AnyMethod << true;
    QTest::newRow("socks - inband") << QXmppTransferJob::SocksMethod << QXmppTransferJob::InBandMethod << false;
    QTest::newRow("socks - socks") << QXmppTransferJob::SocksMethod << QXmppTransferJob::SocksMethod << true;
}

void tst_QXmppTransferManager::testSendFile()
{
    QFETCH(QXmppTransferJob::Method, senderMethods);
    QFETCH(QXmppTransferJob::Method, receiverMethods);
    QFETCH(bool, works);

    const QString testDomain("localhost");
    const QHostAddress testHost(QHostAddress::LocalHost);
    const quint16 testPort = 12345;

    QXmppLogger logger;
    //logger.setLoggingType(QXmppLogger::StdoutLogging);

    // prepare server
    TestPasswordChecker passwordChecker;
    passwordChecker.addCredentials("sender", "testpwd");
    passwordChecker.addCredentials("receiver", "testpwd");

    QXmppServer server;
    server.setDomain(testDomain);
    server.setLogger(&logger);
    server.setPasswordChecker(&passwordChecker);
    server.listenForClients(testHost, testPort);

    // prepare sender
    QXmppClient sender;
    QXmppTransferManager *senderManager = new QXmppTransferManager;
    senderManager->setSupportedMethods(senderMethods);
    sender.addExtension(senderManager);
    sender.setLogger(&logger);

    QEventLoop senderLoop;
    connect(&sender, SIGNAL(connected()), &senderLoop, SLOT(quit()));
    connect(&sender, SIGNAL(disconnected()), &senderLoop, SLOT(quit()));

    QXmppConfiguration config;
    config.setDomain(testDomain);
    config.setHost(testHost.toString());
    config.setPort(testPort);
    config.setUser("sender");
    config.setPassword("testpwd");
    sender.connectToServer(config);
    senderLoop.exec();
    QCOMPARE(sender.isConnected(), true);

    // prepare receiver
    QXmppClient receiver;
    QXmppTransferManager *receiverManager = new QXmppTransferManager;
    receiverManager->setSupportedMethods(receiverMethods);
    connect(receiverManager, SIGNAL(fileReceived(QXmppTransferJob*)),
            this, SLOT(acceptFile(QXmppTransferJob*)));
    receiver.addExtension(receiverManager);
    receiver.setLogger(&logger);

    QEventLoop receiverLoop;
    connect(&receiver, SIGNAL(connected()), &receiverLoop, SLOT(quit()));
    connect(&receiver, SIGNAL(disconnected()), &receiverLoop, SLOT(quit()));

    config.setUser("receiver");
    config.setPassword("testpwd");
    receiver.connectToServer(config);
    receiverLoop.exec();
    QCOMPARE(receiver.isConnected(), true);

    // send file
    QEventLoop loop;
    QXmppTransferJob *senderJob = senderManager->sendFile("receiver@localhost/QXmpp", ":/test.svg");
    QVERIFY(senderJob);
    QCOMPARE(senderJob->localFileUrl(), QUrl::fromLocalFile(":/test.svg"));
    connect(senderJob, SIGNAL(finished()), &loop, SLOT(quit()));
    loop.exec();

    if (works) {
        QCOMPARE(senderJob->state(), QXmppTransferJob::FinishedState);
        QCOMPARE(senderJob->error(), QXmppTransferJob::NoError);

        // finish receiving file
        QVERIFY(receiverJob);
        connect(receiverJob, SIGNAL(finished()), &loop, SLOT(quit()));
        loop.exec();

        QCOMPARE(receiverJob->state(), QXmppTransferJob::FinishedState);
        QCOMPARE(receiverJob->error(), QXmppTransferJob::NoError);

        // check received file
        QFile expectedFile(":/test.svg");
        QVERIFY(expectedFile.open(QIODevice::ReadOnly));
        const QByteArray expectedData = expectedFile.readAll();
        QCOMPARE(receiverBuffer.data(), expectedData);
    } else {
        QCOMPARE(senderJob->state(), QXmppTransferJob::FinishedState);
        QCOMPARE(senderJob->error(), QXmppTransferJob::AbortError);

        QVERIFY(!receiverJob);

        QCOMPARE(receiverBuffer.data(), QByteArray());
    }
}

QTEST_MAIN(tst_QXmppTransferManager)
#include "tst_qxmpptransfermanager.moc"
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2003-2010, John Wiegley.  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 New Artisans LLC 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
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <system.hh>

#include "pyinterp.h"
#include "account.h"
#include "xact.h"
#include "post.h"

namespace ledger {

using namespace python;

shared_ptr<python_interpreter_t> python_session;

char * argv0;

void export_account();
void export_amount();
void export_balance();
void export_commodity();
void export_expr();
void export_format();
void export_item();
void export_journal();
void export_post();
void export_times();
void export_utils();
void export_value();
void export_xact();

void initialize_for_python()
{
  export_times();
  export_utils();
  export_commodity();
  export_amount();
  export_value();
  export_account();
  export_balance();
  export_expr();
  export_format();
  export_item();
  export_post();
  export_xact();
  export_journal();
}

struct python_run
{
  object result;

  python_run(python_interpreter_t * intepreter,
             const string& str, int input_mode)
    : result(handle<>(borrowed(PyRun_String(str.c_str(), input_mode,
                                            intepreter->main_nspace.ptr(),
                                            intepreter->main_nspace.ptr())))) {}
  operator object() {
    return result;
  }
};

void python_interpreter_t::initialize()
{
  TRACE_START(python_init, 1, "Initialized Python");

  try {
    DEBUG("python.interp", "Initializing Python");

    Py_Initialize();
    assert(Py_IsInitialized());

    hack_system_paths();

    object main_module = python::import("__main__");
    if (! main_module)
      throw_(std::runtime_error,
             _("Python failed to initialize (couldn't find __main__)"));

    main_nspace = extract<dict>(main_module.attr("__dict__"));
    if (! main_nspace)
      throw_(std::runtime_error,
             _("Python failed to initialize (couldn't find __dict__)"));

    python::detail::init_module("ledger", &initialize_for_python);

    is_initialized = true;
  }
  catch (const error_already_set&) {
    PyErr_Print();
    throw_(std::runtime_error, _("Python failed to initialize"));
  }

  TRACE_FINISH(python_init, 1);
}

void python_interpreter_t::hack_system_paths()
{
  // Hack ledger.__path__ so it points to a real location
  python::object sys_module = python::import("sys");
  python::object sys_dict   = sys_module.attr("__dict__");

  python::list paths(sys_dict["path"]);

#if defined(DEBUG_ON)
  bool path_initialized = false;
#endif
  int n = python::extract<int>(paths.attr("__len__")());
  for (int i = 0; i < n; i++) {
    python::extract<std::string> str(paths[i]);
    path pathname(str());
    DEBUG("python.interp", "sys.path = " << pathname);

    if (exists(pathname / "ledger" / "__init__.py")) {
      if (python::object module_ledger = python::import("ledger")) {
        DEBUG("python.interp",
              "Setting ledger.__path__ = " << (pathname / "ledger"));

        python::object ledger_dict = module_ledger.attr("__dict__");
        python::list temp_list;
        temp_list.append((pathname / "ledger").string());

        ledger_dict["__path__"] = temp_list;
      } else {
        throw_(std::runtime_error,
               _("Python failed to initialize (couldn't find ledger)"));
      }
#if defined(DEBUG_ON)
      path_initialized = true;
#endif
      break;
    }
  }
#if defined(DEBUG_ON)
  if (! path_initialized)
    DEBUG("python.init",
          "Ledger failed to find 'ledger/__init__.py' on the PYTHONPATH");
#endif
}

object python_interpreter_t::import_into_main(const string& str)
{
  if (! is_initialized)
    initialize();

  try {
    object mod = python::import(str.c_str());
    if (! mod)
      throw_(std::runtime_error,
             _("Failed to import Python module %1") << str);

    // Import all top-level entries directly into the main namespace
    main_nspace.update(mod.attr("__dict__"));

    return mod;
  }
  catch (const error_already_set&) {
    PyErr_Print();
  }
  return object();
}

object python_interpreter_t::import_option(const string& str)
{
  path file(str);

  python::object sys_module = python::import("sys");
  python::object sys_dict   = sys_module.attr("__dict__");

  python::list paths(sys_dict["path"]);

#if BOOST_VERSION >= 103700
  paths.insert(0, file.parent_path().string());
  sys_dict["path"] = paths;

#if BOOST_VERSION >= 104600
  string name = file.filename().string();
  if (contains(name, ".py"))
    name = file.stem().string();
#else
  string name = file.filename();
  if (contains(name, ".py"))
    name = file.stem();
#endif
#else // BOOST_VERSION >= 103700
  paths.insert(0, file.branch_path().string());
  sys_dict["path"] = paths;

  string name = file.leaf();
#endif // BOOST_VERSION >= 103700

  return python::import(python::str(name.c_str()));
}

object python_interpreter_t::eval(std::istream& in, py_eval_mode_t mode)
{
  bool   first = true;
  string buffer;
  buffer.reserve(4096);

  while (! in.eof()) {
    char buf[256];
    in.getline(buf, 255);
    if (buf[0] == '!')
      break;
    if (first)
      first = false;
    else
      buffer += "\n";
    buffer += buf;
  }

  if (! is_initialized)
    initialize();

  try {
    int input_mode = -1;
    switch (mode) {
    case PY_EVAL_EXPR:  input_mode = Py_eval_input;   break;
    case PY_EVAL_STMT:  input_mode = Py_single_input; break;
    case PY_EVAL_MULTI: input_mode = Py_file_input;   break;
    }

    return python_run(this, buffer, input_mode);
  }
  catch (const error_already_set&) {
    PyErr_Print();
    throw_(std::runtime_error, _("Failed to evaluate Python code"));
  }
  return object();
}

object python_interpreter_t::eval(const string& str, py_eval_mode_t mode)
{
  if (! is_initialized)
    initialize();

  try {
    int input_mode = -1;
    switch (mode) {
    case PY_EVAL_EXPR:  input_mode = Py_eval_input;   break;
    case PY_EVAL_STMT:  input_mode = Py_single_input; break;
    case PY_EVAL_MULTI: input_mode = Py_file_input;   break;
    }

    return python_run(this, str, input_mode);
  }
  catch (const error_already_set&) {
    PyErr_Print();
    throw_(std::runtime_error, _("Failed to evaluate Python code"));
  }
  return object();
}

value_t python_interpreter_t::python_command(call_scope_t& args)
{
  if (! is_initialized)
    initialize();

  char ** argv(new char *[args.size() + 1]);

  argv[0] = new char[std::strlen(argv0) + 1];
  std::strcpy(argv[0], argv0);

  for (std::size_t i = 0; i < args.size(); i++) {
    string arg = args.get<string>(i);
    argv[i + 1] = new char[arg.length() + 1];
    std::strcpy(argv[i + 1], arg.c_str());
  }

  int status = 1;

  try {
    status = Py_Main(static_cast<int>(args.size()) + 1, argv);
  }
  catch (const error_already_set&) {
    PyErr_Print();
    throw_(std::runtime_error, _("Failed to execute Python module"));
  }
  catch (...) {
    for (std::size_t i = 0; i < args.size() + 1; i++)
      delete[] argv[i];
    delete[] argv;
    throw;
  }

  for (std::size_t i = 0; i < args.size() + 1; i++)
    delete[] argv[i];
  delete[] argv;

  if (status != 0)
    throw status;

  return NULL_VALUE;
}

value_t python_interpreter_t::server_command(call_scope_t& args)
{
  if (! is_initialized)
    initialize();

  python::object server_module;

  try {
    server_module = python::import("ledger.server");
    if (! server_module)
      throw_(std::runtime_error,
             _("Could not import ledger.server; please check your PYTHONPATH"));
  }
  catch (const error_already_set&) {
    PyErr_Print();
    throw_(std::runtime_error,
           _("Could not import ledger.server; please check your PYTHONPATH"));
  }

  if (python::object main_function = server_module.attr("main")) {
    functor_t func(main_function, "main");
    try {
      func(args);
    }
    catch (const error_already_set&) {
      PyErr_Print();
      throw_(std::runtime_error,
             _("Error while invoking ledger.server's main() function"));
    }
    return true;
  } else {
      throw_(std::runtime_error,
             _("The ledger.server module is missing its main() function!"));
  }

  return false;
}

option_t<python_interpreter_t> *
python_interpreter_t::lookup_option(const char * p)
{
  switch (*p) {
  case 'i':
    OPT(import_);
    break;
  }
  return NULL;
}

expr_t::ptr_op_t python_interpreter_t::lookup(const symbol_t::kind_t kind,
                                              const string& name)
{
  // Give our superclass first dibs on symbol definitions
  if (expr_t::ptr_op_t op = session_t::lookup(kind, name))
    return op;

  switch (kind) {
  case symbol_t::FUNCTION:
    if (option_t<python_interpreter_t> * handler = lookup_option(name.c_str()))
      return MAKE_OPT_FUNCTOR(python_interpreter_t, handler);

    if (is_initialized && main_nspace.has_key(name.c_str())) {
      DEBUG("python.interp", "Python lookup: " << name);

      if (python::object obj = main_nspace.get(name.c_str()))
        return WRAP_FUNCTOR(functor_t(obj, name));
    }
    break;

  case symbol_t::OPTION:
    if (option_t<python_interpreter_t> * handler = lookup_option(name.c_str()))
      return MAKE_OPT_HANDLER(python_interpreter_t, handler);
    break;

  case symbol_t::PRECOMMAND: {
    const char * p = name.c_str();
    switch (*p) {
    case 'p':
      if (is_eq(p, "python"))
        return MAKE_FUNCTOR(python_interpreter_t::python_command);
      break;

    case 's':
      if (is_eq(p, "server"))
        return MAKE_FUNCTOR(python_interpreter_t::server_command);
      break;
    }
  }

  default:
    break;
  }

  return NULL;
}

namespace {
  void append_value(list& lst, const value_t& value)
  {
    if (value.is_scope()) {
      const scope_t * scope = value.as_scope();
      if (const post_t * post = dynamic_cast<const post_t *>(scope))
        lst.append(ptr(post));
      else if (const xact_t * xact = dynamic_cast<const xact_t *>(scope))
        lst.append(ptr(xact));
      else if (const account_t * account =
               dynamic_cast<const account_t *>(scope))
        lst.append(ptr(account));
      else if (const period_xact_t * period_xact =
               dynamic_cast<const period_xact_t *>(scope))
        lst.append(ptr(period_xact));
      else if (const auto_xact_t * auto_xact =
               dynamic_cast<const auto_xact_t *>(scope))
        lst.append(ptr(auto_xact));
      else
        throw_(std::logic_error,
               _("Cannot downcast scoped object to specific type"));
    } else {
      lst.append(value);
    }
  }
}

value_t python_interpreter_t::functor_t::operator()(call_scope_t& args)
{
  try {
    std::signal(SIGINT, SIG_DFL);

    if (! PyCallable_Check(func.ptr())) {
      extract<value_t> val(func);
      std::signal(SIGINT, sigint_handler);
      if (val.check())
        return val();
      return NULL_VALUE;
    }
    else if (args.size() > 0) {
      list arglist;
      // jww (2009-11-05): What about a single argument which is a sequence,
      // rather than a sequence of arguments?
      if (args.value().is_sequence())
        foreach (const value_t& value, args.value().as_sequence())
          append_value(arglist, value);
      else
        append_value(arglist, args.value());

      if (PyObject * val =
          PyObject_CallObject(func.ptr(), python::tuple(arglist).ptr())) {
        extract<value_t> xval(val);
        value_t result;
        if (xval.check()) {
          result = xval();
          Py_DECREF(val);
        } else {
          Py_DECREF(val);
          throw_(calc_error,
                 _("Could not evaluate Python variable '%1'") << name);
        }
        std::signal(SIGINT, sigint_handler);
        return result;
      }
      else if (PyErr_Occurred()) {
        PyErr_Print();
        throw_(calc_error, _("Failed call to Python function '%1'") << name);
      } else {
        assert(false);
      }
    }
    else {
      std::signal(SIGINT, sigint_handler);
      return call<value_t>(func.ptr());
    }
  }
  catch (const error_already_set&) {
    std::signal(SIGINT, sigint_handler);
    PyErr_Print();
    throw_(calc_error, _("Failed call to Python function '%1'") << name);
  }
  catch (...) {
    std::signal(SIGINT, sigint_handler);
  }
  std::signal(SIGINT, sigint_handler);

  return NULL_VALUE;
}

} // namespace ledger
<commit_msg>Fixed Python initialization problem with --import<commit_after>/*
 * Copyright (c) 2003-2010, John Wiegley.  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 New Artisans LLC 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
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <system.hh>

#include "pyinterp.h"
#include "account.h"
#include "xact.h"
#include "post.h"

namespace ledger {

using namespace python;

shared_ptr<python_interpreter_t> python_session;

char * argv0;

void export_account();
void export_amount();
void export_balance();
void export_commodity();
void export_expr();
void export_format();
void export_item();
void export_journal();
void export_post();
void export_times();
void export_utils();
void export_value();
void export_xact();

void initialize_for_python()
{
  export_times();
  export_utils();
  export_commodity();
  export_amount();
  export_value();
  export_account();
  export_balance();
  export_expr();
  export_format();
  export_item();
  export_post();
  export_xact();
  export_journal();
}

struct python_run
{
  object result;

  python_run(python_interpreter_t * intepreter,
             const string& str, int input_mode)
    : result(handle<>(borrowed(PyRun_String(str.c_str(), input_mode,
                                            intepreter->main_nspace.ptr(),
                                            intepreter->main_nspace.ptr())))) {}
  operator object() {
    return result;
  }
};

void python_interpreter_t::initialize()
{
  TRACE_START(python_init, 1, "Initialized Python");

  try {
    DEBUG("python.interp", "Initializing Python");

    Py_Initialize();
    assert(Py_IsInitialized());

    hack_system_paths();

    object main_module = python::import("__main__");
    if (! main_module)
      throw_(std::runtime_error,
             _("Python failed to initialize (couldn't find __main__)"));

    main_nspace = extract<dict>(main_module.attr("__dict__"));
    if (! main_nspace)
      throw_(std::runtime_error,
             _("Python failed to initialize (couldn't find __dict__)"));

    python::detail::init_module("ledger", &initialize_for_python);

    is_initialized = true;
  }
  catch (const error_already_set&) {
    PyErr_Print();
    throw_(std::runtime_error, _("Python failed to initialize"));
  }

  TRACE_FINISH(python_init, 1);
}

void python_interpreter_t::hack_system_paths()
{
  // Hack ledger.__path__ so it points to a real location
  python::object sys_module = python::import("sys");
  python::object sys_dict   = sys_module.attr("__dict__");

  python::list paths(sys_dict["path"]);

#if defined(DEBUG_ON)
  bool path_initialized = false;
#endif
  int n = python::extract<int>(paths.attr("__len__")());
  for (int i = 0; i < n; i++) {
    python::extract<std::string> str(paths[i]);
    path pathname(str());
    DEBUG("python.interp", "sys.path = " << pathname);

    if (exists(pathname / "ledger" / "__init__.py")) {
      if (python::object module_ledger = python::import("ledger")) {
        DEBUG("python.interp",
              "Setting ledger.__path__ = " << (pathname / "ledger"));

        python::object ledger_dict = module_ledger.attr("__dict__");
        python::list temp_list;
        temp_list.append((pathname / "ledger").string());

        ledger_dict["__path__"] = temp_list;
      } else {
        throw_(std::runtime_error,
               _("Python failed to initialize (couldn't find ledger)"));
      }
#if defined(DEBUG_ON)
      path_initialized = true;
#endif
      break;
    }
  }
#if defined(DEBUG_ON)
  if (! path_initialized)
    DEBUG("python.init",
          "Ledger failed to find 'ledger/__init__.py' on the PYTHONPATH");
#endif
}

object python_interpreter_t::import_into_main(const string& str)
{
  if (! is_initialized)
    initialize();

  try {
    object mod = python::import(str.c_str());
    if (! mod)
      throw_(std::runtime_error,
             _("Failed to import Python module %1") << str);

    // Import all top-level entries directly into the main namespace
    main_nspace.update(mod.attr("__dict__"));

    return mod;
  }
  catch (const error_already_set&) {
    PyErr_Print();
  }
  return object();
}

object python_interpreter_t::import_option(const string& str)
{
  if (! is_initialized)
    initialize();

  path file(str);
  string name(str);

  python::object sys_module = python::import("sys");
  python::object sys_dict   = sys_module.attr("__dict__");

  python::list paths(sys_dict["path"]);

  if (contains(str, ".py")) {
#if BOOST_VERSION >= 103700
    path& cwd(get_parsing_context().current_directory);
    paths.insert(0, filesystem::absolute(file, cwd).parent_path().string());
    sys_dict["path"] = paths;

#if BOOST_VERSION >= 104600
    name = file.stem().string();
#else
    name = file.stem();
#endif
#else // BOOST_VERSION >= 103700
    paths.insert(0, file.branch_path().string());
    sys_dict["path"] = paths;
    name = file.leaf();
#endif // BOOST_VERSION >= 103700
  }

  return python::import(python::str(name.c_str()));
}

object python_interpreter_t::eval(std::istream& in, py_eval_mode_t mode)
{
  bool   first = true;
  string buffer;
  buffer.reserve(4096);

  while (! in.eof()) {
    char buf[256];
    in.getline(buf, 255);
    if (buf[0] == '!')
      break;
    if (first)
      first = false;
    else
      buffer += "\n";
    buffer += buf;
  }

  if (! is_initialized)
    initialize();

  try {
    int input_mode = -1;
    switch (mode) {
    case PY_EVAL_EXPR:  input_mode = Py_eval_input;   break;
    case PY_EVAL_STMT:  input_mode = Py_single_input; break;
    case PY_EVAL_MULTI: input_mode = Py_file_input;   break;
    }

    return python_run(this, buffer, input_mode);
  }
  catch (const error_already_set&) {
    PyErr_Print();
    throw_(std::runtime_error, _("Failed to evaluate Python code"));
  }
  return object();
}

object python_interpreter_t::eval(const string& str, py_eval_mode_t mode)
{
  if (! is_initialized)
    initialize();

  try {
    int input_mode = -1;
    switch (mode) {
    case PY_EVAL_EXPR:  input_mode = Py_eval_input;   break;
    case PY_EVAL_STMT:  input_mode = Py_single_input; break;
    case PY_EVAL_MULTI: input_mode = Py_file_input;   break;
    }

    return python_run(this, str, input_mode);
  }
  catch (const error_already_set&) {
    PyErr_Print();
    throw_(std::runtime_error, _("Failed to evaluate Python code"));
  }
  return object();
}

value_t python_interpreter_t::python_command(call_scope_t& args)
{
  if (! is_initialized)
    initialize();

  char ** argv(new char *[args.size() + 1]);

  argv[0] = new char[std::strlen(argv0) + 1];
  std::strcpy(argv[0], argv0);

  for (std::size_t i = 0; i < args.size(); i++) {
    string arg = args.get<string>(i);
    argv[i + 1] = new char[arg.length() + 1];
    std::strcpy(argv[i + 1], arg.c_str());
  }

  int status = 1;

  try {
    status = Py_Main(static_cast<int>(args.size()) + 1, argv);
  }
  catch (const error_already_set&) {
    PyErr_Print();
    throw_(std::runtime_error, _("Failed to execute Python module"));
  }
  catch (...) {
    for (std::size_t i = 0; i < args.size() + 1; i++)
      delete[] argv[i];
    delete[] argv;
    throw;
  }

  for (std::size_t i = 0; i < args.size() + 1; i++)
    delete[] argv[i];
  delete[] argv;

  if (status != 0)
    throw status;

  return NULL_VALUE;
}

value_t python_interpreter_t::server_command(call_scope_t& args)
{
  if (! is_initialized)
    initialize();

  python::object server_module;

  try {
    server_module = python::import("ledger.server");
    if (! server_module)
      throw_(std::runtime_error,
             _("Could not import ledger.server; please check your PYTHONPATH"));
  }
  catch (const error_already_set&) {
    PyErr_Print();
    throw_(std::runtime_error,
           _("Could not import ledger.server; please check your PYTHONPATH"));
  }

  if (python::object main_function = server_module.attr("main")) {
    functor_t func(main_function, "main");
    try {
      func(args);
    }
    catch (const error_already_set&) {
      PyErr_Print();
      throw_(std::runtime_error,
             _("Error while invoking ledger.server's main() function"));
    }
    return true;
  } else {
      throw_(std::runtime_error,
             _("The ledger.server module is missing its main() function!"));
  }

  return false;
}

option_t<python_interpreter_t> *
python_interpreter_t::lookup_option(const char * p)
{
  switch (*p) {
  case 'i':
    OPT(import_);
    break;
  }
  return NULL;
}

expr_t::ptr_op_t python_interpreter_t::lookup(const symbol_t::kind_t kind,
                                              const string& name)
{
  // Give our superclass first dibs on symbol definitions
  if (expr_t::ptr_op_t op = session_t::lookup(kind, name))
    return op;

  switch (kind) {
  case symbol_t::FUNCTION:
    if (option_t<python_interpreter_t> * handler = lookup_option(name.c_str()))
      return MAKE_OPT_FUNCTOR(python_interpreter_t, handler);

    if (is_initialized && main_nspace.has_key(name.c_str())) {
      DEBUG("python.interp", "Python lookup: " << name);

      if (python::object obj = main_nspace.get(name.c_str()))
        return WRAP_FUNCTOR(functor_t(obj, name));
    }
    break;

  case symbol_t::OPTION:
    if (option_t<python_interpreter_t> * handler = lookup_option(name.c_str()))
      return MAKE_OPT_HANDLER(python_interpreter_t, handler);
    break;

  case symbol_t::PRECOMMAND: {
    const char * p = name.c_str();
    switch (*p) {
    case 'p':
      if (is_eq(p, "python"))
        return MAKE_FUNCTOR(python_interpreter_t::python_command);
      break;

    case 's':
      if (is_eq(p, "server"))
        return MAKE_FUNCTOR(python_interpreter_t::server_command);
      break;
    }
  }

  default:
    break;
  }

  return NULL;
}

namespace {
  void append_value(list& lst, const value_t& value)
  {
    if (value.is_scope()) {
      const scope_t * scope = value.as_scope();
      if (const post_t * post = dynamic_cast<const post_t *>(scope))
        lst.append(ptr(post));
      else if (const xact_t * xact = dynamic_cast<const xact_t *>(scope))
        lst.append(ptr(xact));
      else if (const account_t * account =
               dynamic_cast<const account_t *>(scope))
        lst.append(ptr(account));
      else if (const period_xact_t * period_xact =
               dynamic_cast<const period_xact_t *>(scope))
        lst.append(ptr(period_xact));
      else if (const auto_xact_t * auto_xact =
               dynamic_cast<const auto_xact_t *>(scope))
        lst.append(ptr(auto_xact));
      else
        throw_(std::logic_error,
               _("Cannot downcast scoped object to specific type"));
    } else {
      lst.append(value);
    }
  }
}

value_t python_interpreter_t::functor_t::operator()(call_scope_t& args)
{
  try {
    std::signal(SIGINT, SIG_DFL);

    if (! PyCallable_Check(func.ptr())) {
      extract<value_t> val(func);
      std::signal(SIGINT, sigint_handler);
      if (val.check())
        return val();
      return NULL_VALUE;
    }
    else if (args.size() > 0) {
      list arglist;
      // jww (2009-11-05): What about a single argument which is a sequence,
      // rather than a sequence of arguments?
      if (args.value().is_sequence())
        foreach (const value_t& value, args.value().as_sequence())
          append_value(arglist, value);
      else
        append_value(arglist, args.value());

      if (PyObject * val =
          PyObject_CallObject(func.ptr(), python::tuple(arglist).ptr())) {
        extract<value_t> xval(val);
        value_t result;
        if (xval.check()) {
          result = xval();
          Py_DECREF(val);
        } else {
          Py_DECREF(val);
          throw_(calc_error,
                 _("Could not evaluate Python variable '%1'") << name);
        }
        std::signal(SIGINT, sigint_handler);
        return result;
      }
      else if (PyErr_Occurred()) {
        PyErr_Print();
        throw_(calc_error, _("Failed call to Python function '%1'") << name);
      } else {
        assert(false);
      }
    }
    else {
      std::signal(SIGINT, sigint_handler);
      return call<value_t>(func.ptr());
    }
  }
  catch (const error_already_set&) {
    std::signal(SIGINT, sigint_handler);
    PyErr_Print();
    throw_(calc_error, _("Failed call to Python function '%1'") << name);
  }
  catch (...) {
    std::signal(SIGINT, sigint_handler);
  }
  std::signal(SIGINT, sigint_handler);

  return NULL_VALUE;
}

} // namespace ledger
<|endoftext|>


<commit_before>/*
Copyright libCellML 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

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 "xmlnode.h"

#include <algorithm>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <string>

#include "namespaces.h"
#include "xmlattribute.h"

namespace libcellml {

/**
 * @brief The XmlNode::XmlNodeImpl struct.
 *
 * This struct is the private implementation struct for the XmlNode class.  Separating
 * the implementation from the definition allows for greater flexibility when
 * distributing the code.
 */
struct XmlNode::XmlNodeImpl
{
    xmlNodePtr mXmlNodePtr;
};

XmlNode::XmlNode()
    : mPimpl(new XmlNodeImpl())
{
}

XmlNode::~XmlNode()
{
    delete mPimpl;
}

void XmlNode::setXmlNode(const xmlNodePtr &node)
{
    mPimpl->mXmlNodePtr = node;
}

std::string XmlNode::namespaceUri() const
{
    if (mPimpl->mXmlNodePtr->ns == nullptr) {
        return {};
    }
    return reinterpret_cast<const char *>(mPimpl->mXmlNodePtr->ns->href);
}

void XmlNode::addNamespaceDefinition(const std::string &uri, const std::string &prefix)
{
    xmlNsPtr nsPtr = xmlNewNs(mPimpl->mXmlNodePtr, reinterpret_cast<const xmlChar *>(uri.c_str()), reinterpret_cast<const xmlChar *>(prefix.c_str()));
    auto last = mPimpl->mXmlNodePtr->nsDef;
    while (last != nullptr) {
        last = last->next;
    }
    last = nsPtr;
}

void clearNamespace(const xmlNodePtr &node, xmlNsPtr ns)
{
    if (node->ns == ns) {
        node->ns = nullptr;
    }
    xmlAttrPtr attr = node->properties;
    while (attr != nullptr) {
        if (attr->ns == ns) {
            attr->ns = nullptr;
        }
        attr = attr->next;
    }
    if (node->children != nullptr) {
        clearNamespace(node->children, ns);
    }
    if (node->next != nullptr) {
        clearNamespace(node->next, ns);
    }
}

void XmlNode::removeNamespaceDefinition(const std::string &uri)
{
    xmlNsPtr previous = nullptr;
    xmlNsPtr next = nullptr;
    xmlNsPtr namespaceToRemove = nullptr;
    auto current = mPimpl->mXmlNodePtr->nsDef;
    while (current != nullptr) {
        next = current->next;
        namespaceToRemove = nullptr;
        if (xmlStrcmp(reinterpret_cast<const xmlChar *>(uri.c_str()), reinterpret_cast<const xmlChar *>(current->href)) == 0) {
            namespaceToRemove = current;
        } else {
            previous = current;
        }
        current = current->next;
        if (namespaceToRemove != nullptr) {
            if (previous == nullptr) {
                mPimpl->mXmlNodePtr->nsDef = next;
            } else {
                previous->next = next;
            }
            namespaceToRemove->next = nullptr;
            // Search subtree of this node and clear uses of the namespace.
            clearNamespace(mPimpl->mXmlNodePtr, namespaceToRemove);
            xmlFreeNs(namespaceToRemove);
        }
    }
}

bool XmlNode::hasNamespaceDefinition(const std::string &uri)
{
    if (mPimpl->mXmlNodePtr->nsDef != nullptr) {
        auto next = mPimpl->mXmlNodePtr->nsDef;
        while (next != nullptr) {
            std::string href;
            if (next->href != nullptr) {
                href = std::string(reinterpret_cast<const char *>(next->href));
            }
            if (href == uri) {
                return true;
            }
            next = next->next;
        }
    }
    return false;
}

XmlNamespaceMap XmlNode::definedNamespaces() const
{
    XmlNamespaceMap namespaceMap;
    if (mPimpl->mXmlNodePtr->nsDef != nullptr) {
        auto next = mPimpl->mXmlNodePtr->nsDef;
        while (next != nullptr) {
            std::string prefix;
            if (next->prefix != nullptr) {
                prefix = std::string(reinterpret_cast<const char *>(next->prefix));
            }
            std::string href;
            if (next->href != nullptr) {
                href = std::string(reinterpret_cast<const char *>(next->href));
            }
            namespaceMap[prefix] = href;
            next = next->next;
        }
    }
    return namespaceMap;
}

bool XmlNode::isElement(const char *name, const char *ns) const
{
    bool found = false;
    if ((mPimpl->mXmlNodePtr->type == XML_ELEMENT_NODE)
        && (xmlStrcmp(reinterpret_cast<const xmlChar *>(namespaceUri().c_str()), reinterpret_cast<const xmlChar *>(ns)) == 0)
        && ((name == nullptr) || (xmlStrcmp(mPimpl->mXmlNodePtr->name, reinterpret_cast<const xmlChar *>(name)) == 0))) {
        found = true;
    }
    return found;
}

bool XmlNode::isCellmlElement(const char *name) const
{
    return isElement(name, CELLML_2_0_NS);
}

bool XmlNode::isMathmlElement(const char *name) const
{
    return isElement(name, MATHML_NS);
}

bool XmlNode::isText() const
{
    return mPimpl->mXmlNodePtr->type == XML_TEXT_NODE;
}

bool XmlNode::isComment() const
{
    return mPimpl->mXmlNodePtr->type == XML_COMMENT_NODE;
}

std::string XmlNode::name() const
{
    return reinterpret_cast<const char *>(mPimpl->mXmlNodePtr->name);
}

bool XmlNode::hasAttribute(const char *attributeName) const
{
    bool found = false;
    xmlAttrPtr attribute = xmlHasProp(mPimpl->mXmlNodePtr, reinterpret_cast<const xmlChar *>(attributeName));
    if (attribute != nullptr) {
        found = true;
    }
    return found;
}

std::string XmlNode::attribute(const char *attributeName) const
{
    std::string attributeValueString;
    if (hasAttribute(attributeName)) {
        xmlChar *attributeValue = xmlGetProp(mPimpl->mXmlNodePtr, reinterpret_cast<const xmlChar *>(attributeName));
        attributeValueString = std::string(reinterpret_cast<const char *>(attributeValue));
        xmlFree(attributeValue);
    }
    return attributeValueString;
}

XmlAttributePtr XmlNode::firstAttribute() const
{
    xmlAttrPtr attribute = mPimpl->mXmlNodePtr->properties;
    XmlAttributePtr attributeHandle = nullptr;
    if (attribute != nullptr) {
        attributeHandle = std::make_shared<XmlAttribute>();
        attributeHandle->setXmlAttribute(attribute);
    }
    return attributeHandle;
}

XmlNodePtr XmlNode::firstChild() const
{
    xmlNodePtr child = mPimpl->mXmlNodePtr->children;
    XmlNodePtr childHandle = nullptr;
    bool nodeWithContentFound = false;
    while (child != nullptr && !nodeWithContentFound) {
        childHandle = std::make_shared<XmlNode>();
        childHandle->setXmlNode(child);
        bool textNode = childHandle->isText();
        if (textNode && !childHandle->convertToStrippedString().empty()) {
            nodeWithContentFound = true;
        } else if (!textNode) {
            nodeWithContentFound = true;
        } else {
            child = child->next;
        }
    }
    return childHandle;
}

XmlNodePtr XmlNode::next() const
{
    xmlNodePtr next = mPimpl->mXmlNodePtr->next;
    XmlNodePtr nextHandle = nullptr;
    if (next != nullptr) {
        nextHandle = std::make_shared<XmlNode>();
        nextHandle->setXmlNode(next);
    }
    return nextHandle;
}

XmlNodePtr XmlNode::parent() const
{
    xmlNodePtr parent = mPimpl->mXmlNodePtr->parent;
    XmlNodePtr parentHandle = nullptr;
    if (parent != nullptr) {
        parentHandle = std::make_shared<XmlNode>();
        parentHandle->setXmlNode(parent);
    }
    return parentHandle;
}

std::string XmlNode::convertToString(bool format) const
{
    std::string contentString;
    xmlBufferPtr buffer = xmlBufferCreate();
    if (format) {
        xmlKeepBlanksDefault(0);
    }
    int len = xmlNodeDump(buffer, mPimpl->mXmlNodePtr->doc, mPimpl->mXmlNodePtr, 0, format ? 1 : 0);
    if (len > 0) {
        contentString = std::string(reinterpret_cast<const char *>(buffer->content));
    }
    xmlBufferFree(buffer);
    return contentString;
}

std::string XmlNode::convertToStrippedString() const
{
    std::string contentString = convertToString();
    contentString.erase(contentString.begin(), find_if_not(contentString.begin(), contentString.end(), [](int c) { return isspace(c); }));
    contentString.erase(find_if_not(contentString.rbegin(), contentString.rend(), [](int c) { return isspace(c); }).base(), contentString.end());
    return contentString;
}

} // namespace libcellml
<commit_msg>XmlNode: simplified XmlNode::firstChild().<commit_after>/*
Copyright libCellML 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

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 "xmlnode.h"

#include <algorithm>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <string>

#include "namespaces.h"
#include "xmlattribute.h"

namespace libcellml {

/**
 * @brief The XmlNode::XmlNodeImpl struct.
 *
 * This struct is the private implementation struct for the XmlNode class.  Separating
 * the implementation from the definition allows for greater flexibility when
 * distributing the code.
 */
struct XmlNode::XmlNodeImpl
{
    xmlNodePtr mXmlNodePtr;
};

XmlNode::XmlNode()
    : mPimpl(new XmlNodeImpl())
{
}

XmlNode::~XmlNode()
{
    delete mPimpl;
}

void XmlNode::setXmlNode(const xmlNodePtr &node)
{
    mPimpl->mXmlNodePtr = node;
}

std::string XmlNode::namespaceUri() const
{
    if (mPimpl->mXmlNodePtr->ns == nullptr) {
        return {};
    }
    return reinterpret_cast<const char *>(mPimpl->mXmlNodePtr->ns->href);
}

void XmlNode::addNamespaceDefinition(const std::string &uri, const std::string &prefix)
{
    xmlNsPtr nsPtr = xmlNewNs(mPimpl->mXmlNodePtr, reinterpret_cast<const xmlChar *>(uri.c_str()), reinterpret_cast<const xmlChar *>(prefix.c_str()));
    auto last = mPimpl->mXmlNodePtr->nsDef;
    while (last != nullptr) {
        last = last->next;
    }
    last = nsPtr;
}

void clearNamespace(const xmlNodePtr &node, xmlNsPtr ns)
{
    if (node->ns == ns) {
        node->ns = nullptr;
    }
    xmlAttrPtr attr = node->properties;
    while (attr != nullptr) {
        if (attr->ns == ns) {
            attr->ns = nullptr;
        }
        attr = attr->next;
    }
    if (node->children != nullptr) {
        clearNamespace(node->children, ns);
    }
    if (node->next != nullptr) {
        clearNamespace(node->next, ns);
    }
}

void XmlNode::removeNamespaceDefinition(const std::string &uri)
{
    xmlNsPtr previous = nullptr;
    xmlNsPtr next = nullptr;
    xmlNsPtr namespaceToRemove = nullptr;
    auto current = mPimpl->mXmlNodePtr->nsDef;
    while (current != nullptr) {
        next = current->next;
        namespaceToRemove = nullptr;
        if (xmlStrcmp(reinterpret_cast<const xmlChar *>(uri.c_str()), reinterpret_cast<const xmlChar *>(current->href)) == 0) {
            namespaceToRemove = current;
        } else {
            previous = current;
        }
        current = current->next;
        if (namespaceToRemove != nullptr) {
            if (previous == nullptr) {
                mPimpl->mXmlNodePtr->nsDef = next;
            } else {
                previous->next = next;
            }
            namespaceToRemove->next = nullptr;
            // Search subtree of this node and clear uses of the namespace.
            clearNamespace(mPimpl->mXmlNodePtr, namespaceToRemove);
            xmlFreeNs(namespaceToRemove);
        }
    }
}

bool XmlNode::hasNamespaceDefinition(const std::string &uri)
{
    if (mPimpl->mXmlNodePtr->nsDef != nullptr) {
        auto next = mPimpl->mXmlNodePtr->nsDef;
        while (next != nullptr) {
            std::string href;
            if (next->href != nullptr) {
                href = std::string(reinterpret_cast<const char *>(next->href));
            }
            if (href == uri) {
                return true;
            }
            next = next->next;
        }
    }
    return false;
}

XmlNamespaceMap XmlNode::definedNamespaces() const
{
    XmlNamespaceMap namespaceMap;
    if (mPimpl->mXmlNodePtr->nsDef != nullptr) {
        auto next = mPimpl->mXmlNodePtr->nsDef;
        while (next != nullptr) {
            std::string prefix;
            if (next->prefix != nullptr) {
                prefix = std::string(reinterpret_cast<const char *>(next->prefix));
            }
            std::string href;
            if (next->href != nullptr) {
                href = std::string(reinterpret_cast<const char *>(next->href));
            }
            namespaceMap[prefix] = href;
            next = next->next;
        }
    }
    return namespaceMap;
}

bool XmlNode::isElement(const char *name, const char *ns) const
{
    bool found = false;
    if ((mPimpl->mXmlNodePtr->type == XML_ELEMENT_NODE)
        && (xmlStrcmp(reinterpret_cast<const xmlChar *>(namespaceUri().c_str()), reinterpret_cast<const xmlChar *>(ns)) == 0)
        && ((name == nullptr) || (xmlStrcmp(mPimpl->mXmlNodePtr->name, reinterpret_cast<const xmlChar *>(name)) == 0))) {
        found = true;
    }
    return found;
}

bool XmlNode::isCellmlElement(const char *name) const
{
    return isElement(name, CELLML_2_0_NS);
}

bool XmlNode::isMathmlElement(const char *name) const
{
    return isElement(name, MATHML_NS);
}

bool XmlNode::isText() const
{
    return mPimpl->mXmlNodePtr->type == XML_TEXT_NODE;
}

bool XmlNode::isComment() const
{
    return mPimpl->mXmlNodePtr->type == XML_COMMENT_NODE;
}

std::string XmlNode::name() const
{
    return reinterpret_cast<const char *>(mPimpl->mXmlNodePtr->name);
}

bool XmlNode::hasAttribute(const char *attributeName) const
{
    bool found = false;
    xmlAttrPtr attribute = xmlHasProp(mPimpl->mXmlNodePtr, reinterpret_cast<const xmlChar *>(attributeName));
    if (attribute != nullptr) {
        found = true;
    }
    return found;
}

std::string XmlNode::attribute(const char *attributeName) const
{
    std::string attributeValueString;
    if (hasAttribute(attributeName)) {
        xmlChar *attributeValue = xmlGetProp(mPimpl->mXmlNodePtr, reinterpret_cast<const xmlChar *>(attributeName));
        attributeValueString = std::string(reinterpret_cast<const char *>(attributeValue));
        xmlFree(attributeValue);
    }
    return attributeValueString;
}

XmlAttributePtr XmlNode::firstAttribute() const
{
    xmlAttrPtr attribute = mPimpl->mXmlNodePtr->properties;
    XmlAttributePtr attributeHandle = nullptr;
    if (attribute != nullptr) {
        attributeHandle = std::make_shared<XmlAttribute>();
        attributeHandle->setXmlAttribute(attribute);
    }
    return attributeHandle;
}

XmlNodePtr XmlNode::firstChild() const
{
    xmlNodePtr child = mPimpl->mXmlNodePtr->children;
    XmlNodePtr childHandle = nullptr;
    while (child != nullptr) {
        childHandle = std::make_shared<XmlNode>();
        childHandle->setXmlNode(child);
        bool textNode = childHandle->isText();
        if (!textNode || (textNode && !childHandle->convertToStrippedString().empty())) {
            break;
        }
        child = child->next;
    }
    return childHandle;
}

XmlNodePtr XmlNode::next() const
{
    xmlNodePtr next = mPimpl->mXmlNodePtr->next;
    XmlNodePtr nextHandle = nullptr;
    if (next != nullptr) {
        nextHandle = std::make_shared<XmlNode>();
        nextHandle->setXmlNode(next);
    }
    return nextHandle;
}

XmlNodePtr XmlNode::parent() const
{
    xmlNodePtr parent = mPimpl->mXmlNodePtr->parent;
    XmlNodePtr parentHandle = nullptr;
    if (parent != nullptr) {
        parentHandle = std::make_shared<XmlNode>();
        parentHandle->setXmlNode(parent);
    }
    return parentHandle;
}

std::string XmlNode::convertToString(bool format) const
{
    std::string contentString;
    xmlBufferPtr buffer = xmlBufferCreate();
    if (format) {
        xmlKeepBlanksDefault(0);
    }
    int len = xmlNodeDump(buffer, mPimpl->mXmlNodePtr->doc, mPimpl->mXmlNodePtr, 0, format ? 1 : 0);
    if (len > 0) {
        contentString = std::string(reinterpret_cast<const char *>(buffer->content));
    }
    xmlBufferFree(buffer);
    return contentString;
}

std::string XmlNode::convertToStrippedString() const
{
    std::string contentString = convertToString();
    contentString.erase(contentString.begin(), find_if_not(contentString.begin(), contentString.end(), [](int c) { return isspace(c); }));
    contentString.erase(find_if_not(contentString.rbegin(), contentString.rend(), [](int c) { return isspace(c); }).base(), contentString.end());
    return contentString;
}

} // namespace libcellml
<|endoftext|>


<commit_before>#include "internal.hpp"

namespace CaDiCaL {

// Restarts are scheduled by a variant of the Glucose scheme presented in
// our POS'15 paper using exponential moving averages.  There is a slow
// moving average of the average recent glue level of learned clauses as
// well as fast moving average of those glues.  If the end of base restart
// conflict interval has passed and the fast moving average is above a
// certain margin of the slow moving average then we restart.

bool Internal::restarting () {
  if (!opts.restart) return false;
  if (stats.conflicts <= lim.restart) return false;
  if (level < 2) return false;
  if (level < fast_glue_avg) return false;
  double s = slow_glue_avg, f = fast_glue_avg, l = opts.restartmargin * s;
  LOG ("EMA glue slow %.2f fast %.2f limit %.2f", s, f, l);
  return l <= f;
}

// This is Marijn's reuse trail idea.  Instead of always backtracking to the
// top we figure out which decisions would be made again anyhow and only
// backtrack to the level of the last such decision or if no such decision
// exists to the top (in which case we do reuse any level).

int Internal::reuse_trail () {
  if (!opts.reusetrail) return 0;
  long limit = bumped (next_decision_variable ());
  int res = 0;
  while (res < level && bumped (control[res + 1].decision) > limit)
    res++;
  if (res) stats.reused++;
  return res;
}

void Internal::restart () {
  START (restart);
  stats.restarts++;
  LOG ("restart %ld", stats.restarts);
  backtrack (reuse_trail ());
  lim.restart = stats.conflicts + opts.restartint;
  report ('R', 2);
  STOP (restart);
}

};

<commit_msg>rephase coupled with long restart intervals for one third of the time<commit_after>#include "internal.hpp"

namespace CaDiCaL {

// Restarts are scheduled by a variant of the Glucose scheme presented in
// our POS'15 paper using exponential moving averages.  There is a slow
// moving average of the average recent glue level of learned clauses as
// well as fast moving average of those glues.  If the end of base restart
// conflict interval has passed and the fast moving average is above a
// certain margin of the slow moving average then we restart.

bool Internal::restarting () {
  if (!opts.restart) return false;
  if (stats.conflicts <= lim.restart) return false;
  if (level < 2) return false;
  if (level < fast_glue_avg) return false;
  double s = slow_glue_avg, f = fast_glue_avg, l = opts.restartmargin * s;
  LOG ("EMA glue slow %.2f fast %.2f limit %.2f", s, f, l);
  return l <= f;
}

// This is Marijn's reuse trail idea.  Instead of always backtracking to the
// top we figure out which decisions would be made again anyhow and only
// backtrack to the level of the last such decision or if no such decision
// exists to the top (in which case we do reuse any level).

int Internal::reuse_trail () {
  if (!opts.reusetrail) return 0;
  long limit = bumped (next_decision_variable ());
  int res = 0;
  while (res < level && bumped (control[res + 1].decision) > limit)
    res++;
  if (res) stats.reused++;
  return res;
}

void Internal::restart () {
  START (restart);
  stats.restarts++;
  LOG ("restart %ld", stats.restarts);
  backtrack (reuse_trail ());
  long delta = opts.restartint;
  if (stats.rephased % 3 == 2) delta *= 20;
  lim.restart = stats.conflicts + delta;
  report ('R', 2);
  STOP (restart);
}

};

<|endoftext|>