<commit_before>/**************************************************************************
 *
 * Copyright 2011 Jose Fonseca
 * Copyright (C) 2013 Intel Corporation. All rights reversed.
 * Author: Shuang He <shuang.he@intel.com>
 * All Rights Reserved.
 *
 * 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 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 <string.h>

#include "retrace.hpp"
#include "glproc.hpp"
#include "glstate.hpp"
#include "glretrace.hpp"
#include "os_time.hpp"
#include "os_memory.hpp"

/* Synchronous debug output may reduce performance however,
 * without it the callNo in the callback may be inaccurate
 * as the callback may be called at any time.
 */
#define DEBUG_OUTPUT_SYNCHRONOUS 0

namespace glretrace {

glws::Profile defaultProfile = glws::PROFILE_COMPAT;

bool insideList = false;
bool insideGlBeginEnd = false;
bool supportsARBShaderObjects = false;

enum {
    GPU_START = 0,
    GPU_DURATION,
    OCCLUSION,
    NUM_QUERIES,
};

struct CallQuery
{
    GLuint ids[NUM_QUERIES];
    unsigned call;
    bool isDraw;
    GLuint program;
    const trace::FunctionSig *sig;
    int64_t cpuStart;
    int64_t cpuEnd;
    int64_t vsizeStart;
    int64_t vsizeEnd;
    int64_t rssStart;
    int64_t rssEnd;
};

static bool supportsElapsed = true;
static bool supportsTimestamp = true;
static bool supportsOcclusion = true;
static bool supportsDebugOutput = true;

static std::list<CallQuery> callQueries;

static void APIENTRY
debugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam);

void
checkGlError(trace::Call &call) {
    GLenum error = glGetError();
    while (error != GL_NO_ERROR) {
        std::ostream & os = retrace::warning(call);

        os << "glGetError(";
        os << call.name();
        os << ") = ";

        switch (error) {
        case GL_INVALID_ENUM:
            os << "GL_INVALID_ENUM";
            break;
        case GL_INVALID_VALUE:
            os << "GL_INVALID_VALUE";
            break;
        case GL_INVALID_OPERATION:
            os << "GL_INVALID_OPERATION";
            break;
        case GL_STACK_OVERFLOW:
            os << "GL_STACK_OVERFLOW";
            break;
        case GL_STACK_UNDERFLOW:
            os << "GL_STACK_UNDERFLOW";
            break;
        case GL_OUT_OF_MEMORY:
            os << "GL_OUT_OF_MEMORY";
            break;
        case GL_INVALID_FRAMEBUFFER_OPERATION:
            os << "GL_INVALID_FRAMEBUFFER_OPERATION";
            break;
        case GL_TABLE_TOO_LARGE:
            os << "GL_TABLE_TOO_LARGE";
            break;
        default:
            os << error;
            break;
        }
        os << "\n";

        error = glGetError();
    }
}

static inline int64_t
getCurrentTime(void) {
    if (retrace::profilingGpuTimes && supportsTimestamp) {
        /* Get the current GL time without stalling */
        GLint64 timestamp = 0;
        glGetInteger64v(GL_TIMESTAMP, &timestamp);
        return timestamp;
    } else {
        return os::getTime();
    }
}

static inline int64_t
getTimeFrequency(void) {
    if (retrace::profilingGpuTimes && supportsTimestamp) {
        return 1000000000;
    } else {
        return os::timeFrequency;
    }
}

static inline void
getCurrentVsize(int64_t& vsize) {
    vsize = os::getVsize();
}

static inline void
getCurrentRss(int64_t& rss) {
    rss = os::getRss();
}

static void
completeCallQuery(CallQuery& query) {
    /* Get call start and duration */
    int64_t gpuStart = 0, gpuDuration = 0, cpuDuration = 0, pixels = 0, vsizeDuration = 0, rssDuration = 0;

    if (query.isDraw) {
        if (retrace::profilingGpuTimes) {
            if (supportsTimestamp) {
                glGetQueryObjecti64vEXT(query.ids[GPU_START], GL_QUERY_RESULT, &gpuStart);
            }

            glGetQueryObjecti64vEXT(query.ids[GPU_DURATION], GL_QUERY_RESULT, &gpuDuration);
        }

        if (retrace::profilingPixelsDrawn) {
            glGetQueryObjecti64vEXT(query.ids[OCCLUSION], GL_QUERY_RESULT, &pixels);
        }

    } else {
        pixels = -1;
    }

    if (retrace::profilingCpuTimes) {
        double cpuTimeScale = 1.0E9 / getTimeFrequency();
        cpuDuration = (query.cpuEnd - query.cpuStart) * cpuTimeScale;
        query.cpuStart *= cpuTimeScale;
    }

    if (retrace::profilingMemoryUsage) {
        vsizeDuration = query.vsizeEnd - query.vsizeStart;
        rssDuration = query.rssEnd - query.rssStart;
    }

    glDeleteQueries(NUM_QUERIES, query.ids);

    /* Add call to profile */
    retrace::profiler.addCall(query.call, query.sig->name, query.program, pixels, gpuStart, gpuDuration, query.cpuStart, cpuDuration, query.vsizeStart, vsizeDuration, query.rssStart, rssDuration);
}

void
flushQueries() {
    for (std::list<CallQuery>::iterator itr = callQueries.begin(); itr != callQueries.end(); ++itr) {
        completeCallQuery(*itr);
    }

    callQueries.clear();
}

void
beginProfile(trace::Call &call, bool isDraw) {
    glretrace::Context *currentContext = glretrace::getCurrentContext();

    /* Create call query */
    CallQuery query;
    query.isDraw = isDraw;
    query.call = call.no;
    query.sig = call.sig;
    query.program = currentContext ? currentContext->activeProgram : 0;

    glGenQueries(NUM_QUERIES, query.ids);

    /* GPU profiling only for draw calls */
    if (isDraw) {
        if (retrace::profilingGpuTimes) {
            if (supportsTimestamp) {
                glQueryCounter(query.ids[GPU_START], GL_TIMESTAMP);
            }

            glBeginQuery(GL_TIME_ELAPSED, query.ids[GPU_DURATION]);
        }

        if (retrace::profilingPixelsDrawn) {
            glBeginQuery(GL_SAMPLES_PASSED, query.ids[OCCLUSION]);
        }
    }

    callQueries.push_back(query);

    /* CPU profiling for all calls */
    if (retrace::profilingCpuTimes) {
        CallQuery& query = callQueries.back();
        query.cpuStart = getCurrentTime();
    }

    if (retrace::profilingMemoryUsage) {
        CallQuery& query = callQueries.back();
        query.vsizeStart = os::getVsize();
        query.rssStart = os::getRss();
    }
}

void
endProfile(trace::Call &call, bool isDraw) {

    /* CPU profiling for all calls */
    if (retrace::profilingCpuTimes) {
        CallQuery& query = callQueries.back();
        query.cpuEnd = getCurrentTime();
    }

    /* GPU profiling only for draw calls */
    if (isDraw) {
        if (retrace::profilingGpuTimes) {
            glEndQuery(GL_TIME_ELAPSED);
        }

        if (retrace::profilingPixelsDrawn) {
            glEndQuery(GL_SAMPLES_PASSED);
        }
    }

    if (retrace::profilingMemoryUsage) {
        CallQuery& query = callQueries.back();
        query.vsizeEnd = os::getVsize();
        query.rssEnd = os::getRss();
    }
}

void
initContext() {
    glretrace::Context *currentContext = glretrace::getCurrentContext();

    /* Ensure we have adequate extension support */
    assert(currentContext);
    supportsTimestamp   = currentContext->hasExtension("GL_ARB_timer_query");
    supportsElapsed     = currentContext->hasExtension("GL_EXT_timer_query") || supportsTimestamp;
    supportsOcclusion   = currentContext->hasExtension("GL_ARB_occlusion_query");
    supportsDebugOutput = currentContext->hasExtension("GL_ARB_debug_output");
    supportsARBShaderObjects = currentContext->hasExtension("GL_ARB_shader_objects");

    /* Check for timer query support */
    if (retrace::profilingGpuTimes) {
        if (!supportsTimestamp && !supportsElapsed) {
            std::cout << "Error: Cannot run profile, GL_EXT_timer_query extension is not supported." << std::endl;
            exit(-1);
        }

        GLint bits = 0;
        glGetQueryiv(GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS, &bits);

        if (!bits) {
            std::cout << "Error: Cannot run profile, GL_QUERY_COUNTER_BITS == 0." << std::endl;
            exit(-1);
        }
    }

    /* Check for occlusion query support */
    if (retrace::profilingPixelsDrawn && !supportsOcclusion) {
        std::cout << "Error: Cannot run profile, GL_ARB_occlusion_query extension is not supported." << std::endl;
        exit(-1);
    }

    /* Setup debug message call back */
    if (retrace::debug && supportsDebugOutput) {
        glretrace::Context *currentContext = glretrace::getCurrentContext();
        glDebugMessageCallbackARB(&debugOutputCallback, currentContext);

        if (DEBUG_OUTPUT_SYNCHRONOUS) {
            glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
        }
    }

    /* Sync the gpu and cpu start times */
    if (retrace::profilingCpuTimes || retrace::profilingGpuTimes) {
        if (!retrace::profiler.hasBaseTimes()) {
            double cpuTimeScale = 1.0E9 / getTimeFrequency();
            GLint64 currentTime = getCurrentTime() * cpuTimeScale;
            retrace::profiler.setBaseCpuTime(currentTime);
            retrace::profiler.setBaseGpuTime(currentTime);
        }
    }

    if (retrace::profilingMemoryUsage) {
        GLint64 currentVsize, currentRss;
        getCurrentVsize(currentVsize);
        retrace::profiler.setBaseVsizeUsage(currentVsize);
        getCurrentRss(currentRss);
        retrace::profiler.setBaseRssUsage(currentRss);
    }
}

void
frame_complete(trace::Call &call) {
    if (retrace::profiling) {
        /* Complete any remaining queries */
        flushQueries();

        /* Indicate end of current frame */
        retrace::profiler.addFrameEnd();
    }

    retrace::frameComplete(call);

    glretrace::Context *currentContext = glretrace::getCurrentContext();
    if (!currentContext) {
        return;
    }

    assert(currentContext->drawable);
    if (retrace::debug && !currentContext->drawable->visible) {
        retrace::warning(call) << "could not infer drawable size (glViewport never called)\n";
    }
}

static const char*
getDebugOutputSource(GLenum source) {
    switch(source) {
    case GL_DEBUG_SOURCE_API_ARB:
        return "API";
    case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB:
        return "Window System";
    case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB:
        return "Shader Compiler";
    case GL_DEBUG_SOURCE_THIRD_PARTY_ARB:
        return "Third Party";
    case GL_DEBUG_SOURCE_APPLICATION_ARB:
        return "Application";
    case GL_DEBUG_SOURCE_OTHER_ARB:
    default:
        return "";
    }
}

static const char*
getDebugOutputType(GLenum type) {
    switch(type) {
    case GL_DEBUG_TYPE_ERROR_ARB:
        return "error";
    case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:
        return "deprecated behaviour";
    case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB:
        return "undefined behaviour";
    case GL_DEBUG_TYPE_PORTABILITY_ARB:
        return "portability issue";
    case GL_DEBUG_TYPE_PERFORMANCE_ARB:
        return "performance issue";
    case GL_DEBUG_TYPE_OTHER_ARB:
    default:
        return "unknown issue";
    }
}

