<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */

#define BOOST_TEST_MODULE TServerIntegrationTest
#include <boost/test/auto_unit_test.hpp>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <thrift/server/TSimpleServer.h>
#include <thrift/server/TThreadPoolServer.h>
#include <thrift/server/TThreadedServer.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TTransport.h>
#include "gen-cpp/ParentService.h"
#include "TestPortFixture.h"
#include <vector>

using apache::thrift::concurrency::Guard;
using apache::thrift::concurrency::Monitor;
using apache::thrift::concurrency::Mutex;
using apache::thrift::concurrency::Synchronized;
using apache::thrift::protocol::TBinaryProtocol;
using apache::thrift::protocol::TBinaryProtocolFactory;
using apache::thrift::protocol::TProtocol;
using apache::thrift::protocol::TProtocolFactory;
using apache::thrift::transport::TServerSocket;
using apache::thrift::transport::TServerTransport;
using apache::thrift::transport::TSocket;
using apache::thrift::transport::TTransport;
using apache::thrift::transport::TTransportException;
using apache::thrift::transport::TTransportFactory;
using apache::thrift::server::TServer;
using apache::thrift::server::TServerEventHandler;
using apache::thrift::server::TSimpleServer;
using apache::thrift::server::TThreadPoolServer;
using apache::thrift::server::TThreadedServer;
using apache::thrift::test::ParentServiceClient;
using apache::thrift::test::ParentServiceIf;
using apache::thrift::test::ParentServiceIfFactory;
using apache::thrift::test::ParentServiceIfSingletonFactory;
using apache::thrift::test::ParentServiceProcessor;
using apache::thrift::test::ParentServiceProcessorFactory;
using apache::thrift::TProcessor;
using apache::thrift::TProcessorFactory;
using boost::posix_time::milliseconds;

/**
 * preServe runs after listen() is successful, when we can connect
 */
class TServerReadyEventHandler : public TServerEventHandler, public Monitor {
public:
  TServerReadyEventHandler() : isListening_(false), accepted_(0) {}
  virtual ~TServerReadyEventHandler() {}
  virtual void preServe() {
    Synchronized sync(*this);
    isListening_ = true;
    notify();
  }
  virtual void* createContext(boost::shared_ptr<TProtocol> input,
                              boost::shared_ptr<TProtocol> output) {
    Synchronized sync(*this);
    ++accepted_;
    notify();

    (void)input;
    (void)output;
    return NULL;
  }
  bool isListening() const { return isListening_; }
  uint64_t acceptedCount() const { return accepted_; }

private:
  bool isListening_;
  uint64_t accepted_;
};

/**
 * Reusing another generated test, just something to serve up
 */
class ParentHandler : public ParentServiceIf {
public:
  ParentHandler() : generation_(0) {}

  int32_t incrementGeneration() {
    Guard g(mutex_);
    return ++generation_;
  }

  int32_t getGeneration() {
    Guard g(mutex_);
    return generation_;
  }

  void addString(const std::string& s) {
    Guard g(mutex_);
    strings_.push_back(s);
  }

  void getStrings(std::vector<std::string>& _return) {
    Guard g(mutex_);
    _return = strings_;
  }

  void getDataWait(std::string& _return, const int32_t length) {
    THRIFT_UNUSED_VARIABLE(_return);
    THRIFT_UNUSED_VARIABLE(length);
  }

  void onewayWait() {}

  void exceptionWait(const std::string& message) { THRIFT_UNUSED_VARIABLE(message); }

  void unexpectedExceptionWait(const std::string& message) { THRIFT_UNUSED_VARIABLE(message); }

protected:
  Mutex mutex_;
  int32_t generation_;
  std::vector<std::string> strings_;
};

void autoSocketCloser(TSocket* pSock) {
  pSock->close();
  delete pSock;
}

template <class TServerType>
class TServerIntegrationTestFixture : public TestPortFixture {
public:
  TServerIntegrationTestFixture(const boost::shared_ptr<TProcessorFactory>& _processorFactory)
    : pServer(new TServerType(_processorFactory,
                              boost::shared_ptr<TServerTransport>(
                                  new TServerSocket("localhost", m_serverPort)),
                              boost::shared_ptr<TTransportFactory>(new TTransportFactory),
                              boost::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory))),
      pEventHandler(boost::shared_ptr<TServerReadyEventHandler>(new TServerReadyEventHandler)) {
    pServer->setServerEventHandler(pEventHandler);
  }

  TServerIntegrationTestFixture(const boost::shared_ptr<TProcessor>& _processor)
    : pServer(
          new TServerType(_processor,
                          boost::shared_ptr<TServerTransport>(new TServerSocket("localhost", 0)),
                          boost::shared_ptr<TTransportFactory>(new TTransportFactory),
                          boost::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory))),
      pEventHandler(boost::shared_ptr<TServerReadyEventHandler>(new TServerReadyEventHandler)) {
    pServer->setServerEventHandler(pEventHandler);
  }

  void startServer() {
    pServerThread.reset(new boost::thread(boost::bind(&TServerType::serve, pServer.get())));

    // block until listen() completes so clients will be able to connect
    Synchronized sync(*(pEventHandler.get()));
    while (!pEventHandler->isListening()) {
      pEventHandler->wait();
    }

    BOOST_TEST_MESSAGE("server is listening");
  }

  void blockUntilAccepted(uint64_t numAccepted) {
    Synchronized sync(*(pEventHandler.get()));
    while (pEventHandler->acceptedCount() < numAccepted) {
      pEventHandler->wait();
    }

    BOOST_TEST_MESSAGE(boost::format("server has accepted %1%") % numAccepted);
  }

  void stopServer() {
    if (pServerThread) {
      pServer->stop();
      BOOST_TEST_MESSAGE("server stop completed");

      pServerThread->join();
      BOOST_TEST_MESSAGE("server thread joined");
      pServerThread.reset();
    }
  }

  ~TServerIntegrationTestFixture() { stopServer(); }

  int getServerPort() {
    TServerSocket* pSock = dynamic_cast<TServerSocket*>(pServer->getServerTransport().get());
    return pSock->getPort();
  }

  void delayClose(boost::shared_ptr<TTransport> toClose, boost::posix_time::time_duration after) {
    boost::this_thread::sleep(after);
    toClose->close();
  }

  void baseline(int64_t numToMake, int64_t expectedHWM) {
    startServer();
    std::vector<boost::shared_ptr<TSocket> > holdSockets;
    std::vector<boost::shared_ptr<boost::thread> > holdThreads;

    for (int64_t i = 0; i < numToMake; ++i) {
      boost::shared_ptr<TSocket> pClientSock(new TSocket("localhost", getServerPort()),
                                             autoSocketCloser);
      holdSockets.push_back(pClientSock);
      boost::shared_ptr<TProtocol> pClientProtocol(new TBinaryProtocol(pClientSock));
      ParentServiceClient client(pClientProtocol);
      pClientSock->open();
      client.incrementGeneration();
      holdThreads.push_back(boost::shared_ptr<boost::thread>(
          new boost::thread(boost::bind(&TServerIntegrationTestFixture::delayClose,
                                        this,
                                        pClientSock,
                                        milliseconds(100 * numToMake)))));
    }

    BOOST_CHECK_EQUAL(expectedHWM, pServer->getConcurrentClientCountHWM());
    stopServer();
    BOOST_FOREACH (boost::shared_ptr<boost::thread> pThread, holdThreads) { pThread->join(); }
    holdThreads.clear();
    holdSockets.clear();
  }

  boost::shared_ptr<TServerType> pServer;
  boost::shared_ptr<TServerReadyEventHandler> pEventHandler;
  boost::shared_ptr<boost::thread> pServerThread;
};

template <class TServerType>
class TServerIntegrationProcessorFactoryTestFixture
    : public TServerIntegrationTestFixture<TServerType> {
public:
  TServerIntegrationProcessorFactoryTestFixture()
    : TServerIntegrationTestFixture<TServerType>(boost::make_shared<ParentServiceProcessorFactory>(
          boost::make_shared<ParentServiceIfSingletonFactory>(
              boost::make_shared<ParentHandler>()))) {}
};

template <class TServerType>
class TServerIntegrationProcessorTestFixture : public TServerIntegrationTestFixture<TServerType> {
public:
  TServerIntegrationProcessorTestFixture()
    : TServerIntegrationTestFixture<TServerType>(
          boost::make_shared<ParentServiceProcessor>(boost::make_shared<ParentHandler>())) {}
};

BOOST_AUTO_TEST_SUITE(constructors)

BOOST_FIXTURE_TEST_CASE(test_simple_factory,
                        TServerIntegrationProcessorFactoryTestFixture<TSimpleServer>) {
  baseline(3, 1);
}

BOOST_FIXTURE_TEST_CASE(test_simple, TServerIntegrationProcessorTestFixture<TSimpleServer>) {
  baseline(3, 1);
}

BOOST_FIXTURE_TEST_CASE(test_threaded_factory,
                        TServerIntegrationProcessorFactoryTestFixture<TThreadedServer>) {
  baseline(10, 10);
}

BOOST_FIXTURE_TEST_CASE(test_threaded, TServerIntegrationProcessorTestFixture<TThreadedServer>) {
  baseline(10, 10);
}

BOOST_FIXTURE_TEST_CASE(test_threaded_bound,
                        TServerIntegrationProcessorTestFixture<TThreadedServer>) {
  pServer->setConcurrentClientLimit(4);
  baseline(10, 4);
}

BOOST_FIXTURE_TEST_CASE(test_threadpool_factory,
                        TServerIntegrationProcessorFactoryTestFixture<TThreadPoolServer>) {
  pServer->getThreadManager()->threadFactory(
      boost::shared_ptr<apache::thrift::concurrency::ThreadFactory>(
          new apache::thrift::concurrency::PlatformThreadFactory));
  pServer->getThreadManager()->start();

  // thread factory has 4 threads as a default
  // thread factory however is a bad way to limit concurrent clients
  // as accept() will be called to grab a 5th client socket, in this case
  // and then the thread factory will block adding the thread to manage
  // that client.
  baseline(10, 5);
}

BOOST_FIXTURE_TEST_CASE(test_threadpool,
                        TServerIntegrationProcessorTestFixture<TThreadPoolServer>) {
  pServer->getThreadManager()->threadFactory(
      boost::shared_ptr<apache::thrift::concurrency::ThreadFactory>(
          new apache::thrift::concurrency::PlatformThreadFactory));
  pServer->getThreadManager()->start();

  // thread factory has 4 threads as a default
  // thread factory however is a bad way to limit concurrent clients
  // as accept() will be called to grab a 5th client socket, in this case
  // and then the thread factory will block adding the thread to manage
  // that client.
  baseline(10, 5);
}

