<commit_before>#include <brickred/timer_heap.h>

#include <cstddef>
#include <algorithm>
#include <vector>
#include <ext/hash_map>

#include <brickred/base_logger.h>
#include <brickred/timestamp.h>

namespace brickred {

namespace timer_heap_impl {

class TimerIdAllocator {
public:
    TimerIdAllocator() : value_(0) {}
    ~TimerIdAllocator() {}

    int64_t getId()
    {
        if (++value_ < 0) {
            value_ = 0;
        }
        return value_;
    }

private:
    int64_t value_;
};

///////////////////////////////////////////////////////////////////////////////
class Timer {
public:
    typedef TimerHeap::TimerId TimerId;
    typedef TimerHeap::TimerCallback TimerCallback;

    explicit Timer(TimerId id, const Timestamp &timestamp, int64_t timeout,
                   const TimerCallback &timer_cb, int call_times);
    ~Timer() {}

    TimerId getId() const { return id_; }
    const Timestamp &getTimestamp() const { return timestamp_; }
    Timestamp &getTimestamp() { return timestamp_; }
    int getTimeout() const { return timeout_; }
    int getHeapPos() const { return heap_pos_; }
    void setHeapPos(int heap_pos) { heap_pos_ = heap_pos; }
    int getCallTimes() const { return call_times_; }
    void decCallTimes() { --call_times_; }
    TimerCallback getCallback() const { return timer_cb_; }

private:
    TimerId id_;
    Timestamp timestamp_;
    int64_t timeout_;
    TimerCallback timer_cb_;
    int call_times_;
    int heap_pos_;
};

///////////////////////////////////////////////////////////////////////////////
Timer::Timer(TimerId id, const Timestamp &timestamp, int64_t timeout,
             const TimerCallback &timer_cb, int call_times) :
    id_(id), timestamp_(timestamp), timeout_(timeout),
    timer_cb_(timer_cb), call_times_(call_times),
    heap_pos_(-1)
{
}

} using namespace timer_heap_impl;

///////////////////////////////////////////////////////////////////////////////
class TimerHeap::Impl {
public:
    typedef TimerHeap::TimerId TimerId;
    typedef TimerHeap::TimerCallback TimerCallback;
    typedef __gnu_cxx::hash_map<TimerId, Timer *> TimerMap;
    typedef std::vector<Timer *> TimerVector;

    Impl();
    ~Impl();

    int64_t getNextTimeoutMillisecond(const Timestamp &now) const;
    TimerId addTimer(const Timestamp &now, int64_t timeout_ms,
                     const TimerCallback &timer_cb,
                     int call_times = -1);
    void removeTimer(TimerId timer_id);
    void checkTimeout(const Timestamp &now);

public:
    void minHeapReserve();
    void minHeapInsert(Timer *timer);
    void minHeapErase(Timer *timer);
    void minHeapShiftUp(int cur_index);
    void minHeapShiftDown(int cur_index);
    Timer *minHeapTop() const;

private:
    TimerIdAllocator timer_id_allocator_;
    TimerMap timers_;
    TimerVector timer_min_heap_;
};

///////////////////////////////////////////////////////////////////////////////
TimerHeap::Impl::Impl()
{
    timer_min_heap_.push_back(NULL);
}

TimerHeap::Impl::~Impl()
{
    for (TimerMap::iterator iter = timers_.begin();
         iter != timers_.end(); ++iter) {
        delete iter->second;
    }
}

int64_t TimerHeap::Impl::getNextTimeoutMillisecond(const Timestamp &now) const
{
    Timer *timer = minHeapTop();
    if (NULL == timer) {
        return -1;
    }

    if (timer->getTimestamp() < now) {
        return 0;
    }

    return now.distanceMillisecond(timer->getTimestamp());
}

TimerHeap::Impl::TimerId TimerHeap::Impl::addTimer(const Timestamp &now,
    int64_t timeout_ms, const TimerCallback &timer_cb, int call_times)
{
    TimerId timer_id = timer_id_allocator_.getId();
    timeout_ms = std::max((int64_t)0, timeout_ms);
    UniquePtr<Timer> timer(new Timer(timer_id,
                                     now + timeout_ms,
                                     timeout_ms,
                                     timer_cb,
                                     call_times));

    if (timers_.find(timer_id) != timers_.end()) {
        BASE_ERROR("timer(%lu) already in timer map", timer_id);
        return -1;
    }

    // reserve min heap
    minHeapReserve();

    // insert into timer map
    timers_.insert(std::make_pair(timer_id, timer.get()));
    Timer *timer_raw = timer.release();

    // insert into timer min heap
    minHeapInsert(timer_raw);

    return timer_id;
}

void TimerHeap::Impl::removeTimer(TimerId timer_id)
{
    TimerMap::iterator iter = timers_.find(timer_id);
    if (timers_.end() == iter) {
        return;
    }

    Timer *timer = iter->second;

    // remove from timer min heap
    minHeapErase(timer);

    // remove from timer map
    timers_.erase(iter);
    delete timer;
}

void TimerHeap::Impl::checkTimeout(const Timestamp &now)
{
    for (;;) {
        Timer *timer = minHeapTop();
        if (NULL == timer) {
            return;
        }

        if (now.millisecondLess(timer->getTimestamp())) {
            return;
        }

        TimerId timer_id = timer->getId();
        TimerCallback timer_cb = timer->getCallback();

        // remove from timer min heap
        minHeapErase(timer);

        if (timer->getCallTimes() == 1) {
            // remove from timer map
            timers_.erase(timer_id);
            delete timer;
        } else {
            if (timer->getCallTimes() > 0) {
                timer->decCallTimes();
            }
            timer->getTimestamp() += timer->getTimeout();
            // insert into timer min heap again
            minHeapInsert(timer);
        }

        // do callback
        timer_cb(timer_id);
    }
}

void TimerHeap::Impl::minHeapReserve()
{
    timer_min_heap_.reserve(timer_min_heap_.size() + 1);
}

void TimerHeap::Impl::minHeapInsert(Timer *timer)
{
    int cur_index = timer_min_heap_.size();
    timer_min_heap_.push_back(timer);
    timer->setHeapPos(cur_index);

    minHeapShiftUp(cur_index);
}

void TimerHeap::Impl::minHeapErase(Timer *timer)
{
    int cur_index = timer->getHeapPos();
    if (cur_index < 0) {
        return;
    }

    timer->setHeapPos(-1);
    timer_min_heap_[cur_index] = timer_min_heap_.back();
    timer_min_heap_[cur_index]->setHeapPos(cur_index);
    timer_min_heap_.pop_back();

    int parent_index = cur_index / 2;
    if (parent_index > 0 &&
        timer_min_heap_[cur_index]->getTimestamp() <
        timer_min_heap_[parent_index]->getTimestamp()) {
        minHeapShiftUp(cur_index);
    } else {
        minHeapShiftDown(cur_index);
    }
}

void TimerHeap::Impl::minHeapShiftUp(int cur_index)
{
    for (;;) {
        int parent_index = cur_index / 2;

        if (0 == parent_index) {
            break;
        }
        if (timer_min_heap_[parent_index]->getTimestamp() <
            timer_min_heap_[cur_index]->getTimestamp()) {
            break;
        }

        timer_min_heap_[parent_index]->setHeapPos(cur_index);
        timer_min_heap_[cur_index]->setHeapPos(parent_index);
        std::swap(timer_min_heap_[parent_index], timer_min_heap_[cur_index]);

        cur_index = parent_index;
    }
}

void TimerHeap::Impl::minHeapShiftDown(int cur_index)
{
    for (;;) {
        int child_index = cur_index * 2;

        if (child_index >= (int)timer_min_heap_.size()) {
            break;
        }

        if (child_index + 1 < (int)timer_min_heap_.size() &&
            timer_min_heap_[child_index + 1]->getTimestamp() <
            timer_min_heap_[child_index]->getTimestamp()) {
            ++child_index;
        }

        if (timer_min_heap_[cur_index]->getTimestamp() <
            timer_min_heap_[child_index]->getTimestamp()) {
            break;
        }

        timer_min_heap_[cur_index]->setHeapPos(child_index);
        timer_min_heap_[child_index]->setHeapPos(cur_index);
        std::swap(timer_min_heap_[cur_index], timer_min_heap_[child_index]);

        cur_index = child_index;
    }
}

Timer *TimerHeap::Impl::minHeapTop() const
{
    if (timer_min_heap_.size() <= 1) {
        return NULL;
    }
    return timer_min_heap_[1];
}

///////////////////////////////////////////////////////////////////////////////
TimerHeap::TimerHeap() :
    pimpl_(new Impl())
{
}

TimerHeap::~TimerHeap()
{
}

int64_t TimerHeap::getNextTimeoutMillisecond(const Timestamp &now) const
{
    return pimpl_->getNextTimeoutMillisecond(now);
}

TimerHeap::TimerId TimerHeap::addTimer(const Timestamp &now,
    int64_t timeout_ms, const TimerCallback &timer_cb, int call_times)
{
    return pimpl_->addTimer(now, timeout_ms, timer_cb, call_times);
}

void TimerHeap::removeTimer(TimerId timer_id)
{
    pimpl_->removeTimer(timer_id);
}

void TimerHeap::checkTimeout(const Timestamp &now)
{
    pimpl_->checkTimeout(now);
}

}  // namespace brickred
<commit_msg>fix timer_heap minHeapErase() bug<commit_after>#include <brickred/timer_heap.h>

#include <cstddef>
#include <algorithm>
#include <vector>
#include <ext/hash_map>

#include <brickred/base_logger.h>
#include <brickred/timestamp.h>

namespace brickred {

namespace timer_heap_impl {

class TimerIdAllocator {
public:
    TimerIdAllocator() : value_(0) {}
    ~TimerIdAllocator() {}

    int64_t getId()
    {
        if (++value_ < 0) {
            value_ = 0;
        }
        return value_;
    }

private:
    int64_t value_;
};

///////////////////////////////////////////////////////////////////////////////
class Timer {
public:
    typedef TimerHeap::TimerId TimerId;
    typedef TimerHeap::TimerCallback TimerCallback;

    explicit Timer(TimerId id, const Timestamp &timestamp, int64_t timeout,
                   const TimerCallback &timer_cb, int call_times);
    ~Timer() {}

    TimerId getId() const { return id_; }
    const Timestamp &getTimestamp() const { return timestamp_; }
    Timestamp &getTimestamp() { return timestamp_; }
    int getTimeout() const { return timeout_; }
    int getHeapPos() const { return heap_pos_; }
    void setHeapPos(int heap_pos) { heap_pos_ = heap_pos; }
    int getCallTimes() const { return call_times_; }
    void decCallTimes() { --call_times_; }
    TimerCallback getCallback() const { return timer_cb_; }

private:
    TimerId id_;
    Timestamp timestamp_;
    int64_t timeout_;
    TimerCallback timer_cb_;
    int call_times_;
    int heap_pos_;
};

///////////////////////////////////////////////////////////////////////////////
Timer::Timer(TimerId id, const Timestamp &timestamp, int64_t timeout,
             const TimerCallback &timer_cb, int call_times) :
    id_(id), timestamp_(timestamp), timeout_(timeout),
    timer_cb_(timer_cb), call_times_(call_times),
    heap_pos_(-1)
{
}

} using namespace timer_heap_impl;

///////////////////////////////////////////////////////////////////////////////
class TimerHeap::Impl {
public:
    typedef TimerHeap::TimerId TimerId;
    typedef TimerHeap::TimerCallback TimerCallback;
    typedef __gnu_cxx::hash_map<TimerId, Timer *> TimerMap;
    typedef std::vector<Timer *> TimerVector;

    Impl();
    ~Impl();