static const char*
getDebugOutputSeverity(GLenum severity) {
    switch(severity) {
    case GL_DEBUG_SEVERITY_HIGH_ARB:
        return "High";
    case GL_DEBUG_SEVERITY_MEDIUM_ARB:
        return "Medium";
    case GL_DEBUG_SEVERITY_LOW_ARB:
        return "Low";
    default:
        return "usnknown";
    }
}

static void APIENTRY
debugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) {
    std::cerr << retrace::callNo << ": ";
    std::cerr << "glDebugOutputCallback: ";
    std::cerr << getDebugOutputSeverity(severity) << " severity ";
    std::cerr << getDebugOutputSource(source) << " " << getDebugOutputType(type);
    std::cerr << " " << id;
    std::cerr << ", " << message;
    std::cerr << std::endl;
}

} /* namespace glretrace */

class GLDumper : public retrace::Dumper {
public:
    image::Image *
    getSnapshot(void) {
        if (!glretrace::getCurrentContext()) {
            return NULL;
        }
        return glstate::getDrawBufferImage();
    }

    bool
    dumpState(std::ostream &os) {
        glretrace::Context *currentContext = glretrace::getCurrentContext();
        if (glretrace::insideGlBeginEnd ||
            !currentContext) {
            return false;
        }
        glstate::dumpCurrentContext(os);
        return true;
    }
};

static GLDumper glDumper;

void
retrace::setFeatureLevel(const char *featureLevel)
{
    glretrace::defaultProfile = glws::PROFILE_3_2_CORE;
}

void
retrace::setUp(void) {
    glws::init();
    dumper = &glDumper;
}

void
retrace::addCallbacks(retrace::Retracer &retracer)
{
    retracer.addCallbacks(glretrace::gl_callbacks);
    retracer.addCallbacks(glretrace::glx_callbacks);
    retracer.addCallbacks(glretrace::wgl_callbacks);
    retracer.addCallbacks(glretrace::cgl_callbacks);
    retracer.addCallbacks(glretrace::egl_callbacks);
}

void
retrace::flushRendering(void) {
    glretrace::Context *currentContext = glretrace::getCurrentContext();
    if (currentContext) {
        glretrace::flushQueries();
    }
}

void
retrace::finishRendering(void) {
    glretrace::Context *currentContext = glretrace::getCurrentContext();
    if (currentContext) {
        glFinish();
    }
}

void
retrace::waitForInput(void) {
    flushRendering();
    while (glws::processEvents()) {
        os::sleep(100*1000);
    }
}

void
retrace::cleanUp(void) {
    glws::cleanup();
}
<commit_msg>glretrace: Enable all debug output.<commit_after>/**************************************************************************
 *
 * Copyright 2011 Jose Fonseca
 * Copyright (C) 2013 Intel Corporation. All rights reversed.
 * Author: Shuang He <shuang.he@intel.com>
 * All Rights Reserved.
 *
 * 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 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 <string.h>

#include "retrace.hpp"
#include "glproc.hpp"
#include "glstate.hpp"
#include "glretrace.hpp"
#include "os_time.hpp"
#include "os_memory.hpp"

/* Synchronous debug output may reduce performance however,
 * without it the callNo in the callback may be inaccurate
 * as the callback may be called at any time.
 */
#define DEBUG_OUTPUT_SYNCHRONOUS 0

namespace glretrace {

glws::Profile defaultProfile = glws::PROFILE_COMPAT;

bool insideList = false;
bool insideGlBeginEnd = false;
bool supportsARBShaderObjects = false;

enum {
    GPU_START = 0,
    GPU_DURATION,
    OCCLUSION,
    NUM_QUERIES,
};

struct CallQuery
{
    GLuint ids[NUM_QUERIES];
    unsigned call;
    bool isDraw;
    GLuint program;
    const trace::FunctionSig *sig;
    int64_t cpuStart;
    int64_t cpuEnd;
    int64_t vsizeStart;
    int64_t vsizeEnd;
    int64_t rssStart;
    int64_t rssEnd;
};

static bool supportsElapsed = true;
static bool supportsTimestamp = true;
static bool supportsOcclusion = true;
static bool supportsDebugOutput = true;

static std::list<CallQuery> callQueries;

static void APIENTRY
debugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam);

void
checkGlError(trace::Call &call) {
    GLenum error = glGetError();
    while (error != GL_NO_ERROR) {
        std::ostream & os = retrace::warning(call);

        os << "glGetError(";
        os << call.name();
        os << ") = ";

        switch (error) {
        case GL_INVALID_ENUM:
            os << "GL_INVALID_ENUM";
            break;
        case GL_INVALID_VALUE:
            os << "GL_INVALID_VALUE";
            break;
        case GL_INVALID_OPERATION:
            os << "GL_INVALID_OPERATION";
            break;
        case GL_STACK_OVERFLOW:
            os << "GL_STACK_OVERFLOW";
            break;
        case GL_STACK_UNDERFLOW:
            os << "GL_STACK_UNDERFLOW";
            break;
        case GL_OUT_OF_MEMORY:
            os << "GL_OUT_OF_MEMORY";
            break;
        case GL_INVALID_FRAMEBUFFER_OPERATION:
            os << "GL_INVALID_FRAMEBUFFER_OPERATION";
            break;
        case GL_TABLE_TOO_LARGE:
            os << "GL_TABLE_TOO_LARGE";
            break;
        default:
            os << error;
            break;
        }
        os << "\n";

        error = glGetError();
    }
}

static inline int64_t
getCurrentTime(void) {
    if (retrace::profilingGpuTimes && supportsTimestamp) {
        /* Get the current GL time without stalling */
        GLint64 timestamp = 0;
        glGetInteger64v(GL_TIMESTAMP, &timestamp);
        return timestamp;
    } else {
        return os::getTime();
    }
}

static inline int64_t
getTimeFrequency(void) {
    if (retrace::profilingGpuTimes && supportsTimestamp) {
        return 1000000000;
    } else {
        return os::timeFrequency;
    }
}

static inline void
getCurrentVsize(int64_t& vsize) {
    vsize = os::getVsize();
}

static inline void
getCurrentRss(int64_t& rss) {
    rss = os::getRss();
}

static void
completeCallQuery(CallQuery& query) {
    /* Get call start and duration */
    int64_t gpuStart = 0, gpuDuration = 0, cpuDuration = 0, pixels = 0, vsizeDuration = 0, rssDuration = 0;

    if (query.isDraw) {
        if (retrace::profilingGpuTimes) {
            if (supportsTimestamp) {
                glGetQueryObjecti64vEXT(query.ids[GPU_START], GL_QUERY_RESULT, &gpuStart);
            }

            glGetQueryObjecti64vEXT(query.ids[GPU_DURATION], GL_QUERY_RESULT, &gpuDuration);
        }

        if (retrace::profilingPixelsDrawn) {
            glGetQueryObjecti64vEXT(query.ids[OCCLUSION], GL_QUERY_RESULT, &pixels);
        }

    } else {
        pixels = -1;
    }

    if (retrace::profilingCpuTimes) {
        double cpuTimeScale = 1.0E9 / getTimeFrequency();
        cpuDuration = (query.cpuEnd - query.cpuStart) * cpuTimeScale;
        query.cpuStart *= cpuTimeScale;
    }

    if (retrace::profilingMemoryUsage) {
        vsizeDuration = query.vsizeEnd - query.vsizeStart;
        rssDuration = query.rssEnd - query.rssStart;
    }

    glDeleteQueries(NUM_QUERIES, query.ids);

    /* Add call to profile */
    retrace::profiler.addCall(query.call, query.sig->name, query.program, pixels, gpuStart, gpuDuration, query.cpuStart, cpuDuration, query.vsizeStart, vsizeDuration, query.rssStart, rssDuration);
}

void
flushQueries() {
    for (std::list<CallQuery>::iterator itr = callQueries.begin(); itr != callQueries.end(); ++itr) {
        completeCallQuery(*itr);
    }

    callQueries.clear();
}

void
beginProfile(trace::Call &call, bool isDraw) {
    glretrace::Context *currentContext = glretrace::getCurrentContext();

    /* Create call query */
    CallQuery query;
    query.isDraw = isDraw;
    query.call = call.no;
    query.sig = call.sig;
    query.program = currentContext ? currentContext->activeProgram : 0;

    glGenQueries(NUM_QUERIES, query.ids);

    /* GPU profiling only for draw calls */
    if (isDraw) {
        if (retrace::profilingGpuTimes) {
            if (supportsTimestamp) {
                glQueryCounter(query.ids[GPU_START], GL_TIMESTAMP);
            }

            glBeginQuery(GL_TIME_ELAPSED, query.ids[GPU_DURATION]);
        }

        if (retrace::profilingPixelsDrawn) {
            glBeginQuery(GL_SAMPLES_PASSED, query.ids[OCCLUSION]);
        }
    }

    callQueries.push_back(query);

    /* CPU profiling for all calls */
    if (retrace::profilingCpuTimes) {
        CallQuery& query = callQueries.back();
        query.cpuStart = getCurrentTime();
    }

    if (retrace::profilingMemoryUsage) {
        CallQuery& query = callQueries.back();
        query.vsizeStart = os::getVsize();
        query.rssStart = os::getRss();
    }
}

void
endProfile(trace::Call &call, bool isDraw) {

    /* CPU profiling for all calls */
    if (retrace::profilingCpuTimes) {
        CallQuery& query = callQueries.back();
        query.cpuEnd = getCurrentTime();
    }

    /* GPU profiling only for draw calls */
    if (isDraw) {
        if (retrace::profilingGpuTimes) {
            glEndQuery(GL_TIME_ELAPSED);
        }

        if (retrace::profilingPixelsDrawn) {
            glEndQuery(GL_SAMPLES_PASSED);
        }
    }

    if (retrace::profilingMemoryUsage) {
        CallQuery& query = callQueries.back();
        query.vsizeEnd = os::getVsize();
        query.rssEnd = os::getRss();
    }
}