BOOST_FIXTURE_TEST_CASE(test_threadpool_bound,
                        TServerIntegrationProcessorTestFixture<TThreadPoolServer>) {
  pServer->getThreadManager()->threadFactory(
      boost::shared_ptr<apache::thrift::concurrency::ThreadFactory>(
          new apache::thrift::concurrency::PlatformThreadFactory));
  pServer->getThreadManager()->start();
  pServer->setConcurrentClientLimit(4);

  baseline(10, 4);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_FIXTURE_TEST_SUITE(TServerIntegrationTest,
                         TServerIntegrationProcessorTestFixture<TThreadedServer>)

BOOST_AUTO_TEST_CASE(test_stop_with_interruptable_clients_connected) {
  // This tests THRIFT-2441 new behavior: stopping the server disconnects clients

  startServer();

  boost::shared_ptr<TSocket> pClientSock1(new TSocket("localhost", getServerPort()),
                                          autoSocketCloser);
  pClientSock1->open();

  boost::shared_ptr<TSocket> pClientSock2(new TSocket("localhost", getServerPort()),
                                          autoSocketCloser);
  pClientSock2->open();

  // Ensure they have been accepted
  blockUntilAccepted(2);

  // The test fixture destructor will force the sockets to disconnect
  // Prior to THRIFT-2441, pServer->stop() would hang until clients disconnected
  stopServer();

  // extra proof the server end disconnected the clients
  uint8_t buf[1];
  BOOST_CHECK_EQUAL(0, pClientSock1->read(&buf[0], 1)); // 0 = disconnected
  BOOST_CHECK_EQUAL(0, pClientSock2->read(&buf[0], 1)); // 0 = disconnected
}

BOOST_AUTO_TEST_CASE(test_stop_with_uninterruptable_clients_connected) {
  // This tests pre-THRIFT-2441 behavior: stopping the server blocks until clients
  // disconnect.

  boost::dynamic_pointer_cast<TServerSocket>(pServer->getServerTransport())
      ->setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior

  startServer();

  boost::shared_ptr<TSocket> pClientSock1(new TSocket("localhost", getServerPort()),
                                          autoSocketCloser);
  pClientSock1->open();

  boost::shared_ptr<TSocket> pClientSock2(new TSocket("localhost", getServerPort()),
                                          autoSocketCloser);
  pClientSock2->open();

  // Ensure they have been accepted
  blockUntilAccepted(2);

  boost::thread t1(boost::bind(&TServerIntegrationTestFixture::delayClose,
                               this,
                               pClientSock1,
                               milliseconds(250)));
  boost::thread t2(boost::bind(&TServerIntegrationTestFixture::delayClose,
                               this,
                               pClientSock2,
                               milliseconds(250)));

  // Once the clients disconnect the server will stop
  stopServer();
  t1.join();
  t2.join();
}

BOOST_AUTO_TEST_CASE(test_concurrent_client_limit) {
  startServer();

  BOOST_CHECK_EQUAL(INT64_MAX, pServer->getConcurrentClientLimit());
  pServer->setConcurrentClientLimit(2);
  BOOST_CHECK_EQUAL(0, pServer->getConcurrentClientCount());
  BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientLimit());

  boost::shared_ptr<TSocket> pClientSock1(new TSocket("localhost", getServerPort()),
                                          autoSocketCloser);
  pClientSock1->open();
  blockUntilAccepted(1);
  BOOST_CHECK_EQUAL(1, pServer->getConcurrentClientCount());

  boost::shared_ptr<TSocket> pClientSock2(new TSocket("localhost", getServerPort()),
                                          autoSocketCloser);
  pClientSock2->open();
  blockUntilAccepted(2);
  BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientCount());

  // a third client cannot connect until one of the other two closes
  boost::thread t2(boost::bind(&TServerIntegrationTestFixture::delayClose,
                               this,
                               pClientSock2,
                               milliseconds(250)));
  boost::shared_ptr<TSocket> pClientSock3(new TSocket("localhost", getServerPort()),
                                          autoSocketCloser);
  pClientSock2->open();
  blockUntilAccepted(2);
  BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientCount());
  BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientCountHWM());

  stopServer();
  t2.join();
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>THRIFT-3628 Fix lib/cpp/test/TServerIntegrationTest.cpp to use ephemeral ports. Client: Test (C++) Patch: John Sirois<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */

#define BOOST_TEST_MODULE TServerIntegrationTest
#include <boost/test/auto_unit_test.hpp>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <thrift/server/TSimpleServer.h>
#include <thrift/server/TThreadPoolServer.h>
#include <thrift/server/TThreadedServer.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TTransport.h>
#include "gen-cpp/ParentService.h"
#include <vector>

using apache::thrift::concurrency::Guard;
using apache::thrift::concurrency::Monitor;
using apache::thrift::concurrency::Mutex;
using apache::thrift::concurrency::Synchronized;
using apache::thrift::protocol::TBinaryProtocol;
using apache::thrift::protocol::TBinaryProtocolFactory;
using apache::thrift::protocol::TProtocol;
using apache::thrift::protocol::TProtocolFactory;
using apache::thrift::transport::TServerSocket;
using apache::thrift::transport::TServerTransport;
using apache::thrift::transport::TSocket;
using apache::thrift::transport::TTransport;
using apache::thrift::transport::TTransportException;
using apache::thrift::transport::TTransportFactory;
using apache::thrift::server::TServer;
using apache::thrift::server::TServerEventHandler;
using apache::thrift::server::TSimpleServer;
using apache::thrift::server::TThreadPoolServer;
using apache::thrift::server::TThreadedServer;
using apache::thrift::test::ParentServiceClient;
using apache::thrift::test::ParentServiceIf;
using apache::thrift::test::ParentServiceIfFactory;
using apache::thrift::test::ParentServiceIfSingletonFactory;
using apache::thrift::test::ParentServiceProcessor;
using apache::thrift::test::ParentServiceProcessorFactory;
using apache::thrift::TProcessor;
using apache::thrift::TProcessorFactory;
using boost::posix_time::milliseconds;

/**
 * preServe runs after listen() is successful, when we can connect
 */
class TServerReadyEventHandler : public TServerEventHandler, public Monitor {
public:
  TServerReadyEventHandler() : isListening_(false), accepted_(0) {}
  virtual ~TServerReadyEventHandler() {}
  virtual void preServe() {
    Synchronized sync(*this);
    isListening_ = true;
    notify();
  }
  virtual void* createContext(boost::shared_ptr<TProtocol> input,
                              boost::shared_ptr<TProtocol> output) {
    Synchronized sync(*this);
    ++accepted_;
    notify();

    (void)input;
    (void)output;
    return NULL;
  }
  bool isListening() const { return isListening_; }
  uint64_t acceptedCount() const { return accepted_; }

private:
  bool isListening_;
  uint64_t accepted_;
};

/**
 * Reusing another generated test, just something to serve up
 */
class ParentHandler : public ParentServiceIf {
public:
  ParentHandler() : generation_(0) {}

  int32_t incrementGeneration() {
    Guard g(mutex_);
    return ++generation_;
  }

  int32_t getGeneration() {
    Guard g(mutex_);
    return generation_;
  }

  void addString(const std::string& s) {
    Guard g(mutex_);
    strings_.push_back(s);
  }

  void getStrings(std::vector<std::string>& _return) {
    Guard g(mutex_);
    _return = strings_;
  }

  void getDataWait(std::string& _return, const int32_t length) {
    THRIFT_UNUSED_VARIABLE(_return);
    THRIFT_UNUSED_VARIABLE(length);
  }

  void onewayWait() {}

  void exceptionWait(const std::string& message) { THRIFT_UNUSED_VARIABLE(message); }

  void unexpectedExceptionWait(const std::string& message) { THRIFT_UNUSED_VARIABLE(message); }

protected:
  Mutex mutex_;
  int32_t generation_;
  std::vector<std::string> strings_;
};

void autoSocketCloser(TSocket* pSock) {
  pSock->close();
  delete pSock;
}

template <class TServerType>
class TServerIntegrationTestFixture {
public:
  TServerIntegrationTestFixture(const boost::shared_ptr<TProcessorFactory>& _processorFactory)
    : pServer(new TServerType(_processorFactory,
                              boost::shared_ptr<TServerTransport>(
                                  new TServerSocket("localhost", 0)),
                              boost::shared_ptr<TTransportFactory>(new TTransportFactory),
                              boost::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory))),
      pEventHandler(boost::shared_ptr<TServerReadyEventHandler>(new TServerReadyEventHandler)) {
    pServer->setServerEventHandler(pEventHandler);
  }

  TServerIntegrationTestFixture(const boost::shared_ptr<TProcessor>& _processor)
    : pServer(
          new TServerType(_processor,
                          boost::shared_ptr<TServerTransport>(new TServerSocket("localhost", 0)),
                          boost::shared_ptr<TTransportFactory>(new TTransportFactory),
                          boost::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory))),
      pEventHandler(boost::shared_ptr<TServerReadyEventHandler>(new TServerReadyEventHandler)) {
    pServer->setServerEventHandler(pEventHandler);
  }

  void startServer() {
    pServerThread.reset(new boost::thread(boost::bind(&TServerType::serve, pServer.get())));

    // block until listen() completes so clients will be able to connect
    Synchronized sync(*(pEventHandler.get()));
    while (!pEventHandler->isListening()) {
      pEventHandler->wait();
    }

    BOOST_TEST_MESSAGE("server is listening");
  }

  void blockUntilAccepted(uint64_t numAccepted) {
    Synchronized sync(*(pEventHandler.get()));
    while (pEventHandler->acceptedCount() < numAccepted) {
      pEventHandler->wait();
    }

    BOOST_TEST_MESSAGE(boost::format("server has accepted %1%") % numAccepted);
  }

  void stopServer() {
    if (pServerThread) {
      pServer->stop();
      BOOST_TEST_MESSAGE("server stop completed");

      pServerThread->join();
      BOOST_TEST_MESSAGE("server thread joined");
      pServerThread.reset();
    }
  }

  ~TServerIntegrationTestFixture() { stopServer(); }

  int getServerPort() {
    TServerSocket* pSock = dynamic_cast<TServerSocket*>(pServer->getServerTransport().get());
    return pSock->getPort();
  }

  void delayClose(boost::shared_ptr<TTransport> toClose, boost::posix_time::time_duration after) {
    boost::this_thread::sleep(after);
    toClose->close();
  }

  void baseline(int64_t numToMake, int64_t expectedHWM) {
    startServer();
    std::vector<boost::shared_ptr<TSocket> > holdSockets;
    std::vector<boost::shared_ptr<boost::thread> > holdThreads;

    for (int64_t i = 0; i < numToMake; ++i) {
      boost::shared_ptr<TSocket> pClientSock(new TSocket("localhost", getServerPort()),
                                             autoSocketCloser);
      holdSockets.push_back(pClientSock);
      boost::shared_ptr<TProtocol> pClientProtocol(new TBinaryProtocol(pClientSock));
      ParentServiceClient client(pClientProtocol);
      pClientSock->open();
      client.incrementGeneration();
      holdThreads.push_back(boost::shared_ptr<boost::thread>(
          new boost::thread(boost::bind(&TServerIntegrationTestFixture::delayClose,
                                        this,
                                        pClientSock,
                                        milliseconds(100 * numToMake)))));
    }

    BOOST_CHECK_EQUAL(expectedHWM, pServer->getConcurrentClientCountHWM());
    stopServer();
    BOOST_FOREACH (boost::shared_ptr<boost::thread> pThread, holdThreads) { pThread->join(); }
    holdThreads.clear();
    holdSockets.clear();
  }

  boost::shared_ptr<TServerType> pServer;
  boost::shared_ptr<TServerReadyEventHandler> pEventHandler;
  boost::shared_ptr<boost::thread> pServerThread;
};

