<commit_before>// Copyright (c) 2009 - Mozy, Inc.

#include "exception.h"

#ifdef WINDOWS
#include <dbghelp.h>

#include "runtime_linking.h"

#pragma comment(lib, "dbghelp")
#else
#include <errno.h>
#include <execinfo.h>
#include <netdb.h>
#include <string.h>
#endif

#include <boost/thread/mutex.hpp>

#include "socket.h"

namespace Mordor {

#ifdef WINDOWS
static BOOL g_useSymbols;

namespace {

static struct Initializer {
    Initializer()
    {
        SymSetOptions(SYMOPT_DEFERRED_LOADS |
            SYMOPT_FAIL_CRITICAL_ERRORS |
            SYMOPT_LOAD_LINES |
            SYMOPT_NO_PROMPTS);
        g_useSymbols = SymInitialize(GetCurrentProcess(), NULL, TRUE);
    }

    ~Initializer()
    {
        if (g_useSymbols)
            SymCleanup(GetCurrentProcess());
    }
} g_init;

}
#endif

std::string to_string(const std::vector<void *> backtrace)
{
#ifdef WINDOWS
    static boost::mutex s_mutex;
    boost::mutex::scoped_lock lock(s_mutex);
#endif
    std::ostringstream os;
#ifdef POSIX
    boost::shared_ptr<char *> symbols(backtrace_symbols(&backtrace[0],
        backtrace.size()), &free);
#endif
    for (size_t i = 0; i < backtrace.size(); ++i) {
        if (i != 0)
            os << std::endl;
#ifdef WINDOWS
        os << backtrace[i];
        if (g_useSymbols) {
            char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME - 1];
            SYMBOL_INFO *symbol = (SYMBOL_INFO*)buf;
            symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
            symbol->MaxNameLen = MAX_SYM_NAME;
            DWORD64 displacement64 = 0;
            if (pSymFromAddr(GetCurrentProcess(), (DWORD64)backtrace[i],
                &displacement64, symbol)) {
                os << ": " << symbol->Name << "+" << displacement64;
            }
            IMAGEHLP_LINE64 line;
            line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
            DWORD displacement = 0;
            if (pSymGetLineFromAddr64(GetCurrentProcess(),
                (DWORD64)backtrace[i], &displacement, &line)) {
                os << ": " << line.FileName << "(" << line.LineNumber << ")+"
                    << displacement;
            }
        }
#else
        if (symbols)
            os << symbols.get()[i];
        else
            os << backtrace[i];
#endif
    }
    return os.str();
}

std::string to_string( errinfo_backtrace const &bt )
{
    return to_string(bt.value());
}

#ifdef WINDOWS
std::string to_string( errinfo_lasterror const &e)
{
    std::string result;
    char *desc;
    DWORD numChars = FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        e.value(), 0,
        (char*)&desc, 0, NULL);
    if (numChars > 0) {
        result = desc;
        LocalFree((HANDLE)desc);
    }
    std::ostringstream os;
    os << e.value() << ", \"" << result << "\"";
    return os.str();
}
#else
std::string to_string( errinfo_gaierror const &e)
{
    std::ostringstream os;
    os << e.value() << ", \"" << gai_strerror(e.value()) << "\"";
    return os.str();
}
#endif

std::vector<void *> backtrace(int framesToSkip)
{
    std::vector<void *> result;
#ifdef WINDOWS
    result.resize(64);
    WORD count = pRtlCaptureStackBackTrace(1 + framesToSkip, 61 - framesToSkip, &result[0], NULL);
    result.resize(count);
#else
    result.resize(64);
    int count = ::backtrace(&result[0], 64);
    result.resize(count);
    framesToSkip = std::min(count, framesToSkip + 1);
    result.erase(result.begin(), result.begin() + framesToSkip);
#endif
    return result;
}

void removeTopFrames(boost::exception &ex, int framesToSkip)
{
    const std::vector<void *> *oldbt = boost::get_error_info<errinfo_backtrace>(ex);
    if (oldbt && !oldbt->empty()) {
        std::vector<void *> newbt(*oldbt);
        std::vector<void *> bt = backtrace();
        size_t count = 0;
        ++framesToSkip;
        for (; count + framesToSkip < newbt.size() &&
            count + framesToSkip < bt.size(); ++count) {
            if (bt[bt.size() - count - 1] != newbt[newbt.size() - count - 1])
                break;
        }
        count -= framesToSkip;
        newbt.resize(newbt.size() > count ? newbt.size() - count : 0);
        ex << errinfo_backtrace(newbt);
    }
}

void rethrow_exception(boost::exception_ptr const & ep)
{
    // Take the backtrace from here, to avoid additional frames from the
    // exception handler
    std::vector<void *> bt = backtrace(1);
    try {
        boost::rethrow_exception(ep);
    } catch (boost::exception &e) {
        const std::vector<void *> *oldbt =
            boost::get_error_info<errinfo_backtrace>(e);
        if (oldbt)
            bt.insert(bt.begin(), oldbt->begin(), oldbt->end());
        e << errinfo_backtrace(bt);
        throw;
    }
}

#ifdef WINDOWS
#define WSA(error) WSA ## error
#else
#define WSA(error) error
#endif

static void throwSocketException(error_t error)
{
    switch (error) {
        case WSA(EADDRINUSE):
#ifdef WINDOWS
        case ERROR_ADDRESS_ALREADY_ASSOCIATED:
#endif
            throw boost::enable_current_exception(AddressInUseException())
                << errinfo_nativeerror(error);
        case WSA(ECONNABORTED):
#ifdef WINDOWS
        case ERROR_CONNECTION_ABORTED:
#endif
            throw boost::enable_current_exception(ConnectionAbortedException())
                << errinfo_nativeerror(error);
        case WSA(ECONNRESET):
            throw boost::enable_current_exception(ConnectionResetException())
                << errinfo_nativeerror(error);
        case WSA(ECONNREFUSED):
#ifdef WINDOWS
        case ERROR_CONNECTION_REFUSED:
#endif
            throw boost::enable_current_exception(ConnectionRefusedException())
                << errinfo_nativeerror(error);
        case WSA(EHOSTDOWN):
            throw boost::enable_current_exception(HostDownException())
                << errinfo_nativeerror(error);
        case WSA(EHOSTUNREACH):
#ifdef WINDOWS
        case ERROR_HOST_UNREACHABLE:
#endif
            throw boost::enable_current_exception(HostUnreachableException())
                << errinfo_nativeerror(error);
        case WSA(ENETDOWN):
            throw boost::enable_current_exception(NetworkDownException())
                << errinfo_nativeerror(error);
        case WSA(ENETRESET):
#ifdef WINDOWS
        case ERROR_NETNAME_DELETED:
#endif
            throw boost::enable_current_exception(NetworkResetException())
                << errinfo_nativeerror(error);
        case WSA(ENETUNREACH):
#ifdef WINDOWS
        case ERROR_NETWORK_UNREACHABLE:
#endif
            throw boost::enable_current_exception(NetworkUnreachableException())
                << errinfo_nativeerror(error);
        case WSA(ETIMEDOUT):
            throw boost::enable_current_exception(TimedOutException())
                << errinfo_nativeerror(error);
        default:
            break;
    }
}

#ifdef WINDOWS
error_t lastError()
{
    return GetLastError();
}

void lastError(error_t error)
{
    SetLastError(error);
}

void throwExceptionFromLastError(error_t error)
{
    switch (error) {
        case ERROR_INVALID_HANDLE:
        case WSAENOTSOCK:
            throw boost::enable_current_exception(BadHandleException())
                << errinfo_nativeerror(error);
        case ERROR_FILE_NOT_FOUND:
            throw boost::enable_current_exception(FileNotFoundException())
                << errinfo_nativeerror(error);
        case ERROR_ACCESS_DENIED:
            throw boost::enable_current_exception(AccessDeniedException())
                << errinfo_nativeerror(error);
        case ERROR_OPERATION_ABORTED:
            throw boost::enable_current_exception(OperationAbortedException())
                << errinfo_nativeerror(error);
        case ERROR_BROKEN_PIPE:
            throw boost::enable_current_exception(UnexpectedEofException())
                << errinfo_nativeerror(error);
        case WSAESHUTDOWN:
            throw boost::enable_current_exception(BrokenPipeException())
                << errinfo_nativeerror(error);
        case ERROR_SHARING_VIOLATION:
            throw boost::enable_current_exception(SharingViolation())
                << errinfo_nativeerror(error);
        case ERROR_CANT_RESOLVE_FILENAME:
            throw boost::enable_current_exception(UnresolvablePathException())
                << errinfo_nativeerror(error);
        case ERROR_DISK_FULL:
            throw boost::enable_current_exception(OutOfDiskSpaceException())
                << errinfo_nativeerror(error);
        case ERROR_NO_UNICODE_TRANSLATION:
            throw boost::enable_current_exception(InvalidUnicodeException())
                << errinfo_nativeerror(error);
        default:
            throwSocketException(error);
            throw boost::enable_current_exception(NativeException())
                << errinfo_nativeerror(error);
    }
}
#else

error_t lastError()
{
    return errno;
}

void lastError(error_t error)
{
    errno = error;
}

