<commit_before>/*
 * File:   SignalImpl.hpp
 * Author: Barath Kannan
 * Signal/Slots C++14 implementation
 * Created on May 10, 2016, 5:57 PM
 */

#ifndef SIGNALIMPL_HPP
#define SIGNALIMPL_HPP

#include <functional>
#include <map>
#include <unordered_map>
#include <atomic>
#include <mutex>
#include <shared_mutex>
#include <condition_variable>
#include <thread>
#include <utility>
#include <type_traits>

#include "BSignals/details/MPSCQueue.hpp"
#include "BSignals/details/WheeledThreadPool.h"
#include "BSignals/details/TaskQueueProcessor.hpp"
#include "BSignals/details/Semaphore.h"

namespace BSignals{ namespace details{

//These executors determine how message emission to a slot occurs
    // SYNCHRONOUS:
    // Emission occurs synchronously.
    // When emit returns, all connected slots have been invoked and returned.
    // This method is preferred when connected functions have short execution
    // time, quick emission is required, and/or when it is necessary to know
    // that the function has returned before proceeding.

    // ASYNCHRONOUS:
    // Emission occurs asynchronously. A detached thread is spawned on emission.
    // When emit returns, the thread has been spawned. The thread automatically
    // destructs when the connected function returns.
    // This method is recommended when connected functions have long execution
    // time and are independent.

    // STRAND:
    // Emission occurs asynchronously.
    // On connection a dedicated thread (per slot) is spawned to wait for new messages.
    // Emitted parameters are bound to the mapped function and enqueued on the
    // waiting thread. These messages are then processed synchronously in the
    // spawned thread.
    // This method is recommended when connected functions have longer execution
    // time, the overhead of creating/destroying a thread for each slot would be
    // unperformant, and/or connected functions need to be processed in order
    // of arrival (FIFO).

    // THREAD POOLED:
    // Emission occurs asynchronously.
    // On connection, if it is the first thread pooled function by any signal,
    // the thread pool is initialized with 8 threads, all listening for queued
    // emissions. The number of threads in the pool is not currently run-time
    // configurable but may be in the future.
    // Emitted parameters are bound to the mapped function and enqueued on the
    // one of the waiting threads. These messages are then processed when the
    // relevant queue is consumed by the mapped thread pool.
    // This method is recommended when connected functions have longer execution
    // time, the overhead of creating/destroying a thread for each slot would be
    // unperformant, the overhead of a waiting thread for each slot is
    // unnecessary, and/or connected functions do NOT need to be processed in
    // order of arrival.

    // DEFERRED_SYNCHRONOUS:
    // Emissions are queued up to be manually invoked through the invokeDeferred function
//
enum class ExecutorScheme{
    SYNCHRONOUS,
    DEFERRED_SYNCHRONOUS,
    ASYNCHRONOUS,
    STRAND,
    THREAD_POOLED
};

template <typename... Args>
class SignalImpl {
public:
    SignalImpl() = default;

    SignalImpl(bool enforceThreadSafety)
        : enableEmissionGuard{enforceThreadSafety} {}

    SignalImpl(uint32_t maxAsyncThreads)
        : sem{maxAsyncThreads} {}

    SignalImpl(bool enforceThreadSafety, uint32_t maxAsyncThreads)
        : enableEmissionGuard{enforceThreadSafety}, sem{maxAsyncThreads} {}

    ~SignalImpl(){
        disconnectAllSlots();
        sem.acquireAll();
    }

    template<typename F, typename C>
    int connectMemberSlot(const ExecutorScheme &scheme, F&& function, C&& instance) const {
        //type check assertions
        static_assert(std::is_member_function_pointer<F>::value, "function is not a member function");
        static_assert(std::is_object<std::remove_reference<C>>::value, "instance is not a class object");

        //Construct a bound function from the function pointer and object
        auto boundFunc = objectBind(function, instance);
        return connectSlot(scheme, boundFunc);
    }

    int connectSlot(const ExecutorScheme &scheme, std::function<void(Args...)> slot) const {
        uint32_t id = currentId.fetch_add(1);
        if (enableEmissionGuard){
            std::unique_lock<std::shared_timed_mutex> lock(backBufferLock);
            connectBuffer.emplace(id, ConnectDescriptor{scheme, slot});
            return id;
        }
        else{
            return connectSlotFunction(id, scheme, slot);
        }
    }

    void disconnectSlot(const int &id) const{
        if (enableEmissionGuard){
            std::unique_lock<std::shared_timed_mutex> lock(backBufferLock);
            disconnectBuffer.insert(id);
        }
        else{
            disconnectSlotFunction(id);
        }
    }

    void disconnectAllSlots() const{
        std::unique_lock<std::shared_timed_mutex> lock(signalLock);
        for (auto &q : strandQueues){
            q.second.enqueue(nullptr);
        }
        for (auto &t : strandThreads){
            t.second.join();
        }
        strandThreads.clear();
        strandQueues.clear();

        slots.clear();
    }

    void emitSignal(const Args &... p) const {
        enableEmissionGuard ? emitSignalThreadSafe(p...) : emitSignalUnsafe(p...);
    }

    void invokeDeferred() const{
        std::pair<std::function<void()>, uint32_t> deferredInvocation;
        while (deferredQueue.dequeue(deferredInvocation)){
            if (!enableEmissionGuard || getIsStillConnectedFromExecutor(deferredInvocation.second)){
                deferredInvocation.first();
            }
        }
    }

    void operator()(const Args &... p) const{
        emitSignal(p...);
    }

private:
    SignalImpl<Args...>(const SignalImpl<Args...>& that) = delete;
    void operator=(const SignalImpl<Args...>&) = delete;

    inline void disconnectSlotFunction(const int &id) const{
        std::unique_lock<std::shared_timed_mutex> lock(signalLock);
        if (!slots.count(id)) return;
        auto &entry = slots[id];
        if (entry.scheme == ExecutorScheme::STRAND){
            strandQueues[id].enqueue(nullptr);
            strandThreads[id].join();
            strandThreads.erase(id);
            strandQueues.erase(id);
        }
        slots.erase(id);
    }

    inline int connectSlotFunction(const int &id, const ExecutorScheme &scheme, std::function<void(Args...)> slot) const {
        std::unique_lock<std::shared_timed_mutex> lock(signalLock);
        slots.emplace(id, ConnectDescriptor{scheme, slot});
        if (scheme == ExecutorScheme::STRAND){
            strandQueues[id];
            strandThreads.emplace(id, std::thread(&SignalImpl::queueListener, this, id));
        }
        else if (scheme == ExecutorScheme::THREAD_POOLED){
            WheeledThreadPool::startup();
            //TaskQueueProcessor::startup();
        }
        return (int)id;
    }

    inline void invokeRelevantExecutor(const ExecutorScheme &scheme, const uint32_t &id, const std::function<void(Args...)> &slot, const Args &... p) const{
        switch(scheme){
            case(ExecutorScheme::DEFERRED_SYNCHRONOUS):
                enqueueDeferred(id, slot, p...);
                return;
            case(ExecutorScheme::SYNCHRONOUS):
                runSynchronous(id, slot, p...);
                return;
            case(ExecutorScheme::ASYNCHRONOUS):
                runAsynchronous(id, slot, p...);
                return;
            case(ExecutorScheme::STRAND):
                runStrands(id, slot, p...);
                return;
            case(ExecutorScheme::THREAD_POOLED):
                runThreadPooled(id, slot, p...);
                return;
        }
    }

    inline void emitSignalUnsafe(const Args &... p) const {
        for (auto const &kvpair : slots){
            invokeRelevantExecutor(kvpair.second.scheme, kvpair.first, kvpair.second.slot, p...);
        }
    }

    inline bool getIsStillConnected(int const &id) const{
        std::shared_lock<std::shared_timed_mutex> lock(backBufferLock);
        return (!disconnectBuffer.count(id));
    }

    inline bool getIsStillConnectedFromExecutor(int const &id) const{
        std::shared_lock<std::shared_timed_mutex> lock(backBufferLock);
        std::shared_lock<std::shared_timed_mutex> lockSignal(signalLock);
        return (!disconnectBuffer.count(id) && slots.count(id));
    }

    inline bool getHasWaitingDisconnects() const{
        std::shared_lock<std::shared_timed_mutex> lock(backBufferLock);
        return (!disconnectBuffer.empty());
    }

    inline bool getHasWaitingConnects() const{
        std::shared_lock<std::shared_timed_mutex> lock(backBufferLock);
        return (!connectBuffer.empty());
    }

    inline bool getHasWaitingConnectsOrDisconnects() const{
        std::shared_lock<std::shared_timed_mutex> lock(backBufferLock);
        return (!connectBuffer.empty() | !disconnectBuffer.empty());
    }

    inline void emitSignalThreadSafe(const Args &... p) const {
        if (getHasWaitingConnectsOrDisconnects()){
            std::unique_lock<std::shared_timed_mutex> bbLock(backBufferLock);
            while (!connectBuffer.empty()){
                const std::pair<uint32_t, ConnectDescriptor> current = *connectBuffer.begin();
                connectBuffer.erase(connectBuffer.begin());
                //if there is another emit attempting to connect from inside a
                //synchronous slot function it is going to attempt to push to the
                //backbuffer while holding a signal shared mutex. This will
                //cause a deadlock. Hence we need to release the backBufferLock
                //for the duration of the connect
                bbLock.unlock();
                connectSlotFunction(current.first, current.second.scheme, current.second.slot);
                bbLock.lock();
            }
            while (!disconnectBuffer.empty()){
                const uint32_t disconnectId = *disconnectBuffer.begin();
                disconnectBuffer.erase(disconnectBuffer.begin());
                //Same as above, need to relinquish the lock while we disconnect
                //to avoid deadlock
                bbLock.unlock();
                disconnectSlotFunction(disconnectId);
                bbLock.lock();
            }
        }
        std::shared_lock<std::shared_timed_mutex> lock(signalLock);
        for (auto const &kvpair : slots){
            if (getIsStillConnected(kvpair.first)){
                invokeRelevantExecutor(kvpair.second.scheme, kvpair.first, kvpair.second.slot, p...);
            }
        }
    }

    inline void runThreadPooled(const uint32_t &id, const std::function<void(Args...)> &function, const Args &... p) const {
        //TaskQueueProcessor::run([this, &id, &function, p...](){
        WheeledThreadPool::run([this, &id, &function, p...](){
            if (!enableEmissionGuard || getIsStillConnectedFromExecutor(id))
                function(p...);
        });
    }

    inline void runAsynchronous(const uint32_t &id, const std::function<void(Args...)> &function, const Args &... p) const {
        sem.acquire();
        std::thread slotThread([this, &id, &function, p...](){
            if (!enableEmissionGuard || getIsStillConnectedFromExecutor(id))
                function(p...);
            sem.release();
        });
        slotThread.detach();
    }

    inline void runStrands(const uint32_t &id, const std::function<void(Args...)> &function, const Args &... p) const{
        //bind the function arguments to the function using a lambda and store
        //the newly bound function. This changes the function signature in the
        //resultant map, there are no longer any parameters in the bound function
        strandQueues[id].enqueue([&function, p...](){function(p...);});
    }

    inline void runSynchronous(const uint32_t &id, const std::function<void(Args...)> &function, const Args &... p) const{
        function(p...);
    }

    inline void enqueueDeferred(const uint32_t &id, const std::function<void(Args...)> &function, const Args &... p) const{
        deferredQueue.enqueue({[&function, p...](){function(p...);}, id});
    }

    //Reference to instance
    template<typename F, typename I>
    std::function<void(Args...)> objectBind(F&& function, I&& instance) const {
        return[=, &instance](Args... args){
            (instance.*function)(args...);
        };
    }

    //Pointer to instance
    template<typename F, typename I>
    std::function<void(Args...)> objectBind(F&& function, I* instance) const {
        return objectBind(function, *instance);
    }