template <class TServerType>
class TServerIntegrationProcessorFactoryTestFixture
    : public TServerIntegrationTestFixture<TServerType> {
public:
  TServerIntegrationProcessorFactoryTestFixture()
    : TServerIntegrationTestFixture<TServerType>(boost::make_shared<ParentServiceProcessorFactory>(
          boost::make_shared<ParentServiceIfSingletonFactory>(
              boost::make_shared<ParentHandler>()))) {}
};

template <class TServerType>
class TServerIntegrationProcessorTestFixture : public TServerIntegrationTestFixture<TServerType> {
public:
  TServerIntegrationProcessorTestFixture()
    : TServerIntegrationTestFixture<TServerType>(
          boost::make_shared<ParentServiceProcessor>(boost::make_shared<ParentHandler>())) {}
};

BOOST_AUTO_TEST_SUITE(constructors)

BOOST_FIXTURE_TEST_CASE(test_simple_factory,
                        TServerIntegrationProcessorFactoryTestFixture<TSimpleServer>) {
  baseline(3, 1);
}

BOOST_FIXTURE_TEST_CASE(test_simple, TServerIntegrationProcessorTestFixture<TSimpleServer>) {
  baseline(3, 1);
}

BOOST_FIXTURE_TEST_CASE(test_threaded_factory,
                        TServerIntegrationProcessorFactoryTestFixture<TThreadedServer>) {
  baseline(10, 10);
}

BOOST_FIXTURE_TEST_CASE(test_threaded, TServerIntegrationProcessorTestFixture<TThreadedServer>) {
  baseline(10, 10);
}

BOOST_FIXTURE_TEST_CASE(test_threaded_bound,
                        TServerIntegrationProcessorTestFixture<TThreadedServer>) {
  pServer->setConcurrentClientLimit(4);
  baseline(10, 4);
}

BOOST_FIXTURE_TEST_CASE(test_threadpool_factory,
                        TServerIntegrationProcessorFactoryTestFixture<TThreadPoolServer>) {
  pServer->getThreadManager()->threadFactory(
      boost::shared_ptr<apache::thrift::concurrency::ThreadFactory>(
          new apache::thrift::concurrency::PlatformThreadFactory));
  pServer->getThreadManager()->start();

  // thread factory has 4 threads as a default
  // thread factory however is a bad way to limit concurrent clients
  // as accept() will be called to grab a 5th client socket, in this case
  // and then the thread factory will block adding the thread to manage
  // that client.
  baseline(10, 5);
}

BOOST_FIXTURE_TEST_CASE(test_threadpool,
                        TServerIntegrationProcessorTestFixture<TThreadPoolServer>) {
  pServer->getThreadManager()->threadFactory(
      boost::shared_ptr<apache::thrift::concurrency::ThreadFactory>(
          new apache::thrift::concurrency::PlatformThreadFactory));
  pServer->getThreadManager()->start();

  // thread factory has 4 threads as a default
  // thread factory however is a bad way to limit concurrent clients
  // as accept() will be called to grab a 5th client socket, in this case
  // and then the thread factory will block adding the thread to manage
  // that client.
  baseline(10, 5);
}

BOOST_FIXTURE_TEST_CASE(test_threadpool_bound,
                        TServerIntegrationProcessorTestFixture<TThreadPoolServer>) {
  pServer->getThreadManager()->threadFactory(
      boost::shared_ptr<apache::thrift::concurrency::ThreadFactory>(
          new apache::thrift::concurrency::PlatformThreadFactory));
  pServer->getThreadManager()->start();
  pServer->setConcurrentClientLimit(4);

  baseline(10, 4);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_FIXTURE_TEST_SUITE(TServerIntegrationTest,
                         TServerIntegrationProcessorTestFixture<TThreadedServer>)

BOOST_AUTO_TEST_CASE(test_stop_with_interruptable_clients_connected) {
  // This tests THRIFT-2441 new behavior: stopping the server disconnects clients

  startServer();

  boost::shared_ptr<TSocket> pClientSock1(new TSocket("localhost", getServerPort()),
                                          autoSocketCloser);
  pClientSock1->open();

  boost::shared_ptr<TSocket> pClientSock2(new TSocket("localhost", getServerPort()),
                                          autoSocketCloser);
  pClientSock2->open();

  // Ensure they have been accepted
  blockUntilAccepted(2);

  // The test fixture destructor will force the sockets to disconnect
  // Prior to THRIFT-2441, pServer->stop() would hang until clients disconnected
  stopServer();

  // extra proof the server end disconnected the clients
  uint8_t buf[1];
  BOOST_CHECK_EQUAL(0, pClientSock1->read(&buf[0], 1)); // 0 = disconnected
  BOOST_CHECK_EQUAL(0, pClientSock2->read(&buf[0], 1)); // 0 = disconnected
}

BOOST_AUTO_TEST_CASE(test_stop_with_uninterruptable_clients_connected) {
  // This tests pre-THRIFT-2441 behavior: stopping the server blocks until clients
  // disconnect.

  boost::dynamic_pointer_cast<TServerSocket>(pServer->getServerTransport())
      ->setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior

  startServer();

  boost::shared_ptr<TSocket> pClientSock1(new TSocket("localhost", getServerPort()),
                                          autoSocketCloser);
  pClientSock1->open();

  boost::shared_ptr<TSocket> pClientSock2(new TSocket("localhost", getServerPort()),
                                          autoSocketCloser);
  pClientSock2->open();

  // Ensure they have been accepted
  blockUntilAccepted(2);

  boost::thread t1(boost::bind(&TServerIntegrationTestFixture::delayClose,
                               this,
                               pClientSock1,
                               milliseconds(250)));
  boost::thread t2(boost::bind(&TServerIntegrationTestFixture::delayClose,
                               this,
                               pClientSock2,
                               milliseconds(250)));

  // Once the clients disconnect the server will stop
  stopServer();
  t1.join();
  t2.join();
}

BOOST_AUTO_TEST_CASE(test_concurrent_client_limit) {
  startServer();

  BOOST_CHECK_EQUAL(INT64_MAX, pServer->getConcurrentClientLimit());
  pServer->setConcurrentClientLimit(2);
  BOOST_CHECK_EQUAL(0, pServer->getConcurrentClientCount());
  BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientLimit());

  boost::shared_ptr<TSocket> pClientSock1(new TSocket("localhost", getServerPort()),
                                          autoSocketCloser);
  pClientSock1->open();
  blockUntilAccepted(1);
  BOOST_CHECK_EQUAL(1, pServer->getConcurrentClientCount());

  boost::shared_ptr<TSocket> pClientSock2(new TSocket("localhost", getServerPort()),
                                          autoSocketCloser);
  pClientSock2->open();
  blockUntilAccepted(2);
  BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientCount());

  // a third client cannot connect until one of the other two closes
  boost::thread t2(boost::bind(&TServerIntegrationTestFixture::delayClose,
                               this,
                               pClientSock2,
                               milliseconds(250)));
  boost::shared_ptr<TSocket> pClientSock3(new TSocket("localhost", getServerPort()),
                                          autoSocketCloser);
  pClientSock2->open();
  blockUntilAccepted(2);
  BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientCount());
  BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientCountHWM());

  stopServer();
  t2.join();
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>#include "TopkHeader.h"

double inline ori_absdiff(double a, double b) {
    double v = a - b;
    if (v < 0) {
        v = v * -1;
    }
    return v;
}