void
initContext() {
    glretrace::Context *currentContext = glretrace::getCurrentContext();

    /* Ensure we have adequate extension support */
    assert(currentContext);
    supportsTimestamp   = currentContext->hasExtension("GL_ARB_timer_query");
    supportsElapsed     = currentContext->hasExtension("GL_EXT_timer_query") || supportsTimestamp;
    supportsOcclusion   = currentContext->hasExtension("GL_ARB_occlusion_query");
    supportsDebugOutput = currentContext->hasExtension("GL_ARB_debug_output");
    supportsARBShaderObjects = currentContext->hasExtension("GL_ARB_shader_objects");

    /* Check for timer query support */
    if (retrace::profilingGpuTimes) {
        if (!supportsTimestamp && !supportsElapsed) {
            std::cout << "Error: Cannot run profile, GL_EXT_timer_query extension is not supported." << std::endl;
            exit(-1);
        }

        GLint bits = 0;
        glGetQueryiv(GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS, &bits);

        if (!bits) {
            std::cout << "Error: Cannot run profile, GL_QUERY_COUNTER_BITS == 0." << std::endl;
            exit(-1);
        }
    }

    /* Check for occlusion query support */
    if (retrace::profilingPixelsDrawn && !supportsOcclusion) {
        std::cout << "Error: Cannot run profile, GL_ARB_occlusion_query extension is not supported." << std::endl;
        exit(-1);
    }

    /* Setup debug message call back */
    if (retrace::debug && supportsDebugOutput) {
        glretrace::Context *currentContext = glretrace::getCurrentContext();
        glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0, GL_TRUE);
        glDebugMessageCallbackARB(&debugOutputCallback, currentContext);

        if (DEBUG_OUTPUT_SYNCHRONOUS) {
            glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
        }
    }

    /* Sync the gpu and cpu start times */
    if (retrace::profilingCpuTimes || retrace::profilingGpuTimes) {
        if (!retrace::profiler.hasBaseTimes()) {
            double cpuTimeScale = 1.0E9 / getTimeFrequency();
            GLint64 currentTime = getCurrentTime() * cpuTimeScale;
            retrace::profiler.setBaseCpuTime(currentTime);
            retrace::profiler.setBaseGpuTime(currentTime);
        }
    }

    if (retrace::profilingMemoryUsage) {
        GLint64 currentVsize, currentRss;
        getCurrentVsize(currentVsize);
        retrace::profiler.setBaseVsizeUsage(currentVsize);
        getCurrentRss(currentRss);
        retrace::profiler.setBaseRssUsage(currentRss);
    }
}

void
frame_complete(trace::Call &call) {
    if (retrace::profiling) {
        /* Complete any remaining queries */
        flushQueries();

        /* Indicate end of current frame */
        retrace::profiler.addFrameEnd();
    }

    retrace::frameComplete(call);

    glretrace::Context *currentContext = glretrace::getCurrentContext();
    if (!currentContext) {
        return;
    }

    assert(currentContext->drawable);
    if (retrace::debug && !currentContext->drawable->visible) {
        retrace::warning(call) << "could not infer drawable size (glViewport never called)\n";
    }
}

static const char*
getDebugOutputSource(GLenum source) {
    switch(source) {
    case GL_DEBUG_SOURCE_API_ARB:
        return "API";
    case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB:
        return "Window System";
    case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB:
        return "Shader Compiler";
    case GL_DEBUG_SOURCE_THIRD_PARTY_ARB:
        return "Third Party";
    case GL_DEBUG_SOURCE_APPLICATION_ARB:
        return "Application";
    case GL_DEBUG_SOURCE_OTHER_ARB:
    default:
        return "";
    }
}

static const char*
getDebugOutputType(GLenum type) {
    switch(type) {
    case GL_DEBUG_TYPE_ERROR_ARB:
        return "error";
    case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:
        return "deprecated behaviour";
    case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB:
        return "undefined behaviour";
    case GL_DEBUG_TYPE_PORTABILITY_ARB:
        return "portability issue";
    case GL_DEBUG_TYPE_PERFORMANCE_ARB:
        return "performance issue";
    case GL_DEBUG_TYPE_OTHER_ARB:
    default:
        return "unknown issue";
    }
}

static const char*
getDebugOutputSeverity(GLenum severity) {
    switch(severity) {
    case GL_DEBUG_SEVERITY_HIGH_ARB:
        return "High";
    case GL_DEBUG_SEVERITY_MEDIUM_ARB:
        return "Medium";
    case GL_DEBUG_SEVERITY_LOW_ARB:
        return "Low";
    default:
        return "usnknown";
    }
}

static void APIENTRY
debugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) {
    std::cerr << retrace::callNo << ": ";
    std::cerr << "glDebugOutputCallback: ";
    std::cerr << getDebugOutputSeverity(severity) << " severity ";
    std::cerr << getDebugOutputSource(source) << " " << getDebugOutputType(type);
    std::cerr << " " << id;
    std::cerr << ", " << message;
    std::cerr << std::endl;
}

} /* namespace glretrace */

class GLDumper : public retrace::Dumper {
public:
    image::Image *
    getSnapshot(void) {
        if (!glretrace::getCurrentContext()) {
            return NULL;
        }
        return glstate::getDrawBufferImage();
    }

    bool
    dumpState(std::ostream &os) {
        glretrace::Context *currentContext = glretrace::getCurrentContext();
        if (glretrace::insideGlBeginEnd ||
            !currentContext) {
            return false;
        }
        glstate::dumpCurrentContext(os);
        return true;
    }
};

static GLDumper glDumper;

void
retrace::setFeatureLevel(const char *featureLevel)
{
    glretrace::defaultProfile = glws::PROFILE_3_2_CORE;
}

void
retrace::setUp(void) {
    glws::init();
    dumper = &glDumper;
}

void
retrace::addCallbacks(retrace::Retracer &retracer)
{
    retracer.addCallbacks(glretrace::gl_callbacks);
    retracer.addCallbacks(glretrace::glx_callbacks);
    retracer.addCallbacks(glretrace::wgl_callbacks);
    retracer.addCallbacks(glretrace::cgl_callbacks);
    retracer.addCallbacks(glretrace::egl_callbacks);
}

void
retrace::flushRendering(void) {
    glretrace::Context *currentContext = glretrace::getCurrentContext();
    if (currentContext) {
        glretrace::flushQueries();
    }
}

void
retrace::finishRendering(void) {
    glretrace::Context *currentContext = glretrace::getCurrentContext();
    if (currentContext) {
        glFinish();
    }
}

void
retrace::waitForInput(void) {
    flushRendering();
    while (glws::processEvents()) {
        os::sleep(100*1000);
    }
}

void
retrace::cleanUp(void) {
    glws::cleanup();
}
<|endoftext|>


<commit_before>/*
This file is part of khmer, https://github.com/dib-lab/khmer/, and is
Copyright (C) 2010-2015, Michigan State University.
Copyright (C) 2015-2016, The Regents of the University of California.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above
      copyright notice, this list of conditions and the following
      disclaimer in the documentation and/or other materials provided
      with the distribution.

    * Neither the name of the Michigan State University nor the names
      of its contributors may be used to endorse or promote products
      derived from this software without specific prior written
      permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
LICENSE (END)

Contact: khmer-project@idyll.org
*/
#ifndef KMER_HASH_HH
#define KMER_HASH_HH

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>

#include "khmer.hh"

// test validity
#ifdef KHMER_EXTRA_SANITY_CHECKS
#   define is_valid_dna(ch) ((toupper(ch)) == 'A' || (toupper(ch)) == 'C' || \
			    (toupper(ch)) == 'G' || (toupper(ch)) == 'T')
#else
// NOTE: Assumes data is already sanitized as it should be by parsers.
//	     This assumption eliminates 4 function calls.
#   define is_valid_dna(ch) ((ch) == 'A' || (ch) == 'C' || \
			     (ch) == 'G' || (ch) == 'T')
#endif

// bit representation of A/T/C/G.
#ifdef KHMER_EXTRA_SANITY_CHECKS
#   define twobit_repr(ch) ((toupper(ch)) == 'A' ? 0LL : \
			    (toupper(ch)) == 'T' ? 1LL : \
			    (toupper(ch)) == 'C' ? 2LL : 3LL)
#else
// NOTE: Assumes data is already sanitized as it should be by parsers.
//	     This assumption eliminates 4 function calls.
#   define twobit_repr(ch) ((ch) == 'A' ? 0LL : \
			    (ch) == 'T' ? 1LL : \
			    (ch) == 'C' ? 2LL : 3LL)
#endif

#define revtwobit_repr(n) ((n) == 0 ? 'A' : \
                           (n) == 1 ? 'T' : \
                           (n) == 2 ? 'C' : 'G')

#ifdef KHMER_EXTRA_SANITY_CHECKS
#   define twobit_comp(ch) ((toupper(ch)) == 'A' ? 1LL : \
			    (toupper(ch)) == 'T' ? 0LL : \
			    (toupper(ch)) == 'C' ? 3LL : 2LL)
#else
// NOTE: Assumes data is already sanitized as it should be by parsers.
//	     This assumption eliminates 4 function calls.
#   define twobit_comp(ch) ((ch) == 'A' ? 1LL : \
			    (ch) == 'T' ? 0LL : \
			    (ch) == 'C' ? 3LL : 2LL)
#endif

// choose wisely between forward and rev comp.
#ifndef NO_UNIQUE_RC
#define uniqify_rc(f, r) ((f) < (r) ? (f) : (r))
#else
#define uniqify_rc(f,r)(f)
#endif

namespace khmer
{
// two-way hash functions.
HashIntoType _hash(const char * kmer, const WordLength k);
HashIntoType _hash(const char * kmer, const WordLength k,
                   HashIntoType& h, HashIntoType& r);
HashIntoType _hash(const std::string kmer, const WordLength k);
HashIntoType _hash(const std::string kmer, const WordLength k,
                   HashIntoType& h, HashIntoType& r);
HashIntoType _hash_forward(const char * kmer, WordLength k);

std::string _revhash(HashIntoType hash, WordLength k);
std::string _revcomp(const std::string& kmer);

// two-way hash functions, MurmurHash3.
HashIntoType _hash_murmur(const std::string& kmer);
HashIntoType _hash_murmur(const std::string& kmer,
                          HashIntoType& h, HashIntoType& r);
HashIntoType _hash_murmur_forward(const std::string& kmer);

/**
 * \class Kmer
 *
 * \brief Hold the hash values corresponding to a single k-mer.
 *
 * This class stores the forward, reverse complement, and
 * uniqified hash values for a given k-mer. It also defines
 * some basic operators and a utility function for getting
 * the string representation of the sequence. This is meant
 * to replace the original inelegant macros used for hashing.
 *
 * \author Camille Scott
 *
 * Contact: camille.scott.w@gmail.com
 *
 */
class Kmer
{

public:

    /// The forward hash
    HashIntoType kmer_f;
    /// The reverse (complement) hash
    HashIntoType kmer_r;
    /// The uniqified hash
    HashIntoType kmer_u;

    /** @param[in]   f forward hash.
     *  @param[in]   r reverse (complement) hash.
     *  @param[in]   u uniqified hash.
     */
    Kmer(HashIntoType f, HashIntoType r, HashIntoType u)
    {
        kmer_f = f;
        kmer_r = r;
        kmer_u = u;
    }

    /** @param[in]   s     DNA k-mer
        @param[in]   ksize k-mer size
     */
    Kmer(const std::string s, WordLength ksize)
    {
        kmer_u = _hash(s.c_str(), ksize, kmer_f, kmer_r);
    }

    /// @warning The default constructor builds an invalid k-mer.
    Kmer()
    {
        kmer_f = kmer_r = kmer_u = 0;
    }

    void set_from_unique_hash(HashIntoType h, WordLength ksize)
    {
        std::string s = _revhash(h, ksize);
        kmer_u = _hash(s.c_str(), ksize, kmer_f, kmer_r);
    }

    /// Allows complete backwards compatibility
    operator HashIntoType() const
    {
        return kmer_u;
    }

    bool operator< (const Kmer &other) const
    {
        return kmer_u < other.kmer_u;
    }

    std::string get_string_rep(WordLength K) const
    {
        return _revhash(kmer_u, K);
    }