    void queueListener(const uint32_t &id) const{
        auto &q = strandQueues[id];
        std::function<void()> func = [](){};
        auto maxWait = WheeledThreadPool::getMaxWait();
        std::chrono::duration<double> waitTime = std::chrono::nanoseconds(1);
        while (func){
            if (q.dequeue(func)){
                if (func) func();
                waitTime = std::chrono::nanoseconds(1);
            }
            else{
                std::this_thread::sleep_for(waitTime);
                    waitTime*=2;
            }
            if (waitTime > maxWait){
                q.blockingDequeue(func);
                if (func) func();
                waitTime = std::chrono::nanoseconds(1);
            }
        }
    }

    //Shared mutex for thread safety
    //Emit acquires shared lock, connect/disconnect acquires unique lock
    mutable std::shared_timed_mutex signalLock;

    //Atomically incremented slotId
    mutable std::atomic<uint32_t> currentId {0};

    //Async Emit Semaphore
    mutable Semaphore sem {1024};

    //EmissionGuard determines if it is necessary to guard emission with a shared mutex
    //This is only required if connection/disconnection could be interleaved with emission
    const bool enableEmissionGuard {false};

    //Strand Queues and Threads
    mutable std::map<uint32_t, MPSCQueue<std::function<void()>>> strandQueues;
    mutable std::map<uint32_t, std::thread> strandThreads;

    //Deferred invocation queue
    mutable MPSCQueue<std::pair<std::function<void()>, uint32_t>> deferredQueue;

    struct ConnectDescriptor{
        ExecutorScheme scheme;
        std::function<void(Args...)> slot;
    };

    //Slot Map
    mutable std::map<uint32_t, ConnectDescriptor> slots;

    //Disconnect backbuffer
    mutable std::set<uint32_t> disconnectBuffer;

    //Connect backbuffer
    mutable std::map<uint32_t, ConnectDescriptor> connectBuffer;

    //Backbuffer lock
    mutable std::shared_timed_mutex backBufferLock;
};

}}

#endif /* SIGNALIMPL_HPP */
<commit_msg>Remove unused features - Round robin with partial (unfair) stealing is always better than MPMC job queue<commit_after>/*
 * File:   SignalImpl.hpp
 * Author: Barath Kannan
 * Signal/Slots C++14 implementation
 * Created on May 10, 2016, 5:57 PM
 */

#ifndef SIGNALIMPL_HPP
#define SIGNALIMPL_HPP

#include <functional>
#include <map>
#include <unordered_map>
#include <atomic>
#include <mutex>
#include <shared_mutex>
#include <condition_variable>
#include <thread>
#include <utility>
#include <type_traits>

#include "BSignals/details/MPSCQueue.hpp"
#include "BSignals/details/WheeledThreadPool.h"
#include "BSignals/details/TaskQueueProcessor.hpp"
#include "BSignals/details/Semaphore.h"

namespace BSignals{ namespace details{

//These executors determine how message emission to a slot occurs
    // SYNCHRONOUS:
    // Emission occurs synchronously.
    // When emit returns, all connected slots have been invoked and returned.
    // This method is preferred when connected functions have short execution
    // time, quick emission is required, and/or when it is necessary to know
    // that the function has returned before proceeding.

    // ASYNCHRONOUS:
    // Emission occurs asynchronously. A detached thread is spawned on emission.
    // When emit returns, the thread has been spawned. The thread automatically
    // destructs when the connected function returns.
    // This method is recommended when connected functions have long execution
    // time and are independent.

    // STRAND:
    // Emission occurs asynchronously.
    // On connection a dedicated thread (per slot) is spawned to wait for new messages.
    // Emitted parameters are bound to the mapped function and enqueued on the
    // waiting thread. These messages are then processed synchronously in the
    // spawned thread.
    // This method is recommended when connected functions have longer execution
    // time, the overhead of creating/destroying a thread for each slot would be
    // unperformant, and/or connected functions need to be processed in order
    // of arrival (FIFO).

    // THREAD POOLED:
    // Emission occurs asynchronously.
    // On connection, if it is the first thread pooled function by any signal,
    // the thread pool is initialized with 8 threads, all listening for queued
    // emissions. The number of threads in the pool is not currently run-time
    // configurable but may be in the future.
    // Emitted parameters are bound to the mapped function and enqueued on the
    // one of the waiting threads. These messages are then processed when the
    // relevant queue is consumed by the mapped thread pool.
    // This method is recommended when connected functions have longer execution
    // time, the overhead of creating/destroying a thread for each slot would be
    // unperformant, the overhead of a waiting thread for each slot is
    // unnecessary, and/or connected functions do NOT need to be processed in
    // order of arrival.

    // DEFERRED_SYNCHRONOUS:
    // Emissions are queued up to be manually invoked through the invokeDeferred function
//
enum class ExecutorScheme{
    SYNCHRONOUS,
    DEFERRED_SYNCHRONOUS,
    ASYNCHRONOUS,
    STRAND,
    THREAD_POOLED
};

template <typename... Args>
class SignalImpl {
public:
    SignalImpl() = default;

    SignalImpl(bool enforceThreadSafety)
        : enableEmissionGuard{enforceThreadSafety} {}

    SignalImpl(uint32_t maxAsyncThreads)
        : sem{maxAsyncThreads} {}

    SignalImpl(bool enforceThreadSafety, uint32_t maxAsyncThreads)
        : enableEmissionGuard{enforceThreadSafety}, sem{maxAsyncThreads} {}

    ~SignalImpl(){
        disconnectAllSlots();
        sem.acquireAll();
    }

    template<typename F, typename C>
    int connectMemberSlot(const ExecutorScheme &scheme, F&& function, C&& instance) const {
        //type check assertions
        static_assert(std::is_member_function_pointer<F>::value, "function is not a member function");
        static_assert(std::is_object<std::remove_reference<C>>::value, "instance is not a class object");

        //Construct a bound function from the function pointer and object
        auto boundFunc = objectBind(function, instance);
        return connectSlot(scheme, boundFunc);
    }

    int connectSlot(const ExecutorScheme &scheme, std::function<void(Args...)> slot) const {
        uint32_t id = currentId.fetch_add(1);
        if (enableEmissionGuard){
            std::unique_lock<std::shared_timed_mutex> lock(backBufferLock);
            connectBuffer.emplace(id, ConnectDescriptor{scheme, slot});
            return id;
        }
        else{
            return connectSlotFunction(id, scheme, slot);
        }
    }

    void disconnectSlot(const int &id) const{
        if (enableEmissionGuard){
            std::unique_lock<std::shared_timed_mutex> lock(backBufferLock);
            disconnectBuffer.insert(id);
        }
        else{
            disconnectSlotFunction(id);
        }
    }

    void disconnectAllSlots() const{
        std::unique_lock<std::shared_timed_mutex> lock(signalLock);
        for (auto &q : strandQueues){
            q.second.enqueue(nullptr);
        }
        for (auto &t : strandThreads){
            t.second.join();
        }
        strandThreads.clear();
        strandQueues.clear();

        slots.clear();
    }

    void emitSignal(const Args &... p) const {
        enableEmissionGuard ? emitSignalThreadSafe(p...) : emitSignalUnsafe(p...);
    }

    void invokeDeferred() const{
        std::pair<std::function<void()>, uint32_t> deferredInvocation;
        while (deferredQueue.dequeue(deferredInvocation)){
            if (!enableEmissionGuard || getIsStillConnectedFromExecutor(deferredInvocation.second)){
                deferredInvocation.first();
            }
        }
    }

    void operator()(const Args &... p) const{
        emitSignal(p...);
    }

private:
    SignalImpl<Args...>(const SignalImpl<Args...>& that) = delete;
    void operator=(const SignalImpl<Args...>&) = delete;

    inline void disconnectSlotFunction(const int &id) const{
        std::unique_lock<std::shared_timed_mutex> lock(signalLock);
        if (!slots.count(id)) return;
        auto &entry = slots[id];
        if (entry.scheme == ExecutorScheme::STRAND){
            strandQueues[id].enqueue(nullptr);
            strandThreads[id].join();
            strandThreads.erase(id);
            strandQueues.erase(id);
        }
        slots.erase(id);
    }

    inline int connectSlotFunction(const int &id, const ExecutorScheme &scheme, std::function<void(Args...)> slot) const {
        std::unique_lock<std::shared_timed_mutex> lock(signalLock);
        slots.emplace(id, ConnectDescriptor{scheme, slot});
        if (scheme == ExecutorScheme::STRAND){
            strandQueues[id];
            strandThreads.emplace(id, std::thread(&SignalImpl::queueListener, this, id));
        }
        else if (scheme == ExecutorScheme::THREAD_POOLED){
            WheeledThreadPool::startup();
        }
        return (int)id;
    }

    inline void invokeRelevantExecutor(const ExecutorScheme &scheme, const uint32_t &id, const std::function<void(Args...)> &slot, const Args &... p) const{
        switch(scheme){
            case(ExecutorScheme::DEFERRED_SYNCHRONOUS):
                enqueueDeferred(id, slot, p...);
                return;
            case(ExecutorScheme::SYNCHRONOUS):
                runSynchronous(id, slot, p...);
                return;
            case(ExecutorScheme::ASYNCHRONOUS):
                runAsynchronous(id, slot, p...);
                return;
            case(ExecutorScheme::STRAND):
                runStrands(id, slot, p...);
                return;
            case(ExecutorScheme::THREAD_POOLED):
                runThreadPooled(id, slot, p...);
                return;
        }
    }

    inline void emitSignalUnsafe(const Args &... p) const {
        for (auto const &kvpair : slots){
            invokeRelevantExecutor(kvpair.second.scheme, kvpair.first, kvpair.second.slot, p...);
        }
    }

    inline bool getIsStillConnected(int const &id) const{
        std::shared_lock<std::shared_timed_mutex> lock(backBufferLock);
        return (!disconnectBuffer.count(id));
    }

    inline bool getIsStillConnectedFromExecutor(int const &id) const{
        std::shared_lock<std::shared_timed_mutex> lock(backBufferLock);
        std::shared_lock<std::shared_timed_mutex> lockSignal(signalLock);
        return (!disconnectBuffer.count(id) && slots.count(id));
    }

    inline bool getHasWaitingDisconnects() const{
        std::shared_lock<std::shared_timed_mutex> lock(backBufferLock);
        return (!disconnectBuffer.empty());
    }

    inline bool getHasWaitingConnects() const{
        std::shared_lock<std::shared_timed_mutex> lock(backBufferLock);
        return (!connectBuffer.empty());
    }

    inline bool getHasWaitingConnectsOrDisconnects() const{
        std::shared_lock<std::shared_timed_mutex> lock(backBufferLock);
        return (!connectBuffer.empty() | !disconnectBuffer.empty());
    }

    inline void emitSignalThreadSafe(const Args &... p) const {
        if (getHasWaitingConnectsOrDisconnects()){
            std::unique_lock<std::shared_timed_mutex> bbLock(backBufferLock);
            while (!connectBuffer.empty()){
                const std::pair<uint32_t, ConnectDescriptor> current = *connectBuffer.begin();
                connectBuffer.erase(connectBuffer.begin());
                //if there is another emit attempting to connect from inside a
                //synchronous slot function it is going to attempt to push to the
                //backbuffer while holding a signal shared mutex. This will
                //cause a deadlock. Hence we need to release the backBufferLock
                //for the duration of the connect
                bbLock.unlock();
                connectSlotFunction(current.first, current.second.scheme, current.second.slot);
                bbLock.lock();
            }
            while (!disconnectBuffer.empty()){
                const uint32_t disconnectId = *disconnectBuffer.begin();
                disconnectBuffer.erase(disconnectBuffer.begin());
                //Same as above, need to relinquish the lock while we disconnect
                //to avoid deadlock
                bbLock.unlock();
                disconnectSlotFunction(disconnectId);
                bbLock.lock();
            }
        }
        std::shared_lock<std::shared_timed_mutex> lock(signalLock);
        for (auto const &kvpair : slots){
            if (getIsStillConnected(kvpair.first)){
                invokeRelevantExecutor(kvpair.second.scheme, kvpair.first, kvpair.second.slot, p...);
            }
        }
    }