void original_topk_sim_join_plain_impl(const Table& ltoken_vector, const Table& rtoken_vector,
                                       CandSet& cand_set, PrefixHeap& prefix_events,
                                       Heap& topk_heap, const unsigned int output_size) {
    long int total_compared_pairs = 0;
    CandSet compared_set;

    InvertedIndex l_inverted_index, r_inverted_index;
    PrefixEvent event;
    int table_indicator, l_rec_idx, r_rec_idx, l_tok_idx, r_tok_idx, token, overlap;
    unsigned long l_len, r_len;
    double sim;

    while (prefix_events.size() > 0) {
        if (topk_heap.size() == output_size && (topk_heap.top().sim >= prefix_events.top().threshold ||
                                                ori_absdiff(topk_heap.top().sim, prefix_events.top().threshold) <= 1e-6)) {
            break;
        }
        event = prefix_events.top();
        prefix_events.pop();
        table_indicator = event.table_indicator;
        if (table_indicator == 0) {
            l_rec_idx = event.rec_idx;
            l_tok_idx = event.tok_idx;
            token = ltoken_vector[l_rec_idx][l_tok_idx];
            l_len = ltoken_vector[l_rec_idx].size();
            if (r_inverted_index.count(token)) {
                set<pair<int, int> > r_records = r_inverted_index[token];

                for (set<pair<int, int> >::iterator it = r_records.begin(); it != r_records.end(); ++it) {
                    pair<int, int> r_rec_tuple = *it;
                    r_rec_idx = r_rec_tuple.first;
                    r_tok_idx = r_rec_tuple.second;
                    r_len = rtoken_vector[r_rec_idx].size();

                    if (topk_heap.size() == output_size && (l_len < topk_heap.top().sim * r_len || l_len > r_len / topk_heap.top().sim)) {
                        continue;
                    }

                    if (cand_set.count(l_rec_idx) && cand_set[l_rec_idx].count(r_rec_idx)) {
                        continue;
                    }

                    if (compared_set.count(l_rec_idx) && compared_set[l_rec_idx].count(r_rec_idx)) {
                        continue;
                    }

                    overlap = original_plain_get_overlap(ltoken_vector[l_rec_idx], rtoken_vector[r_rec_idx]);
                    sim = overlap * 1.0 / (l_len + r_len - overlap);
                    if (topk_heap.size() == output_size) {
                        if (topk_heap.top().sim < sim) {
                            topk_heap.pop();
                            topk_heap.push(TopPair(sim, l_rec_idx, r_rec_idx));
                        }
                    } else {
                        topk_heap.push(TopPair(sim, l_rec_idx, r_rec_idx));
                    }
                    ++total_compared_pairs;
                    if (!compared_set.count(l_rec_idx)) {
                        compared_set[l_rec_idx] = set<int>();
                    }
                    compared_set[l_rec_idx].insert(r_rec_idx);
                }
            }

            double topk_heap_sim_index = 0.0;
            if (topk_heap.size() == output_size) {
                topk_heap_sim_index = topk_heap.top().sim;
            }

            double index_threshold = 1.0;
            int numer_index = l_len - l_tok_idx;
            int denom_index = l_len + l_tok_idx;
            if (denom_index > 0) {
                index_threshold = numer_index * 1.0 / denom_index;
            }

            if (index_threshold >= topk_heap_sim_index) {
                if (!l_inverted_index.count(token)) {
                    l_inverted_index[token] = set<pair<int, int> >();
                }
                l_inverted_index[token].insert(pair<int, int>(l_rec_idx, l_tok_idx));
            }

        } else {
            r_rec_idx = event.rec_idx;
            r_tok_idx = event.tok_idx;
            token = rtoken_vector[r_rec_idx][r_tok_idx];
            r_len = rtoken_vector[r_rec_idx].size();
            if (l_inverted_index.count(token)) {
                set<pair<int, int> > l_records = l_inverted_index[token];
                for (set<pair<int, int> >::iterator it = l_records.begin(); it != l_records.end(); ++it) {
                    pair<int, int> l_rec_tuple = *it;
                    l_rec_idx = l_rec_tuple.first;
                    l_tok_idx = l_rec_tuple.second;
                    l_len = ltoken_vector[l_rec_idx].size();

                    if (topk_heap.size() == output_size && (l_len < topk_heap.top().sim * r_len || l_len > r_len / topk_heap.top().sim)) {
                        continue;
                    }

                    if (cand_set.count(l_rec_idx) && cand_set[l_rec_idx].count(r_rec_idx)) {
                        continue;
                    }

                    if (compared_set.count(l_rec_idx) && compared_set[l_rec_idx].count(r_rec_idx)) {
                        continue;
                    }

                    overlap = original_plain_get_overlap(ltoken_vector[l_rec_idx], rtoken_vector[r_rec_idx]);
                    sim = overlap * 1.0 / (l_len + r_len - overlap);
                    if (topk_heap.size() == output_size) {
                        if (topk_heap.top().sim < sim) {
                            topk_heap.pop();
                            topk_heap.push(TopPair(sim, l_rec_idx, r_rec_idx));
                        }
                    } else {
                        topk_heap.push(TopPair(sim, l_rec_idx, r_rec_idx));
                    }
                    ++total_compared_pairs;
                    if (!compared_set.count(l_rec_idx)) {
                        compared_set[l_rec_idx] = set<int>();
                    }
                    compared_set[l_rec_idx].insert(r_rec_idx);
                }
            }

            double topk_heap_sim_index = 0.0;
            if (topk_heap.size() == output_size) {
                topk_heap_sim_index = topk_heap.top().sim;
            }
            double index_threshold = 1.0;
            int numer_index = r_len - r_tok_idx;
            int denom_index = r_len + r_tok_idx;
            if (denom_index > 0) {
                index_threshold = numer_index * 1.0 / denom_index;
            }
            if (index_threshold >= topk_heap_sim_index) {
                if (!r_inverted_index.count(token)) {
                    r_inverted_index[token] = set<pair<int, int> >();
                }
                r_inverted_index[token].insert(pair<int, int>(r_rec_idx, r_tok_idx));
            }

        }
    }
    //printf("number of compared pairs: %ld\n", total_compared_pairs);
}

Heap original_topk_sim_join_plain(const Table& ltoken_vector, const Table& rtoken_vector,
                                  CandSet& cand_set, const unsigned int output_size) {
    //cout << "In original topk sim plain" << endl;

    PrefixHeap prefix_events;
    original_generate_prefix_events(ltoken_vector, rtoken_vector, prefix_events);

    Heap topk_heap;
    original_topk_sim_join_plain_impl(ltoken_vector, rtoken_vector, cand_set, prefix_events, topk_heap, output_size);

    return topk_heap;
}
<commit_msg>remove unused print info<commit_after>#include "TopkHeader.h"

double inline ori_absdiff(double a, double b) {
    double v = a - b;
    if (v < 0) {
        v = v * -1;
    }
    return v;
}

void original_topk_sim_join_plain_impl(const Table& ltoken_vector, const Table& rtoken_vector,
                                       CandSet& cand_set, PrefixHeap& prefix_events,
                                       Heap& topk_heap, const unsigned int output_size) {
    long int total_compared_pairs = 0;
    CandSet compared_set;

    InvertedIndex l_inverted_index, r_inverted_index;
    PrefixEvent event;
    int table_indicator, l_rec_idx, r_rec_idx, l_tok_idx, r_tok_idx, token, overlap;
    unsigned long l_len, r_len;
    double sim;

    while (prefix_events.size() > 0) {
        if (topk_heap.size() == output_size && (topk_heap.top().sim >= prefix_events.top().threshold ||
                                                ori_absdiff(topk_heap.top().sim, prefix_events.top().threshold) <= 1e-6)) {
            break;
        }
        event = prefix_events.top();
        prefix_events.pop();
        table_indicator = event.table_indicator;
        if (table_indicator == 0) {
            l_rec_idx = event.rec_idx;
            l_tok_idx = event.tok_idx;
            token = ltoken_vector[l_rec_idx][l_tok_idx];
            l_len = ltoken_vector[l_rec_idx].size();
            if (r_inverted_index.count(token)) {
                set<pair<int, int> > r_records = r_inverted_index[token];

                for (set<pair<int, int> >::iterator it = r_records.begin(); it != r_records.end(); ++it) {
                    pair<int, int> r_rec_tuple = *it;
                    r_rec_idx = r_rec_tuple.first;
                    r_tok_idx = r_rec_tuple.second;
                    r_len = rtoken_vector[r_rec_idx].size();

                    if (topk_heap.size() == output_size && (l_len < topk_heap.top().sim * r_len || l_len > r_len / topk_heap.top().sim)) {
                        continue;
                    }

                    if (cand_set.count(l_rec_idx) && cand_set[l_rec_idx].count(r_rec_idx)) {
                        continue;
                    }

                    if (compared_set.count(l_rec_idx) && compared_set[l_rec_idx].count(r_rec_idx)) {
                        continue;
                    }

                    overlap = original_plain_get_overlap(ltoken_vector[l_rec_idx], rtoken_vector[r_rec_idx]);
                    sim = overlap * 1.0 / (l_len + r_len - overlap);
                    if (topk_heap.size() == output_size) {
                        if (topk_heap.top().sim < sim) {
                            topk_heap.pop();
                            topk_heap.push(TopPair(sim, l_rec_idx, r_rec_idx));
                        }
                    } else {
                        topk_heap.push(TopPair(sim, l_rec_idx, r_rec_idx));
                    }
                    ++total_compared_pairs;
                    if (!compared_set.count(l_rec_idx)) {
                        compared_set[l_rec_idx] = set<int>();
                    }
                    compared_set[l_rec_idx].insert(r_rec_idx);
                }
            }

            double topk_heap_sim_index = 0.0;
            if (topk_heap.size() == output_size) {
                topk_heap_sim_index = topk_heap.top().sim;
            }

            double index_threshold = 1.0;
            int numer_index = l_len - l_tok_idx;
            int denom_index = l_len + l_tok_idx;
            if (denom_index > 0) {
                index_threshold = numer_index * 1.0 / denom_index;
            }

            if (index_threshold >= topk_heap_sim_index) {
                if (!l_inverted_index.count(token)) {
                    l_inverted_index[token] = set<pair<int, int> >();
                }
                l_inverted_index[token].insert(pair<int, int>(l_rec_idx, l_tok_idx));
            }

        } else {
            r_rec_idx = event.rec_idx;
            r_tok_idx = event.tok_idx;
            token = rtoken_vector[r_rec_idx][r_tok_idx];
            r_len = rtoken_vector[r_rec_idx].size();
            if (l_inverted_index.count(token)) {
                set<pair<int, int> > l_records = l_inverted_index[token];
                for (set<pair<int, int> >::iterator it = l_records.begin(); it != l_records.end(); ++it) {
                    pair<int, int> l_rec_tuple = *it;
                    l_rec_idx = l_rec_tuple.first;
                    l_tok_idx = l_rec_tuple.second;
                    l_len = ltoken_vector[l_rec_idx].size();

                    if (topk_heap.size() == output_size && (l_len < topk_heap.top().sim * r_len || l_len > r_len / topk_heap.top().sim)) {
                        continue;
                    }

                    if (cand_set.count(l_rec_idx) && cand_set[l_rec_idx].count(r_rec_idx)) {
                        continue;
                    }

                    if (compared_set.count(l_rec_idx) && compared_set[l_rec_idx].count(r_rec_idx)) {
                        continue;
                    }

                    overlap = original_plain_get_overlap(ltoken_vector[l_rec_idx], rtoken_vector[r_rec_idx]);
                    sim = overlap * 1.0 / (l_len + r_len - overlap);
                    if (topk_heap.size() == output_size) {
                        if (topk_heap.top().sim < sim) {
                            topk_heap.pop();
                            topk_heap.push(TopPair(sim, l_rec_idx, r_rec_idx));
                        }
                    } else {
                        topk_heap.push(TopPair(sim, l_rec_idx, r_rec_idx));
                    }
                    ++total_compared_pairs;
                    if (!compared_set.count(l_rec_idx)) {
                        compared_set[l_rec_idx] = set<int>();
                    }
                    compared_set[l_rec_idx].insert(r_rec_idx);
                }
            }

            double topk_heap_sim_index = 0.0;
            if (topk_heap.size() == output_size) {
                topk_heap_sim_index = topk_heap.top().sim;
            }
            double index_threshold = 1.0;
            int numer_index = r_len - r_tok_idx;
            int denom_index = r_len + r_tok_idx;
            if (denom_index > 0) {
                index_threshold = numer_index * 1.0 / denom_index;
            }
            if (index_threshold >= topk_heap_sim_index) {
                if (!r_inverted_index.count(token)) {
                    r_inverted_index[token] = set<pair<int, int> >();
                }
                r_inverted_index[token].insert(pair<int, int>(r_rec_idx, r_tok_idx));
            }

        }
    }
}