    char get_last_base() const
    {
        return revtwobit_repr(kmer_f & 3);
    }

    std::string repr(WordLength K) const
    {
        std::string s = "<Us=" + _revhash(kmer_u, K) + ", Fs=" +
                        _revhash(kmer_f, K) + ", Rs=" + _revhash(kmer_r, K) + ">";
        //", U=" + std::to_string(kmer_u) + ", F=" + std::to_string(kmer_f) +
        //", R=" + std::to_string(kmer_r) + ">";
        return s;
    }

    bool is_forward() const
    {
        return kmer_f == kmer_u;
    }
};

/**
 * \class KmerFactory
 *
 * \brief Build complete Kmer objects.
 *
 * The KmerFactory is a simple construct to emit complete
 * Kmer objects. The design decision leading to this class
 * stems from the issue of overloading the Kmer constructor
 * while also giving it a K size: you get ambiguous signatures
 * between the (kmer_u, K) and (kmer_f, kmer_r) cases. This
 * implementation also allows a logical architecture wherein
 * KmerIterator and Hashtable inherit directly from KmerFactory,
 * extending the latter's purpose of "create k-mers" to
 * "emit k-mers from a sequence" and "create and store k-mers".
 *
 * \author Camille Scott
 *
 * Contact: camille.scott.w@gmail.com
 *
 */
class KmerFactory
{
protected:
    WordLength _ksize;

public:

    explicit KmerFactory(WordLength K): _ksize(K) {}

    /** @param[in]  kmer_u Uniqified hash value.
     *  @return A complete Kmer object.
     */
    Kmer build_kmer(HashIntoType kmer_u)
    const
    {
        HashIntoType kmer_f, kmer_r;
        std:: string kmer_s = _revhash(kmer_u, _ksize);
        _hash(kmer_s.c_str(), _ksize, kmer_f, kmer_r);
        return Kmer(kmer_f, kmer_r, kmer_u);
    }

    /** Call the uniqify function and build a complete Kmer.
     *
     *  @param[in]  kmer_f Forward hash value.
     *  @param[in]  kmer_r Reverse complement hash value.
     *  @return A complete Kmer object.
     */
    Kmer build_kmer(HashIntoType kmer_f, HashIntoType kmer_r)
    const
    {
        HashIntoType kmer_u = uniqify_rc(kmer_f, kmer_r);
        return Kmer(kmer_f, kmer_r, kmer_u);
    }

    /** Hash the given sequence and call the uniqify function
     *  on its results to build a complete Kmer.
     *
     *  @param[in]  kmer_s String representation of a k-mer.
     *  @return A complete Kmer object hashed from the given string.
     */
    Kmer build_kmer(std::string kmer_s) const
    {
        HashIntoType kmer_f, kmer_r, kmer_u;
        kmer_u = _hash(kmer_s.c_str(), _ksize, kmer_f, kmer_r);
        return Kmer(kmer_f, kmer_r, kmer_u);
    }

    /** Hash the given sequence and call the uniqify function
     *  on its results to build a complete Kmer.
     *
     *  @param[in]  kmer_c The character array representation of a k-mer.
     *  @return A complete Kmer object hashed from the given char array.
     */
    Kmer build_kmer(const char * kmer_c) const
    {
        HashIntoType kmer_f, kmer_r, kmer_u;
        kmer_u = _hash(kmer_c, _ksize, kmer_f, kmer_r);
        return Kmer(kmer_f, kmer_r, kmer_u);
    }
};

/**
 * \class KmerIterator
 *
 * \brief Emit Kmer objects generated from the given sequence.
 *
 * Given a string \f$S\f$ and a length \f$K > 0\f$, we define
 * the k-mers of \f$S\f$ as the set \f$S_{i..i+K} \forall i \in \{0..|S|-K+1\}\f$,
 * where \f$|S|\f$ is the length and \f$S_{j..k}\f$ is the half-open
 * substring starting at \f$j\f$ and terminating at \f$k\f$.
 *
 * KmerIterator mimics a python-style generator function which
 * emits the k-mers of the given sequence, in order, as Kmer objects.
 *
 * @warning This is not actually a valid C++ iterator, though it is close.
 *
 * \author Camille Scott
 *
 * Contact: camille.scott.w@gmail.com
 *
 */
class KmerIterator: public KmerFactory
{
protected:
    const char * _seq;

    HashIntoType _kmer_f, _kmer_r;
    HashIntoType bitmask;
    unsigned int _nbits_sub_1;
    unsigned int index;
    size_t length;
    bool initialized;
public:
    KmerIterator(const char * seq, unsigned char k);

    /** @param[in]  f The forward hash value.
     *  @param[in]  r The reverse complement hash value.
     *  @return The first Kmer of the sequence.
     */
    Kmer first(HashIntoType& f, HashIntoType& r);

    /** @param[in]  f The current forward hash value
     *  @param[in]  r The current reverse complement hash value
     *  @return The next Kmer in the sequence
     */
    Kmer next(HashIntoType& f, HashIntoType& r);

    Kmer first()
    {
        return first(_kmer_f, _kmer_r);
    }

    Kmer next()
    {
        return next(_kmer_f, _kmer_r);
    }

    /// @return Whether or not the iterator has completed.
    bool done() const
    {
        return index >= length;
    }

    unsigned int get_start_pos() const
    {
        return index - _ksize;
    }

    unsigned int get_end_pos() const
    {
        return index;
    }
}; // class KmerIterator

//
// KmerHashIterator - analogous to KmerIterator classes, but returns only
// only HashIntoType hashes, not full Kmer objects.  This supports irreversible
// hashing.
//

class KmerHashIterator {
public:
    virtual HashIntoType first() = 0;
    virtual HashIntoType next() = 0;
    virtual bool done() const = 0;
    virtual unsigned int get_start_pos() const = 0;
    virtual unsigned int get_end_pos() const = 0;
    virtual ~KmerHashIterator() { };
};

// TwoBitKmerHashIterator -- just wrap KmerIterator.

class TwoBitKmerHashIterator : public KmerHashIterator {
protected:
    KmerIterator iter;
public:
    TwoBitKmerHashIterator(const char * seq, WordLength k) :
        iter(seq, k) { } ;

    HashIntoType first() { return iter.first(); }

    HashIntoType next() { return iter.next(); }

    bool done() const { return iter.done(); }

    virtual unsigned int get_start_pos() const {
        return iter.get_start_pos();
    }

    virtual unsigned int get_end_pos() const {
        return iter.get_end_pos();
    }
};

}

#endif // KMER_HASH_HH
<commit_msg>fix dupl only<commit_after>/*
This file is part of khmer, https://github.com/dib-lab/khmer/, and is
Copyright (C) 2010-2015, Michigan State University.
Copyright (C) 2015-2016, The Regents of the University of California.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above
      copyright notice, this list of conditions and the following
      disclaimer in the documentation and/or other materials provided
      with the distribution.

    * Neither the name of the Michigan State University nor the names
      of its contributors may be used to endorse or promote products
      derived from this software without specific prior written
      permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
LICENSE (END)

Contact: khmer-project@idyll.org
*/
#ifndef KMER_HASH_HH
#define KMER_HASH_HH

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>

#include "khmer.hh"

// test validity
#ifdef KHMER_EXTRA_SANITY_CHECKS
#   define is_valid_dna(ch) ((toupper(ch)) == 'A' || (toupper(ch)) == 'C' || \
			    (toupper(ch)) == 'G' || (toupper(ch)) == 'T')
#else
// NOTE: Assumes data is already sanitized as it should be by parsers.
//	     This assumption eliminates 4 function calls.
#   define is_valid_dna(ch) ((ch) == 'A' || (ch) == 'C' || \
			     (ch) == 'G' || (ch) == 'T')
#endif

// bit representation of A/T/C/G.
#ifdef KHMER_EXTRA_SANITY_CHECKS
#   define twobit_repr(ch) ((toupper(ch)) == 'A' ? 0LL : \
			    (toupper(ch)) == 'T' ? 1LL : \
			    (toupper(ch)) == 'C' ? 2LL : 3LL)
#else
// NOTE: Assumes data is already sanitized as it should be by parsers.
//	     This assumption eliminates 4 function calls.
#   define twobit_repr(ch) ((ch) == 'A' ? 0LL : \
			    (ch) == 'T' ? 1LL : \
			    (ch) == 'C' ? 2LL : 3LL)
#endif

#define revtwobit_repr(n) ((n) == 0 ? 'A' : \
                           (n) == 1 ? 'T' : \
                           (n) == 2 ? 'C' : 'G')

#ifdef KHMER_EXTRA_SANITY_CHECKS
#   define twobit_comp(ch) ((toupper(ch)) == 'A' ? 1LL : \
			    (toupper(ch)) == 'T' ? 0LL : \
			    (toupper(ch)) == 'C' ? 3LL : 2LL)
#else
// NOTE: Assumes data is already sanitized as it should be by parsers.
//	     This assumption eliminates 4 function calls.
#   define twobit_comp(ch) ((ch) == 'A' ? 1LL : \
			    (ch) == 'T' ? 0LL : \
			    (ch) == 'C' ? 3LL : 2LL)
#endif

// choose wisely between forward and rev comp.
#ifndef NO_UNIQUE_RC
#define uniqify_rc(f, r) ((f) < (r) ? (f) : (r))
#else
#define uniqify_rc(f,r)(f)
#endif

namespace khmer
{
// two-way hash functions.
HashIntoType _hash(const char * kmer, const WordLength k);
HashIntoType _hash(const char * kmer, const WordLength k,
                   HashIntoType& h, HashIntoType& r);
HashIntoType _hash(const std::string kmer, const WordLength k);
HashIntoType _hash(const std::string kmer, const WordLength k,
                   HashIntoType& h, HashIntoType& r);
HashIntoType _hash_forward(const char * kmer, WordLength k);

std::string _revhash(HashIntoType hash, WordLength k);
std::string _revcomp(const std::string& kmer);

// two-way hash functions, MurmurHash3.
HashIntoType _hash_murmur(const std::string& kmer);
HashIntoType _hash_murmur(const std::string& kmer,
                          HashIntoType& h, HashIntoType& r);
HashIntoType _hash_murmur_forward(const std::string& kmer);

/**
 * \class Kmer
 *
 * \brief Hold the hash values corresponding to a single k-mer.
 *
 * This class stores the forward, reverse complement, and
 * uniqified hash values for a given k-mer. It also defines
 * some basic operators and a utility function for getting
 * the string representation of the sequence. This is meant
 * to replace the original inelegant macros used for hashing.
 *
 * \author Camille Scott
 *
 * Contact: camille.scott.w@gmail.com
 *
 */
class Kmer
{

public:

    /// The forward hash
    HashIntoType kmer_f;
    /// The reverse (complement) hash
    HashIntoType kmer_r;
    /// The uniqified hash
    HashIntoType kmer_u;

    /** @param[in]   f forward hash.
     *  @param[in]   r reverse (complement) hash.
     *  @param[in]   u uniqified hash.
     */
    Kmer(HashIntoType f, HashIntoType r, HashIntoType u)
    {
        kmer_f = f;
        kmer_r = r;
        kmer_u = u;
    }