    int64_t getNextTimeoutMillisecond(const Timestamp &now) const;
    TimerId addTimer(const Timestamp &now, int64_t timeout_ms,
                     const TimerCallback &timer_cb,
                     int call_times = -1);
    void removeTimer(TimerId timer_id);
    void checkTimeout(const Timestamp &now);

public:
    void minHeapReserve();
    void minHeapInsert(Timer *timer);
    void minHeapErase(Timer *timer);
    void minHeapShiftUp(int cur_index);
    void minHeapShiftDown(int cur_index);
    Timer *minHeapTop() const;

private:
    TimerIdAllocator timer_id_allocator_;
    TimerMap timers_;
    TimerVector timer_min_heap_;
};

///////////////////////////////////////////////////////////////////////////////
TimerHeap::Impl::Impl()
{
    timer_min_heap_.push_back(NULL);
}

TimerHeap::Impl::~Impl()
{
    for (TimerMap::iterator iter = timers_.begin();
         iter != timers_.end(); ++iter) {
        delete iter->second;
    }
}

int64_t TimerHeap::Impl::getNextTimeoutMillisecond(const Timestamp &now) const
{
    Timer *timer = minHeapTop();
    if (NULL == timer) {
        return -1;
    }

    if (timer->getTimestamp() < now) {
        return 0;
    }

    return now.distanceMillisecond(timer->getTimestamp());
}

TimerHeap::Impl::TimerId TimerHeap::Impl::addTimer(const Timestamp &now,
    int64_t timeout_ms, const TimerCallback &timer_cb, int call_times)
{
    TimerId timer_id = timer_id_allocator_.getId();
    timeout_ms = std::max((int64_t)0, timeout_ms);
    UniquePtr<Timer> timer(new Timer(timer_id,
                                     now + timeout_ms,
                                     timeout_ms,
                                     timer_cb,
                                     call_times));

    if (timers_.find(timer_id) != timers_.end()) {
        BASE_ERROR("timer(%lu) already in timer map", timer_id);
        return -1;
    }

    // reserve min heap
    minHeapReserve();

    // insert into timer map
    timers_.insert(std::make_pair(timer_id, timer.get()));
    Timer *timer_raw = timer.release();

    // insert into timer min heap
    minHeapInsert(timer_raw);

    return timer_id;
}

void TimerHeap::Impl::removeTimer(TimerId timer_id)
{
    TimerMap::iterator iter = timers_.find(timer_id);
    if (timers_.end() == iter) {
        return;
    }

    Timer *timer = iter->second;

    // remove from timer min heap
    minHeapErase(timer);

    // remove from timer map
    timers_.erase(iter);
    delete timer;
}

void TimerHeap::Impl::checkTimeout(const Timestamp &now)
{
    for (;;) {
        Timer *timer = minHeapTop();
        if (NULL == timer) {
            return;
        }

        if (now.millisecondLess(timer->getTimestamp())) {
            return;
        }

        TimerId timer_id = timer->getId();
        TimerCallback timer_cb = timer->getCallback();

        // remove from timer min heap
        minHeapErase(timer);

        if (timer->getCallTimes() == 1) {
            // remove from timer map
            timers_.erase(timer_id);
            delete timer;
        } else {
            if (timer->getCallTimes() > 0) {
                timer->decCallTimes();
            }
            timer->getTimestamp() += timer->getTimeout();
            // insert into timer min heap again
            minHeapInsert(timer);
        }

        // do callback
        timer_cb(timer_id);
    }
}

void TimerHeap::Impl::minHeapReserve()
{
    timer_min_heap_.reserve(timer_min_heap_.size() + 1);
}

void TimerHeap::Impl::minHeapInsert(Timer *timer)
{
    int cur_index = timer_min_heap_.size();
    timer_min_heap_.push_back(timer);
    timer->setHeapPos(cur_index);

    minHeapShiftUp(cur_index);
}

void TimerHeap::Impl::minHeapErase(Timer *timer)
{
    int cur_index = timer->getHeapPos();
    if (cur_index < 0) {
        return;
    }

    timer->setHeapPos(-1);
    timer_min_heap_[cur_index] = timer_min_heap_.back();
    timer_min_heap_[cur_index]->setHeapPos(cur_index);
    timer_min_heap_.pop_back();

    if (timer_min_heap_.size() <= 1) {
        return;
    }

    int parent_index = cur_index / 2;
    if (parent_index > 0 &&
        timer_min_heap_[cur_index]->getTimestamp() <
        timer_min_heap_[parent_index]->getTimestamp()) {
        minHeapShiftUp(cur_index);
    } else {
        minHeapShiftDown(cur_index);
    }
}

void TimerHeap::Impl::minHeapShiftUp(int cur_index)
{
    for (;;) {
        int parent_index = cur_index / 2;

        if (0 == parent_index) {
            break;
        }
        if (timer_min_heap_[parent_index]->getTimestamp() <
            timer_min_heap_[cur_index]->getTimestamp()) {
            break;
        }

        timer_min_heap_[parent_index]->setHeapPos(cur_index);
        timer_min_heap_[cur_index]->setHeapPos(parent_index);
        std::swap(timer_min_heap_[parent_index], timer_min_heap_[cur_index]);

        cur_index = parent_index;
    }
}

void TimerHeap::Impl::minHeapShiftDown(int cur_index)
{
    for (;;) {
        int child_index = cur_index * 2;

        if (child_index >= (int)timer_min_heap_.size()) {
            break;
        }

        if (child_index + 1 < (int)timer_min_heap_.size() &&
            timer_min_heap_[child_index + 1]->getTimestamp() <
            timer_min_heap_[child_index]->getTimestamp()) {
            ++child_index;
        }

        if (timer_min_heap_[cur_index]->getTimestamp() <
            timer_min_heap_[child_index]->getTimestamp()) {
            break;
        }

        timer_min_heap_[cur_index]->setHeapPos(child_index);
        timer_min_heap_[child_index]->setHeapPos(cur_index);
        std::swap(timer_min_heap_[cur_index], timer_min_heap_[child_index]);

        cur_index = child_index;
    }
}

Timer *TimerHeap::Impl::minHeapTop() const
{
    if (timer_min_heap_.size() <= 1) {
        return NULL;
    }
    return timer_min_heap_[1];
}

///////////////////////////////////////////////////////////////////////////////
TimerHeap::TimerHeap() :
    pimpl_(new Impl())
{
}

TimerHeap::~TimerHeap()
{
}

int64_t TimerHeap::getNextTimeoutMillisecond(const Timestamp &now) const
{
    return pimpl_->getNextTimeoutMillisecond(now);
}

TimerHeap::TimerId TimerHeap::addTimer(const Timestamp &now,
    int64_t timeout_ms, const TimerCallback &timer_cb, int call_times)
{
    return pimpl_->addTimer(now, timeout_ms, timer_cb, call_times);
}

void TimerHeap::removeTimer(TimerId timer_id)
{
    pimpl_->removeTimer(timer_id);
}

void TimerHeap::checkTimeout(const Timestamp &now)
{
    pimpl_->checkTimeout(now);
}

}  // namespace brickred
<|endoftext|>


