<commit_before>/*
 * Copyright © 2019 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "glmemshadow.hpp"

#include <unordered_map>
#include <algorithm>

#include <assert.h>

#ifdef _WIN32

#include <windows.h>

#else

#include <unistd.h>
#include <signal.h>
#include <sys/mman.h>

#endif

#include "gltrace.hpp"
#include "os_thread.hpp"
#include "os.hpp"

static bool sInitialized = false;

static std::unordered_map<size_t, GLMemoryShadow*> sPages;
static size_t sPageSize;

static os::mutex mutex;

enum class MemProtection {
#ifdef _WIN32
    NO_ACCESS = PAGE_NOACCESS,
    READ_ONLY = PAGE_READONLY,
    READ_WRITE = PAGE_READWRITE,
#else
    NO_ACCESS = PROT_NONE,
    READ_ONLY = PROT_READ,
    READ_WRITE = PROT_READ | PROT_WRITE,
#endif
};

size_t getSystemPageSize() {
#ifdef _WIN32
    SYSTEM_INFO info;
    GetSystemInfo(&info);
    return info.dwPageSize;
#else
    return sysconf(_SC_PAGESIZE);
#endif
}

void memProtect(void *addr, size_t size, MemProtection protection) {
#ifdef _WIN32
    DWORD flOldProtect;
    BOOL bRet = VirtualProtect(addr, size, static_cast<DWORD>(protection), &flOldProtect);
    if (!bRet) {
        DWORD dwLastError = GetLastError();
        os::log("apitrace: error: VirtualProtect failed with error 0x%lx\n", dwLastError);
        os::abort();
    }
#else
    const int err = mprotect(addr, size, static_cast<int>(protection));
    if (err) {
        const char *errorStr = strerror(err);
        os::log("apitrace: error: mprotect failed with error \"%s\"\n", errorStr);
        os::abort();
    }
#endif
}

template<typename T, typename U>
auto divRoundUp(T a, U b) -> decltype(a / b) {
    return (a + b - 1) / b;
}

#ifdef _WIN32
static LONG CALLBACK
VectoredHandler(PEXCEPTION_POINTERS pExceptionInfo)
{
    PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
    DWORD ExceptionCode = pExceptionRecord->ExceptionCode;

    if (ExceptionCode == EXCEPTION_ACCESS_VIOLATION &&
        pExceptionRecord->NumberParameters >= 2 &&
        pExceptionRecord->ExceptionInformation[0] == 1) { // writing

        const uintptr_t addr = static_cast<uintptr_t>(pExceptionRecord->ExceptionInformation[1]);
        const size_t page = addr / sPageSize;

        os::unique_lock<os::mutex> lock(mutex);

        const auto it = sPages.find(page);
        if (it != sPages.end()) {
            GLMemoryShadow *shadow = it->second;
            shadow->onAddressWrite(addr, page);
            return EXCEPTION_CONTINUE_EXECUTION;
        } else {
            os::log("apitrace: error: %s: access violation at non-tracked page\n", __FUNCTION__);
            os::abort();
        }
    }

    return EXCEPTION_CONTINUE_SEARCH;
}

#else

void PageGuardExceptionHandler(int sig, siginfo_t *si, void *unused) {
    if (sig == SIGSEGV && si->si_code == SEGV_ACCERR) {
        const uintptr_t addr = reinterpret_cast<uintptr_t>(si->si_addr);
        const size_t page = addr / sPageSize;

        os::unique_lock<os::mutex> lock(mutex);

        const auto it = sPages.find(page);
        if (it != sPages.end()) {
            GLMemoryShadow *shadow = it->second;
            shadow->onAddressWrite(addr, page);
        } else {
            os::log("apitrace: error: %s: access violation at non-tracked page\n", __FUNCTION__);
            os::abort();
        }
    }
}
#endif

void initializeGlobals()
{
    sPageSize = getSystemPageSize();

#ifdef _WIN32
    if (AddVectoredExceptionHandler(1, VectoredHandler) == NULL) {
        os::log("apitrace: error: %s: add vectored exception handler failed\n", __FUNCTION__);
    }
#else
    struct sigaction sa, oldSa;
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    sa.sa_sigaction = PageGuardExceptionHandler;
    if (sigaction(SIGSEGV, &sa, &oldSa) == -1) {
        os::log("apitrace: error: %s: set page guard exception handler failed\n", __FUNCTION__);
    }
#endif
}

GLMemoryShadow::~GLMemoryShadow()
{
    os::unique_lock<os::mutex> lock(mutex);

    const size_t startPage = reinterpret_cast<uintptr_t>(shadowMemory) / sPageSize;
    for (size_t i = 0; i < nPages; i++) {
        sPages.erase(startPage + i);
    }

#ifdef _WIN32
    VirtualFree(shadowMemory, nPages * sPageSize, MEM_RELEASE);
#else
    munmap(shadowMemory, nPages * sPageSize);
#endif
}

bool GLMemoryShadow::init(const void *data, size_t size)
{
    if (!sInitialized) {
        initializeGlobals();
        sInitialized = true;
    }

    nPages = divRoundUp(size, sPageSize);
    const size_t adjustedSize = nPages * sPageSize;

#ifdef _WIN32
    shadowMemory = reinterpret_cast<uint8_t*>(VirtualAlloc(nullptr, adjustedSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
#else
    shadowMemory = reinterpret_cast<uint8_t*>(mmap(nullptr, adjustedSize, PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
#endif

    if (!shadowMemory) {
        os::log("apitrace: error: %s: Failed to allocate shadow memory!\n", __FUNCTION__);
        return false;
    }

    if (data != nullptr) {
        memcpy(shadowMemory, data, size);
    }

    memProtect(shadowMemory, adjustedSize, MemProtection::NO_ACCESS);

    {
        os::unique_lock<os::mutex> lock(mutex);

        const size_t startPage = reinterpret_cast<uintptr_t>(shadowMemory) / sPageSize;
        for (size_t i = 0; i < nPages; i++) {
            sPages.emplace(startPage + i, this);
        }
    }

    dirtyPages.resize(divRoundUp(nPages, 32));

    return true;
}

void *GLMemoryShadow::map(gltrace::Context *_ctx, void *_glMemory, GLbitfield _flags, size_t start, size_t size)
{
    sharedRes = _ctx->sharedRes;
    glMemory = reinterpret_cast<uint8_t*>(_glMemory);
    flags = _flags;
    mappedStart = start;
    mappedSize = size;

    mappedStartPage = start / sPageSize;
    mappedEndPage = divRoundUp(start + size, sPageSize);

    uint8_t *protectStart = shadowMemory + mappedStartPage * sPageSize;
    const size_t protectSize = (mappedEndPage - mappedStartPage) * sPageSize;

    // The buffer may have been updated before the mapping.
    // TODO: handle write only buffers
    if (flags & GL_MAP_READ_BIT) {
        memProtect(protectStart, protectSize, MemProtection::READ_WRITE);
        memcpy(shadowMemory + start, glMemory, size);
    }

    memProtect(protectStart, protectSize, MemProtection::READ_ONLY);

    return shadowMemory + start;
}

void GLMemoryShadow::unmap(Callback callback)
{
    if (isDirty) {
        os::unique_lock<os::mutex> lock(mutex);
        commitWrites(callback);
    }

    {
        os::unique_lock<os::mutex> lock(mutex);

        shared_context_res_ptr_t res = sharedRes.lock();
        if (res) {
            auto it = std::find(res->dirtyShadows.begin(), res->dirtyShadows.end(), this);
            if (it != res->dirtyShadows.end()) {
                res->dirtyShadows.erase(it);
            }
        } else {
            os::log("apitrace: error: %s: context(s) are destroyed!\n", __FUNCTION__);
        }
    }

    memProtect(shadowMemory, nPages * sPageSize, MemProtection::NO_ACCESS);

    sharedRes.reset();
    glMemory = nullptr;
    flags = 0;
    mappedStart = 0;
    mappedSize = 0;
    pagesToDirtyOnConsecutiveWrites = 1;
}

void GLMemoryShadow::onAddressWrite(uintptr_t addr, size_t page)
{
    const size_t relativePage = (addr - reinterpret_cast<uintptr_t>(shadowMemory)) / sPageSize;
    if (isPageDirty(relativePage)) {
        // It is possible if writing to the same buffer from two threads
        return;
    }

    if ((relativePage == lastDirtiedRelativePage + 1) && isPageDirty(relativePage - 1)) {
        /* Ensure that we would have log(n) page exceptions if traced application writes
         * to n consecutive pages.
         */
        pagesToDirtyOnConsecutiveWrites *= 2;
    } else {
        pagesToDirtyOnConsecutiveWrites = 1;
    }

    const size_t endPageToDirty = std::min(relativePage + pagesToDirtyOnConsecutiveWrites, nPages);
    for (size_t pageToDirty = relativePage; pageToDirty < endPageToDirty; pageToDirty++) {
        setPageDirty(pageToDirty);
    }

    lastDirtiedRelativePage = endPageToDirty - 1;

    memProtect(reinterpret_cast<void*>(page * sPageSize),
               (endPageToDirty - relativePage) * sPageSize, MemProtection::READ_WRITE);
}

GLbitfield GLMemoryShadow::getMapFlags() const
{
    return flags;
}

void GLMemoryShadow::setPageDirty(size_t relativePage)
{
    assert(relativePage < nPages);
    dirtyPages[relativePage / 32] |= 1U << (relativePage % 32);

    if (!isDirty) {
        shared_context_res_ptr_t res = sharedRes.lock();
        if (res) {
            res->dirtyShadows.push_back(this);
            isDirty = true;
        } else {
            os::log("apitrace: error: %s: context(s) are destroyed!\n", __FUNCTION__);
        }
    }
}

bool GLMemoryShadow::isPageDirty(size_t relativePage)
{
    assert(relativePage < nPages);
    return dirtyPages[relativePage / 32] & (1U << (relativePage % 32));
}

void GLMemoryShadow::commitWrites(Callback callback)
{
    assert(isDirty);

    uint8_t *shadowSlice = shadowMemory + mappedStartPage * sPageSize;
    const size_t glStartOffset = mappedStart % sPageSize;

    /* Other thread may write to the buffers at this very moment
     * so we need to protect pages before we read from them.
     * The other thread will have to wait until we commit all writes we want.
     */
    for (size_t i = mappedStartPage; i < mappedEndPage; i++) {
        if (isPageDirty(i)) {
            memProtect(shadowMemory + i * sPageSize, sPageSize, MemProtection::READ_ONLY);
        }
    }

    for (size_t i = mappedStartPage; i < mappedEndPage; i++) {
        if (isPageDirty(i)) {
            // We coalesce consecutive writes into one
            size_t firstDirty = i;
            while (++i < mappedEndPage && isPageDirty(i)) { }

            const size_t pages = i - firstDirty;
            if (firstDirty != mappedStartPage) {
                const size_t shadowOffset = (firstDirty - mappedStartPage) * sPageSize;
                const size_t glOffset = shadowOffset - glStartOffset;
                const size_t size = std::min(glStartOffset + mappedSize - shadowOffset, sPageSize * pages);

                memcpy(glMemory + glOffset, shadowSlice + shadowOffset, size);
                callback(shadowSlice + shadowOffset, size);
            } else {
                const size_t size = std::min(sPageSize * pages - glStartOffset, mappedSize);

                memcpy(glMemory, shadowSlice + glStartOffset, size);
                callback(shadowSlice + glStartOffset, size);
            }
        }
    }

    std::fill(dirtyPages.begin(), dirtyPages.end(), 0);
    isDirty = false;
    pagesToDirtyOnConsecutiveWrites = 1;
    lastDirtiedRelativePage = UINT32_MAX - 1;
}

void GLMemoryShadow::updateForReads()
{
    uint8_t *protectStart = shadowMemory + mappedStartPage * sPageSize;
    const size_t protectSize = (mappedEndPage - mappedStartPage) * sPageSize;

    memProtect(protectStart, protectSize, MemProtection::READ_WRITE);

    memcpy(shadowMemory + mappedStart, glMemory, mappedSize);

    memProtect(protectStart, protectSize, MemProtection::READ_ONLY);
}