void throwExceptionFromLastError(error_t error)
{
    switch (error) {
        case EBADF:
            throw boost::enable_current_exception(BadHandleException())
                << errinfo_nativeerror(error);
        case ENOENT:
            throw boost::enable_current_exception(FileNotFoundException())
                << errinfo_nativeerror(error);
        case EACCES:
            throw boost::enable_current_exception(AccessDeniedException())
                << errinfo_nativeerror(error);
        case ECANCELED:
            throw boost::enable_current_exception(OperationAbortedException())
                << errinfo_nativeerror(error);
        case EPIPE:
            throw boost::enable_current_exception(BrokenPipeException())
                << errinfo_nativeerror(error);
        case EISDIR:
            throw boost::enable_current_exception(IsDirectoryException())
                << errinfo_nativeerror(error);
        case ENOTDIR:
            throw boost::enable_current_exception(IsNotDirectoryException())
                << errinfo_nativeerror(error);
        case ELOOP:
            throw boost::enable_current_exception(TooManySymbolicLinksException())
                << errinfo_nativeerror(error);
        case ENOSPC:
            throw boost::enable_current_exception(OutOfDiskSpaceException())
            << errinfo_nativeerror(error);
        default:
            throwSocketException(error);
            throw boost::enable_current_exception(NativeException())
                << errinfo_nativeerror(error);
    }
}
#endif

}
<commit_msg>Treat WSAEACCES as AddressInUseException<commit_after>// Copyright (c) 2009 - Mozy, Inc.

#include "exception.h"

#ifdef WINDOWS
#include <dbghelp.h>

#include "runtime_linking.h"

#pragma comment(lib, "dbghelp")
#else
#include <errno.h>
#include <execinfo.h>
#include <netdb.h>
#include <string.h>
#endif

#include <boost/thread/mutex.hpp>

#include "socket.h"

namespace Mordor {

#ifdef WINDOWS
static BOOL g_useSymbols;

namespace {

static struct Initializer {
    Initializer()
    {
        SymSetOptions(SYMOPT_DEFERRED_LOADS |
            SYMOPT_FAIL_CRITICAL_ERRORS |
            SYMOPT_LOAD_LINES |
            SYMOPT_NO_PROMPTS);
        g_useSymbols = SymInitialize(GetCurrentProcess(), NULL, TRUE);
    }

    ~Initializer()
    {
        if (g_useSymbols)
            SymCleanup(GetCurrentProcess());
    }
} g_init;

}
#endif

std::string to_string(const std::vector<void *> backtrace)
{
#ifdef WINDOWS
    static boost::mutex s_mutex;
    boost::mutex::scoped_lock lock(s_mutex);
#endif
    std::ostringstream os;
#ifdef POSIX
    boost::shared_ptr<char *> symbols(backtrace_symbols(&backtrace[0],
        backtrace.size()), &free);
#endif
    for (size_t i = 0; i < backtrace.size(); ++i) {
        if (i != 0)
            os << std::endl;
#ifdef WINDOWS
        os << backtrace[i];
        if (g_useSymbols) {
            char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME - 1];
            SYMBOL_INFO *symbol = (SYMBOL_INFO*)buf;
            symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
            symbol->MaxNameLen = MAX_SYM_NAME;
            DWORD64 displacement64 = 0;
            if (pSymFromAddr(GetCurrentProcess(), (DWORD64)backtrace[i],
                &displacement64, symbol)) {
                os << ": " << symbol->Name << "+" << displacement64;
            }
            IMAGEHLP_LINE64 line;
            line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
            DWORD displacement = 0;
            if (pSymGetLineFromAddr64(GetCurrentProcess(),
                (DWORD64)backtrace[i], &displacement, &line)) {
                os << ": " << line.FileName << "(" << line.LineNumber << ")+"
                    << displacement;
            }
        }
#else
        if (symbols)
            os << symbols.get()[i];
        else
            os << backtrace[i];
#endif
    }
    return os.str();
}

std::string to_string( errinfo_backtrace const &bt )
{
    return to_string(bt.value());
}

#ifdef WINDOWS
std::string to_string( errinfo_lasterror const &e)
{
    std::string result;
    char *desc;
    DWORD numChars = FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        e.value(), 0,
        (char*)&desc, 0, NULL);
    if (numChars > 0) {
        result = desc;
        LocalFree((HANDLE)desc);
    }
    std::ostringstream os;
    os << e.value() << ", \"" << result << "\"";
    return os.str();
}
#else
std::string to_string( errinfo_gaierror const &e)
{
    std::ostringstream os;
    os << e.value() << ", \"" << gai_strerror(e.value()) << "\"";
    return os.str();
}
#endif

std::vector<void *> backtrace(int framesToSkip)
{
    std::vector<void *> result;
#ifdef WINDOWS
    result.resize(64);
    WORD count = pRtlCaptureStackBackTrace(1 + framesToSkip, 61 - framesToSkip, &result[0], NULL);
    result.resize(count);
#else
    result.resize(64);
    int count = ::backtrace(&result[0], 64);
    result.resize(count);
    framesToSkip = std::min(count, framesToSkip + 1);
    result.erase(result.begin(), result.begin() + framesToSkip);
#endif
    return result;
}

void removeTopFrames(boost::exception &ex, int framesToSkip)
{
    const std::vector<void *> *oldbt = boost::get_error_info<errinfo_backtrace>(ex);
    if (oldbt && !oldbt->empty()) {
        std::vector<void *> newbt(*oldbt);
        std::vector<void *> bt = backtrace();
        size_t count = 0;
        ++framesToSkip;
        for (; count + framesToSkip < newbt.size() &&
            count + framesToSkip < bt.size(); ++count) {
            if (bt[bt.size() - count - 1] != newbt[newbt.size() - count - 1])
                break;
        }
        count -= framesToSkip;
        newbt.resize(newbt.size() > count ? newbt.size() - count : 0);
        ex << errinfo_backtrace(newbt);
    }
}

void rethrow_exception(boost::exception_ptr const & ep)
{
    // Take the backtrace from here, to avoid additional frames from the
    // exception handler
    std::vector<void *> bt = backtrace(1);
    try {
        boost::rethrow_exception(ep);
    } catch (boost::exception &e) {
        const std::vector<void *> *oldbt =
            boost::get_error_info<errinfo_backtrace>(e);
        if (oldbt)
            bt.insert(bt.begin(), oldbt->begin(), oldbt->end());
        e << errinfo_backtrace(bt);
        throw;
    }
}

#ifdef WINDOWS
#define WSA(error) WSA ## error
#else
#define WSA(error) error
#endif

static void throwSocketException(error_t error)
{
    switch (error) {
        case WSA(EADDRINUSE):
#ifdef WINDOWS
        // WSAEACESS is returned from bind when you set SO_REUSEADDR, and
        // another socket has set SO_EXCLUSIVEADDRUSE
        case WSAEACCES:
        case ERROR_ADDRESS_ALREADY_ASSOCIATED:
#endif
            throw boost::enable_current_exception(AddressInUseException())
                << errinfo_nativeerror(error);
        case WSA(ECONNABORTED):
#ifdef WINDOWS
        case ERROR_CONNECTION_ABORTED:
#endif
            throw boost::enable_current_exception(ConnectionAbortedException())
                << errinfo_nativeerror(error);
        case WSA(ECONNRESET):
            throw boost::enable_current_exception(ConnectionResetException())
                << errinfo_nativeerror(error);
        case WSA(ECONNREFUSED):
#ifdef WINDOWS
        case ERROR_CONNECTION_REFUSED:
#endif
            throw boost::enable_current_exception(ConnectionRefusedException())
                << errinfo_nativeerror(error);
        case WSA(EHOSTDOWN):
            throw boost::enable_current_exception(HostDownException())
                << errinfo_nativeerror(error);
        case WSA(EHOSTUNREACH):
#ifdef WINDOWS
        case ERROR_HOST_UNREACHABLE:
#endif
            throw boost::enable_current_exception(HostUnreachableException())
                << errinfo_nativeerror(error);
        case WSA(ENETDOWN):
            throw boost::enable_current_exception(NetworkDownException())
                << errinfo_nativeerror(error);
        case WSA(ENETRESET):
#ifdef WINDOWS
        case ERROR_NETNAME_DELETED:
#endif
            throw boost::enable_current_exception(NetworkResetException())
                << errinfo_nativeerror(error);
        case WSA(ENETUNREACH):
#ifdef WINDOWS
        case ERROR_NETWORK_UNREACHABLE:
#endif
            throw boost::enable_current_exception(NetworkUnreachableException())
                << errinfo_nativeerror(error);
        case WSA(ETIMEDOUT):
            throw boost::enable_current_exception(TimedOutException())
                << errinfo_nativeerror(error);
        default:
            break;
    }
}

#ifdef WINDOWS
error_t lastError()
{
    return GetLastError();
}

void lastError(error_t error)
{
    SetLastError(error);
}

void throwExceptionFromLastError(error_t error)
{
    switch (error) {
        case ERROR_INVALID_HANDLE:
        case WSAENOTSOCK:
            throw boost::enable_current_exception(BadHandleException())
                << errinfo_nativeerror(error);
        case ERROR_FILE_NOT_FOUND:
            throw boost::enable_current_exception(FileNotFoundException())
                << errinfo_nativeerror(error);
        case ERROR_ACCESS_DENIED:
            throw boost::enable_current_exception(AccessDeniedException())
                << errinfo_nativeerror(error);
        case ERROR_OPERATION_ABORTED:
            throw boost::enable_current_exception(OperationAbortedException())
                << errinfo_nativeerror(error);
        case ERROR_BROKEN_PIPE:
            throw boost::enable_current_exception(UnexpectedEofException())
                << errinfo_nativeerror(error);
        case WSAESHUTDOWN:
            throw boost::enable_current_exception(BrokenPipeException())
                << errinfo_nativeerror(error);
        case ERROR_SHARING_VIOLATION:
            throw boost::enable_current_exception(SharingViolation())
                << errinfo_nativeerror(error);
        case ERROR_CANT_RESOLVE_FILENAME:
            throw boost::enable_current_exception(UnresolvablePathException())
                << errinfo_nativeerror(error);
        case ERROR_DISK_FULL:
            throw boost::enable_current_exception(OutOfDiskSpaceException())
                << errinfo_nativeerror(error);
        case ERROR_NO_UNICODE_TRANSLATION:
            throw boost::enable_current_exception(InvalidUnicodeException())
                << errinfo_nativeerror(error);
        default:
            throwSocketException(error);
            throw boost::enable_current_exception(NativeException())
                << errinfo_nativeerror(error);
    }
}
#else