Heap original_topk_sim_join_plain(const Table& ltoken_vector, const Table& rtoken_vector,
                                  CandSet& cand_set, const unsigned int output_size) {
    PrefixHeap prefix_events;
    original_generate_prefix_events(ltoken_vector, rtoken_vector, prefix_events);

    Heap topk_heap;
    original_topk_sim_join_plain_impl(ltoken_vector, rtoken_vector, cand_set, prefix_events, topk_heap, output_size);

    return topk_heap;
}
<|endoftext|>


<commit_before>/*******************************************************************************
 * Copyright 2017, 2018 MINRES Technologies GmbH
 *
 * 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.
 *******************************************************************************/
/*
 * configurer.cpp
 *
 *  Created on: 27.09.2017
 *      Author: eyck
 */

#include "scc/configurer.h"
#include "scc/report.h"
#include <cci_configuration>
#include <cci_utils/broker.h>

#include <boost/algorithm/string/predicate.hpp>
#include <fstream>

scc::configurer::configurer(const std::string &filename)
: base_type("configurer")
, cci_originator("configurer")
, cci_broker(cci::cci_get_global_broker(cci_originator)) {
    if (filename.length() > 0) {
        std::ifstream is(filename);
        if (is.is_open()) {
            try {
                is >> root;
                configure_cci_hierarchical(root, "");
                config_valid=true;
            } catch (Json::RuntimeError &e) {
                LOG(ERROR) << "Could not parse input file " << filename << ", reason: " << e.what();
            }
        } else {
            LOG(ERROR) << "Could not open input file " << filename;
        }
    }
}

void scc::configurer::dump_hierarchy(std::ostream &os, sc_core::sc_object *obj) {
    if (obj) {
        os << obj->name() << " of type " << typeid(*obj).name() << "\n";
        for (auto *o : obj->get_child_objects()) dump_hierarchy(os);
    } else {
        for (auto *o : sc_core::sc_get_top_level_objects(sc_core::sc_curr_simcontext)) dump_hierarchy(os, o);
    }
}

inline const std::vector<sc_core::sc_object *> &get_sc_objects(sc_core::sc_object *obj = nullptr) {
    if (obj)
        return obj->get_child_objects();
    else
        return sc_core::sc_get_top_level_objects(sc_core::sc_curr_simcontext);
}

void scc::configurer::dump_configuration(std::ostream &os, sc_core::sc_object *obj) {
    Json::Value root{Json::objectValue};
    for (auto *o : get_sc_objects(obj)) {
        dump_configuration(o, root);
    }
    // For convenience, use `writeString()` with a specialized builder.
    Json::StreamWriterBuilder wbuilder;
    wbuilder["indentation"] = "\t";
    wbuilder["enableYAMLCompatibility"] = true;
    wbuilder["commentStyle"] ="None";
    wbuilder.newStreamWriter()->write(root, &os);
    // another (default) option:
    // os << root;
}

#define CHECK_N_ASSIGN_VAL(TYPE, ATTR)                                                                                \
    {                                                                                \
        auto *a = dynamic_cast<sc_core::sc_attribute<TYPE> *>(ATTR);                                                   \
        if (a != nullptr) {                                                                                \
            node[a->name()] = a->value;                                                                                \
            continue;                                                                                \
        }                                                                                \
    }

void scc::configurer::dump_configuration(sc_core::sc_object *obj, Json::Value &parent) {
    auto mod = dynamic_cast<sc_core::sc_module *>(obj);
    Json::Value node{Json::objectValue};
    for (sc_core::sc_attr_base *attr_base : obj->attr_cltn()) {
        CHECK_N_ASSIGN_VAL(int, attr_base);
        CHECK_N_ASSIGN_VAL(unsigned, attr_base);
        CHECK_N_ASSIGN_VAL(int64_t, attr_base);
        CHECK_N_ASSIGN_VAL(uint64_t, attr_base);
        CHECK_N_ASSIGN_VAL(bool, attr_base);
        CHECK_N_ASSIGN_VAL(float, attr_base);
        CHECK_N_ASSIGN_VAL(double, attr_base);
        CHECK_N_ASSIGN_VAL(std::string, attr_base);
        CHECK_N_ASSIGN_VAL(char *, attr_base);
    }
    const std::string hier_name{obj->name()};
    cci::cci_param_predicate pred{[hier_name](cci::cci_param_untyped_handle h) -> bool {
    	std::string h_name {h.name()};
    	auto sep = hier_name.length();
    	return 	h_name.compare(0, sep-1, hier_name)==0 && // begins with hier_name
				h_name[sep]=='.' && // has additional part
				h_name.substr(sep+1).find('.')==h_name.npos; // but no other hierarchy separator
    }};
    auto handles = cci_broker.get_param_handles(pred);
    for (auto &h : handles) {
        auto value = h.get_cci_value();
        auto paramname = std::string{h.name()};
        auto basename = paramname.substr(paramname.find_last_of('.') + 1);
        if (value.is_bool())
            node[basename] = value.get_bool();
        else if (value.is_int())
            node[basename] = value.get_int();
        else if (value.is_int64())
            node[basename] = static_cast<int64_t>(value.get_int64());
        else if (value.is_uint())
            node[basename] = value.get_uint();
        else if (value.is_uint64())
            node[basename] = static_cast<uint64_t>(value.get_uint64());
        else if (value.is_double())
            node[basename] = value.get_double();
        else if (value.is_string())
            node[basename] = value.get_string().c_str();
    }
    for (auto *o : get_sc_objects(obj)) dump_configuration(o, node);
    if (!node.empty()) parent[obj->basename()] = node;
}

void scc::configurer::configure() {
	if(config_valid)
		for (auto *o : sc_core::sc_get_top_level_objects(sc_core::sc_curr_simcontext)) {
			Json::Value &val = root[o->name()];
			if (!val.isNull())
				configure_sc_attribute_hierarchical(o, val);
		}
}

void scc::configurer::configure_sc_attribute_hierarchical(sc_core::sc_object *obj, Json::Value &hier_val) {
    for (auto *attr : obj->attr_cltn()) {
        Json::Value &val = hier_val[attr->name()];
        if (!val.isNull()) set_value(attr, val);
    }
    for (auto *o : obj->get_child_objects()) {
        Json::Value &val = hier_val[o->basename()];
        if (!val.isNull()) configure_sc_attribute_hierarchical(o, val);
    }
}

#define CHECK_N_ASSIGN(TYPE, ATTR, VAL)                                                                                \
    {                                                                                \
        auto *a = dynamic_cast<sc_core::sc_attribute<TYPE> *>(ATTR);                                                   \
        if (a != nullptr) {                                                                                \
            a->value = VAL;                                                                                \
            return;                                                                                \
        }                                                                                \
    }

void scc::configurer::set_value(sc_core::sc_attr_base *attr_base, Json::Value &hier_val) {
    CHECK_N_ASSIGN(int, attr_base, hier_val.asInt());
    CHECK_N_ASSIGN(unsigned, attr_base, hier_val.asUInt());
    CHECK_N_ASSIGN(int64_t, attr_base, hier_val.asInt64());
    CHECK_N_ASSIGN(uint64_t, attr_base, hier_val.asUInt64());
    CHECK_N_ASSIGN(bool, attr_base, hier_val.asBool());
    CHECK_N_ASSIGN(float, attr_base, hier_val.asFloat());
    CHECK_N_ASSIGN(double, attr_base, hier_val.asDouble());
    CHECK_N_ASSIGN(std::string, attr_base, hier_val.asString());
    CHECK_N_ASSIGN(char *, attr_base, strdup(hier_val.asCString()));
}

Json::Value &scc::configurer::get_value_from_hierarchy(const std::string &hier_name) {
    size_t npos = hier_name.find_first_of('.');
    Json::Value &val = root[hier_name.substr(0, npos)];
    if (val.isNull() || npos == hier_name.size()) return val;
    return get_value_from_hierarchy(hier_name.substr(npos + 1, hier_name.size()), val);
}

Json::Value &scc::configurer::get_value_from_hierarchy(const std::string &hier_name, Json::Value &value) {
    size_t npos = hier_name.find_first_of('.');
    Json::Value &val = value[hier_name.substr(0, npos)];
    if (val.isNull() || npos == std::string::npos || !val.isObject()) return val;
    return get_value_from_hierarchy(hier_name.substr(npos + 1, hier_name.size()), val);
}

void scc::configurer::set_configuration_value(sc_core::sc_attr_base *attr_base, sc_core::sc_object *owner) {
    std::string name(owner->name());
    name += ".";
    name += attr_base->name();
    Json::Value &val = get_value_from_hierarchy(name);
    if (!val.isNull()) set_value(attr_base, val);
}