void GLMemoryShadow::commitAllWrites(gltrace::Context *_ctx, Callback callback)
{
    if (!_ctx->sharedRes->dirtyShadows.empty()) {
        os::unique_lock<os::mutex> lock(mutex);

        for (GLMemoryShadow *memoryShadow : _ctx->sharedRes->dirtyShadows) {
            memoryShadow->commitWrites(callback);
        }

        _ctx->sharedRes->dirtyShadows.clear();
    }
}

void GLMemoryShadow::syncAllForReads(gltrace::Context *_ctx)
{
    if (!_ctx->sharedRes->bufferToShadowMemory.empty()) {
        os::unique_lock<os::mutex> lock(mutex);

        for (auto& it : _ctx->sharedRes->bufferToShadowMemory) {
            GLMemoryShadow* memoryShadow = it.second.get();
            if (memoryShadow->getMapFlags() & GL_MAP_READ_BIT) {
                memoryShadow->updateForReads();
            }
        }
    }
}
<commit_msg>gltrace: Forward unhandled signals/exceptions to a next handler<commit_after>/*
 * Copyright © 2019 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "glmemshadow.hpp"

#include <unordered_map>
#include <algorithm>

#include <assert.h>

#ifdef _WIN32

#include <windows.h>

#else

#include <unistd.h>
#include <signal.h>
#include <sys/mman.h>

#endif

#include "gltrace.hpp"
#include "os_thread.hpp"
#include "os.hpp"

static bool sInitialized = false;

static std::unordered_map<size_t, GLMemoryShadow*> sPages;
static size_t sPageSize;

static os::mutex mutex;

enum class MemProtection {
#ifdef _WIN32
    NO_ACCESS = PAGE_NOACCESS,
    READ_ONLY = PAGE_READONLY,
    READ_WRITE = PAGE_READWRITE,
#else
    NO_ACCESS = PROT_NONE,
    READ_ONLY = PROT_READ,
    READ_WRITE = PROT_READ | PROT_WRITE,
#endif
};

size_t getSystemPageSize() {
#ifdef _WIN32
    SYSTEM_INFO info;
    GetSystemInfo(&info);
    return info.dwPageSize;
#else
    return sysconf(_SC_PAGESIZE);
#endif
}

void memProtect(void *addr, size_t size, MemProtection protection) {
#ifdef _WIN32
    DWORD flOldProtect;
    BOOL bRet = VirtualProtect(addr, size, static_cast<DWORD>(protection), &flOldProtect);
    if (!bRet) {
        DWORD dwLastError = GetLastError();
        os::log("apitrace: error: VirtualProtect failed with error 0x%lx\n", dwLastError);
        os::abort();
    }
#else
    const int err = mprotect(addr, size, static_cast<int>(protection));
    if (err) {
        const char *errorStr = strerror(err);
        os::log("apitrace: error: mprotect failed with error \"%s\"\n", errorStr);
        os::abort();
    }
#endif
}

template<typename T, typename U>
auto divRoundUp(T a, U b) -> decltype(a / b) {
    return (a + b - 1) / b;
}

#ifdef _WIN32
static LONG CALLBACK
VectoredHandler(PEXCEPTION_POINTERS pExceptionInfo)
{
    PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
    DWORD ExceptionCode = pExceptionRecord->ExceptionCode;

    if (ExceptionCode == EXCEPTION_ACCESS_VIOLATION &&
        pExceptionRecord->NumberParameters >= 2 &&
        pExceptionRecord->ExceptionInformation[0] == 1) { // writing

        const uintptr_t addr = static_cast<uintptr_t>(pExceptionRecord->ExceptionInformation[1]);
        const size_t page = addr / sPageSize;

        os::unique_lock<os::mutex> lock(mutex);

        const auto it = sPages.find(page);
        if (it != sPages.end()) {
            GLMemoryShadow *shadow = it->second;
            shadow->onAddressWrite(addr, page);
            return EXCEPTION_CONTINUE_EXECUTION;
        }
    }

    return EXCEPTION_CONTINUE_SEARCH;
}

#else

static struct sigaction sPrevSigAction;

void PageGuardExceptionHandler(int sig, siginfo_t *si, void *context) {
    if (sig == SIGSEGV && si->si_code == SEGV_ACCERR) {
        const uintptr_t addr = reinterpret_cast<uintptr_t>(si->si_addr);
        const size_t page = addr / sPageSize;

        os::unique_lock<os::mutex> lock(mutex);

        const auto it = sPages.find(page);
        if (it != sPages.end()) {
            GLMemoryShadow *shadow = it->second;
            shadow->onAddressWrite(addr, page);
            return;
        }
    }

    if (sPrevSigAction.sa_flags & SA_SIGINFO) {
        sPrevSigAction.sa_sigaction(sig, si, context);
    } else {
        if (sPrevSigAction.sa_handler == SIG_DFL) {
            signal(sig, SIG_DFL);
            raise(sig);
        } else if (sPrevSigAction.sa_handler == SIG_IGN) {
            // Ignore
        } else {
            sPrevSigAction.sa_handler(sig);
        }
    }
}
#endif

void initializeGlobals()
{
    sPageSize = getSystemPageSize();

#ifdef _WIN32
    if (AddVectoredExceptionHandler(1, VectoredHandler) == NULL) {
        os::log("apitrace: error: %s: add vectored exception handler failed\n", __FUNCTION__);
    }
#else
    struct sigaction sa;
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    sa.sa_sigaction = PageGuardExceptionHandler;
    if (sigaction(SIGSEGV, &sa, &sPrevSigAction) == -1) {
        os::log("apitrace: error: %s: set page guard exception handler failed\n", __FUNCTION__);
    }
#endif
}

GLMemoryShadow::~GLMemoryShadow()
{
    os::unique_lock<os::mutex> lock(mutex);

    const size_t startPage = reinterpret_cast<uintptr_t>(shadowMemory) / sPageSize;
    for (size_t i = 0; i < nPages; i++) {
        sPages.erase(startPage + i);
    }

#ifdef _WIN32
    VirtualFree(shadowMemory, nPages * sPageSize, MEM_RELEASE);
#else
    munmap(shadowMemory, nPages * sPageSize);
#endif
}

bool GLMemoryShadow::init(const void *data, size_t size)
{
    if (!sInitialized) {
        initializeGlobals();
        sInitialized = true;
    }

    nPages = divRoundUp(size, sPageSize);
    const size_t adjustedSize = nPages * sPageSize;

#ifdef _WIN32
    shadowMemory = reinterpret_cast<uint8_t*>(VirtualAlloc(nullptr, adjustedSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
#else
    shadowMemory = reinterpret_cast<uint8_t*>(mmap(nullptr, adjustedSize, PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
#endif

    if (!shadowMemory) {
        os::log("apitrace: error: %s: Failed to allocate shadow memory!\n", __FUNCTION__);
        return false;
    }

    if (data != nullptr) {
        memcpy(shadowMemory, data, size);
    }

    memProtect(shadowMemory, adjustedSize, MemProtection::NO_ACCESS);

    {
        os::unique_lock<os::mutex> lock(mutex);

        const size_t startPage = reinterpret_cast<uintptr_t>(shadowMemory) / sPageSize;
        for (size_t i = 0; i < nPages; i++) {
            sPages.emplace(startPage + i, this);
        }
    }

    dirtyPages.resize(divRoundUp(nPages, 32));

    return true;
}

void *GLMemoryShadow::map(gltrace::Context *_ctx, void *_glMemory, GLbitfield _flags, size_t start, size_t size)
{
    sharedRes = _ctx->sharedRes;
    glMemory = reinterpret_cast<uint8_t*>(_glMemory);
    flags = _flags;
    mappedStart = start;
    mappedSize = size;

    mappedStartPage = start / sPageSize;
    mappedEndPage = divRoundUp(start + size, sPageSize);

    uint8_t *protectStart = shadowMemory + mappedStartPage * sPageSize;
    const size_t protectSize = (mappedEndPage - mappedStartPage) * sPageSize;

    // The buffer may have been updated before the mapping.
    // TODO: handle write only buffers
    if (flags & GL_MAP_READ_BIT) {
        memProtect(protectStart, protectSize, MemProtection::READ_WRITE);
        memcpy(shadowMemory + start, glMemory, size);
    }

    memProtect(protectStart, protectSize, MemProtection::READ_ONLY);

    return shadowMemory + start;
}

void GLMemoryShadow::unmap(Callback callback)
{
    if (isDirty) {
        os::unique_lock<os::mutex> lock(mutex);
        commitWrites(callback);
    }

    {
        os::unique_lock<os::mutex> lock(mutex);

        shared_context_res_ptr_t res = sharedRes.lock();
        if (res) {
            auto it = std::find(res->dirtyShadows.begin(), res->dirtyShadows.end(), this);
            if (it != res->dirtyShadows.end()) {
                res->dirtyShadows.erase(it);
            }
        } else {
            os::log("apitrace: error: %s: context(s) are destroyed!\n", __FUNCTION__);
        }
    }

    memProtect(shadowMemory, nPages * sPageSize, MemProtection::NO_ACCESS);

    sharedRes.reset();
    glMemory = nullptr;
    flags = 0;
    mappedStart = 0;
    mappedSize = 0;
    pagesToDirtyOnConsecutiveWrites = 1;
}

void GLMemoryShadow::onAddressWrite(uintptr_t addr, size_t page)
{
    const size_t relativePage = (addr - reinterpret_cast<uintptr_t>(shadowMemory)) / sPageSize;
    if (isPageDirty(relativePage)) {
        // It is possible if writing to the same buffer from two threads
        return;
    }

    if ((relativePage == lastDirtiedRelativePage + 1) && isPageDirty(relativePage - 1)) {
        /* Ensure that we would have log(n) page exceptions if traced application writes
         * to n consecutive pages.
         */
        pagesToDirtyOnConsecutiveWrites *= 2;
    } else {
        pagesToDirtyOnConsecutiveWrites = 1;
    }

    const size_t endPageToDirty = std::min(relativePage + pagesToDirtyOnConsecutiveWrites, nPages);
    for (size_t pageToDirty = relativePage; pageToDirty < endPageToDirty; pageToDirty++) {
        setPageDirty(pageToDirty);
    }

    lastDirtiedRelativePage = endPageToDirty - 1;

    memProtect(reinterpret_cast<void*>(page * sPageSize),
               (endPageToDirty - relativePage) * sPageSize, MemProtection::READ_WRITE);
}

GLbitfield GLMemoryShadow::getMapFlags() const
{
    return flags;
}

void GLMemoryShadow::setPageDirty(size_t relativePage)
{
    assert(relativePage < nPages);
    dirtyPages[relativePage / 32] |= 1U << (relativePage % 32);

    if (!isDirty) {
        shared_context_res_ptr_t res = sharedRes.lock();
        if (res) {
            res->dirtyShadows.push_back(this);
            isDirty = true;
        } else {
            os::log("apitrace: error: %s: context(s) are destroyed!\n", __FUNCTION__);
        }
    }
}

bool GLMemoryShadow::isPageDirty(size_t relativePage)
{
    assert(relativePage < nPages);
    return dirtyPages[relativePage / 32] & (1U << (relativePage % 32));
}

void GLMemoryShadow::commitWrites(Callback callback)
{
    assert(isDirty);

    uint8_t *shadowSlice = shadowMemory + mappedStartPage * sPageSize;
    const size_t glStartOffset = mappedStart % sPageSize;

    /* Other thread may write to the buffers at this very moment
     * so we need to protect pages before we read from them.
     * The other thread will have to wait until we commit all writes we want.
     */
    for (size_t i = mappedStartPage; i < mappedEndPage; i++) {
        if (isPageDirty(i)) {
            memProtect(shadowMemory + i * sPageSize, sPageSize, MemProtection::READ_ONLY);
        }
    }

    for (size_t i = mappedStartPage; i < mappedEndPage; i++) {
        if (isPageDirty(i)) {
            // We coalesce consecutive writes into one
            size_t firstDirty = i;
            while (++i < mappedEndPage && isPageDirty(i)) { }

            const size_t pages = i - firstDirty;
            if (firstDirty != mappedStartPage) {
                const size_t shadowOffset = (firstDirty - mappedStartPage) * sPageSize;
                const size_t glOffset = shadowOffset - glStartOffset;
                const size_t size = std::min(glStartOffset + mappedSize - shadowOffset, sPageSize * pages);

                memcpy(glMemory + glOffset, shadowSlice + shadowOffset, size);
                callback(shadowSlice + shadowOffset, size);
            } else {
                const size_t size = std::min(sPageSize * pages - glStartOffset, mappedSize);

                memcpy(glMemory, shadowSlice + glStartOffset, size);
                callback(shadowSlice + glStartOffset, size);
            }
        }
    }

    std::fill(dirtyPages.begin(), dirtyPages.end(), 0);
    isDirty = false;
    pagesToDirtyOnConsecutiveWrites = 1;
    lastDirtiedRelativePage = UINT32_MAX - 1;
}