    /** @param[in]   s     DNA k-mer
        @param[in]   ksize k-mer size
     */
    Kmer(const std::string s, WordLength ksize)
    {
        kmer_u = _hash(s.c_str(), ksize, kmer_f, kmer_r);
    }

    /// @warning The default constructor builds an invalid k-mer.
    Kmer()
    {
        kmer_f = kmer_r = kmer_u = 0;
    }

    void set_from_unique_hash(HashIntoType h, WordLength ksize)
    {
        std::string s = _revhash(h, ksize);
        kmer_u = _hash(s.c_str(), ksize, kmer_f, kmer_r);
    }

    /// Allows complete backwards compatibility
    operator HashIntoType() const
    {
        return kmer_u;
    }

    bool operator< (const Kmer &other) const
    {
        return kmer_u < other.kmer_u;
    }

    std::string get_string_rep(WordLength K) const
    {
        return _revhash(kmer_u, K);
    }

    char get_last_base() const
    {
        return revtwobit_repr(kmer_f & 3);
    }

    std::string repr(WordLength K) const
    {
        std::string s = "<Us=" + _revhash(kmer_u, K) + ", Fs=" +
                        _revhash(kmer_f, K) + ", Rs=" + _revhash(kmer_r, K) + ">";
        //", U=" + std::to_string(kmer_u) + ", F=" + std::to_string(kmer_f) +
        //", R=" + std::to_string(kmer_r) + ">";
        return s;
    }

    bool is_forward() const
    {
        return kmer_f == kmer_u;
    }
};

/**
 * \class KmerFactory
 *
 * \brief Build complete Kmer objects.
 *
 * The KmerFactory is a simple construct to emit complete
 * Kmer objects. The design decision leading to this class
 * stems from the issue of overloading the Kmer constructor
 * while also giving it a K size: you get ambiguous signatures
 * between the (kmer_u, K) and (kmer_f, kmer_r) cases. This
 * implementation also allows a logical architecture wherein
 * KmerIterator and Hashtable inherit directly from KmerFactory,
 * extending the latter's purpose of "create k-mers" to
 * "emit k-mers from a sequence" and "create and store k-mers".
 *
 * \author Camille Scott
 *
 * Contact: camille.scott.w@gmail.com
 *
 */
class KmerFactory
{
protected:
    WordLength _ksize;

public:

    explicit KmerFactory(WordLength K): _ksize(K) {}

    /** @param[in]  kmer_u Uniqified hash value.
     *  @return A complete Kmer object.
     */
    Kmer build_kmer(HashIntoType kmer_u)
    const
    {
        HashIntoType kmer_f, kmer_r;
        std:: string kmer_s = _revhash(kmer_u, _ksize);
        _hash(kmer_s.c_str(), _ksize, kmer_f, kmer_r);
        return Kmer(kmer_f, kmer_r, kmer_u);
    }

    /** Call the uniqify function and build a complete Kmer.
     *
     *  @param[in]  kmer_f Forward hash value.
     *  @param[in]  kmer_r Reverse complement hash value.
     *  @return A complete Kmer object.
     */
    Kmer build_kmer(HashIntoType kmer_f, HashIntoType kmer_r)
    const
    {
        HashIntoType kmer_u = uniqify_rc(kmer_f, kmer_r);
        return Kmer(kmer_f, kmer_r, kmer_u);
    }

    /** Hash the given sequence and call the uniqify function
     *  on its results to build a complete Kmer.
     *
     *  @param[in]  kmer_s String representation of a k-mer.
     *  @return A complete Kmer object hashed from the given string.
     */
    Kmer build_kmer(std::string kmer_s) const
    {
        HashIntoType kmer_f, kmer_r, kmer_u;
        kmer_u = _hash(kmer_s.c_str(), _ksize, kmer_f, kmer_r);
        return Kmer(kmer_f, kmer_r, kmer_u);
    }

    /** Hash the given sequence and call the uniqify function
     *  on its results to build a complete Kmer.
     *
     *  @param[in]  kmer_c The character array representation of a k-mer.
     *  @return A complete Kmer object hashed from the given char array.
     */
    Kmer build_kmer(const char * kmer_c) const
    {
        HashIntoType kmer_f, kmer_r, kmer_u;
        kmer_u = _hash(kmer_c, _ksize, kmer_f, kmer_r);
        return Kmer(kmer_f, kmer_r, kmer_u);
    }
};

/**
 * \class KmerIterator
 *
 * \brief Emit Kmer objects generated from the given sequence.
 *
 * Given a string \f$S\f$ and a length \f$K > 0\f$, we define
 * the k-mers of \f$S\f$ as the set \f$S_{i..i+K} \forall i \in \{0..|S|-K+1\}\f$,
 * where \f$|S|\f$ is the length and \f$S_{j..k}\f$ is the half-open
 * substring starting at \f$j\f$ and terminating at \f$k\f$.
 *
 * KmerIterator mimics a python-style generator function which
 * emits the k-mers of the given sequence, in order, as Kmer objects.
 *
 * @warning This is not actually a valid C++ iterator, though it is close.
 *
 * \author Camille Scott
 *
 * Contact: camille.scott.w@gmail.com
 *
 */
class KmerIterator: public KmerFactory
{
protected:
    const char * _seq;

    HashIntoType _kmer_f, _kmer_r;
    HashIntoType bitmask;
    unsigned int _nbits_sub_1;
    unsigned int index;
    size_t length;
    bool initialized;
public:
    KmerIterator(const char * seq, unsigned char k);

    /** @param[in]  f The forward hash value.
     *  @param[in]  r The reverse complement hash value.
     *  @return The first Kmer of the sequence.
     */
    Kmer first(HashIntoType& f, HashIntoType& r);

    /** @param[in]  f The current forward hash value
     *  @param[in]  r The current reverse complement hash value
     *  @return The next Kmer in the sequence
     */
    Kmer next(HashIntoType& f, HashIntoType& r);

    Kmer first()
    {
        return first(_kmer_f, _kmer_r);
    }

    Kmer next()
    {
        return next(_kmer_f, _kmer_r);
    }

    /// @return Whether or not the iterator has completed.
    bool done() const
    {
        return index >= length;
    }

    unsigned int get_start_pos() const
    {
        return index - _ksize;
    }

    unsigned int get_end_pos() const
    {
        return index;
    }
}; // class KmerIterator

//
// KmerHashIterator - analogous to KmerIterator classes, but returns only
// HashIntoType hashes, not full Kmer objects.  This supports irreversible
// hashing.
//

class KmerHashIterator {
public:
    virtual HashIntoType first() = 0;
    virtual HashIntoType next() = 0;
    virtual bool done() const = 0;
    virtual unsigned int get_start_pos() const = 0;
    virtual unsigned int get_end_pos() const = 0;
    virtual ~KmerHashIterator() { };
};

// TwoBitKmerHashIterator -- just wrap KmerIterator.

class TwoBitKmerHashIterator : public KmerHashIterator {
protected:
    KmerIterator iter;
public:
    TwoBitKmerHashIterator(const char * seq, WordLength k) :
        iter(seq, k) { } ;

    HashIntoType first() { return iter.first(); }

    HashIntoType next() { return iter.next(); }

    bool done() const { return iter.done(); }

    virtual unsigned int get_start_pos() const {
        return iter.get_start_pos();
    }

    virtual unsigned int get_end_pos() const {
        return iter.get_end_pos();
    }
};

}

#endif // KMER_HASH_HH
<|endoftext|>


<commit_before><commit_msg>DDF don't overwrite legacy values with default value if already set<commit_after><|endoftext|>


<commit_before>//     Copyright 2013, Kay Hayen, mailto:kay.hayen@gmail.com
//
//     Part of "Nuitka", an optimizing Python compiler that is compatible and
//     integrates with CPython, but also works on its own.
//
//     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.
//
#ifndef __NUITKA_HELPER_RAISING_H__
#define __NUITKA_HELPER_RAISING_H__

#if PYTHON_VERSION < 300
#define WRONG_EXCEPTION_TYPE_ERROR_MESSAGE "exceptions must be old-style classes or derived from BaseException, not %s"
#else
#define WRONG_EXCEPTION_TYPE_ERROR_MESSAGE "exceptions must derive from BaseException"
#endif

#if PYTHON_VERSION >= 300
static void CHAIN_EXCEPTION( PyObject *exception_type, PyObject *exception_value )
{
    // Implicit chain of exception already existing.
    PyThreadState *thread_state = PyThreadState_GET();

    // Normalize existing exception first.
    PyErr_NormalizeException( &thread_state->exc_type, &thread_state->exc_value, &thread_state->exc_traceback );

    PyObject *old_exc_value = thread_state->exc_value;

    if ( old_exc_value != NULL && old_exc_value != Py_None && old_exc_value != exception_value )
    {
        Py_INCREF( old_exc_value );

        PyObject *o = old_exc_value, *context;
        while (( context = PyException_GetContext(o) ))
        {
            Py_DECREF( context );

            if ( context == exception_value )
            {
                PyException_SetContext( o, NULL );
                break;
            }

            o = context;
        }

        PyException_SetContext( exception_value, old_exc_value );
        PyException_SetTraceback( old_exc_value, thread_state->exc_traceback );
    }
}
#endif

NUITKA_NO_RETURN NUITKA_MAY_BE_UNUSED static void RAISE_EXCEPTION_WITH_TYPE( PyObject *exception_type, PyObject *exception_tb )
{
    PyTracebackObject *traceback = (PyTracebackObject *)exception_tb;
    assertObject( traceback );
    assert( PyTraceBack_Check( traceback ) );
    assertObject( exception_type );

    if ( PyExceptionClass_Check( exception_type ) )
    {
        PyObject *value = NULL;

        Py_INCREF( exception_type );
        Py_XINCREF( traceback );

        NORMALIZE_EXCEPTION( &exception_type, &value, &traceback );
#if PYTHON_VERSION >= 270
        if (unlikely( !PyExceptionInstance_Check( value ) ))
        {
            Py_DECREF( exception_type );
            Py_DECREF( value );
            Py_XDECREF( traceback );

            PyErr_Format(
                PyExc_TypeError,
                "calling %s() should have returned an instance of BaseException, not '%s'",
                ((PyTypeObject *)exception_type)->tp_name,
                Py_TYPE( value )->tp_name
            );

            throw PythonException();
        }
#endif

#if PYTHON_VERSION >= 300
        CHAIN_EXCEPTION( exception_type, value );
#endif
        throw PythonException(
            exception_type,
            value,
            traceback
        );
    }
    else if ( PyExceptionInstance_Check( exception_type ) )
    {
        PyObject *value = exception_type;
        exception_type = PyExceptionInstance_Class( exception_type );

#if PYTHON_VERSION >= 300
        CHAIN_EXCEPTION( exception_type, value );

        PyTracebackObject *prev = (PyTracebackObject *)PyException_GetTraceback( value );

        if ( prev != NULL )
        {
            assert( traceback->tb_next == NULL );
            traceback->tb_next = prev;
        }

        PyException_SetTraceback( value, (PyObject *)traceback );
#endif

        throw PythonException(
            INCREASE_REFCOUNT( exception_type ),
            INCREASE_REFCOUNT( value ),
            INCREASE_REFCOUNT( traceback )
        );
    }
    else
    {
        PyErr_Format( PyExc_TypeError, WRONG_EXCEPTION_TYPE_ERROR_MESSAGE, Py_TYPE( exception_type )->tp_name );

        PythonException to_throw;
        to_throw.setTraceback( INCREASE_REFCOUNT( traceback ) );

        throw to_throw;
    }
}