    inline void runThreadPooled(const uint32_t &id, const std::function<void(Args...)> &function, const Args &... p) const {
        WheeledThreadPool::run([this, &id, &function, p...](){
            if (!enableEmissionGuard || getIsStillConnectedFromExecutor(id))
                function(p...);
        });
    }

    inline void runAsynchronous(const uint32_t &id, const std::function<void(Args...)> &function, const Args &... p) const {
        sem.acquire();
        std::thread slotThread([this, &id, &function, p...](){
            if (!enableEmissionGuard || getIsStillConnectedFromExecutor(id))
                function(p...);
            sem.release();
        });
        slotThread.detach();
    }

    inline void runStrands(const uint32_t &id, const std::function<void(Args...)> &function, const Args &... p) const{
        //bind the function arguments to the function using a lambda and store
        //the newly bound function. This changes the function signature in the
        //resultant map, there are no longer any parameters in the bound function
        strandQueues[id].enqueue([&function, p...](){function(p...);});
    }

    inline void runSynchronous(const uint32_t &id, const std::function<void(Args...)> &function, const Args &... p) const{
        function(p...);
    }

    inline void enqueueDeferred(const uint32_t &id, const std::function<void(Args...)> &function, const Args &... p) const{
        deferredQueue.enqueue({[&function, p...](){function(p...);}, id});
    }

    //Reference to instance
    template<typename F, typename I>
    std::function<void(Args...)> objectBind(F&& function, I&& instance) const {
        return[=, &instance](Args... args){
            (instance.*function)(args...);
        };
    }

    //Pointer to instance
    template<typename F, typename I>
    std::function<void(Args...)> objectBind(F&& function, I* instance) const {
        return objectBind(function, *instance);
    }

    void queueListener(const uint32_t &id) const{
        auto &q = strandQueues[id];
        std::function<void()> func = [](){};
        auto maxWait = WheeledThreadPool::getMaxWait();
        std::chrono::duration<double> waitTime = std::chrono::nanoseconds(1);
        while (func){
            if (q.dequeue(func)){
                if (func) func();
                waitTime = std::chrono::nanoseconds(1);
            }
            else{
                std::this_thread::sleep_for(waitTime);
                    waitTime*=2;
            }
            if (waitTime > maxWait){
                q.blockingDequeue(func);
                if (func) func();
                waitTime = std::chrono::nanoseconds(1);
            }
        }
    }

    //Shared mutex for thread safety
    //Emit acquires shared lock, connect/disconnect acquires unique lock
    mutable std::shared_timed_mutex signalLock;

    //Atomically incremented slotId
    mutable std::atomic<uint32_t> currentId {0};

    //Async Emit Semaphore
    mutable Semaphore sem {1024};

    //EmissionGuard determines if it is necessary to guard emission with a shared mutex
    //This is only required if connection/disconnection could be interleaved with emission
    const bool enableEmissionGuard {false};

    //Strand Queues and Threads
    mutable std::map<uint32_t, MPSCQueue<std::function<void()>>> strandQueues;
    mutable std::map<uint32_t, std::thread> strandThreads;

    //Deferred invocation queue
    mutable MPSCQueue<std::pair<std::function<void()>, uint32_t>> deferredQueue;

    struct ConnectDescriptor{
        ExecutorScheme scheme;
        std::function<void(Args...)> slot;
    };

    //Slot Map
    mutable std::map<uint32_t, ConnectDescriptor> slots;

    //Disconnect backbuffer
    mutable std::set<uint32_t> disconnectBuffer;

    //Connect backbuffer
    mutable std::map<uint32_t, ConnectDescriptor> connectBuffer;

    //Backbuffer lock
    mutable std::shared_timed_mutex backBufferLock;
};

}}

#endif /* SIGNALIMPL_HPP */
<|endoftext|>


<commit_before>
#include <chrono>
#include <fstream>
#include <unistd.h>
#include <vector>

#include <jnode.h>
#include <stdafx.h>

int main(int argc, char* argv[]) {

#if 0
  opterr = 0;
  int opt;
  while ((opt = getopt(argc, argv, "fk:g:")) != -1) {
    switch (opt) {
      case 'f':
        do_faqs = !do_faqs;
        break;
      case 'k':
        num_parts = atoll(optarg);
        break;
      case 'g':
        graph_filename = optarg;
        break;
      case '?':
        if (optopt == 'c')
          printf("Option -%c requires a long long.\n", optopt);
        else if (optopt == 'g')
          printf("Option -%c requires a string.\n", optopt);
        else
          printf("Unknown option character '\\x%x'.\n", optopt);
        return 1;
      default:
        abort();
    }
  }
#endif

  if (optind + 1 >= argc) {
    printf("USAGE: tree2adj input_tree output_graph\n");
    return 1;
  }
  char const *const tree_filename = argv[optind];
  char const *const adj_filename = argv[optind + 1];

  auto start_point = std::chrono::steady_clock::now();

  JNodeTable jnodes(tree_filename);

  auto load_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
      std::chrono::steady_clock::now() - start_point);
  printf("Loaded in: %lums\n\n", load_duration.count());

  size_t edge_cnt = 0;
  std::vector<size_t> subt(jnodes.size(), 1);
  for (jnid_t id = 0; id != jnodes.size(); ++id) {
    if (jnodes.parent(id) != INVALID_JNID) {
      ++edge_cnt;
      subt.at(jnodes.parent(id)) += subt.at(id);
    }
  }

  std::vector<size_t> supr(jnodes.size(), 1);
  for (jnid_t id = jnodes.size() - 1; id != (jnid_t) -1; --id) {
    if (jnodes.parent(id) != INVALID_JNID) {
      supr.at(id) += supr.at(jnodes.parent(id));
    }
  }

  std::ofstream adj(adj_filename);
  adj << jnodes.size() << ' ' << edge_cnt << " 011" << std::endl;
  for (jnid_t id = 0; id != jnodes.size(); ++id) {
    adj << 1; // vertex weight

    jnid_t const par_id = jnodes.parent(id);
    if (par_id != INVALID_JNID)
      adj << ' ' << par_id + 1 << ' ' << (subt.at(id) + supr.at(par_id));

    for (auto itr = jnodes.kids(id).cbegin(); itr != jnodes.kids(id).cend(); ++itr) {
      jnid_t const kid_id = *itr;
      adj << ' ' << kid_id + 1 << ' ' << (subt.at(kid_id) + supr.at(id));
    }

    adj << std::endl;
  }

  auto run_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
      (std::chrono::steady_clock::now() - start_point) - load_duration);
  printf("Finished in: %lums\n", run_duration.count());

  return 0;
}
<commit_msg>more additions to tree2adj<commit_after>
#include <chrono>
#include <fstream>
#include <unistd.h>
#include <vector>

#include <jnode.h>
#include <stdafx.h>

int main(int argc, char* argv[]) {

#if 0
  opterr = 0;
  int opt;
  while ((opt = getopt(argc, argv, "fk:g:")) != -1) {
    switch (opt) {
      case 'f':
        do_faqs = !do_faqs;
        break;
      case 'k':
        num_parts = atoll(optarg);
        break;
      case 'g':
        graph_filename = optarg;
        break;
      case '?':
        if (optopt == 'c')
          printf("Option -%c requires a long long.\n", optopt);
        else if (optopt == 'g')
          printf("Option -%c requires a string.\n", optopt);
        else
          printf("Unknown option character '\\x%x'.\n", optopt);
        return 1;
      default:
        abort();
    }
  }
#endif

  if (optind + 1 >= argc) {
    printf("USAGE: tree2adj input_tree output_graph\n");
    return 1;
  }
  char const *const tree_filename = argv[optind];
  char const *const adj_filename = argv[optind + 1];

  auto start_point = std::chrono::steady_clock::now();

  JNodeTable jnodes(tree_filename);

  auto load_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
      std::chrono::steady_clock::now() - start_point);
  printf("Loaded in: %lums\n\n", load_duration.count());

  size_t edge_count = 0;
  std::vector<size_t> edge_width(jnodes.size(), 0);
  std::vector<size_t> subt(jnodes.size(), 1);
  std::vector<size_t> supr(jnodes.size(), 1);

  for (jnid_t id = 0; id != jnodes.size(); ++id) {
    edge_width.at(id) += jnodes.pst_weight(id);
    if (jnodes.parent(id) != INVALID_JNID) {
      ++edge_count;
      edge_width.at(jnodes.parent(id)) += edge_width.at(id) - jnodes.pre_weight(id);
      subt.at(jnodes.parent(id)) += subt.at(id);
    }
  }
  for (jnid_t id = jnodes.size() - 1; id != (jnid_t) -1; --id) {
    if (jnodes.parent(id) != INVALID_JNID) {
      supr.at(id) += supr.at(jnodes.parent(id));
    }
  }

  std::ofstream adj(adj_filename);
  adj << jnodes.size() << ' ' << edge_count << " 011" << std::endl;
  for (jnid_t id = 0; id != jnodes.size(); ++id) {
    adj << 1; // vertex weight

    jnid_t const par_id = jnodes.parent(id);
    if (par_id != INVALID_JNID)
      adj << ' ' << par_id + 1 << ' ' <<
        (std::min(subt.at(id),edge_width.at(id)) + std::min(supr.at(par_id),edge_width.at(id)));

    for (auto kid_id : jnodes.kids(id))
      adj << ' ' << kid_id + 1 << ' ' <<
        (std::min(subt.at(kid_id),edge_width.at(kid_id)) + std::min(supr.at(id),edge_width.at(kid_id)));

    adj << std::endl;
  }

  auto run_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
      (std::chrono::steady_clock::now() - start_point) - load_duration);
  printf("Finished in: %lums\n", run_duration.count());

  return 0;
}
<|endoftext|>


<commit_before>//
// Copyright Copyright 2009-2021, AMT – The Association For Manufacturing Technology (“AMT”)
// All rights reserved.
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
//

#include "file_cache.hpp"

#include <boost/algorithm/string.hpp>
#include <boost/asio.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/filter/zlib.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/system/error_code.hpp>

#include <chrono>
#include <future>
#include <thread>

#include "cached_file.hpp"
#include "logging.hpp"

using namespace std;

namespace mtconnect {
  namespace rest_sink {
    FileCache::FileCache(size_t max)
      : m_mimeTypes({{{".xsl", "text/xsl"},
                      {".xml", "text/xml"},
                      {".json", "application/json"},
                      {".js", "text/javascript"},
                      {".obj", "model/obj"},
                      {".stl", "model/stl"},
                      {".css", "text/css"},
                      {".xsd", "text/xml"},
                      {".jpg", "image/jpeg"},
                      {".jpeg", "image/jpeg"},
                      {".png", "image/png"},
                      {".txt", "text/plain"},
                      {".html", "text/html"},
                      {".ico", "image/x-icon"}}}),
        m_maxCachedFileSize(max)
    {
      NAMED_SCOPE("file_cache");
    }

    namespace fs = std::filesystem;

    XmlNamespaceList FileCache::registerFiles(const string &uri, const string &pathName,
                                              const string &version)
    {
      return registerDirectory(uri, pathName, version);
    }

    // Register a file
    XmlNamespaceList FileCache::registerDirectory(const string &uri, const string &pathName,
                                                  const string &version)
    {
      XmlNamespaceList namespaces;

      try
      {
        fs::path path(pathName);

        if (!fs::exists(path))
        {
          LOG(warning) << "The following path " << pathName
                       << " cannot be found, full path: " << path;
        }
        else if (!fs::is_directory(path))
        {
          auto ns = registerFile(uri, path, version);
          if (ns)
            namespaces.emplace_back(*ns);
        }
        else
        {
          fs::path baseUri(uri, fs::path::format::generic_format);

          for (auto &file : fs::directory_iterator(path))
          {
            string name = (file.path().filename()).string();
            fs::path uri = baseUri / name;

            auto ns = registerFile(uri.generic_string(), (file.path()).string(), version);
            if (ns)
              namespaces.emplace_back(*ns);
          }
        }
      }
      catch (fs::filesystem_error e)
      {
        LOG(warning) << "The following path " << pathName << " cannot be accessed: " << e.what();
      }

      return namespaces;
    }