<commit_before>/* Copyright (C) 2013 BMW Group
 * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
 * Author: Juergen Gehring (juergen.gehring@bmw.de)
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#define _GLIBCXX_USE_NANOSLEEP

#include <CommonAPI/DBus/DBusInputStream.h>
#include <CommonAPI/DBus/DBusMessage.h>
#include <CommonAPI/DBus/DBusProxy.h>
#include <CommonAPI/DBus/DBusConnection.h>
#include <CommonAPI/DBus/DBusStubAdapter.h>
#include <CommonAPI/DBus/DBusUtils.h>

#include <commonapi/tests/TestInterfaceDBusProxy.h>
#include <commonapi/tests/TestInterfaceDBusStubAdapter.h>
#include <commonapi/tests/TestInterfaceStubDefault.h>

#include <gtest/gtest.h>

#include <algorithm>
#include <cstdint>
#include <iostream>
#include <string>
#include <thread>
#include <vector>

static const std::string commonApiAddress = "local:CommonAPI.DBus.tests.DBusProxyTestInterface:CommonAPI.DBus.tests.DBusProxyTestService";
static const std::string commonApiServiceName = "CommonAPI.DBus.tests.DBusProxyTest";
static const std::string interfaceName = "CommonAPI.DBus.tests.DBusProxyTestInterface";
static const std::string busName = "CommonAPI.DBus.tests.DBusProxyTestService";
static const std::string objectPath = "/CommonAPI/DBus/tests/DBusProxyTest/TestObject";

class ProxyTest: public ::testing::Test {
protected:

    virtual void TearDown() {
        proxyDBusConnection_->disconnect();

        stubAdapter_.reset();

        if (stubDBusConnection_) {
            stubDBusConnection_->releaseServiceName(busName);
            stubDBusConnection_->disconnect();
            stubDBusConnection_.reset();
        }
    }

    void SetUp() {
        proxyDBusConnection_ = CommonAPI::DBus::DBusConnection::getSessionBus();
        ASSERT_TRUE(proxyDBusConnection_->connect());

        proxy_ = std::make_shared<commonapi::tests::TestInterfaceDBusProxy>(
                        commonApiAddress,
                        interfaceName,
                        busName,
                        objectPath,
                        proxyDBusConnection_);
    }

    void registerTestStub() {
        stubDBusConnection_ = CommonAPI::DBus::DBusConnection::getSessionBus();
        ASSERT_TRUE(stubDBusConnection_->connect());

        ASSERT_TRUE(stubDBusConnection_->requestServiceNameAndBlock(busName));

        auto stubDefault = std::make_shared<commonapi::tests::TestInterfaceStubDefault>();
        stubAdapter_ = std::make_shared<commonapi::tests::TestInterfaceDBusStubAdapter>(
                        commonApiAddress,
                        interfaceName,
                        busName,
                        objectPath,
                        stubDBusConnection_,
                        stubDefault);
        stubAdapter_->init();
    }

    void proxyRegisterForAvailabilityStatus() {
        proxyAvailabilityStatus_ = CommonAPI::AvailabilityStatus::UNKNOWN;

        proxy_->getProxyStatusEvent().subscribe([&](const CommonAPI::AvailabilityStatus& availabilityStatus) {
            std::cout << "Proxy AvailabilityStatus changed to " << (int) availabilityStatus << std::endl;
            proxyAvailabilityStatus_ = availabilityStatus;
        });
    }

    bool proxyWaitForAvailabilityStatus(const CommonAPI::AvailabilityStatus& availabilityStatus) const {
        std::chrono::milliseconds loopWaitDuration(100);

        if (proxyAvailabilityStatus_ == availabilityStatus)
            return true;

        for (int i = 0; i < 10; i++) {
            std::this_thread::sleep_for(loopWaitDuration);

            if (proxyAvailabilityStatus_ == availabilityStatus)
                return true;
        }

        return false;
    }

    std::shared_ptr<CommonAPI::DBus::DBusConnection> proxyDBusConnection_;
    std::shared_ptr<commonapi::tests::TestInterfaceDBusProxy> proxy_;
    CommonAPI::AvailabilityStatus proxyAvailabilityStatus_;

    std::shared_ptr<CommonAPI::DBus::DBusConnection> stubDBusConnection_;
    std::shared_ptr<commonapi::tests::TestInterfaceDBusStubAdapter> stubAdapter_;
};

TEST_F(ProxyTest, HasCorrectConnectionName) {
  std::string actualName = proxy_->getDBusBusName();
  EXPECT_EQ(busName, actualName);
}

TEST_F(ProxyTest, HasCorrectObjectPath) {
  std::string actualPath = proxy_->getDBusObjectPath();
  EXPECT_EQ(objectPath, actualPath);
}

TEST_F(ProxyTest, HasCorrectInterfaceName) {
  std::string actualName = proxy_->getInterfaceName();
  EXPECT_EQ(interfaceName, actualName);
}

TEST_F(ProxyTest, IsNotAvailable) {
	bool isAvailable = proxy_->isAvailable();
	EXPECT_FALSE(isAvailable);
}

TEST_F(ProxyTest, ServiceRegistry) {
	std::shared_ptr<CommonAPI::DBus::DBusProxyConnection> connection = proxy_->getDBusConnection();
	auto registry = connection->getDBusServiceRegistry();
	ASSERT_FALSE(!registry);
}

TEST_F(ProxyTest, DBusProxyStatusEventBeforeServiceIsRegistered) {
    proxyRegisterForAvailabilityStatus();

    ASSERT_NE(proxyAvailabilityStatus_, CommonAPI::AvailabilityStatus::AVAILABLE);

    registerTestStub();

    ASSERT_TRUE(proxyWaitForAvailabilityStatus(CommonAPI::AvailabilityStatus::AVAILABLE));
}

TEST_F(ProxyTest, DBusProxyStatusEventAfterServiceIsRegistered) {
    proxyDBusConnection_->disconnect();

    registerTestStub();

    proxyDBusConnection_->connect();
    proxyRegisterForAvailabilityStatus();
    ASSERT_TRUE(proxyWaitForAvailabilityStatus(CommonAPI::AvailabilityStatus::AVAILABLE));
}

TEST_F(ProxyTest, ServiceStatus) {
    proxyDBusConnection_->requestServiceNameAndBlock(busName);

    std::shared_ptr<commonapi::tests::TestInterfaceStubDefault> stubDefault = std::make_shared<commonapi::tests::TestInterfaceStubDefault>();
    std::shared_ptr<commonapi::tests::TestInterfaceDBusStubAdapter> stubAdapter =  std::make_shared<commonapi::tests::TestInterfaceDBusStubAdapter>(
                    commonApiAddress,
                    interfaceName,
                    busName,
                    objectPath,
                    proxyDBusConnection_,
                    stubDefault);

    stubAdapter->init();

    auto testConnection = CommonAPI::DBus::DBusConnection::getSessionBus();
    testConnection->connect();

	std::vector<std::string> actuallyAvailableServices;
	actuallyAvailableServices = testConnection->getDBusServiceRegistry()->getAvailableServiceInstances(commonApiServiceName,
			"local");

	auto found = std::find(actuallyAvailableServices.begin(), actuallyAvailableServices.end(), commonApiAddress);

	ASSERT_TRUE(actuallyAvailableServices.begin() != actuallyAvailableServices.end());
	ASSERT_TRUE(found != actuallyAvailableServices.end());

	testConnection->disconnect();
}

TEST_F(ProxyTest, IsAvailableBlocking) {
    std::shared_ptr<commonapi::tests::TestInterfaceStubDefault> stubDefault = std::make_shared<commonapi::tests::TestInterfaceStubDefault>();
    std::shared_ptr<commonapi::tests::TestInterfaceDBusStubAdapter> stubAdapter =  std::make_shared<commonapi::tests::TestInterfaceDBusStubAdapter>(
                    commonApiAddress,
                    interfaceName,
                    busName,
                    objectPath,
                    proxyDBusConnection_,
                    stubDefault);

    stubAdapter->init();

    bool registered = proxyDBusConnection_->requestServiceNameAndBlock(busName);
    bool isAvailable = proxy_->isAvailableBlocking();
    EXPECT_EQ(registered, isAvailable);
}

TEST_F(ProxyTest, HasNecessaryAttributesAndEvents) {
	CommonAPI::InterfaceVersionAttribute& versionAttribute = (proxy_->getInterfaceVersionAttribute());
	CommonAPI::ProxyStatusEvent& statusEvent = (proxy_->getProxyStatusEvent());
}

TEST_F(ProxyTest, IsConnected) {
  ASSERT_TRUE(proxy_->getDBusConnection()->isConnected());
}

TEST_F(ProxyTest, TestInterfaceVersionAttribute) {
	CommonAPI::InterfaceVersionAttribute& versionAttribute = proxy_->getInterfaceVersionAttribute();
	CommonAPI::Version version;
	CommonAPI::CallStatus status = versionAttribute.getValue(version);
	ASSERT_EQ(CommonAPI::CallStatus::NOT_AVAILABLE, status);
}

int main(int argc, char** argv) {
	::testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();
}
<commit_msg>tests: extend DBusProxyTest<commit_after>/* Copyright (C) 2013 BMW Group
 * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
 * Author: Juergen Gehring (juergen.gehring@bmw.de)
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _GLIBCXX_USE_NANOSLEEP
#define _GLIBCXX_USE_NANOSLEEP
#endif

#include <CommonAPI/DBus/DBusInputStream.h>
#include <CommonAPI/DBus/DBusMessage.h>
#include <CommonAPI/DBus/DBusProxy.h>
#include <CommonAPI/DBus/DBusConnection.h>
#include <CommonAPI/DBus/DBusStubAdapter.h>
#include <CommonAPI/DBus/DBusUtils.h>

#include <commonapi/tests/TestInterfaceDBusProxy.h>
#include <commonapi/tests/TestInterfaceDBusStubAdapter.h>
#include <commonapi/tests/TestInterfaceStubDefault.h>

#include <gtest/gtest.h>

#include <algorithm>
#include <cstdint>
#include <iostream>
#include <string>
#include <thread>
#include <vector>

static const std::string commonApiAddress = "local:CommonAPI.DBus.tests.DBusProxyTestInterface:CommonAPI.DBus.tests.DBusProxyTestService";
static const std::string commonApiServiceName = "CommonAPI.DBus.tests.DBusProxyTest";
static const std::string interfaceName = "CommonAPI.DBus.tests.DBusProxyTestInterface";
static const std::string busName = "CommonAPI.DBus.tests.DBusProxyTestService";
static const std::string objectPath = "/CommonAPI/DBus/tests/DBusProxyTestService";

class ProxyTest: public ::testing::Test {
protected:

    virtual void TearDown() {
        proxyDBusConnection_->disconnect();

        stubAdapter_.reset();

        if (stubDBusConnection_) {
            if (stubDBusConnection_->isConnected()) {
                stubDBusConnection_->releaseServiceName(busName);
                stubDBusConnection_->disconnect();
            }
            stubDBusConnection_.reset();
        }
    }

    void SetUp() {
        proxyDBusConnection_ = CommonAPI::DBus::DBusConnection::getSessionBus();
        ASSERT_TRUE(proxyDBusConnection_->connect());

        proxy_ = std::make_shared<commonapi::tests::TestInterfaceDBusProxy>(
                        commonApiAddress,
                        interfaceName,
                        busName,
                        objectPath,
                        proxyDBusConnection_);
    }

    void registerTestStub() {
        stubDBusConnection_ = CommonAPI::DBus::DBusConnection::getSessionBus();
        ASSERT_TRUE(stubDBusConnection_->connect());

        ASSERT_TRUE(stubDBusConnection_->requestServiceNameAndBlock(busName));

        auto stubDefault = std::make_shared<commonapi::tests::TestInterfaceStubDefault>();
        stubAdapter_ = std::make_shared<commonapi::tests::TestInterfaceDBusStubAdapter>(
                        commonApiAddress,
                        interfaceName,
                        busName,
                        objectPath,
                        stubDBusConnection_,
                        stubDefault);
        stubAdapter_->init();
    }

    void proxyRegisterForAvailabilityStatus() {
        proxyAvailabilityStatus_ = CommonAPI::AvailabilityStatus::UNKNOWN;

        proxy_->getProxyStatusEvent().subscribe([&](const CommonAPI::AvailabilityStatus& availabilityStatus) {
            std::cout << "Proxy AvailabilityStatus changed to " << (int) availabilityStatus << std::endl;
            proxyAvailabilityStatus_ = availabilityStatus;
        });
    }

    bool proxyWaitForAvailabilityStatus(const CommonAPI::AvailabilityStatus& availabilityStatus) const {
        std::chrono::milliseconds loopWaitDuration(100);

        if (proxyAvailabilityStatus_ == availabilityStatus)
            return true;

        for (int i = 0; i < 10; i++) {
            std::this_thread::sleep_for(loopWaitDuration);

            if (proxyAvailabilityStatus_ == availabilityStatus)
                return true;
        }

        return false;
    }

    std::shared_ptr<CommonAPI::DBus::DBusConnection> proxyDBusConnection_;
    std::shared_ptr<commonapi::tests::TestInterfaceDBusProxy> proxy_;
    CommonAPI::AvailabilityStatus proxyAvailabilityStatus_;

    std::shared_ptr<CommonAPI::DBus::DBusConnection> stubDBusConnection_;
    std::shared_ptr<commonapi::tests::TestInterfaceDBusStubAdapter> stubAdapter_;
};

TEST_F(ProxyTest, HasCorrectConnectionName) {
  std::string actualName = proxy_->getDBusBusName();
  EXPECT_EQ(busName, actualName);
}

TEST_F(ProxyTest, HasCorrectObjectPath) {
  std::string actualPath = proxy_->getDBusObjectPath();
  EXPECT_EQ(objectPath, actualPath);
}

TEST_F(ProxyTest, HasCorrectInterfaceName) {
  std::string actualName = proxy_->getInterfaceName();
  EXPECT_EQ(interfaceName, actualName);
}

TEST_F(ProxyTest, IsNotAvailable) {
	bool isAvailable = proxy_->isAvailable();
	EXPECT_FALSE(isAvailable);
}

TEST_F(ProxyTest, ServiceRegistry) {
	std::shared_ptr<CommonAPI::DBus::DBusProxyConnection> connection = proxy_->getDBusConnection();
	auto registry = connection->getDBusServiceRegistry();
	ASSERT_FALSE(!registry);
}

TEST_F(ProxyTest, DBusProxyStatusEventBeforeServiceIsRegistered) {
    proxyRegisterForAvailabilityStatus();

    ASSERT_NE(proxyAvailabilityStatus_, CommonAPI::AvailabilityStatus::AVAILABLE);

    registerTestStub();

    ASSERT_TRUE(proxyWaitForAvailabilityStatus(CommonAPI::AvailabilityStatus::AVAILABLE));

    stubDBusConnection_->disconnect();

    ASSERT_TRUE(proxyWaitForAvailabilityStatus(CommonAPI::AvailabilityStatus::NOT_AVAILABLE));
}

TEST_F(ProxyTest, DBusProxyStatusEventAfterServiceIsRegistered) {
    proxyDBusConnection_->disconnect();

    registerTestStub();

    ASSERT_TRUE(proxyDBusConnection_->connect());

    proxyRegisterForAvailabilityStatus();

    ASSERT_TRUE(proxyWaitForAvailabilityStatus(CommonAPI::AvailabilityStatus::AVAILABLE));

    stubDBusConnection_->disconnect();

    ASSERT_TRUE(proxyWaitForAvailabilityStatus(CommonAPI::AvailabilityStatus::NOT_AVAILABLE));
}

TEST_F(ProxyTest, ServiceStatus) {
    proxyDBusConnection_->requestServiceNameAndBlock(busName);

    std::shared_ptr<commonapi::tests::TestInterfaceStubDefault> stubDefault = std::make_shared<commonapi::tests::TestInterfaceStubDefault>();
    std::shared_ptr<commonapi::tests::TestInterfaceDBusStubAdapter> stubAdapter =  std::make_shared<commonapi::tests::TestInterfaceDBusStubAdapter>(
                    commonApiAddress,
                    interfaceName,
                    busName,
                    objectPath,
                    proxyDBusConnection_,
                    stubDefault);

    stubAdapter->init();

    auto testConnection = CommonAPI::DBus::DBusConnection::getSessionBus();
    testConnection->connect();

	std::vector<std::string> actuallyAvailableServices;
	actuallyAvailableServices = testConnection->getDBusServiceRegistry()->getAvailableServiceInstances(commonApiServiceName,
			"local");

	auto found = std::find(actuallyAvailableServices.begin(), actuallyAvailableServices.end(), commonApiAddress);

	ASSERT_TRUE(actuallyAvailableServices.begin() != actuallyAvailableServices.end());
	ASSERT_TRUE(found != actuallyAvailableServices.end());

	testConnection->disconnect();
}

TEST_F(ProxyTest, IsAvailableBlocking) {
    std::shared_ptr<commonapi::tests::TestInterfaceStubDefault> stubDefault = std::make_shared<commonapi::tests::TestInterfaceStubDefault>();
    std::shared_ptr<commonapi::tests::TestInterfaceDBusStubAdapter> stubAdapter =  std::make_shared<commonapi::tests::TestInterfaceDBusStubAdapter>(
                    commonApiAddress,
                    interfaceName,
                    busName,
                    objectPath,
                    proxyDBusConnection_,
                    stubDefault);

    stubAdapter->init();

    bool registered = proxyDBusConnection_->requestServiceNameAndBlock(busName);
    bool isAvailable = proxy_->isAvailableBlocking();
    EXPECT_EQ(registered, isAvailable);
}

TEST_F(ProxyTest, HasNecessaryAttributesAndEvents) {
	CommonAPI::InterfaceVersionAttribute& versionAttribute = (proxy_->getInterfaceVersionAttribute());
	CommonAPI::ProxyStatusEvent& statusEvent = (proxy_->getProxyStatusEvent());
}

TEST_F(ProxyTest, IsConnected) {
  ASSERT_TRUE(proxy_->getDBusConnection()->isConnected());
}

TEST_F(ProxyTest, TestInterfaceVersionAttribute) {
	CommonAPI::InterfaceVersionAttribute& versionAttribute = proxy_->getInterfaceVersionAttribute();
	CommonAPI::Version version;
	CommonAPI::CallStatus status = versionAttribute.getValue(version);
	ASSERT_EQ(CommonAPI::CallStatus::NOT_AVAILABLE, status);
}

int main(int argc, char** argv) {
	::testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>#include <TfTest/TfTest>
#include <QtCore>
#include <QDebug>
#include "tcachestore.h"
#include "tcachefactory.h"
#include "tcachesqlitestore.h"

static qint64 FirstKey;
const int NUM = 500;

class TestCache : public QObject
{
    Q_OBJECT
private slots:
    void initTestCase();
    void cleanupTestCase();
    void test();
    void insert_data();
    void insert();
    void bench_insert_binary();
    void bench_value_binary();
    void bench_insert_binary_lz4();
    void bench_value_binary_lz4();
    void bench_insert_text();
    void bench_value_text();
    void bench_insert_text_lz4();
    void bench_value_text_lz4();
};

static QByteArray genval(const QByteArray &key)
{
    QByteArray ret;

    auto d = QCryptographicHash::hash(key, QCryptographicHash::Sha3_512);
    int n = 5 * (d.mid(0,2).toHex().mid(0,1).toInt(nullptr, 16) + 1);
    ret.reserve(n * d.length());
    for (int i = 0; i < n; i++) {
        ret += d;
    }
    return ret;
}

void TestCache::initTestCase()
{
    FirstKey = QDateTime::currentDateTime().toMSecsSinceEpoch();
}

void TestCache::cleanupTestCase()
{ }

void TestCache::test()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();
    QByteArray buf;
    cache->clear();

    TCacheSQLiteStore *sqlite = dynamic_cast<TCacheSQLiteStore*>(cache);

    QVERIFY(cache->get("hoge") == QByteArray());
    QVERIFY(cache->set("hoge", "value", 1) == true);
    QVERIFY(cache->get("hoge") == "value");
    QVERIFY(sqlite->exists("hoge"));
    QVERIFY(!sqlite->exists("foo"));

    QVERIFY(cache->set("hoge", "value", 1) == true);
    QVERIFY(cache->set("hoge", "value2", 1) == true);
    QVERIFY(cache->get("hoge") == "value2");
    cache->remove("hoge");
    QVERIFY(cache->get("hoge") == QByteArray());

    QVERIFY(cache->set("hoge", "value", 1) == true);
    Tf::msleep(1100);
    QVERIFY(cache->get("hoge") == QByteArray());
    QVERIFY(!sqlite->exists("hoge"));

    sqlite->set("hoge", "dummy", 1);
    QVERIFY(sqlite->dbSize() > 0);

    cache->clear();

    for (int i = 0; i < 1000; i++) {
        QByteArray key = "foo";
        key += QByteArray::number(i);
        QVERIFY(cache->set(key, QByteArray::number(Tf::rand32_r()), 5) == true);
    }
    QVERIFY(! cache->get("foo1").isEmpty());
    Tf::msleep(5000);
    QVERIFY(cache->get("foo1") == QByteArray());
    cache->clear();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::insert_data()
{
    QTest::addColumn<QByteArray>("key");
    QTest::addColumn<QByteArray>("val");

    qint64 n = FirstKey;
    for (int i = 0; i < 20; i++) {
        QTest::newRow(QByteArray::number(i).data()) << QByteArray::number(n+i) << genval(QByteArray::number(n+i));
    }
}

void TestCache::insert()
{
    QFETCH(QByteArray, key);
    QFETCH(QByteArray, val);

    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();

    cache->set(key, val, 1);
    QByteArray res = cache->get(key);
    QCOMPARE(res, val);
    qDebug() << "length of value: " << val.size();
    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::bench_insert_binary()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();
    cache->clear();

    for (int i = 0; i < 200; i++) {
        cache->set(QByteArray::number(FirstKey + i), genval(QByteArray::number(FirstKey + i)), 60);
    }

    QBENCHMARK {
        for (int i = 0; i < 100; i++) {
            int r = Tf::random(FirstKey, FirstKey + NUM - 1);
            cache->set(QByteArray::number(r), genval(QByteArray::number(r)), 60);
        }
    }
    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::bench_value_binary()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();

    QBENCHMARK {
        for (int i = 0; i < 100; i++) {
            int r = Tf::random(FirstKey, FirstKey + NUM - 1);
            auto val = cache->get(QByteArray::number(r));
            Q_UNUSED(val);
        }
    }
    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::bench_insert_binary_lz4()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();
    cache->clear();

    for (int i = 0; i < 200; i++) {
        cache->set(QByteArray::number(FirstKey + i), Tf::lz4Compress(genval(QByteArray::number(FirstKey + i))), 60);
    }

    QBENCHMARK {
        for (int i = 0; i < 100; i++) {
            int r = Tf::random(FirstKey, FirstKey + NUM - 1);
            auto key = QByteArray::number(r);
            auto val = (genval(key));
            cache->set(key, val, 60);
        }
    }
    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::bench_value_binary_lz4()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();

    QBENCHMARK {
        for (int i = 0; i < 100; i++) {
            int r = Tf::random(FirstKey, FirstKey + NUM - 1);
            auto val = Tf::lz4Uncompress(cache->get(QByteArray::number(r)));
            Q_UNUSED(val);
        }
    }
    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::bench_insert_text()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();
    cache->clear();

    for (int i = 0; i < 200; i++) {
        cache->set(QByteArray::number(FirstKey + i), genval(QByteArray::number(FirstKey + i)), 60);
    }

    QBENCHMARK {
        for (int i = 0; i < 100; i++) {
            int r = Tf::random(FirstKey, FirstKey + NUM - 1);
            cache->set(QByteArray::number(r), genval(QByteArray::number(r).toHex()), 60);
        }
    }
    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::bench_value_text()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();

    QBENCHMARK {
        for (int i = 0; i < 100; i++) {
            int r = Tf::random(FirstKey, FirstKey + NUM - 1);
            auto val = cache->get(QByteArray::number(r));
            Q_UNUSED(val);
        }
    }

    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::bench_insert_text_lz4()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();
    cache->clear();

    for (int i = 0; i < 200; i++) {
        cache->set(QByteArray::number(FirstKey + i), Tf::lz4Compress(genval(QByteArray::number(FirstKey + i))), 60);
    }

    QBENCHMARK {
        for (int i = 0; i < 100; i++) {
            int r = Tf::random(FirstKey, FirstKey + NUM - 1);
            auto key = QByteArray::number(r);
            auto val = (genval(key).toHex());
            cache->set(key, val, 60);
        }
    }
    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::bench_value_text_lz4()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();

    QBENCHMARK {
        for (int i = 0; i < 100; i++) {
            int r = Tf::random(FirstKey, FirstKey + NUM - 1);
            auto val = Tf::lz4Uncompress(cache->get(QByteArray::number(r)));
            Q_UNUSED(val);
        }
    }

    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

TF_TEST_MAIN(TestCache)
#include "main.moc"
<commit_msg>upated a testcase.<commit_after>#include <TfTest/TfTest>
#include <QtCore>
#include <QDebug>
#include "tcachestore.h"
#include "tcachefactory.h"
#include "tcachesqlitestore.h"

static qint64 FirstKey;
const int NUM = 500;

class TestCache : public QObject
{
    Q_OBJECT
private slots:
    void initTestCase();
    void cleanupTestCase();
    void test();
    void insert_data();
    void insert();
    void bench_insert_binary();
    void bench_value_binary();
    void bench_insert_binary_lz4();
    void bench_value_binary_lz4();
    void bench_insert_text();
    void bench_value_text();
    void bench_insert_text_lz4();
    void bench_value_text_lz4();
};

static QByteArray genval(const QByteArray &key)
{
    QByteArray ret;

    auto d = QCryptographicHash::hash(key, QCryptographicHash::Sha3_512);
    int n = 5 * (d.mid(0,2).toHex().mid(0,1).toInt(nullptr, 16) + 1);
    ret.reserve(n * d.length());
    for (int i = 0; i < n; i++) {
        ret += d;
    }
    return ret;
}

void TestCache::initTestCase()
{
    FirstKey = QDateTime::currentDateTime().toMSecsSinceEpoch();
}

void TestCache::cleanupTestCase()
{ }

void TestCache::test()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->init();
    cache->open();
    QByteArray buf;
    cache->clear();

    TCacheSQLiteStore *sqlite = dynamic_cast<TCacheSQLiteStore*>(cache);

    QVERIFY(cache->get("hoge") == QByteArray());
    QVERIFY(cache->set("hoge", "value", 1) == true);
    QVERIFY(cache->get("hoge") == "value");
    QVERIFY(sqlite->exists("hoge"));
    QVERIFY(!sqlite->exists("foo"));

    QVERIFY(cache->set("hoge", "value", 1) == true);
    QVERIFY(cache->set("hoge", "value2", 1) == true);
    QVERIFY(cache->get("hoge") == "value2");
    cache->remove("hoge");
    QVERIFY(cache->get("hoge") == QByteArray());

    QVERIFY(cache->set("hoge", "value", 1) == true);
    Tf::msleep(1100);
    QVERIFY(cache->get("hoge") == QByteArray());
    QVERIFY(!sqlite->exists("hoge"));

    sqlite->set("hoge", "dummy", 1);
    QVERIFY(sqlite->dbSize() > 0);

    cache->clear();

    for (int i = 0; i < 1000; i++) {
        QByteArray key = "foo";
        key += QByteArray::number(i);
        QVERIFY(cache->set(key, QByteArray::number(Tf::rand32_r()), 5) == true);
    }
    QVERIFY(! cache->get("foo1").isEmpty());
    Tf::msleep(5000);
    QVERIFY(cache->get("foo1") == QByteArray());
    cache->clear();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::insert_data()
{
    QTest::addColumn<QByteArray>("key");
    QTest::addColumn<QByteArray>("val");

    qint64 n = FirstKey;
    for (int i = 0; i < 20; i++) {
        QTest::newRow(QByteArray::number(i).data()) << QByteArray::number(n+i) << genval(QByteArray::number(n+i));
    }
}

void TestCache::insert()
{
    QFETCH(QByteArray, key);
    QFETCH(QByteArray, val);

    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();

    cache->set(key, val, 1);
    QByteArray res = cache->get(key);
    QCOMPARE(res, val);
    qDebug() << "length of value: " << val.size();
    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::bench_insert_binary()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();
    cache->clear();

    for (int i = 0; i < 200; i++) {
        cache->set(QByteArray::number(FirstKey + i), genval(QByteArray::number(FirstKey + i)), 60);
    }

    QBENCHMARK {
        for (int i = 0; i < 100; i++) {
            int r = Tf::random(FirstKey, FirstKey + NUM - 1);
            cache->set(QByteArray::number(r), genval(QByteArray::number(r)), 60);
        }
    }
    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::bench_value_binary()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();

    QBENCHMARK {
        for (int i = 0; i < 100; i++) {
            int r = Tf::random(FirstKey, FirstKey + NUM - 1);
            auto val = cache->get(QByteArray::number(r));
            Q_UNUSED(val);
        }
    }
    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::bench_insert_binary_lz4()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();
    cache->clear();

    for (int i = 0; i < 200; i++) {
        cache->set(QByteArray::number(FirstKey + i), Tf::lz4Compress(genval(QByteArray::number(FirstKey + i))), 60);
    }

    QBENCHMARK {
        for (int i = 0; i < 100; i++) {
            int r = Tf::random(FirstKey, FirstKey + NUM - 1);
            auto key = QByteArray::number(r);
            auto val = (genval(key));
            cache->set(key, val, 60);
        }
    }
    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::bench_value_binary_lz4()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();

    QBENCHMARK {
        for (int i = 0; i < 100; i++) {
            int r = Tf::random(FirstKey, FirstKey + NUM - 1);
            auto val = Tf::lz4Uncompress(cache->get(QByteArray::number(r)));
            Q_UNUSED(val);
        }
    }
    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::bench_insert_text()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();
    cache->clear();

    for (int i = 0; i < 200; i++) {
        cache->set(QByteArray::number(FirstKey + i), genval(QByteArray::number(FirstKey + i)), 60);
    }

    QBENCHMARK {
        for (int i = 0; i < 100; i++) {
            int r = Tf::random(FirstKey, FirstKey + NUM - 1);
            cache->set(QByteArray::number(r), genval(QByteArray::number(r).toHex()), 60);
        }
    }
    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::bench_value_text()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();

    QBENCHMARK {
        for (int i = 0; i < 100; i++) {
            int r = Tf::random(FirstKey, FirstKey + NUM - 1);
            auto val = cache->get(QByteArray::number(r));
            Q_UNUSED(val);
        }
    }

    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::bench_insert_text_lz4()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();
    cache->clear();

    for (int i = 0; i < 200; i++) {
        cache->set(QByteArray::number(FirstKey + i), Tf::lz4Compress(genval(QByteArray::number(FirstKey + i))), 60);
    }

    QBENCHMARK {
        for (int i = 0; i < 100; i++) {
            int r = Tf::random(FirstKey, FirstKey + NUM - 1);
            auto key = QByteArray::number(r);
            auto val = (genval(key).toHex());
            cache->set(key, val, 60);
        }
    }
    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

void TestCache::bench_value_text_lz4()
{
    TCacheStore *cache = TCacheFactory::create("sqlite");
    cache->open();

    QBENCHMARK {
        for (int i = 0; i < 100; i++) {
            int r = Tf::random(FirstKey, FirstKey + NUM - 1);
            auto val = Tf::lz4Uncompress(cache->get(QByteArray::number(r)));
            Q_UNUSED(val);
        }
    }

    cache->close();
    TCacheFactory::destroy("sqlite", cache);
}

TF_TEST_MAIN(TestCache)
#include "main.moc"
<|endoftext|>


<commit_before>#include "chip8.hpp"

#include <cassert>

#include <iostream>
#include <iomanip>
#include <algorithm>

static constexpr std::array<uint8_t, 80> CHIP8_FONTSET =
{{
    0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
    0x20, 0x60, 0x20, 0x20, 0x70, // 1
    0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
    0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
    0x90, 0x90, 0xF0, 0x10, 0x10, // 4
    0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
    0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
    0xF0, 0x10, 0x20, 0x40, 0x40, // 7
    0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
    0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
    0xF0, 0x90, 0xF0, 0x90, 0x90, // A
    0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
    0xF0, 0x80, 0x80, 0x80, 0xF0, // C
    0xE0, 0x90, 0x90, 0x90, 0xE0, // D
    0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
    0xF0, 0x80, 0xF0, 0x80, 0x80  // F
}};

// This is the starting location on where the emulator should start
// reading any loaded program.
static constexpr size_t PROG_OFFSET = 512;

cee::Chip8::Chip8()
{
    this->reset();

    // Load chip8 fontset
    for (size_t i = 0; i < CHIP8_FONTSET.size(); i++)
        mMemory[i] = CHIP8_FONTSET[i];

    // Load operations
    #ifndef ADD_OP
    #define ADD_OP(n) mOps.emplace(n, std::bind(&Chip8::op##n, this));

    ADD_OP(0x0000)
    ADD_OP(0x000E)
    ADD_OP(0x00EE)
    ADD_OP(0x1000)
    ADD_OP(0x2000)
    ADD_OP(0x3000)
    ADD_OP(0x4000)
    ADD_OP(0x5000)
    ADD_OP(0x6000)
    ADD_OP(0x7000)
    ADD_OP(0x8000)
    ADD_OP(0x8001)
    ADD_OP(0x8002)
    ADD_OP(0x8003)
    ADD_OP(0x8004)
    ADD_OP(0x8005)
    ADD_OP(0x8006)
    ADD_OP(0x8007)
    ADD_OP(0x800E)
    ADD_OP(0x9000)
    ADD_OP(0xA000)
    ADD_OP(0xB000)
    ADD_OP(0xC000)
    ADD_OP(0xD000)
    ADD_OP(0xE0A1)
    ADD_OP(0xE09E)
    ADD_OP(0xF007)
    ADD_OP(0xF00A)
    ADD_OP(0xF015)
    ADD_OP(0xF018)
    ADD_OP(0xF01E)
    ADD_OP(0xF029)
    ADD_OP(0xF033)
    ADD_OP(0xF055)
    ADD_OP(0xF065)

    #undef ADD_OP
    #endif // ADD_OP
}

void cee::Chip8::loadProgram(std::vector<uint8_t> program)
{
    // Prevent Memory Overflow
    assert(program.size() < (mMemory.size() - PROG_OFFSET));
    for (size_t i = 0; i < program.size(); i++)
        mMemory[i + PROG_OFFSET] = program[i];
}

void cee::Chip8::reset()
{
    mCounter      = 0x200; // Program counter starts at 0x200
    mOpCode       = 0;     // Reset current opcode
    mIndex        = 0;     // Reset index register
    mStackPointer = 0;     // Reset stack pointer
    mDelayTimer   = 0;     // Reset delay timer
    mSoundTimer   = 0;     // Reset sound timer
    mRegisters.fill(0);    // Reset registers
    mStack.fill(0);        // Reset stack
    mKeys.fill(0);         // Reset key states
    mGfx.fill(0);          // Reset display

    // Reset memory (i.e. clear program data)
    // We don't clear before PROG_OFFSET because there are data
    // there that we use and doesn't need resetting such as the chip8 fontset.
    std::fill(mMemory.begin() + PROG_OFFSET, mMemory.end(), 0);
}

void cee::Chip8::updateCycle()
{
    // Fetch opcode
    mOpCode = mMemory[mCounter] << 8 | mMemory[mCounter + 1];

    // Decode opcode
    auto op = mOpCode & 0xF000;

    // In cases when there's variation of options for an instruction.
    switch (op)
    {
    case 0x0000: case 0xE000: case 0xF000:
        op = mOpCode & 0xF0FF;
        break;
    case 0x8000:
        op = mOpCode & 0xF00F;
        break;
    default:
        // Do nothing...
        break;
    }

    // Execute opcode
    if (mOps.count(op)) mOps[op]();
    else printf("Chip8 Error: Unknown OpCode 0x%x\n", op);

    // Update delay timer
    if (mDelayTimer > 0) mDelayTimer -= 1;
    else // Do nothing

    // Update sound timer
    if (mSoundTimer > 0) mSoundTimer -= 1;
    else printf("Beep!\n");
}

void cee::Chip8::updateKeys(std::array<uint8_t, 16> keys)
{
    mKeys = keys;
}

//   ___  ____   ____ ___  ____  _____ ____
//  / _ \|  _ \ / ___/ _ \|  _ \| ____/ ___|
// | | | | |_) | |  | | | | | | |  _| \___ \
// | |_| |  __/| |__| |_| | |_| | |___ ___) |
//  \___/|_|    \____\___/|____/|_____|____/

// Calls RCA 1802 program at address NNN.
void cee::Chip8::op0x0000()
{
    // TODO
}

// Clears the screen.
void cee::Chip8::op0x000E()
{
    // TODO
}

// Returns from a subroutine.
void cee::Chip8::op0x00EE()
{
    // TODO
}

// Jumps to address NNN.
void cee::Chip8::op0x1000()
{
    // TODO
}

// Calls subroutine at NNN.
void cee::Chip8::op0x2000()
{
    // TODO
}

// Skips the next instruction if VX equals NN.
void cee::Chip8::op0x3000()
{
    // TODO
}

// Skips the next instruction if VX doesn't equal NN.
void cee::Chip8::op0x4000()
{
    // TODO
}

// Skips the next instruction if VX equals VY.
void cee::Chip8::op0x5000()
{
    // TODO
}

// Sets VX to NN.
void cee::Chip8::op0x6000()
{
    // TODO
}

// Adds NN to VX.
void cee::Chip8::op0x7000()
{
    // TODO
}

// Sets VX to the value of VY.
void cee::Chip8::op0x8000()
{
    // TODO
}

// Sets VX to VX or VY.
void cee::Chip8::op0x8001()
{
    // TODO
}

// Sets VX to VX and VY.
void cee::Chip8::op0x8002()
{
    // TODO
}

// Sets VX to VX xor VY.
void cee::Chip8::op0x8003()
{
    // TODO
}

// Adds VY to VX. VF is set to 1 when carry, and to 0 when isn't.
void cee::Chip8::op0x8004()
{
    // TODO
}

// VY is subtracted from VX. VF is set to 0 when borrow, and 1 when isn't.
void cee::Chip8::op0x8005()
{
    // TODO
}

// Shifts VX right by 1. VF is set value of the least sig bit of VX before shift.
void cee::Chip8::op0x8006()
{
    // TODO
}

// Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when isn't.
void cee::Chip8::op0x8007()
{
    // TODO
}

// Shifts VX left by 1. VF is set value of the most sig bit of VX before shift.
void cee::Chip8::op0x800E()
{
    // TODO
}

// Skips the next instruction if VX doesn't equal VY.
void cee::Chip8::op0x9000()
{
    // TODO
}

// Sets I to the address NNN.
void cee::Chip8::op0xA000()
{
    // TODO
}

// Jumps to the address NNN plus V0.
void cee::Chip8::op0xB000()
{
    // TODO
}

// Sets VX to a random number, masked by NN.
void cee::Chip8::op0xC000()
{
    // TODO
}

// Sprites stored in memory at location in index register (I), maximum 8bits wide.
// Wraps around the screen.
// If when drawn, clears a pixel, register VF is set to 1 otherwise it is zero.
// All drawing is XOR drawing (i.e. it toggles the screen pixels).
void cee::Chip8::op0xD000()
{
    // TODO
}

// Skips the next instruction if the key stored in VX is pressed.
void cee::Chip8::op0xE0A1()
{
    // TODO
}

// Skips the next instruction if the key stored in VX isn't pressed.
void cee::Chip8::op0xE09E()
{
    // TODO
}

// Sets VX to the value of the delay timer.
void cee::Chip8::op0xF007()
{
    // TODO
}

// A key press is awaited, and then stored in VX.
void cee::Chip8::op0xF00A()
{
    // TODO
}

// Sets the delay timer to VX.
void cee::Chip8::op0xF015()
{
    // TODO
}

// Sets the sound timer to VX.
void cee::Chip8::op0xF018()
{
    // TODO
}

// Adds VX to I.
void cee::Chip8::op0xF01E()
{
    // TODO
}

// Sets I to the location of the sprite for the character in VX.
// Characters 0-F (in hexadecimal) are represented by a 4x5 font.
void cee::Chip8::op0xF029()
{
    // TODO
}

// Stores the Binary-coded decimal representation of VX,
// with the most significant of three digits at the address in I,
// the middle digit at I plus 1, and the least significant digit at I plus 2.
// (In other words, take the decimal representation of VX,
// place the hundreds digit in memory at location in I, the tens digit at location I+1,
// and the ones digit at location I+2.).
void cee::Chip8::op0xF033()
{
    // TODO
}

// Stores V0 to VX in memory starting at address I.
void cee::Chip8::op0xF055()
{
    // TODO
}

// Fills V0 to VX with values from memory starting at address I.
void cee::Chip8::op0xF065()
{
    // TODO
}
<commit_msg>Implemented the first 6 opcodes (0x0000 - 0x5000).<commit_after>#include "chip8.hpp"

#include <cassert>

#include <iostream>
#include <iomanip>
#include <algorithm>

static constexpr std::array<uint8_t, 80> CHIP8_FONTSET =
{{
    0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
    0x20, 0x60, 0x20, 0x20, 0x70, // 1
    0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
    0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
    0x90, 0x90, 0xF0, 0x10, 0x10, // 4
    0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
    0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
    0xF0, 0x10, 0x20, 0x40, 0x40, // 7
    0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
    0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
    0xF0, 0x90, 0xF0, 0x90, 0x90, // A
    0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
    0xF0, 0x80, 0x80, 0x80, 0xF0, // C
    0xE0, 0x90, 0x90, 0x90, 0xE0, // D
    0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
    0xF0, 0x80, 0xF0, 0x80, 0x80  // F
}};

// This is the starting location on where the emulator should start
// reading any loaded program.
static constexpr size_t PROG_OFFSET = 512;

cee::Chip8::Chip8()
{
    this->reset();

    // Load chip8 fontset
    for (size_t i = 0; i < CHIP8_FONTSET.size(); i++)
        mMemory[i] = CHIP8_FONTSET[i];

    // Load operations
    #ifndef ADD_OP
    #define ADD_OP(n) mOps.emplace(n, std::bind(&Chip8::op##n, this));

    ADD_OP(0x0000)
    ADD_OP(0x000E)
    ADD_OP(0x00EE)
    ADD_OP(0x1000)
    ADD_OP(0x2000)
    ADD_OP(0x3000)
    ADD_OP(0x4000)
    ADD_OP(0x5000)
    ADD_OP(0x6000)
    ADD_OP(0x7000)
    ADD_OP(0x8000)
    ADD_OP(0x8001)
    ADD_OP(0x8002)
    ADD_OP(0x8003)
    ADD_OP(0x8004)
    ADD_OP(0x8005)
    ADD_OP(0x8006)
    ADD_OP(0x8007)
    ADD_OP(0x800E)
    ADD_OP(0x9000)
    ADD_OP(0xA000)
    ADD_OP(0xB000)
    ADD_OP(0xC000)
    ADD_OP(0xD000)
    ADD_OP(0xE0A1)
    ADD_OP(0xE09E)
    ADD_OP(0xF007)
    ADD_OP(0xF00A)
    ADD_OP(0xF015)
    ADD_OP(0xF018)
    ADD_OP(0xF01E)
    ADD_OP(0xF029)
    ADD_OP(0xF033)
    ADD_OP(0xF055)
    ADD_OP(0xF065)

    #undef ADD_OP
    #endif // ADD_OP
}

void cee::Chip8::loadProgram(std::vector<uint8_t> program)
{
    // Prevent Memory Overflow
    assert(program.size() < (mMemory.size() - PROG_OFFSET));
    for (size_t i = 0; i < program.size(); i++)
        mMemory[i + PROG_OFFSET] = program[i];
}

void cee::Chip8::reset()
{
    mCounter      = 0x200; // Program counter starts at 0x200
    mOpCode       = 0;     // Reset current opcode
    mIndex        = 0;     // Reset index register
    mStackPointer = 0;     // Reset stack pointer
    mDelayTimer   = 0;     // Reset delay timer
    mSoundTimer   = 0;     // Reset sound timer
    mRegisters.fill(0);    // Reset registers
    mStack.fill(0);        // Reset stack
    mKeys.fill(0);         // Reset key states
    mGfx.fill(0);          // Reset display

    // Reset memory (i.e. clear program data)
    // We don't clear before PROG_OFFSET because there are data
    // there that we use and doesn't need resetting such as the chip8 fontset.
    std::fill(mMemory.begin() + PROG_OFFSET, mMemory.end(), 0);
}

void cee::Chip8::updateCycle()
{
    // Fetch opcode
    mOpCode = mMemory[mCounter] << 8 | mMemory[mCounter + 1];

    // Decode opcode
    auto op = mOpCode & 0xF000;

    // In cases when there's variation of options for an instruction.
    switch (op)
    {
    case 0x0000: case 0xE000: case 0xF000:
        op = mOpCode & 0xF0FF;
        break;
    case 0x8000:
        op = mOpCode & 0xF00F;
        break;
    default:
        // Do nothing...
        break;
    }

    // Execute opcode
    if (mOps.count(op)) mOps[op]();
    else printf("Chip8 Error: Unknown OpCode 0x%x\n", op);

    // Update delay timer
    if (mDelayTimer > 0) mDelayTimer -= 1;
    else // Do nothing

    // Update sound timer
    if (mSoundTimer > 0) mSoundTimer -= 1;
    else printf("Beep!\n");
}

void cee::Chip8::updateKeys(std::array<uint8_t, 16> keys)
{
    mKeys = keys;
}

//   ___  ____   ____ ___  ____  _____ ____
//  / _ \|  _ \ / ___/ _ \|  _ \| ____/ ___|
// | | | | |_) | |  | | | | | | |  _| \___ \
// | |_| |  __/| |__| |_| | |_| | |___ ___) |
//  \___/|_|    \____\___/|____/|_____|____/

// Calls RCA 1802 program at address NNN.
void cee::Chip8::op0x0000()
{
    // This is ignored since we don't have that microprocessor.
}

// Clears the screen.
void cee::Chip8::op0x000E()
{
    mGfx.fill(0);
    mCounter += 2;
}

// Returns from a subroutine.
void cee::Chip8::op0x00EE()
{
    mCounter = mStack[mStackPointer];
    mStackPointer -= 1;
    mCounter += 2;
}

// Jumps to address NNN.
void cee::Chip8::op0x1000()
{
    mCounter = mOpCode & 0x0FFF;
}

// Calls subroutine at NNN.
void cee::Chip8::op0x2000()
{
    mStackPointer += 1;
    mStack[mStackPointer] = mCounter;
    mCounter = mOpCode & 0x0FFF;
}

// Skips the next instruction if VX equals NN.
void cee::Chip8::op0x3000()
{
    uint8_t vx = mRegisters[(mOpCode & 0x0F00) >> 8];
    uint8_t nn = mOpCode & 0x00FF;
    mCounter += (vx == nn ? 4 : 2);
}

// Skips the next instruction if VX doesn't equal NN.
void cee::Chip8::op0x4000()
{
    uint8_t vx = mRegisters[(mOpCode & 0x0F00) >> 8];
    uint8_t nn = mOpCode & 0x00FF;
    mCounter += (vx == nn ? 2 : 4);
}

// Skips the next instruction if VX equals VY.
void cee::Chip8::op0x5000()
{
    uint8_t vx = mRegisters[(mOpCode & 0x0F00) >> 8];
    uint8_t vy = mRegisters[(mOpCode & 0x00F0) >> 4];
    mCounter += (vx == vy ? 4 : 2);
}

// Sets VX to NN.
void cee::Chip8::op0x6000()
{
    // TODO
}

// Adds NN to VX.
void cee::Chip8::op0x7000()
{
    // TODO
}

// Sets VX to the value of VY.
void cee::Chip8::op0x8000()
{
    // TODO
}

// Sets VX to VX or VY.
void cee::Chip8::op0x8001()
{
    // TODO
}

// Sets VX to VX and VY.
void cee::Chip8::op0x8002()
{
    // TODO
}

// Sets VX to VX xor VY.
void cee::Chip8::op0x8003()
{
    // TODO
}

// Adds VY to VX. VF is set to 1 when carry, and to 0 when isn't.
void cee::Chip8::op0x8004()
{
    // TODO
}

// VY is subtracted from VX. VF is set to 0 when borrow, and 1 when isn't.
void cee::Chip8::op0x8005()
{
    // TODO
}

// Shifts VX right by 1. VF is set value of the least sig bit of VX before shift.
void cee::Chip8::op0x8006()
{
    // TODO
}

// Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when isn't.
void cee::Chip8::op0x8007()
{
    // TODO
}

// Shifts VX left by 1. VF is set value of the most sig bit of VX before shift.
void cee::Chip8::op0x800E()
{
    // TODO
}

// Skips the next instruction if VX doesn't equal VY.
void cee::Chip8::op0x9000()
{
    // TODO
}

// Sets I to the address NNN.
void cee::Chip8::op0xA000()
{
    // TODO
}

// Jumps to the address NNN plus V0.
void cee::Chip8::op0xB000()
{
    // TODO
}

// Sets VX to a random number, masked by NN.
void cee::Chip8::op0xC000()
{
    // TODO
}

// Sprites stored in memory at location in index register (I), maximum 8bits wide.
// Wraps around the screen.
// If when drawn, clears a pixel, register VF is set to 1 otherwise it is zero.
// All drawing is XOR drawing (i.e. it toggles the screen pixels).
void cee::Chip8::op0xD000()
{
    // TODO
}

// Skips the next instruction if the key stored in VX is pressed.
void cee::Chip8::op0xE0A1()
{
    // TODO
}

// Skips the next instruction if the key stored in VX isn't pressed.
void cee::Chip8::op0xE09E()
{
    // TODO
}

// Sets VX to the value of the delay timer.
void cee::Chip8::op0xF007()
{
    // TODO
}

// A key press is awaited, and then stored in VX.
void cee::Chip8::op0xF00A()
{
    // TODO
}

// Sets the delay timer to VX.
void cee::Chip8::op0xF015()
{
    // TODO
}

// Sets the sound timer to VX.
void cee::Chip8::op0xF018()
{
    // TODO
}

// Adds VX to I.
void cee::Chip8::op0xF01E()
{
    // TODO
}

// Sets I to the location of the sprite for the character in VX.
// Characters 0-F (in hexadecimal) are represented by a 4x5 font.
void cee::Chip8::op0xF029()
{
    // TODO
}

// Stores the Binary-coded decimal representation of VX,
// with the most significant of three digits at the address in I,
// the middle digit at I plus 1, and the least significant digit at I plus 2.
// (In other words, take the decimal representation of VX,
// place the hundreds digit in memory at location in I, the tens digit at location I+1,
// and the ones digit at location I+2.).
void cee::Chip8::op0xF033()
{
    // TODO
}

// Stores V0 to VX in memory starting at address I.
void cee::Chip8::op0xF055()
{
    // TODO
}

// Fills V0 to VX with values from memory starting at address I.
void cee::Chip8::op0xF065()
{
    // TODO
}
<|endoftext|>


<commit_before>#include "config.h"

#include "upath.h"
#include "ykey.h"
#include "yconfig.h"
#include "ypaint.h"
#include "yprefs.h"
#include "ypaths.h"
#include "sysdep.h"
#include "binascii.h"
#include "yapp.h"
#include "intl.h"

upath findPath(ustring path, int mode, upath name, bool /*path_relative*/) {
#ifdef __EMX__
    if (mode & X_OK)
        name = name.addExtension(".exe");
#endif
    if (name.isAbsolute()) { // check for root in XFreeOS/2
        if (name.fileExists())
            return name;
    } else {
        if (path == null)
            return null;

        ustring s(null), r(null);
        for (s = path; s.splitall(PATHSEP, &s, &r); s = r) {
            upath prog = upath(s).relative(name);
            if (prog.access(mode) == 0)
                return prog;

#if 0
            if (!(mode & X_OK) &&
                !prog.path().endsWith(".xpm") &&
                !prog.path().endsWith(".png"))
            {
                upath prog_png = prog.addExtension(".png");
                if (prog_png.access(mode) == 0)
                    return prog_png;
            }
#endif
        }
    }
    return null;
}