error_t lastError()
{
    return errno;
}

void lastError(error_t error)
{
    errno = error;
}

void throwExceptionFromLastError(error_t error)
{
    switch (error) {
        case EBADF:
            throw boost::enable_current_exception(BadHandleException())
                << errinfo_nativeerror(error);
        case ENOENT:
            throw boost::enable_current_exception(FileNotFoundException())
                << errinfo_nativeerror(error);
        case EACCES:
            throw boost::enable_current_exception(AccessDeniedException())
                << errinfo_nativeerror(error);
        case ECANCELED:
            throw boost::enable_current_exception(OperationAbortedException())
                << errinfo_nativeerror(error);
        case EPIPE:
            throw boost::enable_current_exception(BrokenPipeException())
                << errinfo_nativeerror(error);
        case EISDIR:
            throw boost::enable_current_exception(IsDirectoryException())
                << errinfo_nativeerror(error);
        case ENOTDIR:
            throw boost::enable_current_exception(IsNotDirectoryException())
                << errinfo_nativeerror(error);
        case ELOOP:
            throw boost::enable_current_exception(TooManySymbolicLinksException())
                << errinfo_nativeerror(error);
        case ENOSPC:
            throw boost::enable_current_exception(OutOfDiskSpaceException())
            << errinfo_nativeerror(error);
        default:
            throwSocketException(error);
            throw boost::enable_current_exception(NativeException())
                << errinfo_nativeerror(error);
    }
}
#endif

}
<|endoftext|>


<commit_before><commit_msg>use bind instead of connect<commit_after><|endoftext|>


<commit_before><commit_msg>Add one more example of flatmap<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2009 - Decho Corp.

#include "mordor/pch.h"

#include "test.h"

#include <iostream>

#include "mordor/config.h"

#ifdef WINDOWS
#include <windows.h>
#elif defined (LINUX)
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#elif defined (OSX)
#include <sys/sysctl.h>
#endif

using namespace Mordor;
using namespace Mordor::Test;

static TestSuites *g_allTests;
#ifdef LINUX
static bool g_traced;
#endif

static ConfigVar<bool>::ptr g_protect = Config::lookup(
    "test.protect", false,
    "Protect test while running under a debugger");

namespace {
static struct Initializer {
#ifdef LINUX
    Initializer()
    {
        char buffer[1024];
        snprintf(buffer, 1024, "/proc/%d/status", getpid());
        int fd = open(buffer, O_RDONLY);
        if (fd >= 0) {
            int rc = read(fd, buffer, 1024);
            if (rc > 0) {
                const char *tracerPidStr = strstr(buffer, "TracerPid:");
                if (tracerPidStr) {
                    int tracingPid = atoi(tracerPidStr + 13);
                    if (tracingPid != 0) {
                        g_traced = true;
                    }
                }
            }
            close(fd);
        }
    }
#endif
    ~Initializer()
    {
        if (g_allTests)
            delete g_allTests;
    }
} g_init;
}

void
Test::registerTest(const std::string &suite, const std::string &testName,
             TestDg test)
{
    if (!g_allTests)
        g_allTests = new TestSuites();
    (*g_allTests)[suite].second[testName] = test;
}

void
Test::registerSuiteInvariant(const std::string &suite, TestDg invariant)
{
    if (!g_allTests)
        g_allTests = new TestSuites();
    MORDOR_ASSERT((*g_allTests)[suite].first == NULL);
    (*g_allTests)[suite].first = invariant;
}

void
Test::assertion(const char *file, int line, const char *function,
                const std::string &expr)
{
    throw boost::enable_current_exception(Assertion(expr))
        << boost::throw_file(file) << boost::throw_line(line)
        << boost::throw_function(function)
        << errinfo_backtrace(backtrace());
}

static bool
runTest(TestListener *listener, const std::string &suite,
             const std::string &testName, TestDg test)
{
    if (listener)
        listener->testStarted(suite, testName);

    bool protect = true;
#ifdef WINDOWS
    protect = !IsDebuggerPresent();
#elif defined (LINUX)
    protect = !g_traced;
#elif defined (OSX)
    int mib[4];
    kinfo_proc info;
    size_t size;
    mib[0] = CTL_KERN;
    mib[1] = KERN_PROC;
    mib[2] = KERN_PROC_PID;
    mib[3] = getpid();
    size = sizeof(kinfo_proc);
    info.kp_proc.p_flag = 0;
    sysctl(mib, 4, &info, &size, NULL, 0);
    protect = !(info.kp_proc.p_flag & P_TRACED);
#endif
    protect = protect || g_protect->val();
    if (protect) {
        try {
            test();
            if (listener)
                listener->testComplete(suite, testName);
        } catch (const Assertion &assertion) {
            if (listener)
                listener->testAsserted(suite, testName, assertion);
            return false;
        } catch (...) {
            if (listener)
                listener->testException(suite, testName);
            return false;
        }
    } else {
        test();
        if (listener)
            listener->testComplete(suite, testName);
    }
    return true;
}

static bool
runTests(const TestSuites *suites, TestListener *listener)
{
    bool result = true;
    if (!suites) suites = g_allTests;
    if (suites) {
        for (TestSuites::const_iterator it(suites->begin());
            it != suites->end();
            ++it) {
            for (TestSuite::second_type::const_iterator
                    it2(it->second.second.begin());
                it2 != it->second.second.end();
                ++it2) {
                if (it->second.first) {
                    result = result && runTest(listener, it->first,
                        "<invariant>", it->second.first);
                }
                result = runTest(listener, it->first, it2->first,
                    it2->second) && result;
            }
            if (it->second.first) {
                result = runTest(listener, it->first,
                    "<invariant>", it->second.first) && result;
            }
        }
    }
    if (listener)
        listener->testsComplete();
    return result;
}

const TestSuites &
Test::allTests()
{
    if (!g_allTests)
        g_allTests = new TestSuites();
    return *g_allTests;
}

TestSuites
Test::testsForArguments(int argc, const char **argv)
{
    TestSuites tests;
    const TestSuites &all = allTests();
    for (int i = 0; i < argc; ++i) {
        std::string suite = argv[i];
        std::string test;
        size_t offset = suite.find("::");
        if (offset != std::string::npos) {
            test = suite.substr(offset + 2);
            suite = suite.substr(0, offset);
        }
        TestSuites::const_iterator suiteIt = all.find(suite);
        if (suiteIt != all.end()) {
            if (test.empty()) {
                tests[suite] = suiteIt->second;
            } else {
                TestSuite::second_type::const_iterator testIt =
                    suiteIt->second.second.find(test);
                if (testIt != suiteIt->second.second.end()) {
                    tests[suite].first = suiteIt->second.first;
                    tests[suite].second[test] = testIt->second;
                }
            }
        }
    }
    return tests;
}

bool
Test::runTests()
{
    return ::runTests(g_allTests, NULL);
}

bool
Test::runTests(const TestSuites &suites)
{
    return ::runTests(&suites, NULL);
}

bool
Test::runTests(TestListener &listener)
{
    return ::runTests(g_allTests, &listener);
}

bool
Test::runTests(const TestSuites &suites, TestListener &listener)
{
    return ::runTests(&suites, &listener);
}

template <>
void Test::assertEqual<const char *, const char *>(const char *file,
    int line, const char *function, const char *lhs, const char *rhs,
    const char *lhsExpr, const char *rhsExpr)
{
    if (!(strcmp(lhs, rhs) == 0)) {
        assertComparison(file, line, function, lhs, rhs, lhsExpr, rhsExpr,
            "==");
    }
}

template <>
void Test::assertNotEqual<const char *, const char *>(const char *file,
    int line, const char *lhs, const char *function, const char *rhs,
    const char *lhsExpr, const char *rhsExpr)
{
    if (!(strcmp(lhs, rhs) != 0)) {
        assertComparison(file, line, function, lhs, rhs, lhsExpr, rhsExpr,
            "!=");
    }
}

template <>
void Test::assertLessThan<const char *, const char *>(const char *file,
    int line, const char *function, const char *lhs, const char *rhs,
    const char *lhsExpr, const char *rhsExpr)
{
    if (!(strcmp(lhs, rhs) < 0)) {
        assertComparison(file, line, function, lhs, rhs, lhsExpr, rhsExpr,
            "<");
    }
}

template <>
void Test::assertLessThanOrEqual<const char *, const char *>(const char *file,
    int line, const char *function, const char *lhs, const char *rhs,
    const char *lhsExpr, const char *rhsExpr)
{
    if (!(strcmp(lhs, rhs) <= 0)) {
        assertComparison(file, line, function, lhs, rhs, lhsExpr, rhsExpr,
            "<=");
    }
}