    std::optional<XmlNamespace> FileCache::registerFile(const std::string &uri,
                                                        const fs::path &path,
                                                        const std::string &version)
    {
      optional<XmlNamespace> ns;

      if (!fs::exists(path))
      {
        LOG(warning) << "The following path " << path
                     << " cannot be found, full path: " << fs::absolute(path);
        return nullopt;
      }
      else if (!fs::is_regular_file(path))
      {
        LOG(warning) << "The following path " << path
                     << " is not a regular file: " << fs::absolute(path);
        return nullopt;
      }

      // Make sure the uri is using /
      string gen;
      gen.resize(uri.size());
      replace_copy(uri.begin(), uri.end(), gen.begin(), L'\\', L'/');

      m_fileMap.emplace(gen, fs::absolute(path));
      string name = path.filename().string();

      // Check if the file name maps to a standard MTConnect schema file.
      if (!name.find("MTConnect") && name.substr(name.length() - 4u, 4u) == ".xsd" &&
          version == name.substr(name.length() - 7u, 3u))
      {
        string version = name.substr(name.length() - 7u, 3u);

        if (name.substr(9u, 5u) == "Error")
        {
          string urn = "urn:mtconnect.org:MTConnectError:" + version;
          ns = make_optional<XmlNamespace>(urn, uri);
        }
        else if (name.substr(9u, 7u) == "Devices")
        {
          string urn = "urn:mtconnect.org:MTConnectDevices:" + version;
          ns = make_optional<XmlNamespace>(urn, uri);
        }
        else if (name.substr(9u, 6u) == "Assets")
        {
          string urn = "urn:mtconnect.org:MTConnectAssets:" + version;
          ns = make_optional<XmlNamespace>(urn, uri);
        }
        else if (name.substr(9u, 7u) == "Streams")
        {
          string urn = "urn:mtconnect.org:MTConnectStreams:" + version;
          ns = make_optional<XmlNamespace>(urn, uri);
        }
      }

      return ns;
    }

    CachedFilePtr FileCache::redirect(const std::string &name, const Directory &dir)
    {
      static const char *body = R"(
<title>301 Moved Permanently</title>

301 Moved Permanently

MTConnect Agent

)";

      auto file = make_shared<CachedFile>(body, strlen(body), "text/html"s);
      file->m_redirect = dir.first + "/" + dir.second.second;
      m_fileCache.insert_or_assign(name, file);
      return file;
    }

    void FileCache::compressFile(CachedFilePtr file, boost::asio::io_context *context)
    {
      NAMED_SCOPE("FileCache::compressFile")

      namespace fs = std::filesystem;
      namespace io = boost::iostreams;
      using namespace std::chrono_literals;

      fs::path zipped(file->m_path.string() + ".gz");

      if (!fs::exists(zipped))
      {
        promise<bool> promise;
        auto future = promise.get_future();

        thread work([file, &zipped, &promise, context] {
          try
          {
            NAMED_SCOPE("work");
            LOG(debug) << "gzipping " << file->m_path << " to " << zipped;

            ifstream input(file->m_path, ios_base::in | ios_base::binary);

            io::filtering_ostream output;
            output.push(io::gzip_compressor(io::gzip_params(io::gzip::best_compression)));
            output.push(io::file_sink(zipped, ios_base::out | ios_base::binary));

            io::copy(input, output);

            promise.set_value(true);

            LOG(debug) << "done";
          }
          catch (...)
          {
            LOG(error) << "Error occurred compressing file " << file->m_path;
            promise.set_exception(std::current_exception());
          }

          if (context != nullptr)
          {
            boost::asio::post(*context, [] {});
          }
        });

        if (context)
        {
          while (future.wait_for(1ms) == std::future_status::timeout)
            context->run_one_for(1s);
        }

        try
        {
          if (future.get())
            file->m_pathGz.emplace(zipped);
        }
        catch (std::runtime_error &e)
        {
          LOG(error) << "Error occurred compressing: " << e.what();
        }
        catch (...)
        {
          LOG(error) << "Error occurred gettting future for " << file->m_path;
        }

        work.join();
      }
      else if (!file->m_pathGz)
      {
        file->m_pathGz.emplace(zipped);
      }
    }

    CachedFilePtr FileCache::findFileInDirectories(const std::string &name)
    {
      namespace fs = std::filesystem;

      for (const auto &dir : m_directories)
      {
        if (boost::starts_with(name, dir.first))
        {
          auto fileName = boost::erase_first_copy(name, dir.first);
          if (fileName.empty())
          {
            return redirect(name, dir);
          }

          if (fileName[0] == '/')
            fileName.erase(0, 1);

          if (fileName.empty())
          {
            fileName = dir.second.second;
          }

          optional<string> contentEncoding;
          fs::path path {dir.second.first / fileName};

          if (fs::exists(path))
          {
            auto size = fs::file_size(path);
            auto ext = path.extension().string();

            auto file =
                make_shared<CachedFile>(path, getMimeType(ext), size <= m_maxCachedFileSize, size);

            m_fileCache.insert_or_assign(name, file);
            return file;
          }
        }
      }

      LOG(warning) << "Cannot find file: " << name;

      return nullptr;
    }

    CachedFilePtr FileCache::getFile(const std::string &name,
                                     const std::optional<std::string> acceptEncoding,
                                     boost::asio::io_context *context)
    {
      namespace fs = std::filesystem;

      try
      {
        CachedFilePtr file;

        auto cached = m_fileCache.find(name);
        if (cached != m_fileCache.end())
        {
          auto fp = cached->second;
          if (!fp->m_cached || fp->m_redirect)
          {
            file = fp;
          }
          else
          {
            // Cleanup files if they have changed since last cached
            // Also remove any gzipped content as well
            auto lastWrite = std::filesystem::last_write_time(fp->m_path);
            if (lastWrite == fp->m_lastWrite)
              file = fp;
            else if (file->m_pathGz && fs::exists(*file->m_pathGz))
              fs::remove(*file->m_pathGz);
          }
        }

        if (!file)
        {
          auto path = m_fileMap.find(name);
          if (path != m_fileMap.end())
          {
            auto ext = path->second.extension().string();
            auto size = fs::file_size(path->second);
            file = make_shared<CachedFile>(path->second, getMimeType(ext),
                                           size <= m_maxCachedFileSize, size);
            m_fileCache.insert_or_assign(name, file);
          }
          else
          {
            file = findFileInDirectories(name);
          }
        }

        if (file)
        {
          if (acceptEncoding && acceptEncoding->find("gzip") != string::npos &&
              file->m_size >= m_minCompressedFileSize)
          {
            compressFile(file, context);
          }
        }
        else
        {
          LOG(warning) << "Cannot find file: " << name;
        }

        return file;
      }
      catch (fs::filesystem_error e)
      {
        LOG(warning) << "Cannot open file " << name << ": " << e.what();
      }

      return nullptr;
    }

    void FileCache::addDirectory(const std::string &uri, const std::string &pathName,
                                 const std::string &index)
    {
      fs::path path(pathName);
      if (fs::exists(path))
      {
        string root(uri);
        if (boost::ends_with(root, "/"))
        {
          boost::erase_last(root, "/");
        }
        m_directories.emplace(root, make_pair(fs::canonical(path), index));
      }
      else
      {
        LOG(warning) << "Cannot find path " << pathName << " for " << uri;
      }
    }
  }  // namespace rest_sink
}  // namespace mtconnect
<commit_msg>Fixed windows build issue with file_sink and std paths<commit_after>//
// Copyright Copyright 2009-2021, AMT – The Association For Manufacturing Technology (“AMT”)
// All rights reserved.
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
//

#include "file_cache.hpp"

#include <boost/algorithm/string.hpp>
#include <boost/asio.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/filter/zlib.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/system/error_code.hpp>

#include <chrono>
#include <future>
#include <thread>

#include "cached_file.hpp"
#include "logging.hpp"

using namespace std;

namespace mtconnect {
  namespace rest_sink {
    FileCache::FileCache(size_t max)
      : m_mimeTypes({{{".xsl", "text/xsl"},
                      {".xml", "text/xml"},
                      {".json", "application/json"},
                      {".js", "text/javascript"},
                      {".obj", "model/obj"},
                      {".stl", "model/stl"},
                      {".css", "text/css"},
                      {".xsd", "text/xml"},
                      {".jpg", "image/jpeg"},
                      {".jpeg", "image/jpeg"},
                      {".png", "image/png"},
                      {".txt", "text/plain"},
                      {".html", "text/html"},
                      {".ico", "image/x-icon"}}}),
        m_maxCachedFileSize(max)
    {
      NAMED_SCOPE("file_cache");
    }

    namespace fs = std::filesystem;

    XmlNamespaceList FileCache::registerFiles(const string &uri, const string &pathName,
                                              const string &version)
    {
      return registerDirectory(uri, pathName, version);
    }

    // Register a file
    XmlNamespaceList FileCache::registerDirectory(const string &uri, const string &pathName,
                                                  const string &version)
    {
      XmlNamespaceList namespaces;

      try
      {
        fs::path path(pathName);

        if (!fs::exists(path))
        {
          LOG(warning) << "The following path " << pathName
                       << " cannot be found, full path: " << path;
        }
        else if (!fs::is_directory(path))
        {
          auto ns = registerFile(uri, path, version);
          if (ns)
            namespaces.emplace_back(*ns);
        }
        else
        {
          fs::path baseUri(uri, fs::path::format::generic_format);

          for (auto &file : fs::directory_iterator(path))
          {
            string name = (file.path().filename()).string();
            fs::path uri = baseUri / name;

            auto ns = registerFile(uri.generic_string(), (file.path()).string(), version);
            if (ns)
              namespaces.emplace_back(*ns);
          }
        }
      }
      catch (fs::filesystem_error e)
      {
        LOG(warning) << "The following path " << pathName << " cannot be accessed: " << e.what();
      }

      return namespaces;
    }

    std::optional<XmlNamespace> FileCache::registerFile(const std::string &uri,
                                                        const fs::path &path,
                                                        const std::string &version)
    {
      optional<XmlNamespace> ns;

      if (!fs::exists(path))
      {
        LOG(warning) << "The following path " << path
                     << " cannot be found, full path: " << fs::absolute(path);
        return nullopt;
      }
      else if (!fs::is_regular_file(path))
      {
        LOG(warning) << "The following path " << path
                     << " is not a regular file: " << fs::absolute(path);
        return nullopt;
      }

      // Make sure the uri is using /
      string gen;
      gen.resize(uri.size());
      replace_copy(uri.begin(), uri.end(), gen.begin(), L'\\', L'/');

      m_fileMap.emplace(gen, fs::absolute(path));
      string name = path.filename().string();

      // Check if the file name maps to a standard MTConnect schema file.
      if (!name.find("MTConnect") && name.substr(name.length() - 4u, 4u) == ".xsd" &&
          version == name.substr(name.length() - 7u, 3u))
      {
        string version = name.substr(name.length() - 7u, 3u);

        if (name.substr(9u, 5u) == "Error")
        {
          string urn = "urn:mtconnect.org:MTConnectError:" + version;
          ns = make_optional<XmlNamespace>(urn, uri);
        }
        else if (name.substr(9u, 7u) == "Devices")
        {
          string urn = "urn:mtconnect.org:MTConnectDevices:" + version;
          ns = make_optional<XmlNamespace>(urn, uri);
        }
        else if (name.substr(9u, 6u) == "Assets")
        {
          string urn = "urn:mtconnect.org:MTConnectAssets:" + version;
          ns = make_optional<XmlNamespace>(urn, uri);
        }
        else if (name.substr(9u, 7u) == "Streams")
        {
          string urn = "urn:mtconnect.org:MTConnectStreams:" + version;
          ns = make_optional<XmlNamespace>(urn, uri);
        }
      }

      return ns;
    }