#if PYTHON_VERSION >= 300
NUITKA_NO_RETURN NUITKA_MAY_BE_UNUSED static void RAISE_EXCEPTION_WITH_CAUSE( PyObject *exception_type, PyObject *exception_cause, PyObject *exception_tb )
{
    PyTracebackObject *traceback = (PyTracebackObject *)exception_tb;

    assertObject( exception_type );
    assertObject( exception_cause );

#if PYTHON_VERSION >= 330
    // None is not a cause.
    if ( exception_cause == Py_None )
    {
        exception_cause = NULL;
    }
    else
#endif
    if ( PyExceptionClass_Check( exception_cause ) )
    {
        exception_cause = PyObject_CallObject( exception_cause, NULL );

        if (unlikely( exception_cause == NULL ))
        {
            throw PythonException();
        }
    }
    else
    {
        Py_INCREF( exception_cause );
    }

#if PYTHON_VERSION >= 330
    if (unlikely( exception_cause != NULL && !PyExceptionInstance_Check( exception_cause ) ))
#else
    if (unlikely( !PyExceptionInstance_Check( exception_cause ) ))
#endif
    {
        Py_XDECREF( exception_cause );

        PyErr_Format( PyExc_TypeError, "exception causes must derive from BaseException" );
        throw PythonException();
    }

    if ( PyExceptionClass_Check( exception_type ) )
    {
        PyObject *value = NULL;

        Py_INCREF( exception_type );
        Py_INCREF( traceback );

        NORMALIZE_EXCEPTION( &exception_type, &value, &traceback );
        if (unlikely( !PyExceptionInstance_Check( value ) ))
        {
            Py_DECREF( exception_type );
            Py_XDECREF( value );
            Py_DECREF( traceback );
            Py_XDECREF( exception_cause );

            PyErr_Format(
                PyExc_TypeError,
                "calling %s() should have returned an instance of BaseException, not '%s'",
                ((PyTypeObject *)exception_type)->tp_name,
                Py_TYPE( value )->tp_name
            );

            throw PythonException();
        }

        PythonException to_throw( exception_type, value, traceback );
        to_throw.setCause( exception_cause );
        throw to_throw;
    }
    else if ( PyExceptionInstance_Check( exception_type ) )
    {
        Py_XDECREF( exception_cause );

        throw PythonException(
            INCREASE_REFCOUNT( PyExceptionInstance_Class( exception_type ) ),
            INCREASE_REFCOUNT( exception_type ),
            INCREASE_REFCOUNT( traceback )
        );
    }
    else
    {
        Py_XDECREF( exception_cause );

        PyErr_Format( PyExc_TypeError, WRONG_EXCEPTION_TYPE_ERROR_MESSAGE, Py_TYPE( exception_type )->tp_name );

        PythonException to_throw;
        to_throw.setTraceback( INCREASE_REFCOUNT( traceback ) );

        throw to_throw;
    }
}
#endif

NUITKA_NO_RETURN NUITKA_MAY_BE_UNUSED static void RAISE_EXCEPTION_WITH_VALUE( PyObject *exception_type, PyObject *value, PyObject *exception_tb )
{
    assertObject( exception_type );
    PyTracebackObject *traceback = (PyTracebackObject *)exception_tb;

    // Non-empty tuple exceptions are the first element.
    while (unlikely( PyTuple_Check( exception_type ) && PyTuple_Size( exception_type ) > 0 ))
    {
        exception_type = PyTuple_GET_ITEM( exception_type, 0 );
    }

    if ( PyExceptionClass_Check( exception_type ) )
    {
        Py_INCREF( exception_type );
        Py_XINCREF( value );
        Py_XINCREF( traceback );

        NORMALIZE_EXCEPTION( &exception_type, &value, &traceback );
#if PYTHON_VERSION >= 270
        if (unlikely( !PyExceptionInstance_Check( value ) ))
        {
            PyErr_Format(
                PyExc_TypeError,
                "calling %s() should have returned an instance of BaseException, not '%s'",
                ((PyTypeObject *)exception_type)->tp_name,
                Py_TYPE( value )->tp_name
            );

            Py_DECREF( exception_type );
            Py_XDECREF( value );
            Py_XDECREF( traceback );

            throw PythonException();
        }
#endif

        throw PythonException(
            exception_type,
            value,
            traceback
        );
    }
    else if ( PyExceptionInstance_Check( exception_type ) )
    {
        if (unlikely( value != NULL && value != Py_None ))
        {
            PyErr_Format(
                PyExc_TypeError,
                "instance exception may not have a separate value"
            );

            throw PythonException();
        }

        // The type is rather a value, so we are overriding it here.
        value = exception_type;
        exception_type = PyExceptionInstance_Class( exception_type );

        throw PythonException(
            INCREASE_REFCOUNT( exception_type ),
            INCREASE_REFCOUNT( value ),
            INCREASE_REFCOUNT_X( traceback )
        );
    }
    else
    {
        PyErr_Format( PyExc_TypeError, WRONG_EXCEPTION_TYPE_ERROR_MESSAGE, Py_TYPE( exception_type )->tp_name );

        throw PythonException();
    }
}

NUITKA_NO_RETURN NUITKA_MAY_BE_UNUSED static void RAISE_EXCEPTION_WITH_VALUE_NO_NORMALIZE( PyObject *exception_type, PyObject *value, PyObject *tb )
{
    PyTracebackObject *traceback = (PyTracebackObject *)tb;

    assertObject( exception_type );
    assert( !PyTuple_Check( exception_type ) );

    if ( PyExceptionClass_Check( exception_type ) )
    {
        throw PythonException(
            INCREASE_REFCOUNT( exception_type ),
            INCREASE_REFCOUNT( value ),
            INCREASE_REFCOUNT_X( traceback )
        );
    }
    else if ( PyExceptionInstance_Check( exception_type ) )
    {
        assert ( value == NULL || value == Py_None );

        // The type is rather a value, so we are overriding it here.
        value = exception_type;
        exception_type = PyExceptionInstance_Class( exception_type );

        throw PythonException(
            INCREASE_REFCOUNT( exception_type ),
            INCREASE_REFCOUNT( value ),
            INCREASE_REFCOUNT_X( traceback )
        );
    }
    else
    {
        assert( false );

        PyErr_Format( PyExc_TypeError, WRONG_EXCEPTION_TYPE_ERROR_MESSAGE, Py_TYPE( exception_type )->tp_name );

        throw PythonException();
    }
}

NUITKA_NO_RETURN NUITKA_MAY_BE_UNUSED static inline void RAISE_EXCEPTION_WITH_TRACEBACK( PyObject *exception_type, PyObject *value, PyObject *traceback )
{
    if ( traceback == Py_None )
    {
        traceback = NULL;
    }

    // Check traceback
    if( traceback != NULL && !PyTraceBack_Check( traceback ) )
    {
        PyErr_Format( PyExc_TypeError, "raise: arg 3 must be a traceback or None" );
        throw PythonException();
    }

    RAISE_EXCEPTION_WITH_VALUE( exception_type, value, traceback );
}

NUITKA_NO_RETURN NUITKA_MAY_BE_UNUSED static void RERAISE_EXCEPTION( void )
{
    PyThreadState *tstate = PyThreadState_GET();
    assert( tstate );

    PyObject *type = tstate->exc_type != NULL ? tstate->exc_type : Py_None;
    PyObject *value = tstate->exc_value;
    PyObject *tb = tstate->exc_traceback;

    assertObject( type );

#if PYTHON_VERSION >= 300
    if ( type == Py_None )
    {
        PyErr_Format( PyExc_RuntimeError, "No active exception to reraise" );
        throw PythonException();
    }
#endif

    RAISE_EXCEPTION_WITH_TRACEBACK( type, value, tb );
}

// Throw an exception from within an expression, this is without normalization. Note:
// There is also a form for use as a statement, and also doesn't do it, seeing this used
// normally means, the implicit exception was not propagated.
NUITKA_NO_RETURN NUITKA_MAY_BE_UNUSED static PyObject *THROW_EXCEPTION( PyObject *exception_type, PyObject *exception_value, PyObject *traceback )
{
    RAISE_EXCEPTION_WITH_VALUE_NO_NORMALIZE(
        exception_type,
        exception_value,
        traceback
    );
}

NUITKA_MAY_BE_UNUSED static void THROW_IF_ERROR_OCCURED( void )
{
    if ( ERROR_OCCURED() )
    {
        throw PythonException();
    }
}

NUITKA_MAY_BE_UNUSED static void THROW_IF_ERROR_OCCURED_NOT( PyObject *ignored )
{
    if ( ERROR_OCCURED() )
    {
        if ( PyErr_ExceptionMatches( ignored ))
        {
            PyErr_Clear();
        }
        else
        {
            throw PythonException();
        }
    }
}

#endif
<commit_msg>Python3: Fix, need to chain exceptions raised with causes too.<commit_after>//     Copyright 2013, Kay Hayen, mailto:kay.hayen@gmail.com
//
//     Part of "Nuitka", an optimizing Python compiler that is compatible and
//     integrates with CPython, but also works on its own.
//
//     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.
//
#ifndef __NUITKA_HELPER_RAISING_H__
#define __NUITKA_HELPER_RAISING_H__

#if PYTHON_VERSION < 300
#define WRONG_EXCEPTION_TYPE_ERROR_MESSAGE "exceptions must be old-style classes or derived from BaseException, not %s"
#else
#define WRONG_EXCEPTION_TYPE_ERROR_MESSAGE "exceptions must derive from BaseException"
#endif

#if PYTHON_VERSION >= 300
static void CHAIN_EXCEPTION( PyObject *exception_type, PyObject *exception_value )
{
    // Implicit chain of exception already existing.
    PyThreadState *thread_state = PyThreadState_GET();

    // Normalize existing exception first.
    PyErr_NormalizeException( &thread_state->exc_type, &thread_state->exc_value, &thread_state->exc_traceback );

    PyObject *old_exc_value = thread_state->exc_value;

    if ( old_exc_value != NULL && old_exc_value != Py_None && old_exc_value != exception_value )
    {
        Py_INCREF( old_exc_value );

        PyObject *o = old_exc_value, *context;
        while (( context = PyException_GetContext(o) ))
        {
            Py_DECREF( context );

            if ( context == exception_value )
            {
                PyException_SetContext( o, NULL );
                break;
            }

            o = context;
        }

        PyException_SetContext( exception_value, old_exc_value );
        PyException_SetTraceback( old_exc_value, thread_state->exc_traceback );
    }
}
#endif