#if !defined(NO_CONFIGURE) || !defined(NO_CONFIGURE_MENUS)

static bool appendStr(char **dest, int &bufLen, int &len, char c) {
    if (*dest && len + 1 < bufLen) {
        (*dest)[len++] = c;
        (*dest)[len] = 0;
        return true;
    }
    if (bufLen == 0)
        bufLen = 8;
    bufLen *= 2;
    char *d = new char[bufLen];
    if (d == 0)
        return false;
    if (len > 0)
        memcpy(d, *dest, len);
    if (*dest) delete[] *dest;
    *dest = d;
    (*dest)[len++] = c;
    (*dest)[len] = 0;
    return true;
}

char *YConfig::getArgument(char **dest, char *p, bool comma) {
    *dest = new char[1];
    if (*dest == 0) return 0;
    **dest = 0;
    int bufLen = 1;
    int len = 0;
    int sq_open = 0;
    int dq_open = 0;

    while (*p && (*p == ' ' || *p == '\t'))
        p++;

    len = 0;
    while (*p && (sq_open ||
                  dq_open ||
                  (*p != ' ' && *p != '\t' && *p != '\n' && (!comma ||
                                                             *p != ','))))
    {
       char c = *p++; // get current char and push the pointer to the next
       if (sq_open) {
           // single quotes open, pass everything but '
           if (c == '\'')
               sq_open = !sq_open;
           else
               appendStr(dest, bufLen, len, c);
       } else if (dq_open) {
           // double quotes open, pass everything but ". Extra care for \", unescape once.
           if (c == '"')
               dq_open = !dq_open;
           else if (c == '\\' && *p == '"') {
               appendStr(dest, bufLen, len, '"');
               p++;
           }
           else
               appendStr(dest, bufLen, len, c);
       } else {
           if (c == '"')
               dq_open = !dq_open;
           else if (c == '\'')
               sq_open = !sq_open;
           else if (c == '\\' && *p != '\n' && *p != '\r') {
              // add any char protected by backslash and move forward
              // exception: line ending (unwanted, may do bad things). OTOH, if
              // the two last checks are disable, it will cause a side effect
              // (multiline argument parsing with \n after \).
               appendStr(dest, bufLen, len, *p++);
           }
           else
               appendStr(dest, bufLen, len, c);
       }
    }
    return p;
}