void GLMemoryShadow::updateForReads()
{
    uint8_t *protectStart = shadowMemory + mappedStartPage * sPageSize;
    const size_t protectSize = (mappedEndPage - mappedStartPage) * sPageSize;

    memProtect(protectStart, protectSize, MemProtection::READ_WRITE);

    memcpy(shadowMemory + mappedStart, glMemory, mappedSize);

    memProtect(protectStart, protectSize, MemProtection::READ_ONLY);
}

void GLMemoryShadow::commitAllWrites(gltrace::Context *_ctx, Callback callback)
{
    if (!_ctx->sharedRes->dirtyShadows.empty()) {
        os::unique_lock<os::mutex> lock(mutex);

        for (GLMemoryShadow *memoryShadow : _ctx->sharedRes->dirtyShadows) {
            memoryShadow->commitWrites(callback);
        }

        _ctx->sharedRes->dirtyShadows.clear();
    }
}

void GLMemoryShadow::syncAllForReads(gltrace::Context *_ctx)
{
    if (!_ctx->sharedRes->bufferToShadowMemory.empty()) {
        os::unique_lock<os::mutex> lock(mutex);

        for (auto& it : _ctx->sharedRes->bufferToShadowMemory) {
            GLMemoryShadow* memoryShadow = it.second.get();
            if (memoryShadow->getMapFlags() & GL_MAP_READ_BIT) {
                memoryShadow->updateForReads();
            }
        }
    }
}
<|endoftext|>


<commit_before>#define DEBUG 1