NUITKA_NO_RETURN NUITKA_MAY_BE_UNUSED static void RAISE_EXCEPTION_WITH_TYPE( PyObject *exception_type, PyObject *exception_tb )
{
    PyTracebackObject *traceback = (PyTracebackObject *)exception_tb;
    assertObject( traceback );
    assert( PyTraceBack_Check( traceback ) );
    assertObject( exception_type );

    if ( PyExceptionClass_Check( exception_type ) )
    {
        PyObject *value = NULL;

        Py_INCREF( exception_type );
        Py_XINCREF( traceback );

        NORMALIZE_EXCEPTION( &exception_type, &value, &traceback );
#if PYTHON_VERSION >= 270
        if (unlikely( !PyExceptionInstance_Check( value ) ))
        {
            Py_DECREF( exception_type );
            Py_DECREF( value );
            Py_XDECREF( traceback );

            PyErr_Format(
                PyExc_TypeError,
                "calling %s() should have returned an instance of BaseException, not '%s'",
                ((PyTypeObject *)exception_type)->tp_name,
                Py_TYPE( value )->tp_name
            );

            throw PythonException();
        }
#endif

#if PYTHON_VERSION >= 300
        CHAIN_EXCEPTION( exception_type, value );
#endif
        throw PythonException(
            exception_type,
            value,
            traceback
        );
    }
    else if ( PyExceptionInstance_Check( exception_type ) )
    {
        PyObject *value = exception_type;
        exception_type = PyExceptionInstance_Class( exception_type );

#if PYTHON_VERSION >= 300
        CHAIN_EXCEPTION( exception_type, value );

        PyTracebackObject *prev = (PyTracebackObject *)PyException_GetTraceback( value );

        if ( prev != NULL )
        {
            assert( traceback->tb_next == NULL );
            traceback->tb_next = prev;
        }

        PyException_SetTraceback( value, (PyObject *)traceback );
#endif

        throw PythonException(
            INCREASE_REFCOUNT( exception_type ),
            INCREASE_REFCOUNT( value ),
            INCREASE_REFCOUNT( traceback )
        );
    }
    else
    {
        PyErr_Format( PyExc_TypeError, WRONG_EXCEPTION_TYPE_ERROR_MESSAGE, Py_TYPE( exception_type )->tp_name );

        PythonException to_throw;
        to_throw.setTraceback( INCREASE_REFCOUNT( traceback ) );

        throw to_throw;
    }
}

#if PYTHON_VERSION >= 300
NUITKA_NO_RETURN NUITKA_MAY_BE_UNUSED static void RAISE_EXCEPTION_WITH_CAUSE( PyObject *exception_type, PyObject *exception_cause, PyObject *exception_tb )
{
    PyTracebackObject *traceback = (PyTracebackObject *)exception_tb;

    assertObject( exception_type );
    assertObject( exception_cause );

#if PYTHON_VERSION >= 330
    // None is not a cause.
    if ( exception_cause == Py_None )
    {
        exception_cause = NULL;
    }
    else
#endif
    if ( PyExceptionClass_Check( exception_cause ) )
    {
        exception_cause = PyObject_CallObject( exception_cause, NULL );

        if (unlikely( exception_cause == NULL ))
        {
            throw PythonException();
        }
    }
    else
    {
        Py_INCREF( exception_cause );
    }

#if PYTHON_VERSION >= 330
    if (unlikely( exception_cause != NULL && !PyExceptionInstance_Check( exception_cause ) ))
#else
    if (unlikely( !PyExceptionInstance_Check( exception_cause ) ))
#endif
    {
        Py_XDECREF( exception_cause );

        PyErr_Format( PyExc_TypeError, "exception causes must derive from BaseException" );
        throw PythonException();
    }

    if ( PyExceptionClass_Check( exception_type ) )
    {
        PyObject *value = NULL;

        Py_INCREF( exception_type );
        Py_INCREF( traceback );

        NORMALIZE_EXCEPTION( &exception_type, &value, &traceback );
        if (unlikely( !PyExceptionInstance_Check( value ) ))
        {
            Py_DECREF( exception_type );
            Py_XDECREF( value );
            Py_DECREF( traceback );
            Py_XDECREF( exception_cause );

            PyErr_Format(
                PyExc_TypeError,
                "calling %s() should have returned an instance of BaseException, not '%s'",
                ((PyTypeObject *)exception_type)->tp_name,
                Py_TYPE( value )->tp_name
            );

            throw PythonException();
        }

#if PYTHON_VERSION >= 300
        CHAIN_EXCEPTION( exception_type, value );
#endif

        PythonException to_throw( exception_type, value, traceback );
        to_throw.setCause( exception_cause );
        throw to_throw;
    }
    else if ( PyExceptionInstance_Check( exception_type ) )
    {
        PyObject *value = exception_type;
        exception_type = PyExceptionInstance_Class( value );

#if PYTHON_VERSION >= 300
        CHAIN_EXCEPTION( exception_type, value );
#endif

        PythonException to_throw(
            INCREASE_REFCOUNT( exception_type ),
            INCREASE_REFCOUNT( value ),
            INCREASE_REFCOUNT( traceback )
        );
        to_throw.setCause( exception_cause );
        throw to_throw;
    }
    else
    {
        Py_XDECREF( exception_cause );

        PyErr_Format( PyExc_TypeError, WRONG_EXCEPTION_TYPE_ERROR_MESSAGE, Py_TYPE( exception_type )->tp_name );

        PythonException to_throw;
        to_throw.setTraceback( INCREASE_REFCOUNT( traceback ) );

        throw to_throw;
    }
}
#endif

NUITKA_NO_RETURN NUITKA_MAY_BE_UNUSED static void RAISE_EXCEPTION_WITH_VALUE( PyObject *exception_type, PyObject *value, PyObject *exception_tb )
{
    assertObject( exception_type );
    PyTracebackObject *traceback = (PyTracebackObject *)exception_tb;

    // Non-empty tuple exceptions are the first element.
    while (unlikely( PyTuple_Check( exception_type ) && PyTuple_Size( exception_type ) > 0 ))
    {
        exception_type = PyTuple_GET_ITEM( exception_type, 0 );
    }

    if ( PyExceptionClass_Check( exception_type ) )
    {
        Py_INCREF( exception_type );
        Py_XINCREF( value );
        Py_XINCREF( traceback );

        NORMALIZE_EXCEPTION( &exception_type, &value, &traceback );
#if PYTHON_VERSION >= 270
        if (unlikely( !PyExceptionInstance_Check( value ) ))
        {
            PyErr_Format(
                PyExc_TypeError,
                "calling %s() should have returned an instance of BaseException, not '%s'",
                ((PyTypeObject *)exception_type)->tp_name,
                Py_TYPE( value )->tp_name
            );

            Py_DECREF( exception_type );
            Py_XDECREF( value );
            Py_XDECREF( traceback );

            throw PythonException();
        }
#endif

        throw PythonException(
            exception_type,
            value,
            traceback
        );
    }
    else if ( PyExceptionInstance_Check( exception_type ) )
    {
        if (unlikely( value != NULL && value != Py_None ))
        {
            PyErr_Format(
                PyExc_TypeError,
                "instance exception may not have a separate value"
            );

            throw PythonException();
        }

        // The type is rather a value, so we are overriding it here.
        value = exception_type;
        exception_type = PyExceptionInstance_Class( exception_type );

        throw PythonException(
            INCREASE_REFCOUNT( exception_type ),
            INCREASE_REFCOUNT( value ),
            INCREASE_REFCOUNT_X( traceback )
        );
    }
    else
    {
        PyErr_Format( PyExc_TypeError, WRONG_EXCEPTION_TYPE_ERROR_MESSAGE, Py_TYPE( exception_type )->tp_name );

        throw PythonException();
    }
}

NUITKA_NO_RETURN NUITKA_MAY_BE_UNUSED static void RAISE_EXCEPTION_WITH_VALUE_NO_NORMALIZE( PyObject *exception_type, PyObject *value, PyObject *tb )
{
    PyTracebackObject *traceback = (PyTracebackObject *)tb;

    assertObject( exception_type );
    assert( !PyTuple_Check( exception_type ) );

    if ( PyExceptionClass_Check( exception_type ) )
    {
        throw PythonException(
            INCREASE_REFCOUNT( exception_type ),
            INCREASE_REFCOUNT( value ),
            INCREASE_REFCOUNT_X( traceback )
        );
    }
    else if ( PyExceptionInstance_Check( exception_type ) )
    {
        assert ( value == NULL || value == Py_None );

        // The type is rather a value, so we are overriding it here.
        value = exception_type;
        exception_type = PyExceptionInstance_Class( exception_type );

        throw PythonException(
            INCREASE_REFCOUNT( exception_type ),
            INCREASE_REFCOUNT( value ),
            INCREASE_REFCOUNT_X( traceback )
        );
    }
    else
    {
        assert( false );

        PyErr_Format( PyExc_TypeError, WRONG_EXCEPTION_TYPE_ERROR_MESSAGE, Py_TYPE( exception_type )->tp_name );

        throw PythonException();
    }
}

NUITKA_NO_RETURN NUITKA_MAY_BE_UNUSED static inline void RAISE_EXCEPTION_WITH_TRACEBACK( PyObject *exception_type, PyObject *value, PyObject *traceback )
{
    if ( traceback == Py_None )
    {
        traceback = NULL;
    }

    // Check traceback
    if( traceback != NULL && !PyTraceBack_Check( traceback ) )
    {
        PyErr_Format( PyExc_TypeError, "raise: arg 3 must be a traceback or None" );
        throw PythonException();
    }

    RAISE_EXCEPTION_WITH_VALUE( exception_type, value, traceback );
}

NUITKA_NO_RETURN NUITKA_MAY_BE_UNUSED static void RERAISE_EXCEPTION( void )
{
    PyThreadState *tstate = PyThreadState_GET();
    assert( tstate );

    PyObject *type = tstate->exc_type != NULL ? tstate->exc_type : Py_None;
    PyObject *value = tstate->exc_value;
    PyObject *tb = tstate->exc_traceback;

    assertObject( type );

#if PYTHON_VERSION >= 300
    if ( type == Py_None )
    {
        PyErr_Format( PyExc_RuntimeError, "No active exception to reraise" );
        throw PythonException();
    }
#endif

    RAISE_EXCEPTION_WITH_TRACEBACK( type, value, tb );
}

// Throw an exception from within an expression, this is without normalization. Note:
// There is also a form for use as a statement, and also doesn't do it, seeing this used
// normally means, the implicit exception was not propagated.
NUITKA_NO_RETURN NUITKA_MAY_BE_UNUSED static PyObject *THROW_EXCEPTION( PyObject *exception_type, PyObject *exception_value, PyObject *traceback )
{
    RAISE_EXCEPTION_WITH_VALUE_NO_NORMALIZE(
        exception_type,
        exception_value,
        traceback
    );
}

NUITKA_MAY_BE_UNUSED static void THROW_IF_ERROR_OCCURED( void )
{
    if ( ERROR_OCCURED() )
    {
        throw PythonException();
    }
}

NUITKA_MAY_BE_UNUSED static void THROW_IF_ERROR_OCCURED_NOT( PyObject *ignored )
{
    if ( ERROR_OCCURED() )
    {
        if ( PyErr_ExceptionMatches( ignored ))
        {
            PyErr_Clear();
        }
        else
        {
            throw PythonException();
        }
    }
}

#endif
<|endoftext|>


<commit_before>#ifndef K3_RUNTIME_BASESTRING_H
#define K3_RUNTIME_BASESTRING_H

#include <cstring>
#include <memory>
#include <vector>