void scc::configurer::configure_cci_hierarchical(const Json::Value &node, std::string prefix) {
    if (node.size() > 0) {
        for (Json::Value::const_iterator itr = node.begin(); itr != node.end(); ++itr) {
            if (!itr.key().isString()) return;
            std::string key_name = itr.key().asString();
            std::string hier_name{prefix.size() ? prefix + "." + key_name : key_name};
            Json::Value val = node[key_name];
            if (val.isNull() || val.isArray())
                return;
            else if (val.isObject())
                configure_cci_hierarchical(*itr, hier_name);
            else {
                cci::cci_param_handle param_handle = cci_broker.get_param_handle(hier_name);
                if (param_handle.is_valid()) {
                    if (val.isString()) {
                        param_handle.set_cci_value(cci::cci_value(val.asString()));
                    } else if (val.isBool()) {
                        param_handle.set_cci_value(cci::cci_value(val.asBool()));
                    } else if (val.isInt()) {
                        param_handle.set_cci_value(cci::cci_value(val.asInt()));
                    } else if (val.isInt64()) {
                        param_handle.set_cci_value(cci::cci_value(val.asInt64()));
                    } else if (val.isUInt()) {
                        param_handle.set_cci_value(cci::cci_value(val.asUInt()));
                    } else if (val.isUInt64()) {
                        param_handle.set_cci_value(cci::cci_value(val.asUInt64()));
                    } else if (val.isDouble()) {
                        param_handle.set_cci_value(cci::cci_value(val.asDouble()));
                    }
                } else {
                    if (val.isString()) {
                        cci_broker.set_preset_cci_value(hier_name, cci::cci_value(val.asString()));
                    } else if (val.isBool()) {
                        cci_broker.set_preset_cci_value(hier_name, cci::cci_value(val.asBool()));
                    } else if (val.isInt()) {
                        cci_broker.set_preset_cci_value(hier_name, cci::cci_value(val.asInt()));
                    } else if (val.isInt64()) {
                        cci_broker.set_preset_cci_value(hier_name, cci::cci_value(val.asInt64()));
                    } else if (val.isUInt()) {
                        cci_broker.set_preset_cci_value(hier_name, cci::cci_value(val.asUInt()));
                    } else if (val.isUInt64()) {
                        cci_broker.set_preset_cci_value(hier_name, cci::cci_value(val.asUInt64()));
                    } else if (val.isDouble()) {
                        cci_broker.set_preset_cci_value(hier_name, cci::cci_value(val.asDouble()));
                    }
                }
            }
        }
    }
}

void scc::init_cci(std::string name) {
    cci::cci_register_broker(new cci_utils::broker("Global Broker"));
}
<commit_msg>Fixed cci parameter filtering<commit_after>/*******************************************************************************
 * Copyright 2017, 2018 MINRES Technologies GmbH
 *
 * 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.
 *******************************************************************************/
/*
 * configurer.cpp
 *
 *  Created on: 27.09.2017
 *      Author: eyck
 */

#include "scc/configurer.h"
#include "scc/report.h"
#include <cci_configuration>
#include <cci_utils/broker.h>

#include <boost/algorithm/string/predicate.hpp>
#include <fstream>

scc::configurer::configurer(const std::string &filename)
: base_type("configurer")
, cci_originator("configurer")
, cci_broker(cci::cci_get_global_broker(cci_originator)) {
    if (filename.length() > 0) {
        std::ifstream is(filename);
        if (is.is_open()) {
            try {
                is >> root;
                configure_cci_hierarchical(root, "");
                config_valid=true;
            } catch (Json::RuntimeError &e) {
                LOG(ERROR) << "Could not parse input file " << filename << ", reason: " << e.what();
            }
        } else {
            LOG(ERROR) << "Could not open input file " << filename;
        }
    }
}

void scc::configurer::dump_hierarchy(std::ostream &os, sc_core::sc_object *obj) {
    if (obj) {
        os << obj->name() << " of type " << typeid(*obj).name() << "\n";
        for (auto *o : obj->get_child_objects()) dump_hierarchy(os);
    } else {
        for (auto *o : sc_core::sc_get_top_level_objects(sc_core::sc_curr_simcontext)) dump_hierarchy(os, o);
    }
}

inline const std::vector<sc_core::sc_object *> &get_sc_objects(sc_core::sc_object *obj = nullptr) {
    if (obj)
        return obj->get_child_objects();
    else
        return sc_core::sc_get_top_level_objects(sc_core::sc_curr_simcontext);
}

void scc::configurer::dump_configuration(std::ostream &os, sc_core::sc_object *obj) {
    Json::Value root{Json::objectValue};
    for (auto *o : get_sc_objects(obj)) {
        dump_configuration(o, root);
    }
    // For convenience, use `writeString()` with a specialized builder.
    Json::StreamWriterBuilder wbuilder;
    wbuilder["indentation"] = "\t";
    wbuilder["enableYAMLCompatibility"] = true;
    wbuilder["commentStyle"] ="None";
    wbuilder.newStreamWriter()->write(root, &os);
    // another (default) option:
    // os << root;
}

#define CHECK_N_ASSIGN_VAL(TYPE, ATTR)                                                                                \
    {                                                                                \
        auto *a = dynamic_cast<sc_core::sc_attribute<TYPE> *>(ATTR);                                                   \
        if (a != nullptr) {                                                                                \
            node[a->name()] = a->value;                                                                                \
            continue;                                                                                \
        }                                                                                \
    }

void scc::configurer::dump_configuration(sc_core::sc_object *obj, Json::Value &parent) {
    auto mod = dynamic_cast<sc_core::sc_module *>(obj);
    Json::Value node{Json::objectValue};
    for (sc_core::sc_attr_base *attr_base : obj->attr_cltn()) {
        CHECK_N_ASSIGN_VAL(int, attr_base);
        CHECK_N_ASSIGN_VAL(unsigned, attr_base);
        CHECK_N_ASSIGN_VAL(int64_t, attr_base);
        CHECK_N_ASSIGN_VAL(uint64_t, attr_base);
        CHECK_N_ASSIGN_VAL(bool, attr_base);
        CHECK_N_ASSIGN_VAL(float, attr_base);
        CHECK_N_ASSIGN_VAL(double, attr_base);
        CHECK_N_ASSIGN_VAL(std::string, attr_base);
        CHECK_N_ASSIGN_VAL(char *, attr_base);
    }
    const std::string hier_name{obj->name()};
    cci::cci_param_predicate pred{[hier_name](cci::cci_param_untyped_handle h) -> bool {
    	std::string h_name {h.name()};
    	auto sep = hier_name.length();
    	if(h_name.length()>hier_name.length()){
            auto path_match = h_name.compare(0, sep, hier_name)==0; // begins with hier_name
            auto sep_match = h_name[sep]=='.' ; // has additional part
            auto tail_nomatch = h_name.substr(sep+1).find('.')==h_name.npos; // but no other hierarchy separator
            return path_match && sep_match && tail_nomatch;
    	} else
    	    return false;
    }};
    auto handles = cci_broker.get_param_handles(pred);
    for (auto &h : handles) {
        auto value = h.get_cci_value();
        auto paramname = std::string{h.name()};
        auto basename = paramname.substr(paramname.find_last_of('.') + 1);
        if (value.is_bool())
            node[basename] = value.get_bool();
        else if (value.is_int())
            node[basename] = value.get_int();
        else if (value.is_int64())
            node[basename] = static_cast<int64_t>(value.get_int64());
        else if (value.is_uint())
            node[basename] = value.get_uint();
        else if (value.is_uint64())
            node[basename] = static_cast<uint64_t>(value.get_uint64());
        else if (value.is_double())
            node[basename] = value.get_double();
        else if (value.is_string())
            node[basename] = value.get_string().c_str();
    }
    for (auto *o : get_sc_objects(obj)) dump_configuration(o, node);
    if (!node.empty()) parent[obj->basename()] = node;
}

void scc::configurer::configure() {
	if(config_valid)
		for (auto *o : sc_core::sc_get_top_level_objects(sc_core::sc_curr_simcontext)) {
			Json::Value &val = root[o->name()];
			if (!val.isNull())
				configure_sc_attribute_hierarchical(o, val);
		}
}

void scc::configurer::configure_sc_attribute_hierarchical(sc_core::sc_object *obj, Json::Value &hier_val) {
    for (auto *attr : obj->attr_cltn()) {
        Json::Value &val = hier_val[attr->name()];
        if (!val.isNull()) set_value(attr, val);
    }
    for (auto *o : obj->get_child_objects()) {
        Json::Value &val = hier_val[o->basename()];
        if (!val.isNull()) configure_sc_attribute_hierarchical(o, val);
    }
}

#define CHECK_N_ASSIGN(TYPE, ATTR, VAL)                                                                                \
    {                                                                                \
        auto *a = dynamic_cast<sc_core::sc_attribute<TYPE> *>(ATTR);                                                   \
        if (a != nullptr) {                                                                                \
            a->value = VAL;                                                                                \
            return;                                                                                \
        }                                                                                \
    }

void scc::configurer::set_value(sc_core::sc_attr_base *attr_base, Json::Value &hier_val) {
    CHECK_N_ASSIGN(int, attr_base, hier_val.asInt());
    CHECK_N_ASSIGN(unsigned, attr_base, hier_val.asUInt());
    CHECK_N_ASSIGN(int64_t, attr_base, hier_val.asInt64());
    CHECK_N_ASSIGN(uint64_t, attr_base, hier_val.asUInt64());
    CHECK_N_ASSIGN(bool, attr_base, hier_val.asBool());
    CHECK_N_ASSIGN(float, attr_base, hier_val.asFloat());
    CHECK_N_ASSIGN(double, attr_base, hier_val.asDouble());
    CHECK_N_ASSIGN(std::string, attr_base, hier_val.asString());
    CHECK_N_ASSIGN(char *, attr_base, strdup(hier_val.asCString()));
}

Json::Value &scc::configurer::get_value_from_hierarchy(const std::string &hier_name) {
    size_t npos = hier_name.find_first_of('.');
    Json::Value &val = root[hier_name.substr(0, npos)];
    if (val.isNull() || npos == hier_name.size()) return val;
    return get_value_from_hierarchy(hier_name.substr(npos + 1, hier_name.size()), val);
}

Json::Value &scc::configurer::get_value_from_hierarchy(const std::string &hier_name, Json::Value &value) {
    size_t npos = hier_name.find_first_of('.');
    Json::Value &val = value[hier_name.substr(0, npos)];
    if (val.isNull() || npos == std::string::npos || !val.isObject()) return val;
    return get_value_from_hierarchy(hier_name.substr(npos + 1, hier_name.size()), val);
}

void scc::configurer::set_configuration_value(sc_core::sc_attr_base *attr_base, sc_core::sc_object *owner) {
    std::string name(owner->name());
    name += ".";
    name += attr_base->name();
    Json::Value &val = get_value_from_hierarchy(name);
    if (!val.isNull()) set_value(attr_base, val);
}