template <>
void Test::assertGreaterThan<const char *, const char *>(const char *file,
    int line, const char *function, const char *lhs, const char *rhs,
    const char *lhsExpr, const char *rhsExpr)
{
    if (!(strcmp(lhs, rhs) > 0)) {
        assertComparison(file, line, function, lhs, rhs, lhsExpr, rhsExpr,
            ">");
    }
}

template <>
void Test::assertGreaterThanOrEqual<const char *, const char *>(const char *file,
    int line, const char *function, const char *lhs, const char *rhs,
    const char *lhsExpr, const char *rhsExpr)
{
    if (!(strcmp(lhs, rhs) == 0)) {
        assertComparison(file, line, function, lhs, rhs, lhsExpr, rhsExpr,
            ">=");
    }
}
<commit_msg>Config option to wait for a debugger to attach before running unit tests.<commit_after>// Copyright (c) 2009 - Decho Corp.

#include "mordor/pch.h"

#include "test.h"

#include <iostream>

#include "mordor/config.h"
#include "mordor/sleep.h"

#ifdef WINDOWS
#include <windows.h>
#elif defined (LINUX)
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#elif defined (OSX)
#include <sys/sysctl.h>
#endif

using namespace Mordor;
using namespace Mordor::Test;

static TestSuites *g_allTests;

static ConfigVar<bool>::ptr g_protect = Config::lookup(
    "test.protect", false,
    "Protect test while running under a debugger");
static ConfigVar<bool>::ptr g_wait = Config::lookup(
    "test.waitfordebugger", false,
    "Wait for a debugger to attach before running tests");

#ifdef WINDOWS
#elif defined(LINUX)
static bool IsDebuggerPresent()
{
    bool result = false;
    char buffer[1024];
    snprintf(buffer, 1024, "/proc/%d/status", getpid());
    int fd = open(buffer, O_RDONLY);
    if (fd >= 0) {
        int rc = read(fd, buffer, 1024);
        if (rc > 0) {
            const char *tracerPidStr = strstr(buffer, "TracerPid:");
            if (tracerPidStr) {
                int tracingPid = atoi(tracerPidStr + 13);
                if (tracingPid != 0) {
                    result = true;
                }
            }
        }
        close(fd);
    }
    return result;
}
#elif defined(OSX)
static bool IsDebuggerPresent()
{
    int mib[4];
    kinfo_proc info;
    size_t size;
    mib[0] = CTL_KERN;
    mib[1] = KERN_PROC;
    mib[2] = KERN_PROC_PID;
    mib[3] = getpid();
    size = sizeof(kinfo_proc);
    info.kp_proc.p_flag = 0;
    sysctl(mib, 4, &info, &size, NULL, 0);
    return !!(info.kp_proc.p_flag & P_TRACED);
}
#else
static bool IsDebuggerPresent()
{
    return false;
}
#endif

void
Test::registerTest(const std::string &suite, const std::string &testName,
             TestDg test)
{
    if (!g_allTests)
        g_allTests = new TestSuites();
    (*g_allTests)[suite].second[testName] = test;
}

void
Test::registerSuiteInvariant(const std::string &suite, TestDg invariant)
{
    if (!g_allTests)
        g_allTests = new TestSuites();
    MORDOR_ASSERT((*g_allTests)[suite].first == NULL);
    (*g_allTests)[suite].first = invariant;
}

void
Test::assertion(const char *file, int line, const char *function,
                const std::string &expr)
{
    throw boost::enable_current_exception(Assertion(expr))
        << boost::throw_file(file) << boost::throw_line(line)
        << boost::throw_function(function)
        << errinfo_backtrace(backtrace());
}

static bool
runTest(TestListener *listener, const std::string &suite,
             const std::string &testName, TestDg test)
{
    if (listener)
        listener->testStarted(suite, testName);

    bool protect = !IsDebuggerPresent();
    protect = protect || g_protect->val();
    if (protect) {
        try {
            test();
            if (listener)
                listener->testComplete(suite, testName);
        } catch (const Assertion &assertion) {
            if (listener)
                listener->testAsserted(suite, testName, assertion);
            return false;
        } catch (...) {
            if (listener)
                listener->testException(suite, testName);
            return false;
        }
    } else {
        test();
        if (listener)
            listener->testComplete(suite, testName);
    }
    return true;
}

static bool
runTests(const TestSuites *suites, TestListener *listener)
{
    if (g_wait->val()) {
        while (!IsDebuggerPresent())
            sleep(10000ull);
#ifdef WINDOWS
        DebugBreak();
#endif
    }
    bool result = true;
    if (!suites) suites = g_allTests;
    if (suites) {
        for (TestSuites::const_iterator it(suites->begin());
            it != suites->end();
            ++it) {
            for (TestSuite::second_type::const_iterator
                    it2(it->second.second.begin());
                it2 != it->second.second.end();
                ++it2) {
                if (it->second.first) {
                    result = result && runTest(listener, it->first,
                        "<invariant>", it->second.first);
                }
                result = runTest(listener, it->first, it2->first,
                    it2->second) && result;
            }
            if (it->second.first) {
                result = runTest(listener, it->first,
                    "<invariant>", it->second.first) && result;
            }
        }
    }
    if (listener)
        listener->testsComplete();
    return result;
}

const TestSuites &
Test::allTests()
{
    if (!g_allTests)
        g_allTests = new TestSuites();
    return *g_allTests;
}

TestSuites
Test::testsForArguments(int argc, const char **argv)
{
    TestSuites tests;
    const TestSuites &all = allTests();
    for (int i = 0; i < argc; ++i) {
        std::string suite = argv[i];
        std::string test;
        size_t offset = suite.find("::");
        if (offset != std::string::npos) {
            test = suite.substr(offset + 2);
            suite = suite.substr(0, offset);
        }
        TestSuites::const_iterator suiteIt = all.find(suite);
        if (suiteIt != all.end()) {
            if (test.empty()) {
                tests[suite] = suiteIt->second;
            } else {
                TestSuite::second_type::const_iterator testIt =
                    suiteIt->second.second.find(test);
                if (testIt != suiteIt->second.second.end()) {
                    tests[suite].first = suiteIt->second.first;
                    tests[suite].second[test] = testIt->second;
                }
            }
        }
    }
    return tests;
}

bool
Test::runTests()
{
    return ::runTests(g_allTests, NULL);
}

bool
Test::runTests(const TestSuites &suites)
{
    return ::runTests(&suites, NULL);
}

bool
Test::runTests(TestListener &listener)
{
    return ::runTests(g_allTests, &listener);
}

bool
Test::runTests(const TestSuites &suites, TestListener &listener)
{
    return ::runTests(&suites, &listener);
}

template <>
void Test::assertEqual<const char *, const char *>(const char *file,
    int line, const char *function, const char *lhs, const char *rhs,
    const char *lhsExpr, const char *rhsExpr)
{
    if (!(strcmp(lhs, rhs) == 0)) {
        assertComparison(file, line, function, lhs, rhs, lhsExpr, rhsExpr,
            "==");
    }
}

template <>
void Test::assertNotEqual<const char *, const char *>(const char *file,
    int line, const char *lhs, const char *function, const char *rhs,
    const char *lhsExpr, const char *rhsExpr)
{
    if (!(strcmp(lhs, rhs) != 0)) {
        assertComparison(file, line, function, lhs, rhs, lhsExpr, rhsExpr,
            "!=");
    }
}

template <>
void Test::assertLessThan<const char *, const char *>(const char *file,
    int line, const char *function, const char *lhs, const char *rhs,
    const char *lhsExpr, const char *rhsExpr)
{
    if (!(strcmp(lhs, rhs) < 0)) {
        assertComparison(file, line, function, lhs, rhs, lhsExpr, rhsExpr,
            "<");
    }
}

template <>
void Test::assertLessThanOrEqual<const char *, const char *>(const char *file,
    int line, const char *function, const char *lhs, const char *rhs,
    const char *lhsExpr, const char *rhsExpr)
{
    if (!(strcmp(lhs, rhs) <= 0)) {
        assertComparison(file, line, function, lhs, rhs, lhsExpr, rhsExpr,
            "<=");
    }
}

template <>
void Test::assertGreaterThan<const char *, const char *>(const char *file,
    int line, const char *function, const char *lhs, const char *rhs,
    const char *lhsExpr, const char *rhsExpr)
{
    if (!(strcmp(lhs, rhs) > 0)) {
        assertComparison(file, line, function, lhs, rhs, lhsExpr, rhsExpr,
            ">");
    }
}

template <>
void Test::assertGreaterThanOrEqual<const char *, const char *>(const char *file,
    int line, const char *function, const char *lhs, const char *rhs,
    const char *lhsExpr, const char *rhsExpr)
{
    if (!(strcmp(lhs, rhs) == 0)) {
        assertComparison(file, line, function, lhs, rhs, lhsExpr, rhsExpr,
            ">=");
    }
}
<|endoftext|>