#endif

#ifndef NO_CONFIGURE

// FIXME: P1 - parse keys later, not when loading
bool parseKey(const char *arg, KeySym *key, unsigned int *mod) {
    const char *orig_arg = arg;

    *mod = 0;
    for (;;) {
        if (strncmp("Alt+", arg, 4) == 0) {
            *mod |= kfAlt;
            arg += 4;
        } else if (strncmp("Ctrl+", arg, 5) == 0) {
            *mod |= kfCtrl;
            arg += 5;
        } else if (strncmp("Shift+", arg, 6) == 0) {
            *mod |= kfShift;
            arg += 6;
        } else if (strncmp("Meta+", arg, 5) == 0) {
            *mod |= kfMeta;
            arg += 5;
        } else if (strncmp("Super+", arg, 6) == 0) {
            *mod |= kfSuper;
            arg += 6;
        } else if (strncmp("Hyper+", arg, 6) == 0) {
            *mod |= kfHyper;
            arg += 6;
        } else if (strncmp("AltGr+", arg, 6) == 0) {
            *mod |= kfAltGr;
            arg += 6;
        } else
            break;
    }
    if (modSuperIsCtrlAlt && (*mod & kfSuper)) {
        *mod &= ~kfSuper;
        *mod |= kfAlt | kfCtrl;
    }

    if (strcmp(arg, "") == 0) {
        *key = NoSymbol;
        return true;
    } else if (strcmp(arg, "Esc") == 0)
        *key = XK_Escape;
    else if (strcmp(arg, "Enter") == 0)
        *key = XK_Return;
    else if (strcmp(arg, "Space") == 0)
        *key = ' ';
    else if (strcmp(arg, "BackSp") == 0)
        *key = XK_BackSpace;
    else if (strcmp(arg, "Del") == 0)
        *key = XK_Delete;
    else if (strlen(arg) == 1 && arg[0] >= 'A' && arg[0] <= 'Z') {
        char s[2];
        s[0] = (char)(arg[0] - 'A' + 'a');
        s[1] = 0;
        *key = XStringToKeysym(s);
    } else {
        *key = XStringToKeysym(arg);
    }

    if (*key == NoSymbol) {
        msg(_("Unknown key name %s in %s"), arg, orig_arg);
        return false;
    }
    return true;
}