/**
 * File    : A.cpp
 * Author  : Kazune Takahashi
 * Created : 2020/6/28 20:51:38
 * Powered by Visual Studio Code
 */
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <chrono>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <functional>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>
// ----- boost -----
#include <boost/integer/common_factor_rt.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/rational.hpp>
// ----- using directives and manipulations -----
using namespace std;
using boost::rational;
using boost::integer::gcd; // for C++14 or for cpp_int
using boost::integer::lcm; // for C++14 or for cpp_int
using boost::multiprecision::cpp_int;
using ll = long long;
using ld = long double;
template <typename T>
using max_heap = priority_queue<T>;
template <typename T>
using min_heap = priority_queue<T, vector<T>, greater<T>>;
// ----- constexpr for Mint and Combination -----
constexpr ll MOD{1'000'000'007LL};
// constexpr ll MOD{998'244'353LL}; // be careful
constexpr ll MAX_SIZE{3'000'010LL};
// constexpr ll MAX_SIZE{30'000'010LL}; // if 10^7 is needed
// ----- ch_max and ch_min -----
template <typename T>
bool ch_max(T &left, T right)
{
  if (left < right)
  {
    left = right;
    return true;
  }
  return false;
}
template <typename T>
bool ch_min(T &left, T right)
{
  if (left > right)
  {
    left = right;
    return true;
  }
  return false;
}
// ----- Mint -----
template <ll MOD = MOD>
class Mint
{
public:
  ll x;
  Mint() : x{0LL} {}
  Mint(ll x) : x{(x % MOD + MOD) % MOD} {}
  Mint operator-() const { return x ? MOD - x : 0; }
  Mint &operator+=(Mint const &a)
  {
    if ((x += a.x) >= MOD)
    {
      x -= MOD;
    }
    return *this;
  }
  Mint &operator-=(Mint const &a) { return *this += -a; }
  Mint &operator++() { return *this += 1; }
  Mint operator++(int)
  {
    Mint tmp{*this};
    ++*this;
    return tmp;
  }
  Mint &operator--() { return *this -= 1; }
  Mint operator--(int)
  {
    Mint tmp{*this};
    --*this;
    return tmp;
  }
  Mint &operator*=(Mint const &a)
  {
    (x *= a.x) %= MOD;
    return *this;
  }
  Mint &operator/=(Mint const &a)
  {
    Mint b{a};
    return *this *= b.power(MOD - 2);
  }
  Mint operator+(Mint const &a) const { return Mint(*this) += a; }
  Mint operator-(Mint const &a) const { return Mint(*this) -= a; }
  Mint operator*(Mint const &a) const { return Mint(*this) *= a; }
  Mint operator/(Mint const &a) const { return Mint(*this) /= a; }
  bool operator<(Mint const &a) const { return x < a.x; }
  bool operator<=(Mint const &a) const { return x <= a.x; }
  bool operator>(Mint const &a) const { return x > a.x; }
  bool operator>=(Mint const &a) const { return x >= a.x; }
  bool operator==(Mint const &a) const { return x == a.x; }
  bool operator!=(Mint const &a) const { return !(*this == a); }
  Mint power(ll N) const
  {
    if (N == 0)
    {
      return 1;
    }
    else if (N % 2 == 1)
    {
      return *this * power(N - 1);
    }
    else
    {
      Mint half = power(N / 2);
      return half * half;
    }
  }
};
template <ll MOD>
Mint<MOD> operator+(ll lhs, Mint<MOD> const &rhs) { return rhs + lhs; }
template <ll MOD>
Mint<MOD> operator-(ll lhs, Mint<MOD> const &rhs) { return -rhs + lhs; }
template <ll MOD>
Mint<MOD> operator*(ll lhs, Mint<MOD> const &rhs) { return rhs * lhs; }
template <ll MOD>
Mint<MOD> operator/(ll lhs, Mint<MOD> const &rhs) { return Mint<MOD>{lhs} / rhs; }
template <ll MOD>
istream &operator>>(istream &stream, Mint<MOD> &a) { return stream >> a.x; }
template <ll MOD>
ostream &operator<<(ostream &stream, Mint<MOD> const &a) { return stream << a.x; }
// ----- Combination -----
template <ll MOD = MOD, ll MAX_SIZE = MAX_SIZE>
class Combination
{
public:
  vector<Mint<MOD>> inv, fact, factinv;
  Combination() : inv(MAX_SIZE), fact(MAX_SIZE), factinv(MAX_SIZE)
  {
    inv[1] = 1;
    for (auto i{2LL}; i < MAX_SIZE; i++)
    {
      inv[i] = (-inv[MOD % i]) * (MOD / i);
    }
    fact[0] = factinv[0] = 1;
    for (auto i{1LL}; i < MAX_SIZE; i++)
    {
      fact[i] = Mint<MOD>(i) * fact[i - 1];
      factinv[i] = inv[i] * factinv[i - 1];
    }
  }
  Mint<MOD> operator()(int n, int k)
  {
    if (n >= 0 && k >= 0 && n - k >= 0)
    {
      return fact[n] * factinv[k] * factinv[n - k];
    }
    return 0;
  }
  Mint<MOD> catalan(int x, int y)
  {
    return (*this)(x + y, y) - (*this)(x + y, y - 1);
  }
};
// ----- for C++14 -----
using mint = Mint<MOD>;
using combination = Combination<MOD, MAX_SIZE>;
// ----- for C++17 -----
template <typename T, typename enable_if<is_integral<T>::value>::type * = nullptr>
size_t popcount(T x) { return bitset<64>(x).count(); }
size_t popcount(string const &S) { return bitset<200010>{S}.count(); }
// ----- Infty -----
template <typename T>
constexpr T Infty() { return numeric_limits<T>::max(); }
template <typename T>
constexpr T mInfty() { return numeric_limits<T>::min(); }
// ----- frequently used constexpr -----
// constexpr double epsilon{1e-10};
// constexpr ll infty{1'000'000'000'000'010LL}; // or
// constexpr int infty{1'000'000'010};
// constexpr int dx[4] = {1, 0, -1, 0};
// constexpr int dy[4] = {0, 1, 0, -1};
// ----- Yes() and No() -----
void Yes()
{
  cout << "Yes" << endl;
  exit(0);
}
void No()
{
  cout << "No" << endl;
  exit(0);
}

void TLE()
{
  sleep(10);
}

void RE()
{
  assert(false);
}

std::random_device seed_gen;
std::mt19937 engine(seed_gen());

// ----- Solve -----

#if DEBUG == 1
constexpr int D = 5;
#else
constexpr int D = 365;
#endif
constexpr int C = 26;

class Solve
{
  vector<ll> c;
  vector<vector<ll>> s;
  vector<int> t;
  vector<set<int>> contestDays;
  ll totalScore;

public:
  Solve() : c(C), s(D, vector<ll>(C, 0)), t(D), contestDays(C), totalScore{0}
  {
    for (auto i{0}; i < C; ++i)
    {
      cin >> c[i];
    }
    for (auto i{0}; i < D; ++i)
    {
      for (auto j{0}; j < C; ++j)
      {
        cin >> s[i][j];
      }
    }
    for (auto i{0}; i < C; ++i)
    {
      contestDays[i].insert(0);
      contestDays[i].insert(D + 1);
    }
  }

  void flush()
  {
    for (auto i{0}; i < D; ++i)
    {
      t[i] = engine() % C;
    }
    calc_score();
    int M{5000};
    for (auto i{0}; i < M; ++i)
    {
#if DEBUG == 1
      cerr << "i = " << i << endl;
#endif
      int d(engine() % D);
      int q(engine() % C);
      int p{t[d - 1]};
#if DEBUG == 1
      cerr << "d = " << d << ", p = " << p << ", q = " << q << endl;
#endif
      auto pScore{totalScore};
      auto qScore{change_score(d, q)};
      if (pScore > qScore)
      {
        change_score(d, p);
      }
    }
    for (auto e : t)
    {
      cout << e + 1 << endl;
    }
  }

private:
  ll change_score(int d, int q)
  {
    int p{t[d - 1]};
    if (p == q)
    {
      return totalScore;
    }
    ll newScore{totalScore};
    t[d - 1] = q;
    newScore -= s[d - 1][p];
    newScore += s[d - 1][q];
    {
      auto it{contestDays[p].find(d)};
      --it;
      auto x{*it};
      ++it;
      ++it;
      auto y{*it};
      auto dif{(y - d) * (d - x)};
      newScore -= c[p] * dif;
      --it;
      contestDays[p].erase(it);
    }
    {
      contestDays[q].insert(d);
      auto it{contestDays[q].find(d)};
      --it;
      auto x{*it};
      ++it;
      ++it;
      auto y{*it};
      auto dif{(y - d) * (d - x)};
      newScore += c[q] * dif;
    }
    return newScore;
  }

  vector<ll> calc_score()
  {
    vector<ll> ans(D);
    vector<vector<int>> last(D + 1, vector<int>(C, 0));
    ll now{0};
    for (auto d{0}; d < D; ++d)
    {
      now += s[d][t[d]];
      contestDays[t[d]].insert(d + 1);
      last[d + 1] = last[d];
      last[d + 1][t[d]] = d + 1;
      ll penalty{0};
      for (auto i{0}; i < C; ++i)
      {
        penalty += c[i] * (d + 1 - last[d + 1][i]);
      }
      now -= penalty;
      ans[d] = now;
    }
    totalScore = now;
    return ans;
  }
};

// ----- main() -----

int main()
{
  ll d;
  cin >> d;
  Solve solve;
  solve.flush();
}
<commit_msg>tried A.cpp to 'A'<commit_after>#define DEBUG 1

/**
 * File    : A.cpp
 * Author  : Kazune Takahashi
 * Created : 2020/6/28 20:51:38
 * Powered by Visual Studio Code
 */
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <chrono>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <functional>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>
// ----- boost -----
#include <boost/integer/common_factor_rt.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/rational.hpp>
// ----- using directives and manipulations -----
using namespace std;
using boost::rational;
using boost::integer::gcd; // for C++14 or for cpp_int
using boost::integer::lcm; // for C++14 or for cpp_int
using boost::multiprecision::cpp_int;
using ll = long long;
using ld = long double;
template <typename T>
using max_heap = priority_queue<T>;
template <typename T>
using min_heap = priority_queue<T, vector<T>, greater<T>>;
// ----- constexpr for Mint and Combination -----
constexpr ll MOD{1'000'000'007LL};
// constexpr ll MOD{998'244'353LL}; // be careful
constexpr ll MAX_SIZE{3'000'010LL};
// constexpr ll MAX_SIZE{30'000'010LL}; // if 10^7 is needed
// ----- ch_max and ch_min -----
template <typename T>
bool ch_max(T &left, T right)
{
  if (left < right)
  {
    left = right;
    return true;
  }
  return false;
}
template <typename T>
bool ch_min(T &left, T right)
{
  if (left > right)
  {
    left = right;
    return true;
  }
  return false;
}
// ----- Mint -----
template <ll MOD = MOD>
class Mint
{
public:
  ll x;
  Mint() : x{0LL} {}
  Mint(ll x) : x{(x % MOD + MOD) % MOD} {}
  Mint operator-() const { return x ? MOD - x : 0; }
  Mint &operator+=(Mint const &a)
  {
    if ((x += a.x) >= MOD)
    {
      x -= MOD;
    }
    return *this;
  }
  Mint &operator-=(Mint const &a) { return *this += -a; }
  Mint &operator++() { return *this += 1; }
  Mint operator++(int)
  {
    Mint tmp{*this};
    ++*this;
    return tmp;
  }
  Mint &operator--() { return *this -= 1; }
  Mint operator--(int)
  {
    Mint tmp{*this};
    --*this;
    return tmp;
  }
  Mint &operator*=(Mint const &a)
  {
    (x *= a.x) %= MOD;
    return *this;
  }
  Mint &operator/=(Mint const &a)
  {
    Mint b{a};
    return *this *= b.power(MOD - 2);
  }
  Mint operator+(Mint const &a) const { return Mint(*this) += a; }
  Mint operator-(Mint const &a) const { return Mint(*this) -= a; }
  Mint operator*(Mint const &a) const { return Mint(*this) *= a; }
  Mint operator/(Mint const &a) const { return Mint(*this) /= a; }
  bool operator<(Mint const &a) const { return x < a.x; }
  bool operator<=(Mint const &a) const { return x <= a.x; }
  bool operator>(Mint const &a) const { return x > a.x; }
  bool operator>=(Mint const &a) const { return x >= a.x; }
  bool operator==(Mint const &a) const { return x == a.x; }
  bool operator!=(Mint const &a) const { return !(*this == a); }
  Mint power(ll N) const
  {
    if (N == 0)
    {
      return 1;
    }
    else if (N % 2 == 1)
    {
      return *this * power(N - 1);
    }
    else
    {
      Mint half = power(N / 2);
      return half * half;
    }
  }
};
template <ll MOD>
Mint<MOD> operator+(ll lhs, Mint<MOD> const &rhs) { return rhs + lhs; }
template <ll MOD>
Mint<MOD> operator-(ll lhs, Mint<MOD> const &rhs) { return -rhs + lhs; }
template <ll MOD>
Mint<MOD> operator*(ll lhs, Mint<MOD> const &rhs) { return rhs * lhs; }
template <ll MOD>
Mint<MOD> operator/(ll lhs, Mint<MOD> const &rhs) { return Mint<MOD>{lhs} / rhs; }
template <ll MOD>
istream &operator>>(istream &stream, Mint<MOD> &a) { return stream >> a.x; }
template <ll MOD>
ostream &operator<<(ostream &stream, Mint<MOD> const &a) { return stream << a.x; }
// ----- Combination -----
template <ll MOD = MOD, ll MAX_SIZE = MAX_SIZE>
class Combination
{
public:
  vector<Mint<MOD>> inv, fact, factinv;
  Combination() : inv(MAX_SIZE), fact(MAX_SIZE), factinv(MAX_SIZE)
  {
    inv[1] = 1;
    for (auto i{2LL}; i < MAX_SIZE; i++)
    {
      inv[i] = (-inv[MOD % i]) * (MOD / i);
    }
    fact[0] = factinv[0] = 1;
    for (auto i{1LL}; i < MAX_SIZE; i++)
    {
      fact[i] = Mint<MOD>(i) * fact[i - 1];
      factinv[i] = inv[i] * factinv[i - 1];
    }
  }
  Mint<MOD> operator()(int n, int k)
  {
    if (n >= 0 && k >= 0 && n - k >= 0)
    {
      return fact[n] * factinv[k] * factinv[n - k];
    }
    return 0;
  }
  Mint<MOD> catalan(int x, int y)
  {
    return (*this)(x + y, y) - (*this)(x + y, y - 1);
  }
};
// ----- for C++14 -----
using mint = Mint<MOD>;
using combination = Combination<MOD, MAX_SIZE>;
// ----- for C++17 -----
template <typename T, typename enable_if<is_integral<T>::value>::type * = nullptr>
size_t popcount(T x) { return bitset<64>(x).count(); }
size_t popcount(string const &S) { return bitset<200010>{S}.count(); }
// ----- Infty -----
template <typename T>
constexpr T Infty() { return numeric_limits<T>::max(); }
template <typename T>
constexpr T mInfty() { return numeric_limits<T>::min(); }
// ----- frequently used constexpr -----
// constexpr double epsilon{1e-10};
// constexpr ll infty{1'000'000'000'000'010LL}; // or
// constexpr int infty{1'000'000'010};
// constexpr int dx[4] = {1, 0, -1, 0};
// constexpr int dy[4] = {0, 1, 0, -1};
// ----- Yes() and No() -----
void Yes()
{
  cout << "Yes" << endl;
  exit(0);
}
void No()
{
  cout << "No" << endl;
  exit(0);
}

void TLE()
{
  sleep(10);
}

void RE()
{
  assert(false);
}

std::random_device seed_gen;
std::mt19937 engine(seed_gen());

// ----- Solve -----

#if DEBUG == 1
constexpr int D = 5;
#else
constexpr int D = 365;
#endif
constexpr int C = 26;

class Solve
{
  vector<ll> c;
  vector<vector<ll>> s;
  vector<int> t;
  vector<set<int>> contestDays;
  ll totalScore;

public:
  Solve() : c(C), s(D, vector<ll>(C, 0)), t(D), contestDays(C), totalScore{0}
  {
    for (auto i{0}; i < C; ++i)
    {
      cin >> c[i];
    }
    for (auto i{0}; i < D; ++i)
    {
      for (auto j{0}; j < C; ++j)
      {
        cin >> s[i][j];
      }
    }
    for (auto i{0}; i < C; ++i)
    {
      contestDays[i].insert(0);
      contestDays[i].insert(D + 1);
    }
  }

  void flush()
  {
    for (auto i{0}; i < D; ++i)
    {
      t[i] = engine() % C;
    }
    calc_score();
    int M{5000};
    for (auto i{0}; i < M; ++i)
    {
#if DEBUG == 1
      cerr << "i = " << i << endl;
#endif
      int d(engine() % D);
      int q(engine() % C);
      int p{t[d - 1]};
#if DEBUG == 1
      cerr << "d = " << d << ", p = " << p << ", q = " << q << endl;
#endif
      auto pScore{totalScore};
      auto qScore{change_score(d, q)};
#if DEBUG == 1
      cerr << "pScore = " << pScore << ", qScore = " << qScore << endl;
#endif
      if (pScore > qScore)
      {
        change_score(d, p);
      }
    }
    for (auto e : t)
    {
      cout << e + 1 << endl;
    }
  }

private:
  ll change_score(int d, int q)
  {
    int p{t[d - 1]};
    if (p == q)
    {
      return totalScore;
    }
    ll newScore{totalScore};
    t[d - 1] = q;
    newScore -= s[d - 1][p];
    newScore += s[d - 1][q];
    {
      auto it{contestDays[p].find(d)};
      --it;
      auto x{*it};
      ++it;
      ++it;
      auto y{*it};
      auto dif{(y - d) * (d - x)};
      newScore -= c[p] * dif;
      --it;
      contestDays[p].erase(it);
    }
    {
      contestDays[q].insert(d);
      auto it{contestDays[q].find(d)};
      --it;
      auto x{*it};
      ++it;
      ++it;
      auto y{*it};
      auto dif{(y - d) * (d - x)};
      newScore += c[q] * dif;
    }
    return newScore;
  }

  vector<ll> calc_score()
  {
    vector<ll> ans(D);
    vector<vector<int>> last(D + 1, vector<int>(C, 0));
    ll now{0};
    for (auto d{0}; d < D; ++d)
    {
      now += s[d][t[d]];
      contestDays[t[d]].insert(d + 1);
      last[d + 1] = last[d];
      last[d + 1][t[d]] = d + 1;
      ll penalty{0};
      for (auto i{0}; i < C; ++i)
      {
        penalty += c[i] * (d + 1 - last[d + 1][i]);
      }
      now -= penalty;
      ans[d] = now;
    }
    totalScore = now;
    return ans;
  }
};

// ----- main() -----

int main()
{
  ll d;
  cin >> d;
  Solve solve;
  solve.flush();
}
<|endoftext|>


<commit_before>// FindDlg.cpp : implementation file
//

#include "stdafx.h"
#include "passwordsafe.h"

#if defined(POCKET_PC)
  #include "pocketpc/resource.h"
#else
  #include "resource.h"
#endif
#include "FindDlg.h"
#include "DboxMain.h"
#include "ThisMfcApp.h" // for disable/enable accel.

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CFindDlg dialog

CFindDlg *CFindDlg::self = NULL; // for Singleton pattern

void CFindDlg::Doit(CWnd *pParent, BOOL *isCS, CMyString *lastFind)
{
  if (self == NULL) {
    self = new CFindDlg(pParent, isCS, lastFind);
    if (self != NULL)
      if (self->Create(CFindDlg::IDD)) {
	RECT myRect, parentRect;
	// move find dialog so that it doesn't overlap its parent
	pParent->GetWindowRect(&parentRect);
	self->GetWindowRect(&myRect);
	// Move the dialog to the right if parent is on left side
	// of screen,and vice versa
	int screenCenter = GetSystemMetrics(SM_CXSCREEN)/2;
	int parentCenter = (parentRect.right + parentRect.left)/2;

	if (parentCenter < screenCenter) {
	  // move right
	  myRect.right = parentRect.right + myRect.right - myRect.left;
	  myRect.left = parentRect.right;
	} else { // move left
	  myRect.left = parentRect.left - (myRect.right - myRect.left);
	  myRect.right = parentRect.left;
	}
	self->MoveWindow(&myRect);
	self->ShowWindow(SW_SHOW);
      }
  } else {
    self->BringWindowToTop();
  }
  app.DisableAccelerator(); // don't accel Del when this dlg is shown
}

CFindDlg::CFindDlg(CWnd* pParent, BOOL *isCS, CMyString *lastFind)
  : super(CFindDlg::IDD, pParent), m_indices(NULL),
    m_lastshown(-1), m_numFound(0),
    m_last_search_text(_T("")), m_last_cs_search(FALSE),
    m_lastCSPtr(isCS), m_lastTextPtr(lastFind)
{
  ASSERT(isCS !=NULL);
  ASSERT(lastFind != NULL);
  //{{AFX_DATA_INIT(CFindDlg)
  m_cs_search = *isCS;
  m_search_text = *lastFind;
  m_status = _T("");
	//}}AFX_DATA_INIT
}

CFindDlg::~CFindDlg()
{
  delete[] m_indices;
  self = NULL;
}

void CFindDlg::DoDataExchange(CDataExchange* pDX)
{
	super::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CFindDlg)
	DDX_Check(pDX, IDC_FIND_CS, m_cs_search);
	DDX_Text(pDX, IDC_FIND_TEXT, m_search_text);
#if !defined(POCKET_PC)
	DDX_Text(pDX, IDC_STATUS, m_status);
#endif
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CFindDlg, super)
	//{{AFX_MSG_MAP(CFindDlg)
	ON_BN_CLICKED(IDOK, OnFind)
#if defined(POCKET_PC)
	ON_BN_CLICKED(IDCANCEL, OnCancel)
#else
	ON_BN_CLICKED(IDCANCEL, OnClose)
#endif
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CFindDlg message handlers

void CFindDlg::OnFind()
{

  DboxMain* pParent = (DboxMain*) GetParent();
  ASSERT(pParent != NULL);

  const int numEntries = pParent->GetNumEntries();

  if (numEntries == 0) {
    // Right Thing would be to disable find menu item if list is empty
    m_status = _T("Password list is empty.");
    UpdateData(FALSE);
    return;
  }

  UpdateData(TRUE);

  if (m_search_text.IsEmpty()) {
    m_status = _T("Please enter a search string");
    UpdateData(FALSE);
    return;
  }

  // If the user changes the search text or cs, then this is a new search:
  if (m_search_text != m_last_search_text ||
      m_cs_search != m_last_cs_search) {
    m_last_search_text = m_search_text;
    m_last_cs_search = m_cs_search;
    m_lastshown = -1;
  }

  if (m_lastshown == -1) {

    if (m_indices != NULL) {
      // take care of the pathological case where someone added or deleted
      // an entry while this dialog box is open
      delete[] m_indices;
    }
    m_indices = new int[numEntries];

    m_numFound = pParent->FindAll(m_search_text, m_cs_search, m_indices);

    switch (m_numFound) {
    case 0:
      m_status = _T("No matches found.");
      break;
    case 1:
      m_status = _T("Found 1 match.");
      break;
    default:
      // {kjp} this kludge is needed because the declaration of CString in <afx.h> defines
      // {kjp} FormatMessage as implemented, but if you take a look at the source for
      // {kjp} CString in strex.cpp you'll see that it's only actully implemented if the
      // {kjp} platform is not WinCE.  Methinks this is a bug in afx.h
#if defined(POCKET_PC)
      m_status.Format( _T("Found %d matches."), m_numFound );
#else
      m_status.FormatMessage(_T("Found %1!d! matches."), m_numFound);
#endif
      break;
    }
    UpdateData(FALSE);
  } // m_lastshown == -1

  // OK, so now we have a (possibly empty) list of items to select.

  if (m_numFound > 0) {
    m_lastshown = (m_lastshown + 1) % m_numFound; //from -1 to 0, cycle afterwards
    pParent->SelectEntry(m_indices[m_lastshown], TRUE);
  }
  if (m_numFound > 1) {
      SetDlgItemText(IDOK, _T("Find Next"));
  }
  // don't call super::OnOK - user will Cancel() to close dbox
}

#if defined(POCKET_PC)
void CFindDlg::OnCancel()
{
  UpdateData(TRUE);
  *m_lastTextPtr = m_search_text;
  *m_lastCSPtr = m_cs_search;
  self = NULL;
  app.EnableAccelerator(); // restore accel table
  super::DestroyWindow();
}
#else
void CFindDlg::OnClose()
{
  UpdateData(TRUE);
  *m_lastTextPtr = m_search_text;
  *m_lastCSPtr = m_cs_search;

  self = NULL;
  app.EnableAccelerator(); // restore accel table
  super::OnCancel();
}
#endif
<commit_msg>[1207600] Find dialog not moved off-screen if PasswordSafe takes up whole screen.<commit_after>// FindDlg.cpp : implementation file
//

#include "stdafx.h"
#include "passwordsafe.h"

#if defined(POCKET_PC)
  #include "pocketpc/resource.h"
#else
  #include "resource.h"
#endif
#include "FindDlg.h"
#include "DboxMain.h"
#include "ThisMfcApp.h" // for disable/enable accel.

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CFindDlg dialog

CFindDlg *CFindDlg::self = NULL; // for Singleton pattern

void CFindDlg::Doit(CWnd *pParent, BOOL *isCS, CMyString *lastFind)
{
  if (self == NULL) {
    self = new CFindDlg(pParent, isCS, lastFind);
    if (self != NULL)
      if (self->Create(CFindDlg::IDD)) {
	RECT myRect, parentRect;
	// move find dialog so that it doesn't overlap its parent
	pParent->GetWindowRect(&parentRect);
	self->GetWindowRect(&myRect);
	// Move the dialog to the right if parent is on left side
	// of screen,and vice versa,
	// UNLESS parent is close to screen's width!
	const int screenWidth = GetSystemMetrics(SM_CXSCREEN);
	const int screenCenter = screenWidth/2;
	const int parentWidth = (parentRect.right - parentRect.left);
	const int parentCenter = (parentRect.right + parentRect.left)/2;

	if (parentWidth < (screenWidth * 9) / 10) {
	  if (parentCenter < screenCenter) {
	    // move right
	    myRect.right = parentRect.right + myRect.right - myRect.left;
	    myRect.left = parentRect.right;
	  } else { // move left
	    myRect.left = parentRect.left - (myRect.right - myRect.left);
	    myRect.right = parentRect.left;
	  }
	  self->MoveWindow(&myRect);
	} // parent not too wide
	self->ShowWindow(SW_SHOW);
      }
  } else {
    self->BringWindowToTop();
  }
  app.DisableAccelerator(); // don't accel Del when this dlg is shown
}

CFindDlg::CFindDlg(CWnd* pParent, BOOL *isCS, CMyString *lastFind)
  : super(CFindDlg::IDD, pParent), m_indices(NULL),
    m_lastshown(-1), m_numFound(0),
    m_last_search_text(_T("")), m_last_cs_search(FALSE),
    m_lastCSPtr(isCS), m_lastTextPtr(lastFind)
{
  ASSERT(isCS !=NULL);
  ASSERT(lastFind != NULL);
  //{{AFX_DATA_INIT(CFindDlg)
  m_cs_search = *isCS;
  m_search_text = *lastFind;
  m_status = _T("");
	//}}AFX_DATA_INIT
}

CFindDlg::~CFindDlg()
{
  delete[] m_indices;
  self = NULL;
}

void CFindDlg::DoDataExchange(CDataExchange* pDX)
{
	super::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CFindDlg)
	DDX_Check(pDX, IDC_FIND_CS, m_cs_search);
	DDX_Text(pDX, IDC_FIND_TEXT, m_search_text);
#if !defined(POCKET_PC)
	DDX_Text(pDX, IDC_STATUS, m_status);
#endif
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CFindDlg, super)
	//{{AFX_MSG_MAP(CFindDlg)
	ON_BN_CLICKED(IDOK, OnFind)