<commit_before><commit_msg>Added inline to check_required (#51)<commit_after><|endoftext|>


<commit_before>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
 *
 * Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
 *
 * This program 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 program 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 program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "VLCThumbnailer.h"

#include <cstring>
#ifdef WITH_JPEG
#include <jpeglib.h>
#elif defined(WITH_EVAS)
#include <Evas_Engine_Buffer.h>
#endif
#include <setjmp.h>

#include "IMedia.h"
#include "logging/Logger.h"
#include "MediaLibrary.h"

VLCThumbnailer::VLCThumbnailer(const VLC::Instance &vlc)
    : m_instance( vlc )
#ifdef WITH_EVAS
    , m_canvas( nullptr, &evas_free )
#endif
    , m_snapshotRequired( false )
    , m_height( 0 )
{
#ifdef WITH_EVAS
    static int fakeBuffer;
    evas_init();
    auto method = evas_render_method_lookup("buffer");
    m_canvas.reset( evas_new() );
    if ( m_canvas == nullptr )
        throw std::runtime_error( "Failed to allocate canvas" );
    evas_output_method_set( m_canvas.get(), method );
    evas_output_size_set(m_canvas.get(), 1, 1 );
    evas_output_viewport_set( m_canvas.get(), 0, 0, 1, 1 );
    auto einfo = (Evas_Engine_Info_Buffer *)evas_engine_info_get( m_canvas.get() );
    einfo->info.depth_type = EVAS_ENGINE_BUFFER_DEPTH_ARGB32;
    einfo->info.dest_buffer = &fakeBuffer;
    einfo->info.dest_buffer_row_bytes = 4;
    einfo->info.use_color_key = 0;
    einfo->info.alpha_threshold = 0;
    einfo->info.func.new_update_region = NULL;
    einfo->info.func.free_update_region = NULL;
    evas_engine_info_set( m_canvas.get(), (Evas_Engine_Info *)einfo );
#endif
}

VLCThumbnailer::~VLCThumbnailer()
{
#ifdef WITH_EVAS
    evas_shutdown();
#endif
}

bool VLCThumbnailer::initialize(IMetadataServiceCb *callback, MediaLibrary* ml)
{
    m_cb = callback;
    m_ml = ml;
    return true;
}

unsigned int VLCThumbnailer::priority() const
{
    // This needs to be lower than the VLCMetadataService, since we want to know the file type.
    return 50;
}

void VLCThumbnailer::run(std::shared_ptr<Media> file, void *data )
{
    if ( file->type() == IMedia::Type::UnknownType )
    {
        // If we don't know the file type yet, it actually looks more like a bug
        // since this should run after file type deduction, and not run in case
        // that step fails.
        m_cb->done( file, Status::Fatal, data );
        return;
    }
    else if ( file->type() != IMedia::Type::VideoType )
    {
        // There's no point in generating a thumbnail for a non-video file.
        m_cb->done( file, Status::Success, data );
        return;
    }
    else if ( file->snapshot().empty() == false )
    {
        LOG_INFO(file->snapshot(), " already has a snapshot" );
        m_cb->done( file, Status::Success, data );
        return;
    }

    VLC::Media media( m_instance, file->mrl(), VLC::Media::FromPath );
    media.addOption( ":no-audio" );
    VLC::MediaPlayer mp( media );

    setupVout( mp );

    if ( startPlayback( file, mp, data ) == false )
        return;

    // Seek ahead to have a significant preview
    if ( seekAhead( file, mp, data ) == false )
        return;

    takeSnapshot( file, mp, data );
}

bool VLCThumbnailer::startPlayback(std::shared_ptr<Media> file, VLC::MediaPlayer &mp, void* data )
{
    bool failed = true;

    std::unique_lock<std::mutex> lock( m_mutex );

    mp.eventManager().onPlaying([this, &failed]() {
        std::unique_lock<std::mutex> lock( m_mutex );
        failed = false;
        m_cond.notify_all();
    });
    mp.eventManager().onEncounteredError([this]() {
        std::unique_lock<std::mutex> lock( m_mutex );
        m_cond.notify_all();
    });
    mp.play();
    bool success = m_cond.wait_for( lock, std::chrono::seconds( 3 ), [&mp]() {
        return mp.state() == libvlc_Playing || mp.state() == libvlc_Error;
    });
    if ( success == false || failed == true )
    {
        // In case of timeout or error, don't go any further
        m_cb->done( file, Status::Error, data );
        return false;
    }
    return true;
}

bool VLCThumbnailer::seekAhead(std::shared_ptr<Media> file, VLC::MediaPlayer& mp, void* data )
{
    std::unique_lock<std::mutex> lock( m_mutex );
    float pos = .0f;
    auto event = mp.eventManager().onPositionChanged([this, &pos](float p) {
        std::unique_lock<std::mutex> lock( m_mutex );
        pos = p;
        m_cond.notify_all();
    });
    mp.setPosition( .4f );
    bool success = m_cond.wait_for( lock, std::chrono::seconds( 3 ), [&pos]() {
        return pos >= .1f;
    });
    // Since we're locking a mutex for each position changed, let's unregister ASAP
    event->unregister();
    if ( success == false )
    {
        m_cb->done( file, Status::Error, data );
        return false;
    }
    return true;
}

void VLCThumbnailer::setupVout( VLC::MediaPlayer& mp )
{
    mp.setVideoFormatCallbacks(
        // Setup
        [this, &mp](char* chroma, unsigned int* width, unsigned int *height, unsigned int *pitches, unsigned int *lines) {
            strcpy( chroma, "RV32" );

            const float inputAR = (float)*width / *height;

            *width = Width;
            auto prevHeight = m_height;
            m_height = (float)Width / inputAR + 1;
            // If our buffer isn't enough anymore, reallocate a new one.
            if ( m_height > prevHeight )
            {
                m_buff.reset( new uint8_t[Width * m_height * Bpp] );
            }
            *height = m_height;
            *pitches = Width * Bpp;
            *lines = m_height;
            return 1;
        },
        // Cleanup
        nullptr);
    mp.setVideoCallbacks(
        // Lock
        [this](void** pp_buff) {
            *pp_buff = m_buff.get();
            return nullptr;
        },
        //unlock
        [this](void*, void*const*) {
            std::unique_lock<std::mutex> lock(m_mutex);
            if ( m_snapshotRequired == true )
            {
                m_snapshotRequired = false;
                m_cond.notify_all();
            }
        }
        ,
        //display
        nullptr
    );
}

bool VLCThumbnailer::takeSnapshot(std::shared_ptr<Media> file, VLC::MediaPlayer &mp, void *data)
{
    // lock, signal that we want a snapshot, and wait.
    {
        std::unique_lock<std::mutex> lock( m_mutex );
        m_snapshotRequired = true;
        bool success = m_cond.wait_for( lock, std::chrono::seconds( 3 ), [this]() {
            // Keep waiting if the vmem thread hasn't restored m_snapshotRequired to false
            return m_snapshotRequired == true;
        });
        if ( success == false )
        {
            m_cb->done( file, Status::Error, data );
            return false;
        }
    }
    mp.stop();
    return compress( file, data );
}

#ifdef WITH_JPEG

struct jpegError : public jpeg_error_mgr
{
    jmp_buf buff;
    char message[JMSG_LENGTH_MAX];

    static void jpegErrorHandler(j_common_ptr common)
    {
        auto error = reinterpret_cast<jpegError*>(common->err);
        (*error->format_message)( common, error->message );
        longjmp(error->buff, 1);
    }
};

#endif

bool VLCThumbnailer::compress( std::shared_ptr<Media> file, void *data )
{
    auto path = m_ml->snapshotPath();
    path += "/";
    path += std::to_string( file->id() ) +
#ifdef WITH_EVAS
            ".png";
#else
            ".jpg";
#endif

#ifdef WITH_JPEG
    //FIXME: Abstract this away, though libjpeg requires a FILE*...
    auto fOut = fopen(path.c_str(), "wb");
    // ensure we always close the file.
    auto fOutPtr = std::unique_ptr<FILE, int(*)(FILE*)>( fOut, &fclose );
    if ( fOut == nullptr )
    {
        LOG_ERROR("Failed to open snapshot file ", path);
        m_cb->done( file, Status::Error, data );
        return false;
    }

    jpeg_compress_struct compInfo;
    JSAMPROW row_pointer[1];

    //libjpeg's default error handling is to call exit(), which would
    //be slightly problematic...
    jpegError err;
    compInfo.err = jpeg_std_error(&err);
    err.error_exit = jpegError::jpegErrorHandler;

    if ( setjmp( err.buff ) )
    {
        LOG_ERROR("JPEG failure: ", err.message);
        jpeg_destroy_compress(&compInfo);
        m_cb->done( file, Status::Error, data );
        return false;
    }

    jpeg_create_compress(&compInfo);
    jpeg_stdio_dest(&compInfo, fOut);

    compInfo.image_width = Width;
    compInfo.image_height = m_height;
    compInfo.input_components = Bpp;
#if ( !defined(JPEG_LIB_VERSION_MAJOR) && !defined(JPEG_LIB_VERSION_MINOR) ) || \
    ( JPEG_LIB_VERSION_MAJOR <= 8 && JPEG_LIB_VERSION_MINOR < 4 )
    compInfo.in_color_space = JCS_EXT_BGR;
#else
    compInfo.in_color_space = JCS_RGB;
#endif
    jpeg_set_defaults( &compInfo );
    jpeg_set_quality( &compInfo, 85, TRUE );

    jpeg_start_compress( &compInfo, TRUE );

    auto stride = compInfo.image_width * Bpp;

    while (compInfo.next_scanline < compInfo.image_height) {
      row_pointer[0] = &m_buff[compInfo.next_scanline * stride];
      jpeg_write_scanlines(&compInfo, row_pointer, 1);
    }
    jpeg_finish_compress(&compInfo);
    jpeg_destroy_compress(&compInfo);
#elif defined(WITH_EVAS)
    auto evas_obj = std::unique_ptr<Evas_Object, void(*)(Evas_Object*)>( evas_object_image_add( m_canvas.get() ), evas_object_del );
    if ( evas_obj == nullptr )
        return false;
    evas_object_image_colorspace_set( evas_obj.get(), EVAS_COLORSPACE_ARGB8888 );
    evas_object_image_size_set( evas_obj.get(), Width, m_height );
    evas_object_image_data_set( evas_obj.get(), m_buff.get() );

    evas_object_image_save( evas_obj.get(), path.c_str(), NULL, "quality=100 compress=9");
#else
#error FIXME
#endif

    file->setSnapshot( path );
    m_cb->done( file, Status::Success, data );
    return true;
}
<commit_msg>VLCThumbnailer: Fix inverted logic.<commit_after>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
 *
 * Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
 *
 * This program 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 program 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 program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "VLCThumbnailer.h"

#include <cstring>
#ifdef WITH_JPEG
#include <jpeglib.h>
#elif defined(WITH_EVAS)
#include <Evas_Engine_Buffer.h>
#endif
#include <setjmp.h>

#include "IMedia.h"
#include "logging/Logger.h"
#include "MediaLibrary.h"

VLCThumbnailer::VLCThumbnailer(const VLC::Instance &vlc)
    : m_instance( vlc )
#ifdef WITH_EVAS
    , m_canvas( nullptr, &evas_free )
#endif
    , m_snapshotRequired( false )
    , m_height( 0 )
{
#ifdef WITH_EVAS
    static int fakeBuffer;
    evas_init();
    auto method = evas_render_method_lookup("buffer");
    m_canvas.reset( evas_new() );
    if ( m_canvas == nullptr )
        throw std::runtime_error( "Failed to allocate canvas" );
    evas_output_method_set( m_canvas.get(), method );
    evas_output_size_set(m_canvas.get(), 1, 1 );
    evas_output_viewport_set( m_canvas.get(), 0, 0, 1, 1 );
    auto einfo = (Evas_Engine_Info_Buffer *)evas_engine_info_get( m_canvas.get() );
    einfo->info.depth_type = EVAS_ENGINE_BUFFER_DEPTH_ARGB32;
    einfo->info.dest_buffer = &fakeBuffer;
    einfo->info.dest_buffer_row_bytes = 4;
    einfo->info.use_color_key = 0;
    einfo->info.alpha_threshold = 0;
    einfo->info.func.new_update_region = NULL;
    einfo->info.func.free_update_region = NULL;
    evas_engine_info_set( m_canvas.get(), (Evas_Engine_Info *)einfo );
#endif
}

VLCThumbnailer::~VLCThumbnailer()
{
#ifdef WITH_EVAS
    evas_shutdown();
#endif
}

bool VLCThumbnailer::initialize(IMetadataServiceCb *callback, MediaLibrary* ml)
{
    m_cb = callback;
    m_ml = ml;
    return true;
}

unsigned int VLCThumbnailer::priority() const
{
    // This needs to be lower than the VLCMetadataService, since we want to know the file type.
    return 50;
}

void VLCThumbnailer::run(std::shared_ptr<Media> file, void *data )
{
    if ( file->type() == IMedia::Type::UnknownType )
    {
        // If we don't know the file type yet, it actually looks more like a bug
        // since this should run after file type deduction, and not run in case
        // that step fails.
        m_cb->done( file, Status::Fatal, data );
        return;
    }
    else if ( file->type() != IMedia::Type::VideoType )
    {
        // There's no point in generating a thumbnail for a non-video file.
        m_cb->done( file, Status::Success, data );
        return;
    }
    else if ( file->snapshot().empty() == false )
    {
        LOG_INFO(file->snapshot(), " already has a snapshot" );
        m_cb->done( file, Status::Success, data );
        return;
    }

    VLC::Media media( m_instance, file->mrl(), VLC::Media::FromPath );
    media.addOption( ":no-audio" );
    VLC::MediaPlayer mp( media );

    setupVout( mp );

    if ( startPlayback( file, mp, data ) == false )
        return;

    // Seek ahead to have a significant preview
    if ( seekAhead( file, mp, data ) == false )
        return;

    takeSnapshot( file, mp, data );
}

bool VLCThumbnailer::startPlayback(std::shared_ptr<Media> file, VLC::MediaPlayer &mp, void* data )
{
    bool failed = true;

    std::unique_lock<std::mutex> lock( m_mutex );

    mp.eventManager().onPlaying([this, &failed]() {
        std::unique_lock<std::mutex> lock( m_mutex );
        failed = false;
        m_cond.notify_all();
    });
    mp.eventManager().onEncounteredError([this]() {
        std::unique_lock<std::mutex> lock( m_mutex );
        m_cond.notify_all();
    });
    mp.play();
    bool success = m_cond.wait_for( lock, std::chrono::seconds( 3 ), [&mp]() {
        return mp.state() == libvlc_Playing || mp.state() == libvlc_Error;
    });
    if ( success == false || failed == true )
    {
        // In case of timeout or error, don't go any further
        m_cb->done( file, Status::Error, data );
        return false;
    }
    return true;
}

bool VLCThumbnailer::seekAhead(std::shared_ptr<Media> file, VLC::MediaPlayer& mp, void* data )
{
    std::unique_lock<std::mutex> lock( m_mutex );
    float pos = .0f;
    auto event = mp.eventManager().onPositionChanged([this, &pos](float p) {
        std::unique_lock<std::mutex> lock( m_mutex );
        pos = p;
        m_cond.notify_all();
    });
    mp.setPosition( .4f );
    bool success = m_cond.wait_for( lock, std::chrono::seconds( 3 ), [&pos]() {
        return pos >= .1f;
    });
    // Since we're locking a mutex for each position changed, let's unregister ASAP
    event->unregister();
    if ( success == false )
    {
        m_cb->done( file, Status::Error, data );
        return false;
    }
    return true;
}

void VLCThumbnailer::setupVout( VLC::MediaPlayer& mp )
{
    mp.setVideoFormatCallbacks(
        // Setup
        [this, &mp](char* chroma, unsigned int* width, unsigned int *height, unsigned int *pitches, unsigned int *lines) {
            strcpy( chroma, "RV32" );

            const float inputAR = (float)*width / *height;

            *width = Width;
            auto prevHeight = m_height;
            m_height = (float)Width / inputAR + 1;
            // If our buffer isn't enough anymore, reallocate a new one.
            if ( m_height > prevHeight )
            {
                m_buff.reset( new uint8_t[Width * m_height * Bpp] );
            }
            *height = m_height;
            *pitches = Width * Bpp;
            *lines = m_height;
            return 1;
        },
        // Cleanup
        nullptr);
    mp.setVideoCallbacks(
        // Lock
        [this](void** pp_buff) {
            *pp_buff = m_buff.get();
            return nullptr;
        },
        //unlock
        [this](void*, void*const*) {
            std::unique_lock<std::mutex> lock(m_mutex);
            if ( m_snapshotRequired == true )
            {
                m_snapshotRequired = false;
                m_cond.notify_all();
            }
        }
        ,
        //display
        nullptr
    );
}

bool VLCThumbnailer::takeSnapshot(std::shared_ptr<Media> file, VLC::MediaPlayer &mp, void *data)
{
    // lock, signal that we want a snapshot, and wait.
    {
        std::unique_lock<std::mutex> lock( m_mutex );
        m_snapshotRequired = true;
        bool success = m_cond.wait_for( lock, std::chrono::seconds( 3 ), [this]() {
            // Keep waiting if the vmem thread hasn't restored m_snapshotRequired to false
            return m_snapshotRequired == false;
        });
        if ( success == false )
        {
            m_cb->done( file, Status::Error, data );
            return false;
        }
    }
    mp.stop();
    return compress( file, data );
}

#ifdef WITH_JPEG

struct jpegError : public jpeg_error_mgr
{
    jmp_buf buff;
    char message[JMSG_LENGTH_MAX];

    static void jpegErrorHandler(j_common_ptr common)
    {
        auto error = reinterpret_cast<jpegError*>(common->err);
        (*error->format_message)( common, error->message );
        longjmp(error->buff, 1);
    }
};

#endif

bool VLCThumbnailer::compress( std::shared_ptr<Media> file, void *data )
{
    auto path = m_ml->snapshotPath();
    path += "/";
    path += std::to_string( file->id() ) +
#ifdef WITH_EVAS
            ".png";
#else
            ".jpg";
#endif

#ifdef WITH_JPEG
    //FIXME: Abstract this away, though libjpeg requires a FILE*...
    auto fOut = fopen(path.c_str(), "wb");
    // ensure we always close the file.
    auto fOutPtr = std::unique_ptr<FILE, int(*)(FILE*)>( fOut, &fclose );
    if ( fOut == nullptr )
    {
        LOG_ERROR("Failed to open snapshot file ", path);
        m_cb->done( file, Status::Error, data );
        return false;
    }

    jpeg_compress_struct compInfo;
    JSAMPROW row_pointer[1];

    //libjpeg's default error handling is to call exit(), which would
    //be slightly problematic...
    jpegError err;
    compInfo.err = jpeg_std_error(&err);
    err.error_exit = jpegError::jpegErrorHandler;

    if ( setjmp( err.buff ) )
    {
        LOG_ERROR("JPEG failure: ", err.message);
        jpeg_destroy_compress(&compInfo);
        m_cb->done( file, Status::Error, data );
        return false;
    }

    jpeg_create_compress(&compInfo);
    jpeg_stdio_dest(&compInfo, fOut);

    compInfo.image_width = Width;
    compInfo.image_height = m_height;
    compInfo.input_components = Bpp;
#if ( !defined(JPEG_LIB_VERSION_MAJOR) && !defined(JPEG_LIB_VERSION_MINOR) ) || \
    ( JPEG_LIB_VERSION_MAJOR <= 8 && JPEG_LIB_VERSION_MINOR < 4 )
    compInfo.in_color_space = JCS_EXT_BGR;
#else
    compInfo.in_color_space = JCS_RGB;
#endif
    jpeg_set_defaults( &compInfo );
    jpeg_set_quality( &compInfo, 85, TRUE );

    jpeg_start_compress( &compInfo, TRUE );

    auto stride = compInfo.image_width * Bpp;

    while (compInfo.next_scanline < compInfo.image_height) {
      row_pointer[0] = &m_buff[compInfo.next_scanline * stride];
      jpeg_write_scanlines(&compInfo, row_pointer, 1);
    }
    jpeg_finish_compress(&compInfo);
    jpeg_destroy_compress(&compInfo);
#elif defined(WITH_EVAS)
    auto evas_obj = std::unique_ptr<Evas_Object, void(*)(Evas_Object*)>( evas_object_image_add( m_canvas.get() ), evas_object_del );
    if ( evas_obj == nullptr )
        return false;
    evas_object_image_colorspace_set( evas_obj.get(), EVAS_COLORSPACE_ARGB8888 );
    evas_object_image_size_set( evas_obj.get(), Width, m_height );
    evas_object_image_data_set( evas_obj.get(), m_buff.get() );

    evas_object_image_save( evas_obj.get(), path.c_str(), NULL, "quality=100 compress=9");
#else
#error FIXME
#endif

    file->setSnapshot( path );
    m_cb->done( file, Status::Success, data );
    return true;
}
<|endoftext|>


<commit_before><commit_msg>INTEGRATION: CWS os2port02 (1.2.4); FILE MERGED 2007/10/08 14:26:44 obr 1.2.4.3: RESYNC: (1.2-1.3); FILE MERGED 2007/10/05 13:30:32 ydario 1.2.4.2: Implemented cws sb71 sal init/deinit sequence to set/unset exception hooks. 2007/09/30 11:54:50 ydario 1.2.4.1: Issue number: i82034 Submitted by: ydario Reviewed by:  ydario Commit of changes for OS/2 CWS source code integration.<commit_after><|endoftext|>


<commit_before>/*  MoTo - Motion Toolkit
    Copyright (c) 2006-2019 Gino van den Bergen, DTECTA

    Source published under the terms of the MIT License.
    For details please see COPYING file or visit
    http://opensource.org/licenses/MIT
*/

#ifndef MT_ERRORTRACER_HPP
#define MT_ERRORTRACER_HPP

#ifdef USE_OSTREAM
#include <ostream>
#endif

#include "Scalar.hpp"
#include "ScalarTraits.hpp"

namespace mt
{
    template <typename T>
    class ErrorTracer
    {
    public:
        typedef T Value_type;

        explicit ErrorTracer(T value = T(), T error = T());

        /// Floating-point value.
        T value() const;

        /// Estimated error bound.
        T error() const;

        /// Estimated upper bound for the relative rounding error of this value. The value returned by maxRelativeError() is equal to the value returned by error() times the machine epsilon.
        T maxRelativeError() const;

        /// Number of dirty bits in the significand of this value. The number of least-signinifant bits that are contaminated by rounding.
        T dirtyBits() const;

        /// Implicit cast of floating-point value.
        operator T() const;

        ErrorTracer<T>& operator=(T value);
        ErrorTracer<T>& operator+=(const ErrorTracer& x);
        ErrorTracer<T>& operator-=(const ErrorTracer& x);
        ErrorTracer<T>& operator*=(const ErrorTracer& x);
        ErrorTracer<T>& operator/=(const ErrorTracer& x);

    private:
        T mValue;
        T mError;
    };

    template <typename T> ErrorTracer<T> operator-(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> operator+(const ErrorTracer<T>& x,  const ErrorTracer<T>& y);
    template <typename T> ErrorTracer<T> operator-(const ErrorTracer<T>& x,  const ErrorTracer<T>& y);
    template <typename T> ErrorTracer<T> operator*(const ErrorTracer<T>& x,  const ErrorTracer<T>& y);
    template <typename T> ErrorTracer<T> operator/(const ErrorTracer<T>& x,  const ErrorTracer<T>& y);

#ifdef USE_OSTREAM

    template <typename CharT, typename Traits, typename T>
    std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const ErrorTracer<T>& x);

#endif

    template <typename T> ErrorTracer<T> abs(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> acos(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> asin(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> atan(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> atan2(const ErrorTracer<T>& x, const ErrorTracer<T>& y);
    template <typename T> ErrorTracer<T> cos(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> exp(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> log(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> pow(const ErrorTracer<T>& x, const ErrorTracer<T>& y);
    template <typename T> ErrorTracer<T> sin(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> sqrt(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> tan(const ErrorTracer<T>& x);

    template <typename T>
    FORCEINLINE
    ErrorTracer<T>::ErrorTracer(T value, T error)
        : mValue(value)
        , mError(error)
    {}

    template <typename T>
    FORCEINLINE
    T ErrorTracer<T>::value() const
    {
        return mValue;
    }

    template <typename T>
    FORCEINLINE
    T ErrorTracer<T>::error() const
    {
        return mError;
    }

    template <typename T>
    FORCEINLINE
    T ErrorTracer<T>::maxRelativeError() const
    {
        T gMachineEpsilon = std::numeric_limits<T>::epsilon() * T(0.5);

        return mError * gMachineEpsilon;
    }

    template <typename T>
    FORCEINLINE
    T ErrorTracer<T>::dirtyBits() const
    {
        return log2(mError);
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T>::operator T() const
    {
        return mValue;
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T>& ErrorTracer<T>::operator=(T value)
    {
        mValue = value;
        mError = T();
        return *this;
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T>& ErrorTracer<T>::operator+=(const ErrorTracer<T>& x)
    {
        *this = *this + x;
        return *this;
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T>& ErrorTracer<T>::operator-=(const ErrorTracer<T>& x)
    {
        *this = *this - x;
        return *this;
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T>& ErrorTracer<T>::operator*=(const ErrorTracer<T>& x)
    {
        *this = *this * x;
        return *this;
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T>& ErrorTracer<T>::operator/=(const ErrorTracer<T>& x)
    {
        *this = *this / x;
        return *this;
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> operator-(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(-x.value(),
                                    x.error());
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> operator+(const ErrorTracer<T>& x,  const ErrorTracer<T>& y)
    {
        T value = x.value() + y.value();
        return ErrorTracer<T>(value,
                                   !iszero(value) ? (abs(x.value()) * x.error() + abs(y.value()) * y.error()) / abs(value) + T(1) : T());
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> operator-(const ErrorTracer<T>& x, const ErrorTracer<T>& y)
    {
        T value = x.value() - y.value();
        return ErrorTracer<T>(value,
                                   !iszero(value) ? (abs(x.value()) * x.error() + abs(y.value()) * y.error()) / abs(value) + T(1) : T());
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> operator*(const ErrorTracer<T>& x, const ErrorTracer<T>& y)
    {
        return ErrorTracer<T>(x.value() * y.value(),
                                    x.error() + y.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> operator/(const ErrorTracer<T>& x, const ErrorTracer<T>& y)
    {
        return ErrorTracer<T>(x.value() / y.value(),
                                    x.error() + y.error() + T(1));
    }

#ifdef USE_OSTREAM

    template <typename CharT, typename Traits, typename T>
    FORCEINLINE
    std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const ErrorTracer<T>& x)
    {
        return os << x.value() << '[' << x.error() << ']';
    }

#endif

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> abs(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(abs(x.value()), x.error());
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> acos(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(acos(x.value()), x.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> asin(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(asin(x.value()), x.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> atan(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(atan(x.value()), x.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> atan2(const ErrorTracer<T>& x, const ErrorTracer<T>& y)
    {
        return ErrorTracer<T>(atan2(x.value(), y.value()), x.error() + y.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> cos(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(cos(x.value()), x.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> exp(const ErrorTracer<T>& x)
    {
        T value = exp(x.value());
        return ErrorTracer<T>(value, value * x.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> log(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(log(x.value()), x.error() / x.value() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> pow(const ErrorTracer<T>& x, const ErrorTracer<T>& y)
    {
        ASSERT(!isnegative(x.value()));
        T value = pow(x.value(), y.value());
        return ErrorTracer<T>(value, abs(y.value()) * x.error() + abs(value * log(x.value())) * y.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> sin(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(sin(x.value()), x.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> sqrt(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(sqrt(x.value()), x.error() * T(0.5) + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> tan(const ErrorTracer<T>& x)
    {
        T value = tan(x.value());
        return ErrorTracer<T>(value, (T(1) + value * value) * x.error() + T(1));
    }

    template <typename T>
    struct ScalarTraits<ErrorTracer<T> >
    {
        static FORCEINLINE ErrorTracer<T> pi()
        {
            return ErrorTracer<T>(ScalarTraits<T>::pi());
        }

        static FORCEINLINE ErrorTracer<T> infinity()
        {
            return ErrorTracer<T>(ScalarTraits<T>::infinity());
        }

        static FORCEINLINE ErrorTracer<T> epsilon()
        {
            return ErrorTracer<T>(ScalarTraits<T>::epsilon());
        }
    };
}

namespace std
{
    template <typename T>
    class numeric_limits<mt::ErrorTracer<T> >
    {
    public:
        static mt::ErrorTracer<T> min() { return mt::ErrorTracer<T>(numeric_limits<T>::min()); }
        static mt::ErrorTracer<T> max() { return mt::ErrorTracer<T>(numeric_limits<T>::max()); }
        static mt::ErrorTracer<T> epsilon() { return mt::ErrorTracer<T>(numeric_limits<T>::epsilon()); }
        static mt::ErrorTracer<T> round_error() { return mt::ErrorTracer<T>(numeric_limits<T>::round_error()); }
        static mt::ErrorTracer<T> infinity() { return mt::ErrorTracer<T>(numeric_limits<T>::infinity()); }
        static mt::ErrorTracer<T> quiet_NaN() { return mt::ErrorTracer<T>(numeric_limits<T>::quiet_NaN()); }
        static mt::ErrorTracer<T> signaling_NaN() { return mt::ErrorTracer<T>(numeric_limits<T>::signaling_NaN()); }
        static mt::ErrorTracer<T> denorm_min() { return mt::ErrorTracer<T>(numeric_limits<T>::denorm_min()); }
    };
}

#endif
<commit_msg>Layout<commit_after>/*  MoTo - Motion Toolkit
    Copyright (c) 2006-2019 Gino van den Bergen, DTECTA

    Source published under the terms of the MIT License.
    For details please see COPYING file or visit
    http://opensource.org/licenses/MIT
*/

#ifndef MT_ERRORTRACER_HPP
#define MT_ERRORTRACER_HPP

#ifdef USE_OSTREAM
#include <ostream>
#endif

#include "Scalar.hpp"
#include "ScalarTraits.hpp"

namespace mt
{
    template <typename T>
    class ErrorTracer
    {
    public:
        typedef T Value_type;

        explicit ErrorTracer(T value = T(), T error = T());

        /// Floating-point value.
        T value() const;

        /// Estimated error bound.
        T error() const;

        /// Estimated upper bound for the relative rounding error of this value. The value returned by maxRelativeError() is equal to the value returned by error() times the machine epsilon.
        T maxRelativeError() const;

        /// Number of dirty bits in the significand of this value. The number of least-signinifant bits that are contaminated by rounding.
        T dirtyBits() const;

        /// Implicit cast of floating-point value.
        operator T() const;

        ErrorTracer<T>& operator=(T value);
        ErrorTracer<T>& operator+=(const ErrorTracer& x);
        ErrorTracer<T>& operator-=(const ErrorTracer& x);
        ErrorTracer<T>& operator*=(const ErrorTracer& x);
        ErrorTracer<T>& operator/=(const ErrorTracer& x);

    private:
        T mValue;
        T mError;
    };

    template <typename T> ErrorTracer<T> operator-(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> operator+(const ErrorTracer<T>& x,  const ErrorTracer<T>& y);
    template <typename T> ErrorTracer<T> operator-(const ErrorTracer<T>& x,  const ErrorTracer<T>& y);
    template <typename T> ErrorTracer<T> operator*(const ErrorTracer<T>& x,  const ErrorTracer<T>& y);
    template <typename T> ErrorTracer<T> operator/(const ErrorTracer<T>& x,  const ErrorTracer<T>& y);

#ifdef USE_OSTREAM

    template <typename CharT, typename Traits, typename T>
    std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const ErrorTracer<T>& x);

#endif

    template <typename T> ErrorTracer<T> abs(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> acos(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> asin(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> atan(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> atan2(const ErrorTracer<T>& x, const ErrorTracer<T>& y);
    template <typename T> ErrorTracer<T> cos(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> exp(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> log(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> pow(const ErrorTracer<T>& x, const ErrorTracer<T>& y);
    template <typename T> ErrorTracer<T> sin(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> sqrt(const ErrorTracer<T>& x);
    template <typename T> ErrorTracer<T> tan(const ErrorTracer<T>& x);

    template <typename T>
    FORCEINLINE
    ErrorTracer<T>::ErrorTracer(T value, T error)
        : mValue(value)
        , mError(error)
    {}

    template <typename T>
    FORCEINLINE
    T ErrorTracer<T>::value() const
    {
        return mValue;
    }

    template <typename T>
    FORCEINLINE
    T ErrorTracer<T>::error() const
    {
        return mError;
    }

    template <typename T>
    FORCEINLINE
    T ErrorTracer<T>::maxRelativeError() const
    {
        T gMachineEpsilon = std::numeric_limits<T>::epsilon() * T(0.5);

        return mError * gMachineEpsilon;
    }

    template <typename T>
    FORCEINLINE
    T ErrorTracer<T>::dirtyBits() const
    {
        return log2(mError);
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T>::operator T() const
    {
        return mValue;
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T>& ErrorTracer<T>::operator=(T value)
    {
        mValue = value;
        mError = T();
        return *this;
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T>& ErrorTracer<T>::operator+=(const ErrorTracer<T>& x)
    {
        *this = *this + x;
        return *this;
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T>& ErrorTracer<T>::operator-=(const ErrorTracer<T>& x)
    {
        *this = *this - x;
        return *this;
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T>& ErrorTracer<T>::operator*=(const ErrorTracer<T>& x)
    {
        *this = *this * x;
        return *this;
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T>& ErrorTracer<T>::operator/=(const ErrorTracer<T>& x)
    {
        *this = *this / x;
        return *this;
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> operator-(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(-x.value(),
                              x.error());
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> operator+(const ErrorTracer<T>& x,  const ErrorTracer<T>& y)
    {
        T value = x.value() + y.value();
        T error = abs(x.value()) * x.error() + abs(y.value()) * y.error();
        return ErrorTracer<T>(value,
                              !iszero(value) ? error / abs(value) + T(1) : T());
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> operator-(const ErrorTracer<T>& x, const ErrorTracer<T>& y)
    {
        T value = x.value() - y.value();
        T error = abs(x.value()) * x.error() + abs(y.value()) * y.error();
        return ErrorTracer<T>(value,
                              !iszero(value) ? error / abs(value) + T(1) : T());
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> operator*(const ErrorTracer<T>& x, const ErrorTracer<T>& y)
    {
        return ErrorTracer<T>(x.value() * y.value(),
                              x.error() + y.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> operator/(const ErrorTracer<T>& x, const ErrorTracer<T>& y)
    {
        return ErrorTracer<T>(x.value() / y.value(),
                              x.error() + y.error() + T(1));
    }

#ifdef USE_OSTREAM

    template <typename CharT, typename Traits, typename T>
    FORCEINLINE
    std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const ErrorTracer<T>& x)
    {
        return os << x.value() << '[' << x.error() << ']';
    }

#endif

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> abs(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(abs(x.value()), x.error());
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> acos(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(acos(x.value()), x.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> asin(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(asin(x.value()), x.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> atan(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(atan(x.value()), x.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> atan2(const ErrorTracer<T>& x, const ErrorTracer<T>& y)
    {
        return ErrorTracer<T>(atan2(x.value(), y.value()), x.error() + y.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> cos(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(cos(x.value()), x.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> exp(const ErrorTracer<T>& x)
    {
        T value = exp(x.value());
        return ErrorTracer<T>(value, value * x.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> log(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(log(x.value()), x.error() / x.value() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> pow(const ErrorTracer<T>& x, const ErrorTracer<T>& y)
    {
        ASSERT(!isnegative(x.value()));
        T value = pow(x.value(), y.value());
        return ErrorTracer<T>(value, abs(y.value()) * x.error() + abs(value * log(x.value())) * y.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> sin(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(sin(x.value()), x.error() + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> sqrt(const ErrorTracer<T>& x)
    {
        return ErrorTracer<T>(sqrt(x.value()), x.error() * T(0.5) + T(1));
    }

    template <typename T>
    FORCEINLINE
    ErrorTracer<T> tan(const ErrorTracer<T>& x)
    {
        T value = tan(x.value());
        return ErrorTracer<T>(value, (T(1) + value * value) * x.error() + T(1));
    }

    template <typename T>
    struct ScalarTraits<ErrorTracer<T> >
    {
        static FORCEINLINE ErrorTracer<T> pi()
        {
            return ErrorTracer<T>(ScalarTraits<T>::pi());
        }

        static FORCEINLINE ErrorTracer<T> infinity()
        {
            return ErrorTracer<T>(ScalarTraits<T>::infinity());
        }

        static FORCEINLINE ErrorTracer<T> epsilon()
        {
            return ErrorTracer<T>(ScalarTraits<T>::epsilon());
        }
    };
}

namespace std
{
    template <typename T>
    class numeric_limits<mt::ErrorTracer<T> >
    {
    public:
        static mt::ErrorTracer<T> min() { return mt::ErrorTracer<T>(numeric_limits<T>::min()); }
        static mt::ErrorTracer<T> max() { return mt::ErrorTracer<T>(numeric_limits<T>::max()); }
        static mt::ErrorTracer<T> epsilon() { return mt::ErrorTracer<T>(numeric_limits<T>::epsilon()); }
        static mt::ErrorTracer<T> round_error() { return mt::ErrorTracer<T>(numeric_limits<T>::round_error()); }
        static mt::ErrorTracer<T> infinity() { return mt::ErrorTracer<T>(numeric_limits<T>::infinity()); }
        static mt::ErrorTracer<T> quiet_NaN() { return mt::ErrorTracer<T>(numeric_limits<T>::quiet_NaN()); }
        static mt::ErrorTracer<T> signaling_NaN() { return mt::ErrorTracer<T>(numeric_limits<T>::signaling_NaN()); }
        static mt::ErrorTracer<T> denorm_min() { return mt::ErrorTracer<T>(numeric_limits<T>::denorm_min()); }
    };
}

#endif
<|endoftext|>