#include "yaml-cpp/yaml.h"
#include "rapidjson/document.h"

#include "boost/serialization/array.hpp"
#include "boost/functional/hash.hpp"

#include "Common.hpp"
#include "dataspace/Dataspace.hpp"

char* dupstr(const char*) throw ();

namespace K3 {

class base_string {
  public:
  // Constructors/Destructors/Assignment.
  base_string(): buffer(nullptr) {}

  base_string(const base_string& other): buffer(dupstr(other.buffer)) {}

  base_string(base_string&& other): base_string() {
    swap(*this, other);
  }

  base_string(const char* b): buffer(dupstr(b)) {}
  base_string(const std::string& s) : buffer(dupstr(s.c_str())) {}

  base_string(const char* from, std::size_t count): base_string() {
    if (from && count) {
      buffer = new char[count + 1];
      strncpy(buffer, from, count);
      buffer[count] = 0;
    }
  }

  ~base_string() {
    if (buffer) {
      delete [] buffer;
    }
    buffer = 0;
  }

  base_string& operator += (const base_string& other) {
    auto new_string = std::string(buffer) + std::string(other.buffer);
    buffer = dupstr(new_string.c_str());
    return *this;
  }

  base_string& operator += (const char* other) {
    return *this += base_string(other);
  }

  base_string& operator =(const base_string& other) {
    base_string temp(other);
    swap(*this, temp);
    return *this;
  }

  base_string& operator =(base_string&& other) {
    swap(*this, other);
    return *this;
  }

  friend void swap(base_string& first, base_string& second) {
    using std::swap;
    swap(first.buffer, second.buffer);
  }

  // Conversions
  operator std::string() const {
    return std::string(buffer ? buffer : "");
  }

  // Accessors
  std::size_t length() const {
    if (buffer) {
      return strlen(buffer);
    }

    return 0;
  }

  const char* c_str() const {
    return buffer;
  }

  // Comparisons
  bool operator ==(const base_string& other) const {
    return strcmp(buffer ? buffer : "", other.buffer ? other.buffer : "") == 0;
  }

  bool operator !=(const base_string& other) const {
    return strcmp(buffer ? buffer : "", other.buffer ? other.buffer : "") != 0;
  }

  bool operator <=(const base_string& other) const {
    return strcmp(buffer ? buffer : "", other.buffer ? other.buffer : "") <= 0;
  }

  bool operator <(const base_string& other) const {
    return strcmp(buffer ? buffer : "", other.buffer ? other.buffer : "") < 0;
  }

  bool operator >=(const base_string& other) const {
    return strcmp(buffer ? buffer : "", other.buffer ? other.buffer : "") >= 0;
  }

  bool operator >(const base_string& other) const {
    return strcmp(buffer ? buffer : "", other.buffer ? other.buffer : "") > 0;
  }

  // Operations
  base_string substr(std::size_t from, std::size_t to) const {
    if (!buffer) {
      return base_string();
    }

    auto n = length();

    if (from > n) {
      from = n;
    }

    if (to > n) {
      to = n;
    }

    return base_string(buffer + from, to - from);
  }

  // Modifies this string.
  Seq<R_elem<string_impl>> splitString(const string_impl& splitter) {
    Seq<R_elem<string_impl>> results;
    if (!buffer) {
      return results;
    }

    R_elem<string_impl> rec;
    char * pch;
    pch = strtok (buffer, splitter.c_str());
    while (pch != NULL)
    {
      rec.elem = string_impl(pch);
      results.insert(rec);
      pch = strtok (NULL, splitter.c_str());
    }

    return results;
  }

  // Stream Operators
  friend std::ostream& operator <<(std::ostream& out, const K3::base_string& s) {
    if (s.buffer) {
      return out << s.c_str();
    }

    return out;
  }

  char* begin() const {
    return buffer;
  }

  char* end() const {
    return buffer + length();
  }

  template <class archive>
  void serialize(archive& a, const unsigned int) {
    std::size_t len;
    if (archive::is_saving::value) {
      len = length();
    }
    a & len;
    if (archive::is_loading::value) {
      // Possibly extraneous:
      // Buffer might always be null when loading
      // since this base_str was just constructed
      if(buffer) {
        delete [] buffer;
	buffer = 0;
      }

      if (len) {
        buffer = new char[len + 1];
        buffer[len] = 0;
      } else {
        buffer = 0;
      }
    }
    if (buffer) {
      a & boost::serialization::make_array(buffer, len);
    }
  }

 private:
  char* buffer;
};

  inline base_string operator + (base_string s, char const* t) {
    return s += t;
  }

  inline base_string operator + (char const* t, base_string s) {
    auto new_string = base_string(t);
    return new_string += s;
  }

} // namespace K3

namespace JSON {
  template <> struct convert<K3::base_string> {
    template <class Allocator>
    static rapidjson::Value encode(const K3::base_string& from, Allocator& al) {
      Value v;
      if (from.c_str()) {
        v.SetString(from.c_str(), al);
      }
      else {
        v.SetString("", al);
      }
      return v;
    }

  };
}

namespace YAML {
  template <>
  struct convert<K3::base_string> {
    static Node encode(const K3::base_string& s) {
      Node node;
      node = std::string(s.c_str());
      return node;
    }

    static bool decode(const Node& node, K3::base_string& s) {
      try {
        auto t = node.as<std::string>();
        s = K3::base_string(t);
        return true;
      } catch (YAML::TypedBadConversion<std::string> e) {
        return false;
      }
    }
  };
}

#endif
<commit_msg>Make +operator on base_strings slightly more efficient.<commit_after>#ifndef K3_RUNTIME_BASESTRING_H
#define K3_RUNTIME_BASESTRING_H

#include <cstring>
#include <memory>
#include <vector>

#include "yaml-cpp/yaml.h"
#include "rapidjson/document.h"

#include "boost/serialization/array.hpp"
#include "boost/functional/hash.hpp"

#include "Common.hpp"
#include "dataspace/Dataspace.hpp"

char* dupstr(const char*) throw ();

namespace K3 {

class base_string {
  public:
  // Constructors/Destructors/Assignment.
  base_string(): buffer(nullptr) {}

  base_string(const base_string& other): buffer(dupstr(other.buffer)) {}

  base_string(base_string&& other): base_string() {
    swap(*this, other);
  }

  base_string(const char* b): buffer(dupstr(b)) {}
  base_string(const std::string& s) : buffer(dupstr(s.c_str())) {}

  base_string(const char* from, std::size_t count): base_string() {
    if (from && count) {
      buffer = new char[count + 1];
      strncpy(buffer, from, count);
      buffer[count] = 0;
    }
  }

  ~base_string() {
    if (buffer) {
      delete [] buffer;
    }
    buffer = 0;
  }

  base_string& operator += (const base_string& other) {
    auto new_string = std::string(buffer) + std::string(other.buffer);
    buffer = dupstr(new_string.c_str());
    return *this;
  }

  base_string& operator += (const char* other) {
    return *this += base_string(other);
  }

  base_string& operator =(const base_string& other) {
    base_string temp(other);
    swap(*this, temp);
    return *this;
  }

  base_string& operator =(base_string&& other) {
    swap(*this, other);
    return *this;
  }

  friend void swap(base_string& first, base_string& second) {
    using std::swap;
    swap(first.buffer, second.buffer);
  }

  // Conversions
  operator std::string() const {
    return std::string(buffer ? buffer : "");
  }

  // Accessors
  std::size_t length() const {
    if (buffer) {
      return strlen(buffer);
    }

    return 0;
  }

  const char* c_str() const {
    return buffer;
  }

  // Comparisons
  bool operator ==(const base_string& other) const {
    return strcmp(buffer ? buffer : "", other.buffer ? other.buffer : "") == 0;
  }

  bool operator !=(const base_string& other) const {
    return strcmp(buffer ? buffer : "", other.buffer ? other.buffer : "") != 0;
  }

  bool operator <=(const base_string& other) const {
    return strcmp(buffer ? buffer : "", other.buffer ? other.buffer : "") <= 0;
  }

  bool operator <(const base_string& other) const {
    return strcmp(buffer ? buffer : "", other.buffer ? other.buffer : "") < 0;
  }

  bool operator >=(const base_string& other) const {
    return strcmp(buffer ? buffer : "", other.buffer ? other.buffer : "") >= 0;
  }

  bool operator >(const base_string& other) const {
    return strcmp(buffer ? buffer : "", other.buffer ? other.buffer : "") > 0;
  }

  // Operations
  base_string substr(std::size_t from, std::size_t to) const {
    if (!buffer) {
      return base_string();
    }

    auto n = length();

    if (from > n) {
      from = n;
    }

    if (to > n) {
      to = n;
    }

    return base_string(buffer + from, to - from);
  }

  // Modifies this string.
  Seq<R_elem<string_impl>> splitString(const string_impl& splitter) {
    Seq<R_elem<string_impl>> results;
    if (!buffer) {
      return results;
    }

    R_elem<string_impl> rec;
    char * pch;
    pch = strtok (buffer, splitter.c_str());
    while (pch != NULL)
    {
      rec.elem = string_impl(pch);
      results.insert(rec);
      pch = strtok (NULL, splitter.c_str());
    }

    return results;
  }

  // Stream Operators
  friend std::ostream& operator <<(std::ostream& out, const K3::base_string& s) {
    if (s.buffer) {
      return out << s.c_str();
    }

    return out;
  }

  char* begin() const {
    return buffer;
  }

  char* end() const {
    return buffer + length();
  }

  template <class archive>
  void serialize(archive& a, const unsigned int) {
    std::size_t len;
    if (archive::is_saving::value) {
      len = length();
    }
    a & len;
    if (archive::is_loading::value) {
      // Possibly extraneous:
      // Buffer might always be null when loading
      // since this base_str was just constructed
      if(buffer) {
        delete [] buffer;
	buffer = 0;
      }

      if (len) {
        buffer = new char[len + 1];
        buffer[len] = 0;
      } else {
        buffer = 0;
      }
    }
    if (buffer) {
      a & boost::serialization::make_array(buffer, len);
    }
  }

 private:
  char* buffer;
};

  inline base_string operator + (base_string s, base_string const& t) {
    return s += t;
  }

  inline base_string operator + (base_string s, char const* t) {
    return s += t;
  }

  inline base_string operator + (char const* t, base_string const& s) {
    auto new_string = base_string(t);
    return new_string += s;
  }

} // namespace K3

namespace JSON {
  template <> struct convert<K3::base_string> {
    template <class Allocator>
    static rapidjson::Value encode(const K3::base_string& from, Allocator& al) {
      Value v;
      if (from.c_str()) {
        v.SetString(from.c_str(), al);
      }
      else {
        v.SetString("", al);
      }
      return v;
    }

  };
}

namespace YAML {
  template <>
  struct convert<K3::base_string> {
    static Node encode(const K3::base_string& s) {
      Node node;
      node = std::string(s.c_str());
      return node;
    }

    static bool decode(const Node& node, K3::base_string& s) {
      try {
        auto t = node.as<std::string>();
        s = K3::base_string(t);
        return true;
      } catch (YAML::TypedBadConversion<std::string> e) {
        return false;
      }
    }
  };
}

#endif
<|endoftext|>