#if defined(POCKET_PC)
	ON_BN_CLICKED(IDCANCEL, OnCancel)
#else
	ON_BN_CLICKED(IDCANCEL, OnClose)
#endif
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CFindDlg message handlers

void CFindDlg::OnFind()
{

  DboxMain* pParent = (DboxMain*) GetParent();
  ASSERT(pParent != NULL);

  const int numEntries = pParent->GetNumEntries();

  if (numEntries == 0) {
    // Right Thing would be to disable find menu item if list is empty
    m_status = _T("Password list is empty.");
    UpdateData(FALSE);
    return;
  }

  UpdateData(TRUE);

  if (m_search_text.IsEmpty()) {
    m_status = _T("Please enter a search string");
    UpdateData(FALSE);
    return;
  }

  // If the user changes the search text or cs, then this is a new search:
  if (m_search_text != m_last_search_text ||
      m_cs_search != m_last_cs_search) {
    m_last_search_text = m_search_text;
    m_last_cs_search = m_cs_search;
    m_lastshown = -1;
  }

  if (m_lastshown == -1) {

    if (m_indices != NULL) {
      // take care of the pathological case where someone added or deleted
      // an entry while this dialog box is open
      delete[] m_indices;
    }
    m_indices = new int[numEntries];

    m_numFound = pParent->FindAll(m_search_text, m_cs_search, m_indices);

    switch (m_numFound) {
    case 0:
      m_status = _T("No matches found.");
      break;
    case 1:
      m_status = _T("Found 1 match.");
      break;
    default:
      // {kjp} this kludge is needed because the declaration of CString in <afx.h> defines
      // {kjp} FormatMessage as implemented, but if you take a look at the source for
      // {kjp} CString in strex.cpp you'll see that it's only actully implemented if the
      // {kjp} platform is not WinCE.  Methinks this is a bug in afx.h
#if defined(POCKET_PC)
      m_status.Format( _T("Found %d matches."), m_numFound );
#else
      m_status.FormatMessage(_T("Found %1!d! matches."), m_numFound);
#endif
      break;
    }
    UpdateData(FALSE);
  } // m_lastshown == -1

  // OK, so now we have a (possibly empty) list of items to select.

  if (m_numFound > 0) {
    m_lastshown = (m_lastshown + 1) % m_numFound; //from -1 to 0, cycle afterwards
    pParent->SelectEntry(m_indices[m_lastshown], TRUE);
  }
  if (m_numFound > 1) {
      SetDlgItemText(IDOK, _T("Find Next"));
  }
  // don't call super::OnOK - user will Cancel() to close dbox
}

#if defined(POCKET_PC)
void CFindDlg::OnCancel()
{
  UpdateData(TRUE);
  *m_lastTextPtr = m_search_text;
  *m_lastCSPtr = m_cs_search;
  self = NULL;
  app.EnableAccelerator(); // restore accel table
  super::DestroyWindow();
}
#else
void CFindDlg::OnClose()
{
  UpdateData(TRUE);
  *m_lastTextPtr = m_search_text;
  *m_lastCSPtr = m_cs_search;

  self = NULL;
  app.EnableAccelerator(); // restore accel table
  super::OnCancel();
}
#endif
<|endoftext|>


<commit_before>// -------------------------------------------------------------------------
//    @FileName      :    NFCDataNoSqlModule.h
//    @Author           :    LvSheng.Huang
//    @Date             :    2012-12-15
//    @Module           :    NFCDataNoSqlModule
//    Row,Col; ; ; ; ; ; ;
// -------------------------------------------------------------------------

//#include "stdafx.h"
#include <algorithm>
#include "NFCNoSqlModule.h"
#include "NFComm/NFMessageDefine/NFProtocolDefine.hpp"

NFCNoSqlModule::NFCNoSqlModule(NFIPluginManager* p)
{
	pPluginManager = p;
}

NFCNoSqlModule::~NFCNoSqlModule()
{

}

bool NFCNoSqlModule::Init()
{
	mLastCheckTime = 0;

	return true;
}

bool NFCNoSqlModule::Shut()
{

	return true;
}

bool NFCNoSqlModule::AfterInit()
{
	m_pClassModule = pPluginManager->FindModule<NFIClassModule>();
	m_pElementModule = pPluginManager->FindModule<NFIElementModule>();
	m_pLogModule = pPluginManager->FindModule<NFILogModule>();

	NF_SHARE_PTR<NFIClass> xLogicClass = m_pClassModule->GetElement(NFrame::NoSqlServer::ThisName());
	if (xLogicClass)
	{
		NFList<std::string>& strIdList = xLogicClass->GetIdList();
		std::string strId;
		for (bool bRet = strIdList.First(strId); bRet; bRet = strIdList.Next(strId))
		{
			const int nServerID = m_pElementModule->GetPropertyInt(strId, NFrame::NoSqlServer::ServerID());
			const int nPort = m_pElementModule->GetPropertyInt(strId, NFrame::NoSqlServer::Port());
			const std::string& strIP = m_pElementModule->GetPropertyString(strId, NFrame::NoSqlServer::IP());

			if (this->AddConnectSql(strId, strIP, nPort))
			{
				std::ostringstream strLog;
				strLog << "Cannot connect NoSqlServer[" << strIP << "], Port = " << nPort;
				m_pLogModule->LogNormal(NFILogModule::NLL_ERROR_NORMAL, NULL_OBJECT, strLog, __FUNCTION__, __LINE__);
			}
			else
			{
				std::ostringstream strLog;
				strLog << "Connected NoSqlServer[" << strIP << "], Port = " << nPort;
				m_pLogModule->LogNormal(NFILogModule::NF_LOG_LEVEL::NLL_INFO_NORMAL, NULL_OBJECT, strLog, __FUNCTION__, __LINE__);
			}
		}
	}
	return true;
}