char *setOption(cfoption *options, char *name, char *arg, bool append, char *rest) {
    unsigned int a;

    MSG(("SET %s := %s ;", name, arg));

    for (a = 0; options[a].type != cfoption::CF_NONE; a++) {
        if (strcmp(name, options[a].name) != 0)
            continue;
        if (options[a].notify) {
            options[a].notify(name, arg, append);
            return rest;
        }

        switch (options[a].type) {
        case cfoption::CF_BOOL:
            if (options[a].v.bool_value) {
                if ((arg[0] == '1' || arg[0] == '0') && arg[1] == 0) {
                    *(options[a].v.bool_value) = (arg[0] == '1') ? true : false;
                } else {
                    msg(_("Bad argument: %s for %s"), arg, name);
                    return rest;
                }
                return rest;
            }
            break;
        case cfoption::CF_INT:
            if (options[a].v.i.int_value) {
                int const v(atoi(arg));

                if (v >= options[a].v.i.min && v <= options[a].v.i.max)
                    *(options[a].v.i.int_value) = v;
                else {
                    msg(_("Bad argument: %s for %s"), arg, name);
                    return rest;
                }
                return rest;
            }
            break;
        case cfoption::CF_STR:
            if (options[a].v.s.string_value) {
                if (!options[a].v.s.initial)
                    delete[] (char *)*options[a].v.s.string_value;
                *options[a].v.s.string_value = newstr(arg);
                options[a].v.s.initial = false;
                return rest;
            }
            break;
#ifndef NO_KEYBIND
        case cfoption::CF_KEY:
            if (options[a].v.k.key_value) {
                WMKey *wk = options[a].v.k.key_value;

                if (parseKey(arg, &wk->key, &wk->mod)) {
                    if (!wk->initial)
                        delete[] (char *)wk->name;
                    wk->name = newstr(arg);
                    wk->initial = false;
                }
                return rest;
            }
            break;
#endif
        case cfoption::CF_NONE:
            break;
        }
    }
#if 0
    msg(_("Bad option: %s"), name);
#endif
    ///!!! check
    return rest;
}

// parse option name and argument
// name is string without spaces up to =
// option is a " quoted string or characters up to next space
char *parseOption(cfoption *options, char *str) {
    char name[64];
    char *argument = 0;
    char *p = str;
    unsigned int len = 0;
    bool append = false;

    while (*p && *p != '=' && *p != ' ' && *p != '\t' && len < sizeof(name) - 1)
        p++, len++;

    strncpy(name, str, len);
    name[len] = 0;

    while (*p && *p != '=')
        p++;
    if (*p != '=')
        return 0;
    p++;

    do {
        p = YConfig::getArgument(&argument, p, true);
        if (p == 0)
            break;

        p = setOption(options, name, argument, append, p);

        delete[] argument;

        append = true;

        if (p == 0)
            return 0;

        while (*p && (*p == ' ' || *p == '\t'))
            p++;

        if (*p != ',')
            break;
        p++;
    } while (1);

    return p;
}

void parseConfiguration(cfoption *options, char *data) {
    char *p = data;

    while (p && *p) {
        while (*p == ' ' || *p == '\t' || *p == '\n' || (*p == '\\' && p[1] == '\n'))
            p++;

        if (*p != '#')
            p = parseOption(options, p);
        else {
            while (*p && *p != '\n') {
                if (*p == '\\' && p[1] != 0)
                    p++;
                p++;
            }
        }
    }
}

void YConfig::loadConfigFile(cfoption *options, upath fileName) {
    cstring cs(fileName.path());
    int fd = open(cs.c_str(), O_RDONLY | O_TEXT);

    if (fd == -1)
        return ;

    struct stat sb;

    if (fstat(fd, &sb) == -1)
        return ;

    int len = sb.st_size;

    char *buf = new char[len + 1];
    if (buf == 0)
        return ;

    if ((len = read(fd, buf, len)) < 0) {
        delete[] buf;
        return;
    }

    buf[len] = 0;
    close(fd);
    parseConfiguration(options, buf);
    delete[] buf;
}