void scc::configurer::configure_cci_hierarchical(const Json::Value &node, std::string prefix) {
    if (node.size() > 0) {
        for (Json::Value::const_iterator itr = node.begin(); itr != node.end(); ++itr) {
            if (!itr.key().isString()) return;
            std::string key_name = itr.key().asString();
            std::string hier_name{prefix.size() ? prefix + "." + key_name : key_name};
            Json::Value val = node[key_name];
            if (val.isNull() || val.isArray())
                return;
            else if (val.isObject())
                configure_cci_hierarchical(*itr, hier_name);
            else {
                cci::cci_param_handle param_handle = cci_broker.get_param_handle(hier_name);
                if (param_handle.is_valid()) {
                    if (val.isString()) {
                        param_handle.set_cci_value(cci::cci_value(val.asString()));
                    } else if (val.isBool()) {
                        param_handle.set_cci_value(cci::cci_value(val.asBool()));
                    } else if (val.isInt()) {
                        param_handle.set_cci_value(cci::cci_value(val.asInt()));
                    } else if (val.isInt64()) {
                        param_handle.set_cci_value(cci::cci_value(val.asInt64()));
                    } else if (val.isUInt()) {
                        param_handle.set_cci_value(cci::cci_value(val.asUInt()));
                    } else if (val.isUInt64()) {
                        param_handle.set_cci_value(cci::cci_value(val.asUInt64()));
                    } else if (val.isDouble()) {
                        param_handle.set_cci_value(cci::cci_value(val.asDouble()));
                    }
                } else {
                    if (val.isString()) {
                        cci_broker.set_preset_cci_value(hier_name, cci::cci_value(val.asString()));
                    } else if (val.isBool()) {
                        cci_broker.set_preset_cci_value(hier_name, cci::cci_value(val.asBool()));
                    } else if (val.isInt()) {
                        cci_broker.set_preset_cci_value(hier_name, cci::cci_value(val.asInt()));
                    } else if (val.isInt64()) {
                        cci_broker.set_preset_cci_value(hier_name, cci::cci_value(val.asInt64()));
                    } else if (val.isUInt()) {
                        cci_broker.set_preset_cci_value(hier_name, cci::cci_value(val.asUInt()));
                    } else if (val.isUInt64()) {
                        cci_broker.set_preset_cci_value(hier_name, cci::cci_value(val.asUInt64()));
                    } else if (val.isDouble()) {
                        cci_broker.set_preset_cci_value(hier_name, cci::cci_value(val.asDouble()));
                    }
                }
            }
        }
    }
}

void scc::init_cci(std::string name) {
    cci::cci_register_broker(new cci_utils::broker("Global Broker"));
}
<|endoftext|>


<commit_before> /*
 NUI3 - C++ cross-platform GUI framework for OpenGL based applications
 Copyright (C) 2002-2003 Sebastien Metrot

 licence: see nui3/LICENCE.TXT
 */

#include "nui.h"

nuiTextLine::nuiTextLine(const nuiTextLayout& rLayout, float X, float Y)
: mLayout(rLayout), mX(X), mY(Y)
{
}

nuiTextLine::~nuiTextLine()
{
  for (uint32 i = 0; i < mpRuns.size(); i++)
    mpRuns[i]->Release();
}

const std::vector<nuiTextRun*>& nuiTextLine::GetGlyphRuns() const
{
  return mpRuns;
}

float nuiTextLine::GetX() const
{
  return mX;
}

float nuiTextLine::GetY() const
{
  return mY;
}

void nuiTextLine::SetPosition(float X, float Y)
{
  mX = X;
  mY = Y;
}

void nuiTextLine::AddRun(nuiTextRun* pRun)
{
  pRun->Acquire();
  mpRuns.push_back(pRun);
}

int32 nuiTextLine::GetRunCount() const
{
  return mpRuns.size();
}

nuiTextRun* nuiTextLine::GetRun(int32 index) const
{
  return mpRuns[index];
}

float nuiTextLine::GetAdvanceY() const
{
  float y = 0;
  for (uint32 i = 0; i < mpRuns.size(); i++)
  {
    y = MAX(y, mpRuns[i]->GetAdvanceY());
  }

  return y;
}

float nuiTextLine::GetAdvanceX() const
{
  float x = 0;
  for (uint32 i = 0; i < mpRuns.size(); i++)
  {
    x += mpRuns[i]->GetAdvanceX();
  }

  return x;
}

float nuiTextLine::GetAscender() const
{
  float v = 0;
  for (uint32 i = 0; i < mpRuns.size(); i++)
  {
    v = MAX(v, mpRuns[i]->GetAscender());
  }

  return v;
}

float nuiTextLine::GetDescender() const
{
  float v = 0;
  for (uint32 i = 0; i < mpRuns.size(); i++)
  {
    v = MIN(v, mpRuns[i]->GetAscender());
  }

  return v;
}

int32 nuiTextLine::GetGlyphCount() const
{
  int32 count = 0;
  for (uint32 i = 0; i < mpRuns.size(); i++)
  {
    count += mpRuns[i]->GetGlyphCount();
  }

  return count;
}

const nuiTextGlyph* nuiTextLine::GetGlyph(int32 Offset) const
{
  for (uint32 i = 0; i < mpRuns.size(); i++)
  {
    int32 s = mpRuns[i]->GetGlyphCount();
    if (Offset < s)
      return mpRuns[i]->GetGlyph(Offset);
    Offset -= s;
  }

  return NULL;
}

const nuiTextGlyph* nuiTextLine::GetGlyphAt(float X, float Y) const
{
  X -= mX;
  Y -= mY;

  for (uint32 i = 0; i < mpRuns.size(); i++)
  {
    const nuiTextGlyph* pGlyph = mpRuns[i]->GetGlyphAt(X, Y);
    if (pGlyph)
      return pGlyph;
  }

  return NULL;
}

float LayoutDirect(float PenX, float PenY, float globalwidth, float sublinewidth, float spacewidth, std::list<nuiTextRun*>& subline, nuiRect& globalrect)
{
  float h = 0;
  float x = 0;
  float y = 0;
  for (nuiTextRun* pRun : subline)
  {
    nuiFontBase* pFont = pRun->GetFont();

    nuiFontInfo finfo;

    if (pFont)
      pFont->GetInfo(finfo);

    std::vector<nuiTextGlyph>& rGlyphs(pRun->GetGlyphs());

    // Prepare glyphs:
    for (int32 g = 0; g < rGlyphs.size(); g++)
    {
      nuiTextGlyph& rGlyph(rGlyphs.at(g));

      pFont->PrepareGlyph(PenX + x, PenY + y, rGlyph);

      const nuiSize W = rGlyph.AdvanceX;
      const nuiSize X = rGlyph.mX + rGlyph.BearingX;
      const nuiSize Y = rGlyph.mY - finfo.Ascender;
      const nuiSize H = finfo.Height;

      h = MAX(h, H);
      nuiRect rr(globalrect);
      globalrect.Union(rr, nuiRect(PenX + x + X, PenY + y + Y, W, H));
    }

    x += pRun->GetAdvanceX();
  }

  return h;
}

float LayoutJustify(float PenX, float PenY, float globalwidth, float sublinewidth, float spacewidth, std::list<nuiTextRun*>& subline, nuiRect& globalrect)
{
  float h = 0;
  float x = 0;
  float y = 0;

  float ratio = (globalwidth - (sublinewidth - spacewidth)) / spacewidth;
  for (nuiTextRun* pRun : subline)
  {
    nuiFontBase* pFont = pRun->GetFont();

    nuiFontInfo finfo;
    if (pFont)
      pFont->GetInfo(finfo);

    std::vector<nuiTextGlyph>& rGlyphs(pRun->GetGlyphs());

    // Prepare glyphs:
    for (int32 g = 0; g < rGlyphs.size(); g++)
    {
      nuiTextGlyph& rGlyph(rGlyphs.at(g));

      pFont->PrepareGlyph(PenX + x, PenY + y, rGlyph);

      const nuiSize W = rGlyph.AdvanceX;
      const nuiSize X = rGlyph.mX + rGlyph.BearingX;
      const nuiSize Y = rGlyph.mY - finfo.Ascender;
      const nuiSize H = finfo.Height;

      h = MAX(h, H);
      nuiRect rr(globalrect);
      globalrect.Union(rr, nuiRect(PenX + x + X, PenY + y + Y, W, H));
    }

    if (pRun->IsDummy())
    {
      x += pRun->GetAdvanceX() * ratio;
    }
    else
    {
      x += pRun->GetAdvanceX();
    }
  }

  return h;
}

float LayoutLeft(float PenX, float PenY, float globalwidth, float sublinewidth, float spacewidth, std::list<nuiTextRun*>& subline, nuiRect& globalrect)
{
  return LayoutDirect(PenX, PenY, globalwidth, sublinewidth, spacewidth, subline, globalrect);
}

float LayoutRight(float PenX, float PenY, float globalwidth, float sublinewidth, float spacewidth, std::list<nuiTextRun*>& subline, nuiRect& globalrect)
{
  float x = globalwidth - sublinewidth;
  return LayoutDirect(PenX + x, PenY, globalwidth, sublinewidth, spacewidth, subline, globalrect);
}

float LayoutCenter(float PenX, float PenY, float globalwidth, float sublinewidth, float spacewidth, std::list<nuiTextRun*>& subline, nuiRect& globalrect)
{
  float x = (globalwidth - sublinewidth) / 2;
  return LayoutDirect(PenX + x, PenY, globalwidth, sublinewidth, spacewidth, subline, globalrect);
}