bool NFCNoSqlModule::Execute(const float fLasFrametime, const float fStartedTime)
{
	if (mLastCheckTime + 10 > pPluginManager->GetNowTime())
	{
		return;
	}
	mLastCheckTime = pPluginManager->GetNowTime();

	NF_SHARE_PTR<NFINoSqlDriver> xNosqlDriver = this->mxNoSqlDriver.First();
	while (xNosqlDriver)
	{
		if (xNosqlDriver->Enable())
		{
			xNosqlDriver->ReConnect();
		}

		xNosqlDriver = this->mxNoSqlDriver.First();
	}

	return true;
}

NF_SHARE_PTR<NFINoSqlDriver> NFCNoSqlModule::GetDriverBySuitRandom()
{
	NF_SHARE_PTR<NFINoSqlDriver> xDriver = mxNoSqlDriver.GetElementBySuitRandom();
	if (xDriver && xDriver->Enable())
	{
		return xDriver;
	}

	return nullptr;
}

NF_SHARE_PTR<NFINoSqlDriver> NFCNoSqlModule::GetDriverBySuitConsistent()
{
	NF_SHARE_PTR<NFINoSqlDriver> xDriver = mxNoSqlDriver.GetElementBySuitConsistent();
	if (xDriver && xDriver->Enable())
	{
		return xDriver;
	}

	return nullptr;
}

NF_SHARE_PTR<NFINoSqlDriver> NFCNoSqlModule::GetDriverBySuit(const std::string& strHash)
{
	NF_SHARE_PTR<NFINoSqlDriver> xDriver = mxNoSqlDriver.GetElementBySuit(strHash);
	if (xDriver && xDriver->Enable())
	{
		return xDriver;
	}

	return nullptr;
}

/*
NF_SHARE_PTR<NFINoSqlDriver> NFCNoSqlModule::GetDriverBySuit(const int nHash)
{
	return mxNoSqlDriver.GetElementBySuit(nHash);
}
*/
bool NFCNoSqlModule::AddConnectSql(const std::string& strID, const std::string& strIP)
{
	if (!mxNoSqlDriver.ExistElement(strID))
	{
		NF_SHARE_PTR<NFINoSqlDriver> pNoSqlDriver(new NFCNoSqlDriver());
		pNoSqlDriver->Connect(strIP, 6379, "");
		return mxNoSqlDriver.AddElement(strID, pNoSqlDriver);
	}

	return false;
}

bool NFCNoSqlModule::AddConnectSql(const std::string& strID, const std::string& strIP, const int nPort)
{
	if (!mxNoSqlDriver.ExistElement(strID))
	{
		NF_SHARE_PTR<NFINoSqlDriver> pNoSqlDriver(new NFCNoSqlDriver());
		pNoSqlDriver->Connect(strIP, nPort, "");
		return mxNoSqlDriver.AddElement(strID, pNoSqlDriver);
	}

	return false;
}

bool NFCNoSqlModule::AddConnectSql(const std::string& strID, const std::string& strIP, const int nPort, const std::string& strPass)
{
	if (!mxNoSqlDriver.ExistElement(strID))
	{
		NF_SHARE_PTR<NFINoSqlDriver> pNoSqlDriver(new NFCNoSqlDriver());
		pNoSqlDriver->Connect(strIP, nPort, strPass);
		return mxNoSqlDriver.AddElement(strID, pNoSqlDriver);
	}

	return false;
}

NFList<std::string> NFCNoSqlModule::GetDriverIdList()
{
	NFList<std::string> lDriverIdList;
	std::string strDriverId;
	NF_SHARE_PTR<NFINoSqlDriver> pDriver = mxNoSqlDriver.First(strDriverId);
	while (pDriver)
	{
		lDriverIdList.Add(strDriverId);
		pDriver = mxNoSqlDriver.Next(strDriverId);
	}
	return lDriverIdList;
}

NF_SHARE_PTR<NFINoSqlDriver> NFCNoSqlModule::GetDriver(const std::string& strID)
{
	NF_SHARE_PTR<NFINoSqlDriver> xDriver = mxNoSqlDriver.GetElement(strID);
	if (xDriver && xDriver->Enable())
	{
		return xDriver;
	}

	return nullptr;
}

bool NFCNoSqlModule::RemoveConnectSql(const std::string& strID)
{
	return mxNoSqlDriver.RemoveElement(strID);
}<commit_msg>fixed for compile<commit_after>// -------------------------------------------------------------------------
//    @FileName      :    NFCDataNoSqlModule.h
//    @Author           :    LvSheng.Huang
//    @Date             :    2012-12-15
//    @Module           :    NFCDataNoSqlModule
//    Row,Col; ; ; ; ; ; ;
// -------------------------------------------------------------------------

//#include "stdafx.h"
#include <algorithm>
#include "NFCNoSqlModule.h"
#include "NFComm/NFMessageDefine/NFProtocolDefine.hpp"

NFCNoSqlModule::NFCNoSqlModule(NFIPluginManager* p)
{
	pPluginManager = p;
}

NFCNoSqlModule::~NFCNoSqlModule()
{

}

bool NFCNoSqlModule::Init()
{
	mLastCheckTime = 0;

	return true;
}

bool NFCNoSqlModule::Shut()
{

	return true;
}

bool NFCNoSqlModule::AfterInit()
{
	m_pClassModule = pPluginManager->FindModule<NFIClassModule>();
	m_pElementModule = pPluginManager->FindModule<NFIElementModule>();
	m_pLogModule = pPluginManager->FindModule<NFILogModule>();

	NF_SHARE_PTR<NFIClass> xLogicClass = m_pClassModule->GetElement(NFrame::NoSqlServer::ThisName());
	if (xLogicClass)
	{
		NFList<std::string>& strIdList = xLogicClass->GetIdList();
		std::string strId;
		for (bool bRet = strIdList.First(strId); bRet; bRet = strIdList.Next(strId))
		{
			const int nServerID = m_pElementModule->GetPropertyInt(strId, NFrame::NoSqlServer::ServerID());
			const int nPort = m_pElementModule->GetPropertyInt(strId, NFrame::NoSqlServer::Port());
			const std::string& strIP = m_pElementModule->GetPropertyString(strId, NFrame::NoSqlServer::IP());

			if (this->AddConnectSql(strId, strIP, nPort))
			{
				std::ostringstream strLog;
				strLog << "Cannot connect NoSqlServer[" << strIP << "], Port = " << nPort;
				m_pLogModule->LogNormal(NFILogModule::NLL_ERROR_NORMAL, NULL_OBJECT, strLog, __FUNCTION__, __LINE__);
			}
			else
			{
				std::ostringstream strLog;
				strLog << "Connected NoSqlServer[" << strIP << "], Port = " << nPort;
				m_pLogModule->LogNormal(NFILogModule::NF_LOG_LEVEL::NLL_INFO_NORMAL, NULL_OBJECT, strLog, __FUNCTION__, __LINE__);
			}
		}
	}
	return true;
}

bool NFCNoSqlModule::Execute(const float fLasFrametime, const float fStartedTime)
{
	if (mLastCheckTime + 10 > pPluginManager->GetNowTime())
	{
		return false;
	}
	mLastCheckTime = pPluginManager->GetNowTime();

	NF_SHARE_PTR<NFINoSqlDriver> xNosqlDriver = this->mxNoSqlDriver.First();
	while (xNosqlDriver)
	{
		if (xNosqlDriver->Enable())
		{
			xNosqlDriver->ReConnect();
		}

		xNosqlDriver = this->mxNoSqlDriver.First();
	}

	return true;
}

NF_SHARE_PTR<NFINoSqlDriver> NFCNoSqlModule::GetDriverBySuitRandom()
{
	NF_SHARE_PTR<NFINoSqlDriver> xDriver = mxNoSqlDriver.GetElementBySuitRandom();
	if (xDriver && xDriver->Enable())
	{
		return xDriver;
	}

	return nullptr;
}

NF_SHARE_PTR<NFINoSqlDriver> NFCNoSqlModule::GetDriverBySuitConsistent()
{
	NF_SHARE_PTR<NFINoSqlDriver> xDriver = mxNoSqlDriver.GetElementBySuitConsistent();
	if (xDriver && xDriver->Enable())
	{
		return xDriver;
	}

	return nullptr;
}

NF_SHARE_PTR<NFINoSqlDriver> NFCNoSqlModule::GetDriverBySuit(const std::string& strHash)
{
	NF_SHARE_PTR<NFINoSqlDriver> xDriver = mxNoSqlDriver.GetElementBySuit(strHash);
	if (xDriver && xDriver->Enable())
	{
		return xDriver;
	}

	return nullptr;
}

/*
NF_SHARE_PTR<NFINoSqlDriver> NFCNoSqlModule::GetDriverBySuit(const int nHash)
{
	return mxNoSqlDriver.GetElementBySuit(nHash);
}
*/
bool NFCNoSqlModule::AddConnectSql(const std::string& strID, const std::string& strIP)
{
	if (!mxNoSqlDriver.ExistElement(strID))
	{
		NF_SHARE_PTR<NFINoSqlDriver> pNoSqlDriver(new NFCNoSqlDriver());
		pNoSqlDriver->Connect(strIP, 6379, "");
		return mxNoSqlDriver.AddElement(strID, pNoSqlDriver);
	}

	return false;
}

bool NFCNoSqlModule::AddConnectSql(const std::string& strID, const std::string& strIP, const int nPort)
{
	if (!mxNoSqlDriver.ExistElement(strID))
	{
		NF_SHARE_PTR<NFINoSqlDriver> pNoSqlDriver(new NFCNoSqlDriver());
		pNoSqlDriver->Connect(strIP, nPort, "");
		return mxNoSqlDriver.AddElement(strID, pNoSqlDriver);
	}

	return false;
}

bool NFCNoSqlModule::AddConnectSql(const std::string& strID, const std::string& strIP, const int nPort, const std::string& strPass)
{
	if (!mxNoSqlDriver.ExistElement(strID))
	{
		NF_SHARE_PTR<NFINoSqlDriver> pNoSqlDriver(new NFCNoSqlDriver());
		pNoSqlDriver->Connect(strIP, nPort, strPass);
		return mxNoSqlDriver.AddElement(strID, pNoSqlDriver);
	}

	return false;
}

NFList<std::string> NFCNoSqlModule::GetDriverIdList()
{
	NFList<std::string> lDriverIdList;
	std::string strDriverId;
	NF_SHARE_PTR<NFINoSqlDriver> pDriver = mxNoSqlDriver.First(strDriverId);
	while (pDriver)
	{
		lDriverIdList.Add(strDriverId);
		pDriver = mxNoSqlDriver.Next(strDriverId);
	}
	return lDriverIdList;
}

NF_SHARE_PTR<NFINoSqlDriver> NFCNoSqlModule::GetDriver(const std::string& strID)
{
	NF_SHARE_PTR<NFINoSqlDriver> xDriver = mxNoSqlDriver.GetElement(strID);
	if (xDriver && xDriver->Enable())
	{
		return xDriver;
	}

	return nullptr;
}

bool NFCNoSqlModule::RemoveConnectSql(const std::string& strID)
{
	return mxNoSqlDriver.RemoveElement(strID);
}<|endoftext|>