    CachedFilePtr FileCache::redirect(const std::string &name, const Directory &dir)
    {
      static const char *body = R"(
<title>301 Moved Permanently</title>

301 Moved Permanently

MTConnect Agent

)";

      auto file = make_shared<CachedFile>(body, strlen(body), "text/html"s);
      file->m_redirect = dir.first + "/" + dir.second.second;
      m_fileCache.insert_or_assign(name, file);
      return file;
    }

    void FileCache::compressFile(CachedFilePtr file, boost::asio::io_context *context)
    {
      NAMED_SCOPE("FileCache::compressFile")

      namespace fs = std::filesystem;
      namespace io = boost::iostreams;
      using namespace std::chrono_literals;

      fs::path zipped(file->m_path.string() + ".gz");

      if (!fs::exists(zipped))
      {
        promise<bool> promise;
        auto future = promise.get_future();

        thread work([file, &zipped, &promise, context] {
          try
          {
            NAMED_SCOPE("work");
            LOG(debug) << "gzipping " << file->m_path << " to " << zipped;

            ifstream input(file->m_path, ios_base::in | ios_base::binary);

            io::filtering_ostream output;
            output.push(io::gzip_compressor(io::gzip_params(io::gzip::best_compression)));
            output.push(io::file_sink(zipped.string(), ios_base::out | ios_base::binary));

            io::copy(input, output);

            promise.set_value(true);

            LOG(debug) << "done";
          }
          catch (...)
          {
            LOG(error) << "Error occurred compressing file " << file->m_path;
            promise.set_exception(std::current_exception());
          }

          if (context != nullptr)
          {
            boost::asio::post(*context, [] {});
          }
        });

        if (context)
        {
          while (future.wait_for(1ms) == std::future_status::timeout)
            context->run_one_for(1s);
        }

        try
        {
          if (future.get())
            file->m_pathGz.emplace(zipped);
        }
        catch (std::runtime_error &e)
        {
          LOG(error) << "Error occurred compressing: " << e.what();
        }
        catch (...)
        {
          LOG(error) << "Error occurred gettting future for " << file->m_path;
        }

        work.join();
      }
      else if (!file->m_pathGz)
      {
        file->m_pathGz.emplace(zipped);
      }
    }

    CachedFilePtr FileCache::findFileInDirectories(const std::string &name)
    {
      namespace fs = std::filesystem;

      for (const auto &dir : m_directories)
      {
        if (boost::starts_with(name, dir.first))
        {
          auto fileName = boost::erase_first_copy(name, dir.first);
          if (fileName.empty())
          {
            return redirect(name, dir);
          }

          if (fileName[0] == '/')
            fileName.erase(0, 1);

          if (fileName.empty())
          {
            fileName = dir.second.second;
          }

          optional<string> contentEncoding;
          fs::path path {dir.second.first / fileName};

          if (fs::exists(path))
          {
            auto size = fs::file_size(path);
            auto ext = path.extension().string();

            auto file =
                make_shared<CachedFile>(path, getMimeType(ext), size <= m_maxCachedFileSize, size);

            m_fileCache.insert_or_assign(name, file);
            return file;
          }
        }
      }

      LOG(warning) << "Cannot find file: " << name;

      return nullptr;
    }

    CachedFilePtr FileCache::getFile(const std::string &name,
                                     const std::optional<std::string> acceptEncoding,
                                     boost::asio::io_context *context)
    {
      namespace fs = std::filesystem;

      try
      {
        CachedFilePtr file;

        auto cached = m_fileCache.find(name);
        if (cached != m_fileCache.end())
        {
          auto fp = cached->second;
          if (!fp->m_cached || fp->m_redirect)
          {
            file = fp;
          }
          else
          {
            // Cleanup files if they have changed since last cached
            // Also remove any gzipped content as well
            auto lastWrite = std::filesystem::last_write_time(fp->m_path);
            if (lastWrite == fp->m_lastWrite)
              file = fp;
            else if (file->m_pathGz && fs::exists(*file->m_pathGz))
              fs::remove(*file->m_pathGz);
          }
        }

        if (!file)
        {
          auto path = m_fileMap.find(name);
          if (path != m_fileMap.end())
          {
            auto ext = path->second.extension().string();
            auto size = fs::file_size(path->second);
            file = make_shared<CachedFile>(path->second, getMimeType(ext),
                                           size <= m_maxCachedFileSize, size);
            m_fileCache.insert_or_assign(name, file);
          }
          else
          {
            file = findFileInDirectories(name);
          }
        }

        if (file)
        {
          if (acceptEncoding && acceptEncoding->find("gzip") != string::npos &&
              file->m_size >= m_minCompressedFileSize)
          {
            compressFile(file, context);
          }
        }
        else
        {
          LOG(warning) << "Cannot find file: " << name;
        }

        return file;
      }
      catch (fs::filesystem_error e)
      {
        LOG(warning) << "Cannot open file " << name << ": " << e.what();
      }

      return nullptr;
    }

    void FileCache::addDirectory(const std::string &uri, const std::string &pathName,
                                 const std::string &index)
    {
      fs::path path(pathName);
      if (fs::exists(path))
      {
        string root(uri);
        if (boost::ends_with(root, "/"))
        {
          boost::erase_last(root, "/");
        }
        m_directories.emplace(root, make_pair(fs::canonical(path), index));
      }
      else
      {
        LOG(warning) << "Cannot find path " << pathName << " for " << uri;
      }
    }
  }  // namespace rest_sink
}  // namespace mtconnect
<|endoftext|>


<commit_before>#include "TextureFont.hpp"
#include FT_LCD_FILTER_H

using namespace rffalcon;

#define POINT_RES 64
#define DPI 72

TextureFont::TextureFont(std::shared_ptr<TextureAtlas> atlas, const float pointSize, const std::string &filename)
	: _atlas(atlas), _pointSize(pointSize), _filename(filename)
{
	_lcdWeights[0] = 0x10;
	_lcdWeights[0] = 0x40;
	_lcdWeights[0] = 0x70;
	_lcdWeights[0] = 0x40;
	_lcdWeights[0] = 0x10;
	_initialize();
}

TextureFont::~TextureFont()
{
}

float TextureFont::getHeight() const
{
	return _height;
}

void TextureFont::loadGlyphs(const std::string &text)
{
	int width = _atlas->getWidth();
	int height = _atlas->getHeight();
	int depth = _atlas->getDepth();

	FT_Library library;
	FT_Face face;
	_loadFace(&library, &face);

	FT_Int32 flags = _getFlags();
	int length = text.length();
	// Load the glyph for each character in the string
	for (int i = 0; i < length; ++i)
	{
		char charCode = text[i];
		if (_shouldLoadGlyph(charCode))
		{
			_setFiltering(&library);
			FT_UInt glyphIndex = FT_Get_Char_Index(face, charCode);
		}
	}
}

void TextureFont::_setFiltering(FT_Library *library)
{
	if (_atlas->getDepth() == 3)
	{
		FT_Library_SetLcdFilter(*library, FT_LCD_FILTER_LIGHT);
		if (_filtering)
		{
			FT_Library_SetLcdFilterWeights(*library, _lcdWeights);
		}
	}
}

FT_Int32 TextureFont::_getFlags()
{
	FT_Int32 flags = 0;
	if (_outlineType > 0)
	{
		flags |= FT_LOAD_NO_BITMAP;
	}
	else
	{
		flags |= FT_LOAD_RENDER;
	}

	if (!_hinting)
	{
		flags |= FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT;
	}
	else
	{
		flags |= FT_LOAD_FORCE_AUTOHINT;
	}

	if (_atlas->getDepth() == 3)
	{
		flags |= FT_LOAD_TARGET_LCD;
	}

	return flags;
}

bool TextureFont::_shouldLoadGlyph(const char charCode)
{
	// Skip glyphs that have already been loaded
	bool skip = false;
	int sizeGlyphs = static_cast<int>(_glyphs.size());
	for (int j = 0; j < sizeGlyphs; ++j)
	{
		auto glyph = _glyphs[j];
		if (glyph->charCode == charCode &&
			(glyph->outlineType == _outlineType && glyph->outlineThickness == _outlineThickness))
		{
			skip = true;
			break;
		}
	}

	return skip;
}

void TextureFont::_initialize()
{
	FT_Library library;
	FT_Face face;
	FT_Size_Metrics metrics;

	FT_Error error = FT_Init_FreeType(&library);
	if (error != FT_Err_Ok)
	{
		throw new std::exception();
	}

	_loadFace(&library, &face, _pointSize * 100.0f);
	metrics = face->size->metrics;
	_ascender = (metrics.ascender >> 6) / 100.0f;
	_descender = (metrics.descender >> 6) / 100.0f;
	_height = (metrics.height >> 6) / 100.0f;
	_linegap = _height - _ascender + _descender;

	FT_Done_Face(face);
	FT_Done_FreeType(library);
}

void TextureFont::_loadFace(FT_Library *library, FT_Face *face, float pointSize)
{
	FT_Error error = FT_New_Face(*library, _filename.c_str(), 0, face);
	if (error != FT_Err_Ok)
	{
		throw new std::exception();
	}

	error = FT_Select_Charmap(*face, FT_ENCODING_UNICODE);
	if (error)
	{
		throw new std::exception();
	}

	error = FT_Set_Char_Size(*face, (int)(pointSize * POINT_RES), 0, DPI * POINT_RES, DPI);
	if (error)
	{
		throw new std::exception();
	}

	FT_Matrix matrix = {
		(int)((1.0 / POINT_RES) * 0x10000L),
		0,
		0,
		0x10000L
	};

	FT_Set_Transform(*face, &matrix, nullptr);
}

void TextureFont::_loadFace(FT_Library *library, FT_Face *face)
{
	_loadFace(library, face, _pointSize);
}<commit_msg>Load face glyph and throw on error, call done at end<commit_after>#include "TextureFont.hpp"
#include FT_LCD_FILTER_H

using namespace rffalcon;

#define POINT_RES 64
#define DPI 72

TextureFont::TextureFont(std::shared_ptr<TextureAtlas> atlas, const float pointSize, const std::string &filename)
	: _atlas(atlas), _pointSize(pointSize), _filename(filename)
{
	_lcdWeights[0] = 0x10;
	_lcdWeights[0] = 0x40;
	_lcdWeights[0] = 0x70;
	_lcdWeights[0] = 0x40;
	_lcdWeights[0] = 0x10;
	_initialize();
}

TextureFont::~TextureFont()
{
}

float TextureFont::getHeight() const
{
	return _height;
}

void TextureFont::loadGlyphs(const std::string &text)
{
	int width = _atlas->getWidth();
	int height = _atlas->getHeight();
	int depth = _atlas->getDepth();

	FT_Error error;
	FT_Library library;
	FT_Face face;
	_loadFace(&library, &face);

	FT_Int32 flags = _getFlags();
	int length = text.length();
	// Load the glyph for each character in the string
	for (int i = 0; i < length; ++i)
	{
		char charCode = text[i];
		if (_shouldLoadGlyph(charCode))
		{
			_setFiltering(&library);
			FT_UInt glyphIndex = FT_Get_Char_Index(face, charCode);
			error = FT_Load_Glyph(face, glyphIndex, flags);
			if (error) { throw new std::exception(); }
		}
	}

	FT_Done_Face(face);
	FT_Done_FreeType(library);
}

void TextureFont::_setFiltering(FT_Library *library)
{
	if (_atlas->getDepth() == 3)
	{
		FT_Library_SetLcdFilter(*library, FT_LCD_FILTER_LIGHT);
		if (_filtering)
		{
			FT_Library_SetLcdFilterWeights(*library, _lcdWeights);
		}
	}
}