void YConfig::freeConfig(cfoption *options) {
    for (unsigned int a = 0; options[a].type != cfoption::CF_NONE; a++) {
        if (!options[a].v.s.initial) {
            if (options[a].v.s.string_value) {
                delete[] (char *)*options[a].v.s.string_value;
                *options[a].v.s.string_value = 0;
            }
            options[a].v.s.initial = false;
        }
    }
}

bool YConfig::findLoadConfigFile(IApp *app, struct cfoption *options, upath name) {
    upath configFile = app->findConfigFile(name);
    bool rc = false;
    if (configFile != null) {
        YConfig::loadConfigFile(options, configFile);
        rc = true;
    }
    return rc;
}

#endif
<commit_msg>Fix for properly closing open fd on error.<commit_after>#include "config.h"

#include "upath.h"
#include "ykey.h"
#include "yconfig.h"
#include "ypaint.h"
#include "yprefs.h"
#include "ypaths.h"
#include "sysdep.h"
#include "binascii.h"
#include "yapp.h"
#include "intl.h"

upath findPath(ustring path, int mode, upath name, bool /*path_relative*/) {
#ifdef __EMX__
    if (mode & X_OK)
        name = name.addExtension(".exe");
#endif
    if (name.isAbsolute()) { // check for root in XFreeOS/2
        if (name.fileExists())
            return name;
    } else {
        if (path == null)
            return null;

        ustring s(null), r(null);
        for (s = path; s.splitall(PATHSEP, &s, &r); s = r) {
            upath prog = upath(s).relative(name);
            if (prog.access(mode) == 0)
                return prog;

#if 0
            if (!(mode & X_OK) &&
                !prog.path().endsWith(".xpm") &&
                !prog.path().endsWith(".png"))
            {
                upath prog_png = prog.addExtension(".png");
                if (prog_png.access(mode) == 0)
                    return prog_png;
            }
#endif
        }
    }
    return null;
}

#if !defined(NO_CONFIGURE) || !defined(NO_CONFIGURE_MENUS)

static bool appendStr(char **dest, int &bufLen, int &len, char c) {
    if (*dest && len + 1 < bufLen) {
        (*dest)[len++] = c;
        (*dest)[len] = 0;
        return true;
    }
    if (bufLen == 0)
        bufLen = 8;
    bufLen *= 2;
    char *d = new char[bufLen];
    if (d == 0)
        return false;
    if (len > 0)
        memcpy(d, *dest, len);
    if (*dest) delete[] *dest;
    *dest = d;
    (*dest)[len++] = c;
    (*dest)[len] = 0;
    return true;
}

char *YConfig::getArgument(char **dest, char *p, bool comma) {
    *dest = new char[1];
    if (*dest == 0) return 0;
    **dest = 0;
    int bufLen = 1;
    int len = 0;
    int sq_open = 0;
    int dq_open = 0;

    while (*p && (*p == ' ' || *p == '\t'))
        p++;

    len = 0;
    while (*p && (sq_open ||
                  dq_open ||
                  (*p != ' ' && *p != '\t' && *p != '\n' && (!comma ||
                                                             *p != ','))))
    {
       char c = *p++; // get current char and push the pointer to the next
       if (sq_open) {
           // single quotes open, pass everything but '
           if (c == '\'')
               sq_open = !sq_open;
           else
               appendStr(dest, bufLen, len, c);
       } else if (dq_open) {
           // double quotes open, pass everything but ". Extra care for \", unescape once.
           if (c == '"')
               dq_open = !dq_open;
           else if (c == '\\' && *p == '"') {
               appendStr(dest, bufLen, len, '"');
               p++;
           }
           else
               appendStr(dest, bufLen, len, c);
       } else {
           if (c == '"')
               dq_open = !dq_open;
           else if (c == '\'')
               sq_open = !sq_open;
           else if (c == '\\' && *p != '\n' && *p != '\r') {
              // add any char protected by backslash and move forward
              // exception: line ending (unwanted, may do bad things). OTOH, if
              // the two last checks are disable, it will cause a side effect
              // (multiline argument parsing with \n after \).
               appendStr(dest, bufLen, len, *p++);
           }
           else
               appendStr(dest, bufLen, len, c);
       }
    }
    return p;
}

#endif

#ifndef NO_CONFIGURE

// FIXME: P1 - parse keys later, not when loading
bool parseKey(const char *arg, KeySym *key, unsigned int *mod) {
    const char *orig_arg = arg;

    *mod = 0;
    for (;;) {
        if (strncmp("Alt+", arg, 4) == 0) {
            *mod |= kfAlt;
            arg += 4;
        } else if (strncmp("Ctrl+", arg, 5) == 0) {
            *mod |= kfCtrl;
            arg += 5;
        } else if (strncmp("Shift+", arg, 6) == 0) {
            *mod |= kfShift;
            arg += 6;
        } else if (strncmp("Meta+", arg, 5) == 0) {
            *mod |= kfMeta;
            arg += 5;
        } else if (strncmp("Super+", arg, 6) == 0) {
            *mod |= kfSuper;
            arg += 6;
        } else if (strncmp("Hyper+", arg, 6) == 0) {
            *mod |= kfHyper;
            arg += 6;
        } else if (strncmp("AltGr+", arg, 6) == 0) {
            *mod |= kfAltGr;
            arg += 6;
        } else
            break;
    }
    if (modSuperIsCtrlAlt && (*mod & kfSuper)) {
        *mod &= ~kfSuper;
        *mod |= kfAlt | kfCtrl;
    }

    if (strcmp(arg, "") == 0) {
        *key = NoSymbol;
        return true;
    } else if (strcmp(arg, "Esc") == 0)
        *key = XK_Escape;
    else if (strcmp(arg, "Enter") == 0)
        *key = XK_Return;
    else if (strcmp(arg, "Space") == 0)
        *key = ' ';
    else if (strcmp(arg, "BackSp") == 0)
        *key = XK_BackSpace;
    else if (strcmp(arg, "Del") == 0)
        *key = XK_Delete;
    else if (strlen(arg) == 1 && arg[0] >= 'A' && arg[0] <= 'Z') {
        char s[2];
        s[0] = (char)(arg[0] - 'A' + 'a');
        s[1] = 0;
        *key = XStringToKeysym(s);
    } else {
        *key = XStringToKeysym(arg);
    }

    if (*key == NoSymbol) {
        msg(_("Unknown key name %s in %s"), arg, orig_arg);
        return false;
    }
    return true;
}

char *setOption(cfoption *options, char *name, char *arg, bool append, char *rest) {
    unsigned int a;

    MSG(("SET %s := %s ;", name, arg));

    for (a = 0; options[a].type != cfoption::CF_NONE; a++) {
        if (strcmp(name, options[a].name) != 0)
            continue;
        if (options[a].notify) {
            options[a].notify(name, arg, append);
            return rest;
        }

        switch (options[a].type) {
        case cfoption::CF_BOOL:
            if (options[a].v.bool_value) {
                if ((arg[0] == '1' || arg[0] == '0') && arg[1] == 0) {
                    *(options[a].v.bool_value) = (arg[0] == '1') ? true : false;
                } else {
                    msg(_("Bad argument: %s for %s"), arg, name);
                    return rest;
                }
                return rest;
            }
            break;
        case cfoption::CF_INT:
            if (options[a].v.i.int_value) {
                int const v(atoi(arg));

                if (v >= options[a].v.i.min && v <= options[a].v.i.max)
                    *(options[a].v.i.int_value) = v;
                else {
                    msg(_("Bad argument: %s for %s"), arg, name);
                    return rest;
                }
                return rest;
            }
            break;
        case cfoption::CF_STR:
            if (options[a].v.s.string_value) {
                if (!options[a].v.s.initial)
                    delete[] (char *)*options[a].v.s.string_value;
                *options[a].v.s.string_value = newstr(arg);
                options[a].v.s.initial = false;
                return rest;
            }
            break;
#ifndef NO_KEYBIND
        case cfoption::CF_KEY:
            if (options[a].v.k.key_value) {
                WMKey *wk = options[a].v.k.key_value;

                if (parseKey(arg, &wk->key, &wk->mod)) {
                    if (!wk->initial)
                        delete[] (char *)wk->name;
                    wk->name = newstr(arg);
                    wk->initial = false;
                }
                return rest;
            }
            break;
#endif
        case cfoption::CF_NONE:
            break;
        }
    }
#if 0
    msg(_("Bad option: %s"), name);
#endif
    ///!!! check
    return rest;
}

// parse option name and argument
// name is string without spaces up to =
// option is a " quoted string or characters up to next space
char *parseOption(cfoption *options, char *str) {
    char name[64];
    char *argument = 0;
    char *p = str;
    unsigned int len = 0;
    bool append = false;

    while (*p && *p != '=' && *p != ' ' && *p != '\t' && len < sizeof(name) - 1)
        p++, len++;

    strncpy(name, str, len);
    name[len] = 0;

    while (*p && *p != '=')
        p++;
    if (*p != '=')
        return 0;
    p++;

    do {
        p = YConfig::getArgument(&argument, p, true);
        if (p == 0)
            break;

        p = setOption(options, name, argument, append, p);

        delete[] argument;

        append = true;

        if (p == 0)
            return 0;

        while (*p && (*p == ' ' || *p == '\t'))
            p++;

        if (*p != ',')
            break;
        p++;
    } while (1);

    return p;
}

void parseConfiguration(cfoption *options, char *data) {
    char *p = data;

    while (p && *p) {
        while (*p == ' ' || *p == '\t' || *p == '\n' || (*p == '\\' && p[1] == '\n'))
            p++;

        if (*p != '#')
            p = parseOption(options, p);
        else {
            while (*p && *p != '\n') {
                if (*p == '\\' && p[1] != 0)
                    p++;
                p++;
            }
        }
    }
}

void YConfig::loadConfigFile(cfoption *options, upath fileName) {
    cstring cs(fileName.path());
    int fd = open(cs.c_str(), O_RDONLY | O_TEXT);

    if (fd == -1)
        return ;

    struct stat sb;

    if (fstat(fd, &sb) == -1) {
        close(fd);
        return;
    }

    int len = sb.st_size;

    char *buf = new char[len + 1];
    if (buf == 0) {
        close(fd);
        return;
    }

    if ((len = read(fd, buf, len)) < 0) {
        delete[] buf;
        close(fd);
        return;
    }

    buf[len] = 0;
    close(fd);
    parseConfiguration(options, buf);
    delete[] buf;
}

void YConfig::freeConfig(cfoption *options) {
    for (unsigned int a = 0; options[a].type != cfoption::CF_NONE; a++) {
        if (!options[a].v.s.initial) {
            if (options[a].v.s.string_value) {
                delete[] (char *)*options[a].v.s.string_value;
                *options[a].v.s.string_value = 0;
            }
            options[a].v.s.initial = false;
        }
    }
}

bool YConfig::findLoadConfigFile(IApp *app, struct cfoption *options, upath name) {
    upath configFile = app->findConfigFile(name);
    bool rc = false;
    if (configFile != null) {
        YConfig::loadConfigFile(options, configFile);
        rc = true;
    }
    return rc;
}

#endif
<|endoftext|>


<commit_before>/*
 * $Id$
 *
 * Copyright(C) 1998-2003 Satoshi Nakamura
 * All rights reserved.
 *
 */

#include <qmapplication.h>
#include <qmdocument.h>
#include <qmfolder.h>
#include <qmmessage.h>
#include <qmmessageholder.h>
#include <qmtemplate.h>

#include <qsconv.h>
#include <qsnew.h>
#include <qsosutil.h>

#include "templateprocessor.h"
#include "../model/editmessage.h"
#include "../model/templatemanager.h"
#include "../ui/editframewindow.h"
#include "../ui/externaleditor.h"
#include "../ui/foldermodel.h"
#include "../ui/messageselectionmodel.h"

using namespace qm;
using namespace qs;

/****************************************************************************
 *
 * TemplateProcessor
 *
 */

qm::TemplateProcessor::TemplateProcessor(Document* pDocument,
	FolderModel* pFolderModel, MessageSelectionModel* pMessageSelectionModel,
	EditFrameWindowManager* pEditFrameWindowManager,
	ExternalEditorManager* pExternalEditorManager, HWND hwnd,
	Profile* pProfile, bool bExternalEditor) :
	pDocument_(pDocument),
	pFolderModel_(pFolderModel),
	pMessageSelectionModel_(pMessageSelectionModel),
	pEditFrameWindowManager_(pEditFrameWindowManager),
	pExternalEditorManager_(pExternalEditorManager),
	hwnd_(hwnd),
	pProfile_(pProfile),
	bExternalEditor_(bExternalEditor)
{
	assert(pDocument_);
	assert(pFolderModel);
	assert(pMessageSelectionModel);
	assert(pEditFrameWindowManager_);
	assert(hwnd_);
	assert(pProfile_);
}

qm::TemplateProcessor::~TemplateProcessor()
{
}