<commit_before>/* -------------------------------------------------------------------------- *
 *                       OpenSim:  StatesTrajectory.cpp                       *
 * -------------------------------------------------------------------------- *
 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2017 Stanford University and the Authors                *
 * Author(s): Chris Dembia                                                    *
 *                                                                            *
 * 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 "StatesTrajectory.h"

#include <OpenSim/Common/CommonUtilities.h>
#include <OpenSim/Common/Storage.h>
#include <OpenSim/Common/TableUtilities.h>
#include <OpenSim/Simulation/Model/Model.h>

using namespace OpenSim;

size_t StatesTrajectory::getSize() const {
    return m_states.size();
}

void StatesTrajectory::clear() {
    m_states.clear();
}

void StatesTrajectory::append(const SimTK::State& state) {
    if (!m_states.empty()) {

        SimTK_APIARGCHECK2_ALWAYS(m_states.back().getTime() <= state.getTime(),
                "StatesTrajectory", "append",
                "New state's time (%f) must be equal to or greater than the "
                "time for the last state in the trajectory (%f).",
                state.getTime(), m_states.back().getTime()
                );

        // We assume the trajectory (before appending) is already consistent,
        // so we only need to check consistency with a single state in the
        // trajectory.
        OPENSIM_THROW_IF(!m_states.back().isConsistent(state),
          InconsistentState, state.getTime());
    }
    m_states.push_back(state);
}

bool StatesTrajectory::hasIntegrity() const {
    return isNondecreasingInTime() && isConsistent();
}

bool StatesTrajectory::isNondecreasingInTime() const {
    // An empty or size-1 trajectory necessarily has nondecreasing times.
    if (getSize() <= 1) return true;

    for (unsigned itime = 1; itime < getSize(); ++itime) {

        if (get(itime).getTime() < get(itime - 1).getTime()) {
            return false;
        }

    }
    return true;
}

bool StatesTrajectory::isConsistent() const {
    // An empty or size-1 trajectory is necessarily consistent.
    if (getSize() <= 1) return true;

    const auto& state0 = operator[](0);

    for (unsigned itime = 1; itime < getSize(); ++itime) {

        if (!state0.isConsistent(operator[](itime))) {
            return false;
        }

    }
    return true;
}

bool StatesTrajectory::isCompatibleWith(const Model& model) const {
    // An empty trajectory is necessarily compatible.
    if (getSize() == 0) return true;

    if (!isConsistent()) return false;

    // Since we now know all the states are consistent with each other, we only
    // need to check if the first one is compatible with the model.
    const auto& state0 = get(0);

    // We only check the number of speeds because OpenSim does not count
    // quaternion slots, while the SimTK State contains quaternion slots even if
    // quaternions are not used.
    if (model.getNumSpeeds() != state0.getNU()) {
        return false;
    }
    // TODO number of constraints.

    return true;
}

// Hide this function from other translation units.
namespace {
    template <typename T>
    std::vector<std::string> createVector(const T& strings) {
        std::vector<std::string> vec;
        for (int i = 0; i < strings.size(); ++i)
            vec.push_back(strings[i]);
        return vec;
    }
}

TimeSeriesTable StatesTrajectory::exportToTable(const Model& model,
        const std::vector<std::string>& requestedStateVars) const {

    OPENSIM_THROW_IF(!isCompatibleWith(model),
                     StatesTrajectory::IncompatibleModel, model);

    // This code is based on DelimFileAdapter::extendRead().
    TimeSeriesTable table;

    // Set the column labels as metadata.
    std::vector<std::string> stateVars = requestedStateVars.empty() ?
            ::createVector(model.getStateVariableNames()) :
            requestedStateVars;
    table.setColumnLabels(stateVars);
    size_t numDepColumns = stateVars.size();

    // Fill up the table with the data.
    for (size_t itime = 0; itime < getSize(); ++itime) {
        const auto& state = get(itime);
        TimeSeriesTable::RowVector row(static_cast<int>(numDepColumns));

        // Get each state variable's value.
        if (requestedStateVars.empty()) {
            // This is *much* faster than getting the values one-by-one.
            row = model.getStateVariableValues(state).transpose();
        } else {
            for (unsigned icol = 0; icol < numDepColumns; ++icol) {
                row[static_cast<int>(icol)] =
                    model.getStateVariableValue(state, stateVars[icol]);
            }
        }

        table.appendRow(state.getTime(), row);
    }

    return table;
}

StatesTrajectory StatesTrajectory::createFromStatesStorage(
        const Model& model,
        const Storage& sto,
        bool allowMissingColumns,
        bool allowExtraColumns,
        bool assemble) {
    return createFromStatesTable(model, sto.exportToTable(),
            allowMissingColumns, allowExtraColumns, assemble);
}

StatesTrajectory StatesTrajectory::createFromStatesTable(
        const Model& model,
        const TimeSeriesTable& table,
        bool allowMissingColumns,
        bool allowExtraColumns,
        bool assemble) {

    // Assemble the required objects.
    // ==============================

    // This is what we'll return.
    StatesTrajectory states;

    // Make a copy of the model so that we can get a corresponding state.
    Model localModel(model);

    // We'll keep editing this state as we loop through time.
    auto state = localModel.initSystem();

    // The labels of the columns in the storage file.
    const auto& tableLabels = table.getColumnLabels();
    int numDependentColumns = (int)table.getNumColumns();

    // Error checking.
    // ===============
    // Angular quantities must be expressed in radians.
    // TODO we could also manually convert the necessary coords/speeds to
    // radians.
    OPENSIM_THROW_IF(TableUtilities::isInDegrees(table), DataIsInDegrees);

    // If column labels aren't unique, it's unclear which column the user
    // wanted to use for the related state variable.
    TableUtilities::checkNonUniqueLabels(tableLabels);

    // Check if states are missing from the Storage.
    // ---------------------------------------------
    const auto& modelStateNames = localModel.getStateVariableNames();
    std::vector<std::string> missingColumnNames;
    // Also, assemble the indices of the states that we will actually set in the
    // trajectory.
    std::map<int, int> statesToFillUp;
    for (const auto& label : tableLabels) {
        log_info("DEBUG {}", label);
    }
    for (int is = 0; is < modelStateNames.getSize(); ++is) {
        // getStateIndex() will check for pre-4.0 column names.
        const int stateIndex = TableUtilities::findStateLabelIndex(
                tableLabels, modelStateNames[is]);
        if (stateIndex == -1) {
            missingColumnNames.push_back(modelStateNames[is]);
        } else {
            statesToFillUp[stateIndex] = is;
        }
    }
    OPENSIM_THROW_IF(!allowMissingColumns && !missingColumnNames.empty(),
            MissingColumns,
            localModel.getName(), missingColumnNames);

    // Check if the Storage has columns that are not states in the Model.
    // ------------------------------------------------------------------
    if (!allowExtraColumns) {
        if ((unsigned)numDependentColumns > statesToFillUp.size()) {
            std::vector<std::string> extraColumnNames;
            // We want the actual column names, not the state names; the two
            // might be different b/c the state names changed in v4.0.
            for (int ic = 1; ic < (int)tableLabels.size(); ++ic) {
                // Has this label been marked as a model state?
                if (statesToFillUp.count(ic) == 0) {
                    extraColumnNames.push_back(tableLabels[ic]);
                }
            }
            OPENSIM_THROW(ExtraColumns, localModel.getName(),
                    extraColumnNames);
        }
    }

    // Fill up trajectory.
    // ===================

    // Reserve the memory we'll need to fit all the states.
    states.m_states.reserve(table.getNumRows());

    // Working memory for state. Initialize so that missing columns end up as
    // NaN.
    SimTK::Vector statesValues(modelStateNames.getSize(), SimTK::NaN);

    // Initialize so that missing columns end up as NaN.
    state.updY().setToNaN();

    // Loop through all rows of the Storage.
    for (int itime = 0; itime < (int)table.getNumRows(); ++itime) {
        const auto& row = table.getRowAtIndex(itime);

        // Set the correct time in the state.
        state.setTime(table.getIndependentColumn()[itime]);

        // Fill up current State with the data for the current time.
        for (const auto& kv : statesToFillUp) {
            // 'first': index for Storage; 'second': index for Model.
            statesValues[kv.second] = row[kv.first];
        }
        localModel.setStateVariableValues(state, statesValues);
        if (assemble) {
            localModel.assemble(state);
        }

        // Make a copy of the edited state and put it in the trajectory.
        states.append(state);
    }

    return states;
}

StatesTrajectory StatesTrajectory::createFromStatesStorage(
        const Model& model,
        const std::string& filepath) {
    return createFromStatesTable(model, TimeSeriesTable(filepath));
}

StatesTrajectory::IncompatibleModel::IncompatibleModel(
        const std::string& file, size_t line,
        const std::string& func, const Model& model)
        : OpenSim::Exception(file, line, func) {
    std::ostringstream msg;
    auto modelName = model.getName().empty() ? "<empty-name>" :
                     model.getName();
    msg << "The provided model '" << modelName << "' is not "
            "compatible with the StatesTrajectory.";
    addMessage(msg.str());
}
<commit_msg>Remove log_info("DEBUG ...)<commit_after>/* -------------------------------------------------------------------------- *
 *                       OpenSim:  StatesTrajectory.cpp                       *
 * -------------------------------------------------------------------------- *
 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2017 Stanford University and the Authors                *
 * Author(s): Chris Dembia                                                    *
 *                                                                            *
 * 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 "StatesTrajectory.h"

#include <OpenSim/Common/CommonUtilities.h>
#include <OpenSim/Common/Storage.h>
#include <OpenSim/Common/TableUtilities.h>
#include <OpenSim/Simulation/Model/Model.h>

using namespace OpenSim;

size_t StatesTrajectory::getSize() const {
    return m_states.size();
}

void StatesTrajectory::clear() {
    m_states.clear();
}

void StatesTrajectory::append(const SimTK::State& state) {
    if (!m_states.empty()) {

        SimTK_APIARGCHECK2_ALWAYS(m_states.back().getTime() <= state.getTime(),
                "StatesTrajectory", "append",
                "New state's time (%f) must be equal to or greater than the "
                "time for the last state in the trajectory (%f).",
                state.getTime(), m_states.back().getTime()
                );

        // We assume the trajectory (before appending) is already consistent,
        // so we only need to check consistency with a single state in the
        // trajectory.
        OPENSIM_THROW_IF(!m_states.back().isConsistent(state),
          InconsistentState, state.getTime());
    }
    m_states.push_back(state);
}

bool StatesTrajectory::hasIntegrity() const {
    return isNondecreasingInTime() && isConsistent();
}

bool StatesTrajectory::isNondecreasingInTime() const {
    // An empty or size-1 trajectory necessarily has nondecreasing times.
    if (getSize() <= 1) return true;

    for (unsigned itime = 1; itime < getSize(); ++itime) {

        if (get(itime).getTime() < get(itime - 1).getTime()) {
            return false;
        }

    }
    return true;
}

bool StatesTrajectory::isConsistent() const {
    // An empty or size-1 trajectory is necessarily consistent.
    if (getSize() <= 1) return true;

    const auto& state0 = operator[](0);

    for (unsigned itime = 1; itime < getSize(); ++itime) {

        if (!state0.isConsistent(operator[](itime))) {
            return false;
        }

    }
    return true;
}

bool StatesTrajectory::isCompatibleWith(const Model& model) const {
    // An empty trajectory is necessarily compatible.
    if (getSize() == 0) return true;

    if (!isConsistent()) return false;

    // Since we now know all the states are consistent with each other, we only
    // need to check if the first one is compatible with the model.
    const auto& state0 = get(0);

    // We only check the number of speeds because OpenSim does not count
    // quaternion slots, while the SimTK State contains quaternion slots even if
    // quaternions are not used.
    if (model.getNumSpeeds() != state0.getNU()) {
        return false;
    }
    // TODO number of constraints.

    return true;
}

// Hide this function from other translation units.
namespace {
    template <typename T>
    std::vector<std::string> createVector(const T& strings) {
        std::vector<std::string> vec;
        for (int i = 0; i < strings.size(); ++i)
            vec.push_back(strings[i]);
        return vec;
    }
}

TimeSeriesTable StatesTrajectory::exportToTable(const Model& model,
        const std::vector<std::string>& requestedStateVars) const {

    OPENSIM_THROW_IF(!isCompatibleWith(model),
                     StatesTrajectory::IncompatibleModel, model);

    // This code is based on DelimFileAdapter::extendRead().
    TimeSeriesTable table;

    // Set the column labels as metadata.
    std::vector<std::string> stateVars = requestedStateVars.empty() ?
            ::createVector(model.getStateVariableNames()) :
            requestedStateVars;
    table.setColumnLabels(stateVars);
    size_t numDepColumns = stateVars.size();

    // Fill up the table with the data.
    for (size_t itime = 0; itime < getSize(); ++itime) {
        const auto& state = get(itime);
        TimeSeriesTable::RowVector row(static_cast<int>(numDepColumns));

        // Get each state variable's value.
        if (requestedStateVars.empty()) {
            // This is *much* faster than getting the values one-by-one.
            row = model.getStateVariableValues(state).transpose();
        } else {
            for (unsigned icol = 0; icol < numDepColumns; ++icol) {
                row[static_cast<int>(icol)] =
                    model.getStateVariableValue(state, stateVars[icol]);
            }
        }

        table.appendRow(state.getTime(), row);
    }

    return table;
}

StatesTrajectory StatesTrajectory::createFromStatesStorage(
        const Model& model,
        const Storage& sto,
        bool allowMissingColumns,
        bool allowExtraColumns,
        bool assemble) {
    return createFromStatesTable(model, sto.exportToTable(),
            allowMissingColumns, allowExtraColumns, assemble);
}

StatesTrajectory StatesTrajectory::createFromStatesTable(
        const Model& model,
        const TimeSeriesTable& table,
        bool allowMissingColumns,
        bool allowExtraColumns,
        bool assemble) {

    // Assemble the required objects.
    // ==============================

    // This is what we'll return.
    StatesTrajectory states;

    // Make a copy of the model so that we can get a corresponding state.
    Model localModel(model);

    // We'll keep editing this state as we loop through time.
    auto state = localModel.initSystem();

    // The labels of the columns in the storage file.
    const auto& tableLabels = table.getColumnLabels();
    int numDependentColumns = (int)table.getNumColumns();

    // Error checking.
    // ===============
    // Angular quantities must be expressed in radians.
    // TODO we could also manually convert the necessary coords/speeds to
    // radians.
    OPENSIM_THROW_IF(TableUtilities::isInDegrees(table), DataIsInDegrees);

    // If column labels aren't unique, it's unclear which column the user
    // wanted to use for the related state variable.
    TableUtilities::checkNonUniqueLabels(tableLabels);

    // Check if states are missing from the Storage.
    // ---------------------------------------------
    const auto& modelStateNames = localModel.getStateVariableNames();
    std::vector<std::string> missingColumnNames;
    // Also, assemble the indices of the states that we will actually set in the
    // trajectory.
    std::map<int, int> statesToFillUp;
    for (int is = 0; is < modelStateNames.getSize(); ++is) {
        // getStateIndex() will check for pre-4.0 column names.
        const int stateIndex = TableUtilities::findStateLabelIndex(
                tableLabels, modelStateNames[is]);
        if (stateIndex == -1) {
            missingColumnNames.push_back(modelStateNames[is]);
        } else {
            statesToFillUp[stateIndex] = is;
        }
    }
    OPENSIM_THROW_IF(!allowMissingColumns && !missingColumnNames.empty(),
            MissingColumns,
            localModel.getName(), missingColumnNames);

    // Check if the Storage has columns that are not states in the Model.
    // ------------------------------------------------------------------
    if (!allowExtraColumns) {
        if ((unsigned)numDependentColumns > statesToFillUp.size()) {
            std::vector<std::string> extraColumnNames;
            // We want the actual column names, not the state names; the two
            // might be different b/c the state names changed in v4.0.
            for (int ic = 1; ic < (int)tableLabels.size(); ++ic) {
                // Has this label been marked as a model state?
                if (statesToFillUp.count(ic) == 0) {
                    extraColumnNames.push_back(tableLabels[ic]);
                }
            }
            OPENSIM_THROW(ExtraColumns, localModel.getName(),
                    extraColumnNames);
        }
    }

    // Fill up trajectory.
    // ===================

    // Reserve the memory we'll need to fit all the states.
    states.m_states.reserve(table.getNumRows());

    // Working memory for state. Initialize so that missing columns end up as
    // NaN.
    SimTK::Vector statesValues(modelStateNames.getSize(), SimTK::NaN);

    // Initialize so that missing columns end up as NaN.
    state.updY().setToNaN();

    // Loop through all rows of the Storage.
    for (int itime = 0; itime < (int)table.getNumRows(); ++itime) {
        const auto& row = table.getRowAtIndex(itime);

        // Set the correct time in the state.
        state.setTime(table.getIndependentColumn()[itime]);

        // Fill up current State with the data for the current time.
        for (const auto& kv : statesToFillUp) {
            // 'first': index for Storage; 'second': index for Model.
            statesValues[kv.second] = row[kv.first];
        }
        localModel.setStateVariableValues(state, statesValues);
        if (assemble) {
            localModel.assemble(state);
        }

        // Make a copy of the edited state and put it in the trajectory.
        states.append(state);
    }

    return states;
}

StatesTrajectory StatesTrajectory::createFromStatesStorage(
        const Model& model,
        const std::string& filepath) {
    return createFromStatesTable(model, TimeSeriesTable(filepath));
}

StatesTrajectory::IncompatibleModel::IncompatibleModel(
        const std::string& file, size_t line,
        const std::string& func, const Model& model)
        : OpenSim::Exception(file, line, func) {
    std::ostringstream msg;
    auto modelName = model.getName().empty() ? "<empty-name>" :
                     model.getName();
    msg << "The provided model '" << modelName << "' is not "
            "compatible with the StatesTrajectory.";
    addMessage(msg.str());
}
<|endoftext|>


<commit_before>/*
 * Skaffari - a mail account administration web interface based on Cutelyst
 * Copyright (C) 2017 Matthias Fehring <kontakt@buschmann23.de>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "timezoneconverttag.h"

#include <grantlee/exception.h>
#include <grantlee/parser.h>
#include <grantlee/safestring.h>

#include <Cutelyst/Context>
#include <Cutelyst/Plugins/Session/Session>

#include <QLocale>
#include <QDateTime>
#include <QTimeZone>

Grantlee::Node *TimeZoneConvertTag::getNode(const QString &tagContent, Grantlee::Parser *p) const
{
    QStringList parts = smartSplit(tagContent);
    parts.removeFirst();
    if (parts.empty()) {
        throw Grantlee::Exception(Grantlee::TagSyntaxError, QStringLiteral("sk_tzc requires at least the date or time"));
    }

    Grantlee::FilterExpression dateTime(parts.at(0), p);

    Grantlee::FilterExpression format;
    if (parts.size() > 1) {
        format = Grantlee::FilterExpression(parts.at(1), p);
    }

    return new TimeZoneConvert(dateTime, format, p);
}

TimeZoneConvert::TimeZoneConvert(const Grantlee::FilterExpression &dateTime, const Grantlee::FilterExpression &format, Grantlee::Parser *parser) :
    Grantlee::Node(parser), m_dateTime(dateTime), m_format(format)
{

}

void TimeZoneConvert::render(Grantlee::OutputStream *stream, Grantlee::Context *gc) const
{
    const QVariant dtVar = m_dateTime.resolve(gc);
    if (dtVar.userType() != QVariant::DateTime) {
        qWarning("%s", "sk_tzc can only operate on QDateTime values.");
        return;
    }

    auto c = gc->lookup(m_cutelystContext).value<Cutelyst::Context *>();
    if (!c) {
        const QVariantHash hash = gc->stackHash(0);
        auto it = hash.constBegin();
        while (it != hash.constEnd()) {
            if (it.value().userType() == qMetaTypeId<Cutelyst::Context *>()) {
                c = it.value().value<Cutelyst::Context *>();
                if (c) {
                    m_cutelystContext = it.key();
                    break;
                }
            }
            ++it;
        }
    }

    if (!c) {
        qWarning("%s", "Can not find Cutelyst context.");
        return;
    }

    QDateTime dtVal = dtVar.toDateTime();
    QDateTime retVal;

    QTimeZone userTz(Cutelyst::Session::value(c, QStringLiteral("tz"), QStringLiteral("UTZ")).toByteArray());
    if (Q_UNLIKELY(!userTz.isValid())) {
        userTz = QTimeZone::utc();
    }

    if (userTz != QTimeZone::utc()) {
        dtVal.setTimeSpec(Qt::UTC);
        retVal = dtVal.toTimeZone(userTz);
    } else {
        retVal = dtVal;
    }

    QString formatString;
    if (m_format.isValid()) {
        const QVariant formatVar = m_format.resolve(gc);
        const int formatVarType = formatVar.userType();
        if (formatVarType == qMetaTypeId<Grantlee::SafeString>()) {
            formatString = formatVar.value<Grantlee::SafeString>().get();
        } else if (formatVarType == QVariant::String) {
            formatString = formatVar.toString();
        }
    }

    if (formatString.isEmpty()) {
        *stream << c->locale().toString(retVal, QLocale::ShortFormat);
    } else {
        *stream << c->locale().toString(retVal, formatString);
    }
}
<commit_msg>Skaffari Grantlee: directly return input if string on sk_tzc<commit_after>/*
 * Skaffari - a mail account administration web interface based on Cutelyst
 * Copyright (C) 2017 Matthias Fehring <kontakt@buschmann23.de>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "timezoneconverttag.h"

#include <grantlee/exception.h>
#include <grantlee/parser.h>
#include <grantlee/safestring.h>

#include <Cutelyst/Context>
#include <Cutelyst/Plugins/Session/Session>

#include <QLocale>
#include <QDateTime>
#include <QTimeZone>

Grantlee::Node *TimeZoneConvertTag::getNode(const QString &tagContent, Grantlee::Parser *p) const
{
    QStringList parts = smartSplit(tagContent);
    parts.removeFirst();
    if (parts.empty()) {
        throw Grantlee::Exception(Grantlee::TagSyntaxError, QStringLiteral("sk_tzc requires at least the date or time"));
    }

    Grantlee::FilterExpression dateTime(parts.at(0), p);

    Grantlee::FilterExpression format;
    if (parts.size() > 1) {
        format = Grantlee::FilterExpression(parts.at(1), p);
    }

    return new TimeZoneConvert(dateTime, format, p);
}

TimeZoneConvert::TimeZoneConvert(const Grantlee::FilterExpression &dateTime, const Grantlee::FilterExpression &format, Grantlee::Parser *parser) :
    Grantlee::Node(parser), m_dateTime(dateTime), m_format(format)
{

}

void TimeZoneConvert::render(Grantlee::OutputStream *stream, Grantlee::Context *gc) const
{
    const QVariant dtVar = m_dateTime.resolve(gc);
    const int dtVarType = dtVar.userType();
    if (dtVarType == qMetaTypeId<Grantlee::SafeString>()) {
        *stream << dtVar.value<Grantlee::SafeString>().get();
        return;
    } else if (dtVarType == QVariant::String) {
        *stream << dtVar.toString();
        return;
    } else {
        if (dtVar.userType() != QVariant::DateTime) {
            qWarning("%s", "sk_tzc can only operate on QDateTime values.");
            return;
        }
    }

    auto c = gc->lookup(m_cutelystContext).value<Cutelyst::Context *>();
    if (!c) {
        const QVariantHash hash = gc->stackHash(0);
        auto it = hash.constBegin();
        while (it != hash.constEnd()) {
            if (it.value().userType() == qMetaTypeId<Cutelyst::Context *>()) {
                c = it.value().value<Cutelyst::Context *>();
                if (c) {
                    m_cutelystContext = it.key();
                    break;
                }
            }
            ++it;
        }
    }

    if (!c) {
        qWarning("%s", "Can not find Cutelyst context.");
        return;
    }

    QDateTime dtVal = dtVar.toDateTime();
    QDateTime retVal;

    QTimeZone userTz(Cutelyst::Session::value(c, QStringLiteral("tz"), QStringLiteral("UTZ")).toByteArray());
    if (Q_UNLIKELY(!userTz.isValid())) {
        userTz = QTimeZone::utc();
    }

    if (userTz != QTimeZone::utc()) {
        dtVal.setTimeSpec(Qt::UTC);
        retVal = dtVal.toTimeZone(userTz);
    } else {
        retVal = dtVal;
    }

    QString formatString;
    if (m_format.isValid()) {
        const QVariant formatVar = m_format.resolve(gc);
        const int formatVarType = formatVar.userType();
        if (formatVarType == qMetaTypeId<Grantlee::SafeString>()) {
            formatString = formatVar.value<Grantlee::SafeString>().get();
        } else if (formatVarType == QVariant::String) {
            formatString = formatVar.toString();
        }
    }

    if (formatString.isEmpty()) {
        *stream << c->locale().toString(retVal, QLocale::ShortFormat);
    } else {
        *stream << c->locale().toString(retVal, formatString);
    }
}
<|endoftext|>