FT_Int32 TextureFont::_getFlags()
{
	FT_Int32 flags = 0;
	if (_outlineType > 0)
	{
		flags |= FT_LOAD_NO_BITMAP;
	}
	else
	{
		flags |= FT_LOAD_RENDER;
	}

	if (!_hinting)
	{
		flags |= FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT;
	}
	else
	{
		flags |= FT_LOAD_FORCE_AUTOHINT;
	}

	if (_atlas->getDepth() == 3)
	{
		flags |= FT_LOAD_TARGET_LCD;
	}

	return flags;
}

bool TextureFont::_shouldLoadGlyph(const char charCode)
{
	// Skip glyphs that have already been loaded
	bool skip = false;
	int sizeGlyphs = static_cast<int>(_glyphs.size());
	for (int j = 0; j < sizeGlyphs; ++j)
	{
		auto glyph = _glyphs[j];
		if (glyph->charCode == charCode &&
			(glyph->outlineType == _outlineType && glyph->outlineThickness == _outlineThickness))
		{
			skip = true;
			break;
		}
	}

	return skip;
}

void TextureFont::_initialize()
{
	FT_Library library;
	FT_Face face;
	FT_Size_Metrics metrics;

	FT_Error error = FT_Init_FreeType(&library);
	if (error != FT_Err_Ok)
	{
		throw new std::exception();
	}

	_loadFace(&library, &face, _pointSize * 100.0f);
	metrics = face->size->metrics;
	_ascender = (metrics.ascender >> 6) / 100.0f;
	_descender = (metrics.descender >> 6) / 100.0f;
	_height = (metrics.height >> 6) / 100.0f;
	_linegap = _height - _ascender + _descender;

	FT_Done_Face(face);
	FT_Done_FreeType(library);
}

void TextureFont::_loadFace(FT_Library *library, FT_Face *face, float pointSize)
{
	FT_Error error = FT_New_Face(*library, _filename.c_str(), 0, face);
	if (error != FT_Err_Ok)
	{
		throw new std::exception();
	}

	error = FT_Select_Charmap(*face, FT_ENCODING_UNICODE);
	if (error)
	{
		throw new std::exception();
	}

	error = FT_Set_Char_Size(*face, (int)(pointSize * POINT_RES), 0, DPI * POINT_RES, DPI);
	if (error)
	{
		throw new std::exception();
	}

	FT_Matrix matrix = {
		(int)((1.0 / POINT_RES) * 0x10000L),
		0,
		0,
		0x10000L
	};

	FT_Set_Transform(*face, &matrix, nullptr);
}

void TextureFont::_loadFace(FT_Library *library, FT_Face *face)
{
	_loadFace(library, face, _pointSize);
}<|endoftext|>