QSTATUS qm::TemplateProcessor::process(
	const WCHAR* pwszTemplateName, bool bReverseExternalEditor) const
{
	assert(pwszTemplateName);

	DECLARE_QSTATUS();

	Account* pAccount = pFolderModel_->getCurrentAccount();
	Folder* pFolder = 0;
	if (!pAccount) {
		pFolder = pFolderModel_->getCurrentFolder();
		if (pFolder)
			pAccount = pFolder->getAccount();
	}
	if (!pAccount)
		return QSTATUS_FAIL;

	const Template* pTemplate = 0;
	status = pDocument_->getTemplateManager()->getTemplate(
		pAccount, pFolder, pwszTemplateName, &pTemplate);
	CHECK_QSTATUS();

	MessagePtr ptr;
	status = pMessageSelectionModel_->getFocusedMessage(&ptr);
	CHECK_QSTATUS();
	MessagePtrLock mpl(ptr);
	MacroErrorHandlerImpl handler;
	Message msg(&status);
	CHECK_QSTATUS();
	TemplateContext context(mpl, mpl ? &msg : 0, pAccount,
		pDocument_, hwnd_, pProfile_, &handler, &status);
	CHECK_QSTATUS();

	string_ptr<WSTRING> wstrValue;
	status = pTemplate->getValue(context, &wstrValue);
	CHECK_QSTATUS();

	bool bExternalEditor = (bExternalEditor_ && !bReverseExternalEditor) ||
		(!bExternalEditor_ && bReverseExternalEditor);

	if (bExternalEditor) {
#if 1
		status = pExternalEditorManager_->open(wstrValue.get());
		CHECK_QSTATUS();
#else
		string_ptr<WSTRING> wstrEditor;
		status = pProfile_->getString(L"Global", L"ExternalEditor", L"", &wstrEditor);
		CHECK_QSTATUS();
		if (!*wstrEditor.get()) {
			wstrEditor.reset(0);
			status = pProfile_->getString(L"Global", L"Editor", L"", &wstrEditor);
			CHECK_QSTATUS();
		}

		if (*wstrEditor.get()) {
			string_ptr<WSTRING> wstrTempFile(concat(
				Application::getApplication().getTemporaryFolder(),
				L"\\q3edit.txt"));
			if (!wstrTempFile.get())
				return QSTATUS_OUTOFMEMORY;

			FileOutputStream stream(wstrTempFile.get(), &status);
			CHECK_QSTATUS();
			BufferedOutputStream bufferedStream(&stream, false, &status);
			CHECK_QSTATUS();
			OutputStreamWriter writer(&bufferedStream,
				false, getSystemEncoding(), &status);
			CHECK_QSTATUS();
			status = writer.write(wstrValue.get(), wcslen(wstrValue.get()));
			CHECK_QSTATUS();
			status = writer.close();
			CHECK_QSTATUS();

			const WCHAR* pFile = wstrEditor.get();
			WCHAR* pParam = 0;
			if (*wstrEditor.get() == L'\"') {
				++pFile;
				pParam = wcschr(wstrEditor.get() + 1, L'\"');
			}
			else {
				pParam = wcschr(wstrEditor.get(), L' ');
			}
			if (pParam) {
				*pParam = L'\0';
				++pParam;
			}

			StringBuffer<WSTRING> bufParam(&status);
			CHECK_QSTATUS();
			if (pParam) {
				const WCHAR* p = wcsstr(pParam, L"%f");
				if (p) {
					status = bufParam.append(pParam, p - pParam);
					CHECK_QSTATUS();
					status = bufParam.append(wstrTempFile.get());
					CHECK_QSTATUS();
					status = bufParam.append(p + 2);
					CHECK_QSTATUS();
				}
				else {
					status = bufParam.append(pParam);
					CHECK_QSTATUS();
					status = bufParam.append(L' ');
					CHECK_QSTATUS();
					status = bufParam.append(wstrTempFile.get());
					CHECK_QSTATUS();
				}
			}
			else {
				status = bufParam.append(wstrTempFile.get());
				CHECK_QSTATUS();
			}

			W2T(pFile, ptszFile);
			W2T(bufParam.getCharArray(), ptszParam);

			SHELLEXECUTEINFO sei = {
				sizeof(sei),
				0,
				hwnd_,
				0,
				ptszFile,
				ptszParam,
				0,
#ifdef _WIN32_WCE
				SW_SHOWNORMAL,
#else
				SW_SHOWDEFAULT,
#endif
			};
			::ShellExecuteEx(&sei);
		}
#endif
	}
	else {
		MessageCreator creator;
		Message* p = 0;
		status = creator.createMessage(wstrValue.get(), -1, &p);
		CHECK_QSTATUS();
		std::auto_ptr<Message> pMessage(p);

		std::auto_ptr<EditMessage> pEditMessage;
		status = newQsObject(pDocument_, pAccount, &pEditMessage);
		CHECK_QSTATUS();
		status = pEditMessage->setMessage(pMessage.get());
		CHECK_QSTATUS();
		pMessage.release();

		status = pEditFrameWindowManager_->open(pEditMessage.get());
		CHECK_QSTATUS();
		pEditMessage.release();
	}

	return QSTATUS_SUCCESS;
}

/****************************************************************************
 *
 * TemplateProcessor::MacroErrorHandlerImpl
 *
 */

qm::TemplateProcessor::MacroErrorHandlerImpl::MacroErrorHandlerImpl()
{
}

qm::TemplateProcessor::MacroErrorHandlerImpl::~MacroErrorHandlerImpl()
{
}

void qm::TemplateProcessor::MacroErrorHandlerImpl::parseError(
	Code code, const WCHAR* pwsz)
{
	// TODO
}

void qm::TemplateProcessor::MacroErrorHandlerImpl::processError(
	Code code, const WCHAR* pwsz)
{
	// TODO
}
<commit_msg>Load from profile if auto reform is performed or not. <BTS:183><commit_after>/*
 * $Id$
 *
 * Copyright(C) 1998-2003 Satoshi Nakamura
 * All rights reserved.
 *
 */

#include <qmapplication.h>
#include <qmdocument.h>
#include <qmfolder.h>
#include <qmmessage.h>
#include <qmmessageholder.h>
#include <qmtemplate.h>

#include <qsconv.h>
#include <qsnew.h>
#include <qsosutil.h>

#include "templateprocessor.h"
#include "../model/editmessage.h"
#include "../model/templatemanager.h"
#include "../ui/editframewindow.h"
#include "../ui/externaleditor.h"
#include "../ui/foldermodel.h"
#include "../ui/messageselectionmodel.h"

using namespace qm;
using namespace qs;

/****************************************************************************
 *
 * TemplateProcessor
 *
 */

qm::TemplateProcessor::TemplateProcessor(Document* pDocument,
	FolderModel* pFolderModel, MessageSelectionModel* pMessageSelectionModel,
	EditFrameWindowManager* pEditFrameWindowManager,
	ExternalEditorManager* pExternalEditorManager, HWND hwnd,
	Profile* pProfile, bool bExternalEditor) :
	pDocument_(pDocument),
	pFolderModel_(pFolderModel),
	pMessageSelectionModel_(pMessageSelectionModel),
	pEditFrameWindowManager_(pEditFrameWindowManager),
	pExternalEditorManager_(pExternalEditorManager),
	hwnd_(hwnd),
	pProfile_(pProfile),
	bExternalEditor_(bExternalEditor)
{
	assert(pDocument_);
	assert(pFolderModel);
	assert(pMessageSelectionModel);
	assert(pEditFrameWindowManager_);
	assert(hwnd_);
	assert(pProfile_);
}

qm::TemplateProcessor::~TemplateProcessor()
{
}

QSTATUS qm::TemplateProcessor::process(
	const WCHAR* pwszTemplateName, bool bReverseExternalEditor) const
{
	assert(pwszTemplateName);

	DECLARE_QSTATUS();

	Account* pAccount = pFolderModel_->getCurrentAccount();
	Folder* pFolder = 0;
	if (!pAccount) {
		pFolder = pFolderModel_->getCurrentFolder();
		if (pFolder)
			pAccount = pFolder->getAccount();
	}
	if (!pAccount)
		return QSTATUS_FAIL;

	const Template* pTemplate = 0;
	status = pDocument_->getTemplateManager()->getTemplate(
		pAccount, pFolder, pwszTemplateName, &pTemplate);
	CHECK_QSTATUS();

	MessagePtr ptr;
	status = pMessageSelectionModel_->getFocusedMessage(&ptr);
	CHECK_QSTATUS();
	MessagePtrLock mpl(ptr);
	MacroErrorHandlerImpl handler;
	Message msg(&status);
	CHECK_QSTATUS();
	TemplateContext context(mpl, mpl ? &msg : 0, pAccount,
		pDocument_, hwnd_, pProfile_, &handler, &status);
	CHECK_QSTATUS();

	string_ptr<WSTRING> wstrValue;
	status = pTemplate->getValue(context, &wstrValue);
	CHECK_QSTATUS();

	bool bExternalEditor = (bExternalEditor_ && !bReverseExternalEditor) ||
		(!bExternalEditor_ && bReverseExternalEditor);

	if (bExternalEditor) {
#if 1
		status = pExternalEditorManager_->open(wstrValue.get());
		CHECK_QSTATUS();
#else
		string_ptr<WSTRING> wstrEditor;
		status = pProfile_->getString(L"Global", L"ExternalEditor", L"", &wstrEditor);
		CHECK_QSTATUS();
		if (!*wstrEditor.get()) {
			wstrEditor.reset(0);
			status = pProfile_->getString(L"Global", L"Editor", L"", &wstrEditor);
			CHECK_QSTATUS();
		}

		if (*wstrEditor.get()) {
			string_ptr<WSTRING> wstrTempFile(concat(
				Application::getApplication().getTemporaryFolder(),
				L"\\q3edit.txt"));
			if (!wstrTempFile.get())
				return QSTATUS_OUTOFMEMORY;

			FileOutputStream stream(wstrTempFile.get(), &status);
			CHECK_QSTATUS();
			BufferedOutputStream bufferedStream(&stream, false, &status);
			CHECK_QSTATUS();
			OutputStreamWriter writer(&bufferedStream,
				false, getSystemEncoding(), &status);
			CHECK_QSTATUS();
			status = writer.write(wstrValue.get(), wcslen(wstrValue.get()));
			CHECK_QSTATUS();
			status = writer.close();
			CHECK_QSTATUS();

			const WCHAR* pFile = wstrEditor.get();
			WCHAR* pParam = 0;
			if (*wstrEditor.get() == L'\"') {
				++pFile;
				pParam = wcschr(wstrEditor.get() + 1, L'\"');
			}
			else {
				pParam = wcschr(wstrEditor.get(), L' ');
			}
			if (pParam) {
				*pParam = L'\0';
				++pParam;
			}

			StringBuffer<WSTRING> bufParam(&status);
			CHECK_QSTATUS();
			if (pParam) {
				const WCHAR* p = wcsstr(pParam, L"%f");
				if (p) {
					status = bufParam.append(pParam, p - pParam);
					CHECK_QSTATUS();
					status = bufParam.append(wstrTempFile.get());
					CHECK_QSTATUS();
					status = bufParam.append(p + 2);
					CHECK_QSTATUS();
				}
				else {
					status = bufParam.append(pParam);
					CHECK_QSTATUS();
					status = bufParam.append(L' ');
					CHECK_QSTATUS();
					status = bufParam.append(wstrTempFile.get());
					CHECK_QSTATUS();
				}
			}
			else {
				status = bufParam.append(wstrTempFile.get());
				CHECK_QSTATUS();
			}

			W2T(pFile, ptszFile);
			W2T(bufParam.getCharArray(), ptszParam);

			SHELLEXECUTEINFO sei = {
				sizeof(sei),
				0,
				hwnd_,
				0,
				ptszFile,
				ptszParam,
				0,
#ifdef _WIN32_WCE
				SW_SHOWNORMAL,
#else
				SW_SHOWDEFAULT,
#endif
			};
			::ShellExecuteEx(&sei);
		}
#endif
	}
	else {
		MessageCreator creator;
		Message* p = 0;
		status = creator.createMessage(wstrValue.get(), -1, &p);
		CHECK_QSTATUS();
		std::auto_ptr<Message> pMessage(p);

		std::auto_ptr<EditMessage> pEditMessage;
		status = newQsObject(pDocument_, pAccount, &pEditMessage);
		CHECK_QSTATUS();
		status = pEditMessage->setMessage(pMessage.get());
		CHECK_QSTATUS();
		pMessage.release();

		int nAutoReform = 1;
		status = pProfile_->getInt(L"EditWindow", L"AutoReform", 1, &nAutoReform);
		CHECK_QSTATUS();
		pEditMessage->setAutoReform(nAutoReform != 0);

		status = pEditFrameWindowManager_->open(pEditMessage.get());
		CHECK_QSTATUS();
		pEditMessage.release();
	}

	return QSTATUS_SUCCESS;
}

/****************************************************************************
 *
 * TemplateProcessor::MacroErrorHandlerImpl
 *
 */

qm::TemplateProcessor::MacroErrorHandlerImpl::MacroErrorHandlerImpl()
{
}

qm::TemplateProcessor::MacroErrorHandlerImpl::~MacroErrorHandlerImpl()
{
}

void qm::TemplateProcessor::MacroErrorHandlerImpl::parseError(
	Code code, const WCHAR* pwsz)
{
	// TODO
}

void qm::TemplateProcessor::MacroErrorHandlerImpl::processError(
	Code code, const WCHAR* pwsz)
{
	// TODO
}
<|endoftext|>