float nuiTextLine::Layout(float PenX, float PenY, float width, nuiRect& globalrect)
{
  SetPosition(PenX, PenY);

  PenX = 0;
  float x = 0;
  float y = 0;

  std::vector< std::list <nuiTextRun*> > sublines;
  sublines.resize(1);
  bool sublinestart = true;
  // Prepare sublines:
  for (uint32 r = 0; r < GetRunCount(); r++)
  {
    nuiTextRun* pRun = GetRun(r);
    if (pRun->IsWrapStart())
      sublines.resize(sublines.size() + 1);

    sublines.back().push_back(pRun);
  }

  // Trim the dummy runs:
  for (int l = 0; l < sublines.size(); l++)
  {
    // Remove dummy text runs from the start of the lines (except the first line):
    bool skip = false;
    if (sublines[l].front()->GetStyle().GetMode() == nuiTextLayoutJustify && l == 0)
      skip = true;

    if (!skip)
    {
      while (!sublines.empty() && sublines[l].front()->IsDummy())
        sublines[l].pop_front();
    }

    // Remove dummy text runs from the end of the lines:
    while (!sublines.empty() && sublines[l].back()->IsDummy())
      sublines[l].pop_back();
  }

  // Now position each run inside each subline:
  for (int l = 0; l < sublines.size(); l++)
  {
    float w = 0;
    float space = 0;
    for (nuiTextRun* pRun : sublines[l])
    {
      float a = pRun->GetAdvanceX();
      w += a;
      if (pRun->IsDummy())
        space += a;
    }

    float h = 0;
    switch (sublines[l].front()->GetStyle().GetMode())
    {
      case nuiTextLayoutLeft:
        h = LayoutLeft(PenX, PenY, width, w, space, sublines[l], globalrect);
        break;

      case nuiTextLayoutRight:
        h = LayoutRight(PenX, PenY, width, w, space, sublines[l], globalrect);
        break;

      case nuiTextLayoutJustify:
        h = LayoutJustify(PenX, PenY, width, w, space, sublines[l], globalrect);
        break;

      case nuiTextLayoutCenter:
        h = LayoutCenter(PenX, PenY, width, w, space, sublines[l], globalrect);
        break;
    }

    PenY += h;
  }

  return PenY;
}

<commit_msg>fixed corner cases crashes<commit_after> /*
 NUI3 - C++ cross-platform GUI framework for OpenGL based applications
 Copyright (C) 2002-2003 Sebastien Metrot

 licence: see nui3/LICENCE.TXT
 */

#include "nui.h"

nuiTextLine::nuiTextLine(const nuiTextLayout& rLayout, float X, float Y)
: mLayout(rLayout), mX(X), mY(Y)
{
}

nuiTextLine::~nuiTextLine()
{
  for (uint32 i = 0; i < mpRuns.size(); i++)
    mpRuns[i]->Release();
}

const std::vector<nuiTextRun*>& nuiTextLine::GetGlyphRuns() const
{
  return mpRuns;
}

float nuiTextLine::GetX() const
{
  return mX;
}

float nuiTextLine::GetY() const
{
  return mY;
}

void nuiTextLine::SetPosition(float X, float Y)
{
  mX = X;
  mY = Y;
}

void nuiTextLine::AddRun(nuiTextRun* pRun)
{
  pRun->Acquire();
  mpRuns.push_back(pRun);
}

int32 nuiTextLine::GetRunCount() const
{
  return mpRuns.size();
}

nuiTextRun* nuiTextLine::GetRun(int32 index) const
{
  return mpRuns[index];
}

float nuiTextLine::GetAdvanceY() const
{
  float y = 0;
  for (uint32 i = 0; i < mpRuns.size(); i++)
  {
    y = MAX(y, mpRuns[i]->GetAdvanceY());
  }

  return y;
}

float nuiTextLine::GetAdvanceX() const
{
  float x = 0;
  for (uint32 i = 0; i < mpRuns.size(); i++)
  {
    x += mpRuns[i]->GetAdvanceX();
  }

  return x;
}

float nuiTextLine::GetAscender() const
{
  float v = 0;
  for (uint32 i = 0; i < mpRuns.size(); i++)
  {
    v = MAX(v, mpRuns[i]->GetAscender());
  }

  return v;
}

float nuiTextLine::GetDescender() const
{
  float v = 0;
  for (uint32 i = 0; i < mpRuns.size(); i++)
  {
    v = MIN(v, mpRuns[i]->GetAscender());
  }

  return v;
}

int32 nuiTextLine::GetGlyphCount() const
{
  int32 count = 0;
  for (uint32 i = 0; i < mpRuns.size(); i++)
  {
    count += mpRuns[i]->GetGlyphCount();
  }

  return count;
}

const nuiTextGlyph* nuiTextLine::GetGlyph(int32 Offset) const
{
  for (uint32 i = 0; i < mpRuns.size(); i++)
  {
    int32 s = mpRuns[i]->GetGlyphCount();
    if (Offset < s)
      return mpRuns[i]->GetGlyph(Offset);
    Offset -= s;
  }

  return NULL;
}

const nuiTextGlyph* nuiTextLine::GetGlyphAt(float X, float Y) const
{
  X -= mX;
  Y -= mY;

  for (uint32 i = 0; i < mpRuns.size(); i++)
  {
    const nuiTextGlyph* pGlyph = mpRuns[i]->GetGlyphAt(X, Y);
    if (pGlyph)
      return pGlyph;
  }

  return NULL;
}

float LayoutDirect(float PenX, float PenY, float globalwidth, float sublinewidth, float spacewidth, std::list<nuiTextRun*>& subline, nuiRect& globalrect)
{
  float h = 0;
  float x = 0;
  float y = 0;
  for (nuiTextRun* pRun : subline)
  {
    nuiFontBase* pFont = pRun->GetFont();

    nuiFontInfo finfo;

    if (pFont)
      pFont->GetInfo(finfo);

    std::vector<nuiTextGlyph>& rGlyphs(pRun->GetGlyphs());

    // Prepare glyphs:
    for (int32 g = 0; g < rGlyphs.size(); g++)
    {
      nuiTextGlyph& rGlyph(rGlyphs.at(g));

      pFont->PrepareGlyph(PenX + x, PenY + y, rGlyph);

      const nuiSize W = rGlyph.AdvanceX;
      const nuiSize X = rGlyph.mX + rGlyph.BearingX;
      const nuiSize Y = rGlyph.mY - finfo.Ascender;
      const nuiSize H = finfo.Height;

      h = MAX(h, H);
      nuiRect rr(globalrect);
      globalrect.Union(rr, nuiRect(PenX + x + X, PenY + y + Y, W, H));
    }

    x += pRun->GetAdvanceX();
  }

  return h;
}

float LayoutJustify(float PenX, float PenY, float globalwidth, float sublinewidth, float spacewidth, std::list<nuiTextRun*>& subline, nuiRect& globalrect)
{
  float h = 0;
  float x = 0;
  float y = 0;

  float ratio = (globalwidth - (sublinewidth - spacewidth)) / spacewidth;
  for (nuiTextRun* pRun : subline)
  {
    nuiFontBase* pFont = pRun->GetFont();

    nuiFontInfo finfo;
    if (pFont)
      pFont->GetInfo(finfo);

    std::vector<nuiTextGlyph>& rGlyphs(pRun->GetGlyphs());

    // Prepare glyphs:
    for (int32 g = 0; g < rGlyphs.size(); g++)
    {
      nuiTextGlyph& rGlyph(rGlyphs.at(g));

      pFont->PrepareGlyph(PenX + x, PenY + y, rGlyph);

      const nuiSize W = rGlyph.AdvanceX;
      const nuiSize X = rGlyph.mX + rGlyph.BearingX;
      const nuiSize Y = rGlyph.mY - finfo.Ascender;
      const nuiSize H = finfo.Height;

      h = MAX(h, H);
      nuiRect rr(globalrect);
      globalrect.Union(rr, nuiRect(PenX + x + X, PenY + y + Y, W, H));
    }

    if (pRun->IsDummy())
    {
      x += pRun->GetAdvanceX() * ratio;
    }
    else
    {
      x += pRun->GetAdvanceX();
    }
  }

  return h;
}

float LayoutLeft(float PenX, float PenY, float globalwidth, float sublinewidth, float spacewidth, std::list<nuiTextRun*>& subline, nuiRect& globalrect)
{
  return LayoutDirect(PenX, PenY, globalwidth, sublinewidth, spacewidth, subline, globalrect);
}

float LayoutRight(float PenX, float PenY, float globalwidth, float sublinewidth, float spacewidth, std::list<nuiTextRun*>& subline, nuiRect& globalrect)
{
  float x = globalwidth - sublinewidth;
  return LayoutDirect(PenX + x, PenY, globalwidth, sublinewidth, spacewidth, subline, globalrect);
}

float LayoutCenter(float PenX, float PenY, float globalwidth, float sublinewidth, float spacewidth, std::list<nuiTextRun*>& subline, nuiRect& globalrect)
{
  float x = (globalwidth - sublinewidth) / 2;
  return LayoutDirect(PenX + x, PenY, globalwidth, sublinewidth, spacewidth, subline, globalrect);
}

float nuiTextLine::Layout(float PenX, float PenY, float width, nuiRect& globalrect)
{
  SetPosition(PenX, PenY);

  PenX = 0;
  float x = 0;
  float y = 0;

  std::vector< std::list <nuiTextRun*> > sublines;
  sublines.resize(1);
  bool sublinestart = true;
  // Prepare sublines:
  for (uint32 r = 0; r < GetRunCount(); r++)
  {
    nuiTextRun* pRun = GetRun(r);
    if (pRun->IsWrapStart())
      sublines.resize(sublines.size() + 1);

    sublines.back().push_back(pRun);
  }

  // Trim the dummy runs:
  for (int l = 0; l < sublines.size(); l++)
  {
    // Remove dummy text runs from the start of the lines (except the first line):
    bool skip = false;
    if (!sublines[l].empty() && sublines[l].front()->GetStyle().GetMode() == nuiTextLayoutJustify && l == 0)
      skip = true;

    if (!skip)
    {
      while (!sublines.empty() && !sublines[l].empty() && sublines[l].front()->IsDummy())
        sublines[l].pop_front();
    }

    // Remove dummy text runs from the end of the lines:
    while (!sublines.empty() && !sublines[l].empty() && sublines[l].back()->IsDummy())
      sublines[l].pop_back();
  }

  // Now position each run inside each subline:
  for (int l = 0; l < sublines.size(); l++)
  {
    float w = 0;
    float space = 0;
    for (nuiTextRun* pRun : sublines[l])
    {
      float a = pRun->GetAdvanceX();
      w += a;
      if (pRun->IsDummy())
        space += a;
    }

    float h = 0;
    if (!sublines[l].empty())
    {
      switch (sublines[l].front()->GetStyle().GetMode())
      {
        case nuiTextLayoutLeft:
          h = LayoutLeft(PenX, PenY, width, w, space, sublines[l], globalrect);
          break;

        case nuiTextLayoutRight:
          h = LayoutRight(PenX, PenY, width, w, space, sublines[l], globalrect);
          break;

        case nuiTextLayoutJustify:
          h = LayoutJustify(PenX, PenY, width, w, space, sublines[l], globalrect);
          break;

        case nuiTextLayoutCenter:
          h = LayoutCenter(PenX, PenY, width, w, space, sublines[l], globalrect);
          break;
      }
    }

    PenY += h;
  }

  return PenY;
}

<|endoftext|>