<commit_before>/*
 * CompactStorage
 *
 * A C++ library for creating a compact bitstream made up of multiple
 * different variables of variable length overlapping to save space.
 *
 * Copyright (C) 2012 Franz Liedke
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "compactstorage.h"

#include <iostream>

using namespace std;

CompactStorage::CompactStorage(int numberOfBytes)
{
	if (numberOfBytes < 1) {
		numberOfBytes = 1;
	}

	m_numBytes = numberOfBytes;
	m_usedBits = 0;
	m_curPos = 0;
	allocateBytes(numberOfBytes);
}

CompactStorage::~CompactStorage()
{
	freeBytes();
}

void CompactStorage::allocateBytes(int numberOfBytes)
{
	m_bytes = new char[numberOfBytes];

	for (int i = 0; i < numberOfBytes; i++) {
		m_bytes[i] = '\0';
	}
}

void CompactStorage::freeBytes()
{
	delete[] m_bytes;
}

void CompactStorage::reallocateBytes(int numberOfBytes)
{
	char* temp = new char[numberOfBytes];
	for (int i = 0; i < m_numBytes; i++) {
		temp[i] = m_bytes[i];
	}
	freeBytes();
	m_bytes = temp;
	m_numBytes = numberOfBytes;
}

void CompactStorage::ensureRoomFor(int bits)
{
	cout << "make sure we have room for " << bits << " bits..." << endl;
	int newLength = m_usedBits + bits;
	if (newLength > m_numBytes * 8) {
		reallocateBytes(newLength / 8 + 1);
	}
}

int CompactStorage::curByte()
{
	return m_curPos / 8;
}

int CompactStorage::curBit()
{
	return m_curPos % 8;
}

void CompactStorage::dump()
{
	for (int curByte = 0; curByte < m_numBytes; curByte++) {
		char str = m_bytes[curByte];
		for (int i = 128; i > 0; i = i/2) {
			cout << (bool) ((int) str & i);
		}
		cout << " ";
	}
	cout << endl;
	for (int i = 0; i < m_curPos + m_numBytes - 1; i++) {
		cout << " ";
	}
	cout << "^" << endl;
}

void CompactStorage::writeInt(int value, int bits)
{
	ensureRoomFor(bits);

	// Iterate over the data bits that we need to set
	for (int i = curBit(); i < curBit() + bits; i++) {
		int byte = i / 8;
		int bit = i % 8;

		// Create bitmask for the correspondent bit of the integer to store here
		int mask = 1 << (bits + curBit() - i - 1);

		// Whether to set the current bit or not
		bool set = (bool) (value & mask);
		m_bytes[byte] |= (int) set << (7 - bit);
	}

	m_curPos += bits;
	m_usedBits += bits;
}

void CompactStorage::writeBool(bool value)
{
	char byte = m_bytes[curByte()];
	byte |= (int) value << (7 - curBit());
	m_bytes[curByte()] = byte;

	m_curPos += 1;
	m_usedBits += 1;
}

int CompactStorage::readInt(int bits)
{
	// TODO: Signed vs. unsigned might cause problems here
	int value = 0;

	// Iterate over the data bits that we need to set
	for (int i = curBit(); i < curBit() + bits; i++) {
		int byte = i / 8;
		int bit = i % 8;

		// Create bitmask for the correspondent bit of the data that we want to read
		int mask = 1 << (7 - bit);

		// Whether to set the current bit or not
		bool set = (bool) (m_bytes[byte] & mask);
		value |= (int) set << (bits + curBit() - i - 1);
	}

	m_curPos += bits;
	return value;
}

bool CompactStorage::readBool()
{
	unsigned int byte = m_bytes[curByte()];
	unsigned int mask = 1 << (7 - curBit());

	m_curPos += 1;
	return (byte & mask) > 0;
}

char* CompactStorage::getContent(int & length)
{
	char* result = new char[m_numBytes];
	*result = *m_bytes;

	length = m_numBytes;

	return result;
}

void CompactStorage::reset()
{
	m_curPos = 0;
}
<commit_msg>Fix reading and writing integers.<commit_after>/*
 * CompactStorage
 *
 * A C++ library for creating a compact bitstream made up of multiple
 * different variables of variable length overlapping to save space.
 *
 * Copyright (C) 2012 Franz Liedke
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "compactstorage.h"

#include <iostream>

using namespace std;

CompactStorage::CompactStorage(int numberOfBytes)
{
	if (numberOfBytes < 1) {
		numberOfBytes = 1;
	}

	m_numBytes = numberOfBytes;
	m_usedBits = 0;
	m_curPos = 0;
	allocateBytes(numberOfBytes);
}

CompactStorage::~CompactStorage()
{
	freeBytes();
}

void CompactStorage::allocateBytes(int numberOfBytes)
{
	m_bytes = new char[numberOfBytes];

	for (int i = 0; i < numberOfBytes; i++) {
		m_bytes[i] = '\0';
	}
}

void CompactStorage::freeBytes()
{
	delete[] m_bytes;
}

void CompactStorage::reallocateBytes(int numberOfBytes)
{
	char* temp = new char[numberOfBytes];
	for (int i = 0; i < m_numBytes; i++) {
		temp[i] = m_bytes[i];
	}
	freeBytes();
	m_bytes = temp;
	m_numBytes = numberOfBytes;
}

void CompactStorage::ensureRoomFor(int bits)
{
	cout << "make sure we have room for " << bits << " bits..." << endl;
	int newLength = m_usedBits + bits;
	if (newLength > m_numBytes * 8) {
		reallocateBytes(newLength / 8 + 1);
	}
}

int CompactStorage::curByte()
{
	return m_curPos / 8;
}

int CompactStorage::curBit()
{
	return m_curPos % 8;
}

void CompactStorage::dump()
{
	for (int curByte = 0; curByte < m_numBytes; curByte++) {
		char str = m_bytes[curByte];
		for (int i = 128; i > 0; i = i/2) {
			cout << (bool) ((int) str & i);
		}
		cout << " ";
	}
	cout << endl;
	for (int i = 0; i < m_curPos + m_numBytes - 1; i++) {
		cout << " ";
	}
	cout << "^" << endl;
}

void CompactStorage::writeInt(int value, int bits)
{
	ensureRoomFor(bits);

	// Iterate over the data bits that we need to set
	for (int i = 0; i < bits; i++) {
		int byte = curByte();
		int bit = curBit();

		// Create bitmask for the correspondent bit of the integer to store here
		int mask = 1 << (bits - i - 1);

		// Whether to set the current bit or not
		bool set = (bool) (value & mask);
		m_bytes[byte] |= (char) set << (7 - bit);
		m_curPos++;
	}

	m_usedBits += bits;
}

void CompactStorage::writeBool(bool value)
{
	char byte = m_bytes[curByte()];
	byte |= (int) value << (7 - curBit());
	m_bytes[curByte()] = byte;

	m_curPos += 1;
	m_usedBits += 1;
}

int CompactStorage::readInt(int bits)
{
	// TODO: Signed vs. unsigned might cause problems here
	int value = 0;

	// Iterate over the data bits that we need to set
	for (int i = 0; i < bits; i++) {
		int byte = curByte();
		int bit = curBit();

		// Create bitmask for the correspondent bit of the data that we want to read
		int mask = 1 << (7 - bit);

		// Whether to set the current bit or not
		bool set = (bool) (m_bytes[byte] & mask);
		value |= (int) set << (bits - i - 1);
		m_curPos++;
	}

	return value;
}

bool CompactStorage::readBool()
{
	unsigned int byte = m_bytes[curByte()];
	unsigned int mask = 1 << (7 - curBit());

	m_curPos += 1;
	return (byte & mask) > 0;
}

char* CompactStorage::getContent(int & length)
{
	char* result = new char[m_numBytes];
	*result = *m_bytes;

	length = m_numBytes;

	return result;
}

void CompactStorage::reset()
{
	m_curPos = 0;
}
<|endoftext|>


<commit_before>#include "GLFWController.hpp"
#include "ModelView.hpp"
#include "AffinePoint.hpp"
#include "AffineVector.hpp"
#include "ModelViewWithShader.hpp"
#include "Block.hpp"
#include "DependencyContainer.hpp"
#include "otuchaConfig.h"

#include <memory>
#include <iostream>

using namespace otucha;

void set3DViewingInformation(double xyz[6])
{
	ModelView::setMCRegionOfInterest(xyz);

	s1::AffinePoint eye(1.0, 1.0, 1.0);
	s1::AffinePoint center(0, 0, 0);
	s1::AffineVector up(0, 1, 0);

	ModelView::setEyeCenterUp(eye, center, up);

	double zpp = -1.0;
	double zmin = -99.0;
	double zmax = -0.01;

	ModelView::setProjectionType(PERSPECTIVE);
	ModelView::setZProjectionPlane(zpp);
	ModelView::setEyeCoordinatesZMinZMax(zmin, zmax);
}

int main(int argc, char* argv[])
{
	fprintf(stdout, "%s Version %d.%d\n", argv[0], otucha_VERSION_MAJOR, otucha_VERSION_MINOR);
	GLFWController c("otucha", Controller::DEPTH);
	c.reportVersions(std::cout);
	std::string appPath(argv[0]);
	unsigned found = appPath.find_last_of("/\\");
	std::string appDir = appPath.substr(0, found + 1);
	DependencyContainer::getSingleton()->setAppDir(appDir);
	ModelViewWithShader::setShaderSources(appDir + "simple.vsh", appDir + "simple.fsh");
	c.addModel(new Block(-0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f));

	glClearColor(0.9f, 0.9f, 0.9f, 1.0f);
	double xyz[6];
	c.getOverallMCBoundingBox(xyz);
	set3DViewingInformation(xyz);

	// test console code to be removed later
	DependencyContainer::getSingleton()->getConsole()->registerCommand(L"test", [](warbler::t_consoleArgs_ptr args) {
		std::cout << "Console online" << std::endl;
	}, std::make_shared<warbler::t_consoleArgTypes>());
	DependencyContainer::getSingleton()->getConsole()->executeCommand(L"test");
	// end test console code

	auto fontFace = DependencyContainer::getSingleton()->getFontFace();

	c.run();

	return 0;
}
<commit_msg>Switch to shared_ptr in otucha main<commit_after>#include "GLFWController.hpp"
#include "ModelView.hpp"
#include "AffinePoint.hpp"
#include "AffineVector.hpp"
#include "ModelViewWithShader.hpp"
#include "Block.hpp"
#include "DependencyContainer.hpp"
#include "otuchaConfig.h"

#include <memory>
#include <iostream>

using namespace otucha;

void set3DViewingInformation(double xyz[6])
{
	ModelView::setMCRegionOfInterest(xyz);

	s1::AffinePoint eye(1.0, 1.0, 1.0);
	s1::AffinePoint center(0, 0, 0);
	s1::AffineVector up(0, 1, 0);

	ModelView::setEyeCenterUp(eye, center, up);

	double zpp = -1.0;
	double zmin = -99.0;
	double zmax = -0.01;

	ModelView::setProjectionType(PERSPECTIVE);
	ModelView::setZProjectionPlane(zpp);
	ModelView::setEyeCoordinatesZMinZMax(zmin, zmax);
}

int main(int argc, char* argv[])
{
	fprintf(stdout, "%s Version %d.%d\n", argv[0], otucha_VERSION_MAJOR, otucha_VERSION_MINOR);
	GLFWController c("otucha", Controller::DEPTH);
	c.reportVersions(std::cout);
	std::string appPath(argv[0]);
	unsigned found = appPath.find_last_of("/\\");
	std::string appDir = appPath.substr(0, found + 1);
	DependencyContainer::getSingleton()->setAppDir(appDir);
	ModelViewWithShader::setShaderSources(appDir + "simple.vsh", appDir + "simple.fsh");
	c.addModel(std::make_shared<Block>(-0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f));

	glClearColor(0.9f, 0.9f, 0.9f, 1.0f);
	double xyz[6];
	c.getOverallMCBoundingBox(xyz);
	set3DViewingInformation(xyz);

	// test console code to be removed later
	DependencyContainer::getSingleton()->getConsole()->registerCommand(L"test", [](warbler::t_consoleArgs_ptr args) {
		std::cout << "Console online" << std::endl;
	}, std::make_shared<warbler::t_consoleArgTypes>());
	DependencyContainer::getSingleton()->getConsole()->executeCommand(L"test");
	// end test console code

	auto fontFace = DependencyContainer::getSingleton()->getFontFace();

	c.run();

	return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016-2017 deepstreamHub GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <cerrno>
#include <cstdlib>
#include <cstring>

#include <new>
#include <ostream>

#include <buffer.hpp>
#include <message.hpp>
#include <parser.h>
#include <parser.hpp>
#include <scope_guard.hpp>
#include <use.hpp>
extern "C" {
#include <lexer.h>
}

#include <cassert>

namespace deepstream {
namespace parser
{

Error::Error(std::size_t offset, std::size_t size, Tag tag) :
	location_(offset, size),
	tag_(tag)
{}

std::ostream& operator<< (std::ostream& os, Error::Tag tag)
{
	switch(tag)
	{
		case Error::UNEXPECTED_TOKEN:
			os << "unexpected token";
			break;

		case Error::UNEXPECTED_EOF:
			os << "unexpected eof";
			break;

		case Error::CORRUPT_PAYLOAD:
			os << "correct payload";
			break;

		case Error::INVALID_NUMBER_OF_ARGUMENTS:
			os << "invalid number of message arguments";
			break;
	}

	return os;
}

std::ostream& operator<< (std::ostream& os, const Error& e)
{
	os << e.location() << ": " << e.tag();
	return os;
}

std::pair<MessageList, ErrorList> execute(char* p, std::size_t size)
{
	assert(p);
	assert( p[size-2] == 0 );
	assert( p[size-1] == 0 );

	yyscan_t scanner = nullptr;
	deepstream::parser::State parser(p, size-2);

	if( yylex_init(&scanner) != 0 )
	{
		if( errno == ENOMEM )
			throw std::bad_alloc();

		assert( errno == EINVAL );
		assert(0);
		throw std::invalid_argument("nullptr given to yylex_init");
	}

	assert( scanner );
	YY_BUFFER_STATE lexer_buffer = yy_scan_buffer(p, size, scanner);
	assert( lexer_buffer );

	yyset_extra( &parser, scanner );

	DEEPSTREAM_ON_EXIT( [lexer_buffer, scanner] () {
		yy_delete_buffer(lexer_buffer, scanner);
		yylex_destroy(scanner);
	} );

	int ret = 0;
	while( (ret = yylex(scanner)) != TOKEN_EOF );

	return std::make_pair(parser.messages_, parser.errors_);
}

}
}

bool is_header_token(enum deepstream_token token)
{
	int min_event_num = TOKEN_A_A;

	return token >= min_event_num;
}

deepstream_parser_state::deepstream_parser_state(const char* p, std::size_t sz):
	buffer_(p),
	buffer_size_(sz),
	tokenizing_header_(true),
	offset_(0)
{
	assert(buffer_);
}

int deepstream_parser_handle(
	deepstream::parser::State* p_state, deepstream_token token,
	const char* text, std::size_t textlen)
{
	assert( p_state );
	assert( token != TOKEN_MAXVAL );

	return p_state->handle_token(token, text, textlen);
}

int deepstream_parser_state::handle_token(
	deepstream_token token, const char* text, std::size_t textlen)
{
	assert( token != TOKEN_MAXVAL );

	assert( text );
	assert( textlen > 0 );

	assert( token != TOKEN_EOF || *text == '\0' );
	assert( token != TOKEN_EOF || textlen == 1 );
	assert( token != TOKEN_EOF || offset_ + textlen == buffer_size_ + 1 );
	assert( token == TOKEN_EOF || offset_ + textlen <= buffer_size_ );
	assert( token == TOKEN_EOF || !std::memcmp(buffer_+offset_, text, textlen));

	assert( messages_.size() <= offset_ );
	assert( errors_.size() <= offset_ );

	DEEPSTREAM_ON_EXIT([this, textlen] () {
		this->offset_ += textlen;
	} );

	if(token == TOKEN_UNKNOWN)
		handle_error(token, text, textlen);
	else if(token == TOKEN_EOF)
	{
		assert(offset_ == buffer_size_);

		if(!tokenizing_header_)
			handle_error(token, text, textlen);
	}
	else if(token == TOKEN_PAYLOAD)
		handle_payload(token, text, textlen);
	else if(token == TOKEN_MESSAGE_SEPARATOR)
		handle_message_separator(token, text, textlen);
	else if( is_header_token(token) )
		handle_header(token, text, textlen);
	else
	{
		assert(0);
	}

	return token;
}

void deepstream_parser_state::handle_error(
	deepstream_token token, const char*, std::size_t textlen)
{
	using deepstream::parser::Error;

	assert( token == TOKEN_EOF || token == TOKEN_UNKNOWN );
	assert( textlen > 0 || (textlen == 0 && token == EOF) );

	DEEPSTREAM_ON_EXIT( [this] () {
		this->tokenizing_header_ = true; // reset parser status on exit
	} );

	if( token == TOKEN_EOF )
	{
		assert( !tokenizing_header_ );
		assert( !messages_.empty() );

		messages_.pop_back();
		errors_.emplace_back(offset_, textlen, Error::UNEXPECTED_EOF);
	}

	if( token == TOKEN_UNKNOWN && tokenizing_header_ )
	{
		errors_.emplace_back(offset_, textlen, Error::UNEXPECTED_TOKEN);
	}

	if( token == TOKEN_UNKNOWN && !tokenizing_header_ )
	{
		assert( !messages_.empty() );

		std::size_t msg_start = messages_.back().offset();
		std::size_t msg_size = messages_.back().size();
		messages_.pop_back();

		assert( msg_start + msg_size == offset_ );

		errors_.emplace_back(
			msg_start, msg_size+textlen, Error::CORRUPT_PAYLOAD);
	}
}

#define DS_ADD_MSG(...) \
		do { \
			messages_.emplace_back(buffer_, offset_, __VA_ARGS__); \
		} while(false)

void deepstream_parser_state::handle_header(
	deepstream_token token, const char* text, std::size_t textlen)
{
	using deepstream::Topic;
	using deepstream::Action;

	assert( is_header_token(token) );
	assert( tokenizing_header_ );

	DEEPSTREAM_ON_EXIT( [this] () {
		this->tokenizing_header_ = false;
	} );

	switch(token)
	{
		// avoid compiler warnings [-Wswitch]
		case TOKEN_EOF:
		case TOKEN_UNKNOWN:
		case TOKEN_PAYLOAD:
		case TOKEN_MESSAGE_SEPARATOR:
		case TOKEN_MAXVAL:
			assert(0);
			break;

		case TOKEN_A_A:
			DS_ADD_MSG(Topic::AUTH, Action::REQUEST, true);
			break;

		case TOKEN_A_E_IAD:
			DS_ADD_MSG(Topic::AUTH, Action::ERROR_INVALID_AUTH_DATA, true);
			break;

		case TOKEN_A_E_IAM:
			DS_ADD_MSG(Topic::AUTH, Action::ERROR_INVALID_AUTH_MSG );
			break;

		case TOKEN_A_E_TMAA:
			DS_ADD_MSG(Topic::AUTH, Action::ERROR_TOO_MANY_AUTH_ATTEMPTS);
			break;

		case TOKEN_A_REQ:
			DS_ADD_MSG(Topic::AUTH, Action::REQUEST);
			break;

		case TOKEN_C_A:
			DS_ADD_MSG(Topic::CONNECTION, Action::CHALLENGE_RESPONSE, true);
			break;

		case TOKEN_C_CH:
			DS_ADD_MSG(Topic::CONNECTION, Action::CHALLENGE);
			break;

		case TOKEN_C_CHR:
			DS_ADD_MSG(Topic::CONNECTION, Action::CHALLENGE_RESPONSE);
			break;

		case TOKEN_C_RED:
			DS_ADD_MSG(Topic::CONNECTION, Action::REDIRECT);
			break;

		case TOKEN_C_REJ:
			DS_ADD_MSG(Topic::CONNECTION, Action::REJECT);

		case TOKEN_E_A_L:
			DS_ADD_MSG(Topic::EVENT, Action::LISTEN, true);
			break;

		case TOKEN_E_A_S:
			DS_ADD_MSG(Topic::EVENT, Action::SUBSCRIBE, true);
			break;

		case TOKEN_E_L:
			DS_ADD_MSG(Topic::EVENT, Action::LISTEN);
			break;

		case TOKEN_E_S:
			DS_ADD_MSG(Topic::EVENT, Action::SUBSCRIBE);
			break;

		case TOKEN_E_US:
			DS_ADD_MSG(Topic::EVENT, Action::UNSUBSCRIBE);
			break;
	}

#ifndef NDEBUG
	const auto& msg = messages_.back();
	const char* p = msg.header().to_string();
	deepstream::Buffer bin = deepstream::Message::from_human_readable(p);

	assert( textlen == msg.header().size() );
	assert( textlen == bin.size() );
	assert( std::equal(bin.cbegin(), bin.cend(), text) );
#endif
}

void deepstream_parser_state::handle_payload(
	deepstream_token token, const char* text, std::size_t textlen)
{
	using namespace deepstream::parser;

	assert( token == TOKEN_PAYLOAD );
	assert( text );
	assert( textlen > 0 );
	assert( text[0] == deepstream::ASCII_UNIT_SEPARATOR );
	assert( !messages_.empty() );

	deepstream::use(token);
	deepstream::use(text);

	auto& msg = messages_.back();
	msg.arguments_.emplace_back(offset_+1, textlen-1);
	msg.size_ += textlen;
}

void deepstream_parser_state::handle_message_separator(
	deepstream_token token, const char* text, std::size_t textlen)
{
	using namespace deepstream::parser;

	assert( token == TOKEN_MESSAGE_SEPARATOR );
	assert( text );
	assert( textlen == 1 );
	assert( text[0] == deepstream::ASCII_RECORD_SEPARATOR );
	assert( !messages_.empty() );

	deepstream::use(token);
	deepstream::use(text);

	DEEPSTREAM_ON_EXIT( [this] () {
		this->tokenizing_header_ = true;
	} );

	auto& msg = messages_.back();
	msg.size_ += textlen;

	std::size_t msg_offset = msg.offset();
	std::size_t msg_size = msg.size();
	std::size_t num_args = msg.arguments_.size();

	const auto& expected_num_args =
		deepstream::Message::num_arguments( msg.header() );
	std::size_t min_num_args = expected_num_args.first;
	std::size_t max_num_args = expected_num_args.second;

	if( num_args >= min_num_args && num_args <= max_num_args )
		return;

	messages_.pop_back();
	errors_.emplace_back(
		msg_offset, msg_size, Error::INVALID_NUMBER_OF_ARGUMENTS);
}
<commit_msg>Parser: fix bugs<commit_after>/*
 * Copyright 2016-2017 deepstreamHub GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <cerrno>
#include <cstdlib>
#include <cstring>

#include <new>
#include <ostream>

#include <buffer.hpp>
#include <message.hpp>
#include <parser.h>
#include <parser.hpp>
#include <scope_guard.hpp>
#include <use.hpp>
extern "C" {
#include <lexer.h>
}

#include <cassert>

namespace deepstream {
namespace parser
{

Error::Error(std::size_t offset, std::size_t size, Tag tag) :
	location_(offset, size),
	tag_(tag)
{}

std::ostream& operator<< (std::ostream& os, Error::Tag tag)
{
	switch(tag)
	{
		case Error::UNEXPECTED_TOKEN:
			os << "unexpected token";
			break;

		case Error::UNEXPECTED_EOF:
			os << "unexpected eof";
			break;

		case Error::CORRUPT_PAYLOAD:
			os << "correct payload";
			break;

		case Error::INVALID_NUMBER_OF_ARGUMENTS:
			os << "invalid number of message arguments";
			break;
	}

	return os;
}

std::ostream& operator<< (std::ostream& os, const Error& e)
{
	os << e.location() << ": " << e.tag();
	return os;
}

std::pair<MessageList, ErrorList> execute(char* p, std::size_t size)
{
	assert(p);
	assert( p[size-2] == 0 );
	assert( p[size-1] == 0 );

	yyscan_t scanner = nullptr;
	deepstream::parser::State parser(p, size-2);

	if( yylex_init(&scanner) != 0 )
	{
		if( errno == ENOMEM )
			throw std::bad_alloc();

		assert( errno == EINVAL );
		assert(0);
		throw std::invalid_argument("nullptr given to yylex_init");
	}

	assert( scanner );
	YY_BUFFER_STATE lexer_buffer = yy_scan_buffer(p, size, scanner);
	assert( lexer_buffer );

	yyset_extra( &parser, scanner );

	DEEPSTREAM_ON_EXIT( [lexer_buffer, scanner] () {
		yy_delete_buffer(lexer_buffer, scanner);
		yylex_destroy(scanner);
	} );

	int ret = 0;
	while( (ret = yylex(scanner)) != TOKEN_EOF );

	return std::make_pair(parser.messages_, parser.errors_);
}

}
}

bool is_header_token(enum deepstream_token token)
{
	int min_event_num = TOKEN_A_A;

	return token >= min_event_num;
}

deepstream_parser_state::deepstream_parser_state(const char* p, std::size_t sz):
	buffer_(p),
	buffer_size_(sz),
	tokenizing_header_(true),
	offset_(0)
{
	assert(buffer_);
}

int deepstream_parser_handle(
	deepstream::parser::State* p_state, deepstream_token token,
	const char* text, std::size_t textlen)
{
	assert( p_state );
	assert( token != TOKEN_MAXVAL );

	return p_state->handle_token(token, text, textlen);
}

int deepstream_parser_state::handle_token(
	deepstream_token token, const char* text, std::size_t textlen)
{
	assert( token != TOKEN_MAXVAL );

	assert( text );
	assert( textlen > 0 );

	assert( token != TOKEN_EOF || *text == '\0' );
	assert( token != TOKEN_EOF || textlen == 1 );
	assert( token != TOKEN_EOF || offset_ + textlen == buffer_size_ + 1 );
	assert( token == TOKEN_EOF || offset_ + textlen <= buffer_size_ );
	assert( token == TOKEN_EOF || !std::memcmp(buffer_+offset_, text, textlen));

	assert( messages_.size() <= offset_ );
	assert( errors_.size() <= offset_ );

	DEEPSTREAM_ON_EXIT([this, textlen] () {
		this->offset_ += textlen;
	} );

	if(token == TOKEN_UNKNOWN)
		handle_error(token, text, textlen);
	else if(token == TOKEN_EOF)
	{
		assert(offset_ == buffer_size_);

		if(!tokenizing_header_)
			handle_error(token, text, textlen);
	}
	else if(token == TOKEN_PAYLOAD)
		handle_payload(token, text, textlen);
	else if(token == TOKEN_MESSAGE_SEPARATOR)
		handle_message_separator(token, text, textlen);
	else if( is_header_token(token) )
		handle_header(token, text, textlen);
	else
	{
		assert(0);
	}

	return token;
}

void deepstream_parser_state::handle_error(
	deepstream_token token, const char*, std::size_t textlen)
{
	using deepstream::parser::Error;

	assert( token == TOKEN_EOF || token == TOKEN_UNKNOWN );
	assert( textlen > 0 || (textlen == 0 && token == EOF) );

	DEEPSTREAM_ON_EXIT( [this] () {
		this->tokenizing_header_ = true; // reset parser status on exit
	} );

	if( token == TOKEN_EOF )
	{
		assert( !tokenizing_header_ );
		assert( !messages_.empty() );

		messages_.pop_back();
		errors_.emplace_back(offset_, textlen, Error::UNEXPECTED_EOF);
	}

	if( token == TOKEN_UNKNOWN && tokenizing_header_ )
	{
		errors_.emplace_back(offset_, textlen, Error::UNEXPECTED_TOKEN);
	}

	if( token == TOKEN_UNKNOWN && !tokenizing_header_ )
	{
		assert( !messages_.empty() );

		std::size_t msg_start = messages_.back().offset();
		std::size_t msg_size = messages_.back().size();
		messages_.pop_back();

		assert( msg_start + msg_size == offset_ );

		errors_.emplace_back(
			msg_start, msg_size+textlen, Error::CORRUPT_PAYLOAD);
	}
}

#define DS_ADD_MSG(...) \
		do { \
			messages_.emplace_back(buffer_, offset_, __VA_ARGS__); \
		} while(false)

void deepstream_parser_state::handle_header(
	deepstream_token token, const char* text, std::size_t textlen)
{
	using deepstream::Topic;
	using deepstream::Action;

	assert( is_header_token(token) );
	assert( tokenizing_header_ );

	DEEPSTREAM_ON_EXIT( [this] () {
		this->tokenizing_header_ = false;
	} );

	switch(token)
	{
		// avoid compiler warnings [-Wswitch]
		case TOKEN_EOF:
		case TOKEN_UNKNOWN:
		case TOKEN_PAYLOAD:
		case TOKEN_MESSAGE_SEPARATOR:
		case TOKEN_MAXVAL:
			assert(0);
			break;

		case TOKEN_A_A:
			DS_ADD_MSG(Topic::AUTH, Action::REQUEST, true);
			break;

		case TOKEN_A_E_IAD:
			DS_ADD_MSG(Topic::AUTH, Action::ERROR_INVALID_AUTH_DATA);
			break;

		case TOKEN_A_E_IAM:
			DS_ADD_MSG(Topic::AUTH, Action::ERROR_INVALID_AUTH_MSG );
			break;

		case TOKEN_A_E_TMAA:
			DS_ADD_MSG(Topic::AUTH, Action::ERROR_TOO_MANY_AUTH_ATTEMPTS);
			break;

		case TOKEN_A_REQ:
			DS_ADD_MSG(Topic::AUTH, Action::REQUEST);
			break;

		case TOKEN_C_A:
			DS_ADD_MSG(Topic::CONNECTION, Action::CHALLENGE_RESPONSE, true);
			break;

		case TOKEN_C_CH:
			DS_ADD_MSG(Topic::CONNECTION, Action::CHALLENGE);
			break;

		case TOKEN_C_CHR:
			DS_ADD_MSG(Topic::CONNECTION, Action::CHALLENGE_RESPONSE);
			break;

		case TOKEN_C_RED:
			DS_ADD_MSG(Topic::CONNECTION, Action::REDIRECT);
			break;

		case TOKEN_C_REJ:
			DS_ADD_MSG(Topic::CONNECTION, Action::REJECT);
			break;

		case TOKEN_E_A_L:
			DS_ADD_MSG(Topic::EVENT, Action::LISTEN, true);
			break;

		case TOKEN_E_A_S:
			DS_ADD_MSG(Topic::EVENT, Action::SUBSCRIBE, true);
			break;

		case TOKEN_E_L:
			DS_ADD_MSG(Topic::EVENT, Action::LISTEN);
			break;

		case TOKEN_E_S:
			DS_ADD_MSG(Topic::EVENT, Action::SUBSCRIBE);
			break;

		case TOKEN_E_US:
			DS_ADD_MSG(Topic::EVENT, Action::UNSUBSCRIBE);
			break;
	}

#ifndef NDEBUG
	const auto& msg = messages_.back();
	const char* p = msg.header().to_string();
	deepstream::Buffer bin = deepstream::Message::from_human_readable(p);

	assert( textlen == msg.header().size() );
	assert( textlen == bin.size() );
	assert( std::equal(bin.cbegin(), bin.cend(), text) );
#endif
}

void deepstream_parser_state::handle_payload(
	deepstream_token token, const char* text, std::size_t textlen)
{
	using namespace deepstream::parser;

	assert( token == TOKEN_PAYLOAD );
	assert( text );
	assert( textlen > 0 );
	assert( text[0] == deepstream::ASCII_UNIT_SEPARATOR );
	assert( !messages_.empty() );

	deepstream::use(token);
	deepstream::use(text);

	auto& msg = messages_.back();
	msg.arguments_.emplace_back(offset_+1, textlen-1);
	msg.size_ += textlen;
}

void deepstream_parser_state::handle_message_separator(
	deepstream_token token, const char* text, std::size_t textlen)
{
	using namespace deepstream::parser;

	assert( token == TOKEN_MESSAGE_SEPARATOR );
	assert( text );
	assert( textlen == 1 );
	assert( text[0] == deepstream::ASCII_RECORD_SEPARATOR );
	assert( !messages_.empty() );

	deepstream::use(token);
	deepstream::use(text);

	DEEPSTREAM_ON_EXIT( [this] () {
		this->tokenizing_header_ = true;
	} );

	auto& msg = messages_.back();
	msg.size_ += textlen;

	std::size_t msg_offset = msg.offset();
	std::size_t msg_size = msg.size();
	std::size_t num_args = msg.arguments_.size();

	const auto& expected_num_args =
		deepstream::Message::num_arguments( msg.header() );
	std::size_t min_num_args = expected_num_args.first;
	std::size_t max_num_args = expected_num_args.second;

	if( num_args >= min_num_args && num_args <= max_num_args )
		return;

	messages_.pop_back();
	errors_.emplace_back(
		msg_offset, msg_size, Error::INVALID_NUMBER_OF_ARGUMENTS);
}
<|endoftext|>