<commit_before>#include "DeviceInterface.h"
#include "JITModule.h"
#include "Target.h"

#include "runtime/HalideRuntime.h"
#include "runtime/HalideRuntimeCuda.h"
#include "runtime/HalideRuntimeOpenCL.h"
#include "runtime/HalideRuntimeOpenGL.h"
#include "runtime/HalideRuntimeOpenGLCompute.h"

#ifdef _MSC_VER
// We want to export the functions below from Halide.dll, but we can't
// use conventional EXPORT qualifiers because these are all declared
// in HalideRuntime.h without them. Instead we use a pragma that adds
// a linker flag. Because the pragma is in a macro, we use __pragma
// instead of #pragma
#ifdef _WIN64
#define EXPORT_SYM(n) __pragma(comment(linker, "/EXPORT:" #n))
#else
#define EXPORT_SYM(n) __pragma(comment(linker, "/EXPORT:_" #n))
#endif
#else
#define EXPORT_SYM(n)
#endif

using namespace Halide;
using namespace Halide::Internal;

namespace {

template <typename fn_type>
bool lookup_runtime_routine(const std::string &name,
                            const Target &target,
                            fn_type &result) {
    std::vector<JITModule> runtime(
        JITSharedRuntime::get(nullptr, target.with_feature(Target::JIT)));

    for (size_t i = 0; i < runtime.size(); i++) {
        std::map<std::string, JITModule::Symbol>::const_iterator f =
          runtime[i].exports().find(name);
        if (f != runtime[i].exports().end()) {
            result = reinterpret_bits<fn_type>(f->second.address);
            return true;
        }
    }
    return false;
}

}

namespace Halide {
const halide_device_interface_t *get_default_device_interface_for_target(const Target &t) {
    return get_device_interface_for_device_api(DeviceAPI::Default_GPU, t);
}

const halide_device_interface_t *get_device_interface_for_device_api(const DeviceAPI &d, const Target &t) {
    if (d == DeviceAPI::Default_GPU) {
        return get_device_interface_for_device_api(get_default_device_api_for_target(t), t);
    }

    const struct halide_device_interface_t *(*fn)();
    std::string name;
    if (d == DeviceAPI::Metal) {
        name = "metal";
    } else if (d == DeviceAPI::OpenCL) {
        name = "opencl";
    } else if (d == DeviceAPI::CUDA) {
        name = "cuda";
    } else if (d == DeviceAPI::OpenGLCompute) {
        name = "openglcompute";
    } else if (d == DeviceAPI::GLSL) {
        name = "opengl";
    } else {
        return nullptr;
    }

    if (lookup_runtime_routine("halide_" + name + "_device_interface", t, fn)) {
        return (*fn)();
    } else {
        return nullptr;
    }
}

DeviceAPI get_default_device_api_for_target(const Target &target) {
    if (target.has_feature(Target::Metal)) {
        return DeviceAPI::Metal;
    } else if (target.has_feature(Target::OpenCL)) {
        return DeviceAPI::OpenCL;
    } else if (target.has_feature(Target::CUDA)) {
        return DeviceAPI::CUDA;
    } else if (target.has_feature(Target::OpenGLCompute)) {
        return DeviceAPI::OpenGLCompute;
    } else if (target.has_feature(Target::OpenGL)) {
        return DeviceAPI::GLSL;
    } else {
        return DeviceAPI::Host;
    }
}
}

extern "C" {

/** Release all data associated with the current GPU backend, in particular
 * all resources (memory, texture, context handles) allocated by Halide. Must
 * be called explicitly when using AOT compilation. */
void halide_device_release(void *user_context, const halide_device_interface_t *device_interface) {
    user_assert(user_context == nullptr) << "Cannot provide user_context to libHalide.a halide_device_release\n";
    Target target(get_host_target());
    void (*fn)(void *user_context, const halide_device_interface_t *device_interface);
    if (lookup_runtime_routine("halide_device_release", target, fn)) {
        (*fn)(user_context, device_interface);
    }
}
EXPORT_SYM(halide_device_release)

/** Copy image data from device memory to host memory. This must be called
 * explicitly to copy back the results of a GPU-based filter. */
int halide_copy_to_host(void *user_context, struct halide_buffer_t *buf) {
    user_assert(user_context == nullptr) << "Cannot provide user_context to libHalide.a halide_copy_to_host\n";
    // Skip if there is no device buffer.
    if (buf->device == 0) {
        return 0;
    }
    Target target(get_host_target());
    int (*fn)(void *user_context, struct halide_buffer_t *buf);
    if (lookup_runtime_routine("halide_copy_to_host", target, fn)) {
        return (*fn)(user_context, buf);
    }
    return -1;
}
EXPORT_SYM(halide_copy_to_host)

/** Copy image data from host memory to device memory. This should not
 * be called directly; Halide handles copying to the device
 * automatically.  If interface is nullptr and the bug has a non-zero dev
 * field, the device associated with the dev handle will be
 * used. Otherwise if the dev field is 0 and interface is nullptr, an
 * error is returned. */
int halide_copy_to_device(void *user_context, struct halide_buffer_t *buf,
                          const halide_device_interface_t *device_interface) {
    user_assert(user_context == nullptr) << "Cannot provide user_context to libHalide.a halide_copy_to_device\n";
    Target target(get_host_target());
    int (*fn)(void *user_context, struct halide_buffer_t *buf, const halide_device_interface_t *device_interface);
    if (lookup_runtime_routine("halide_copy_to_device", target, fn)) {
        return (*fn)(user_context, buf, device_interface);
    }
    return -1;
}
EXPORT_SYM(halide_copy_to_device)

/** Wait for current GPU operations to complete. Calling this explicitly
 * should rarely be necessary, except maybe for profiling. */
int halide_device_sync(void *user_context, struct halide_buffer_t *buf) {
    user_assert(user_context == nullptr) << "Cannot provide user_context to libHalide.a halide_device_sync\n";
    Target target(get_host_target());
    int (*fn)(void *user_context, struct halide_buffer_t *buf);
    if (lookup_runtime_routine("halide_device_sync", target, fn)) {
      return (*fn)(user_context, buf);
    }
    return -1;
}
EXPORT_SYM(halide_device_sync)

/** Allocate device memory to back a halide_buffer_t. */
int halide_device_malloc(void *user_context, struct halide_buffer_t *buf, const halide_device_interface_t *device_interface) {
    user_assert(user_context == nullptr) << "Cannot provide user_context to libHalide.a halide_device_malloc\n";
    Target target(get_host_target());
    int (*fn)(void *user_context, struct halide_buffer_t *buf, const halide_device_interface_t *device_interface);
    if (lookup_runtime_routine("halide_device_malloc", target, fn)) {
      return (*fn)(user_context, buf, device_interface);
    }
    return -1;
}
EXPORT_SYM(halide_device_malloc)

int halide_device_free(void *user_context, struct halide_buffer_t *buf) {
    user_assert(user_context == nullptr) << "Cannot provide user_context to libHalide.a halide_device_free\n";
    // Skip if there is no device buffer.
    if (buf->device == 0) {
        return 0;
    }
    Target target(get_host_target());
    int (*fn)(void *user_context, struct halide_buffer_t *buf);
    if (lookup_runtime_routine("halide_device_free", target, fn)) {
        return (*fn)(user_context, buf);
    }
    if (buf->device != 0) {
        return -1;
    } else {
        return 0;
    }
}
EXPORT_SYM(halide_device_free)

int halide_weak_device_free(void *user_context, struct halide_buffer_t *buf) {
    return halide_device_free(user_context, buf);
}
EXPORT_SYM(halide_weak_device_free)

const struct halide_device_interface_t *halide_cuda_device_interface() {
    Target t = get_jit_target_from_environment().with_feature(Target::CUDA);
    return get_device_interface_for_device_api(DeviceAPI::CUDA, t);
}
EXPORT_SYM(halide_cuda_device_interface)

const struct halide_device_interface_t *halide_opencl_device_interface() {
    Target t = get_jit_target_from_environment().with_feature(Target::OpenCL);
    return get_device_interface_for_device_api(DeviceAPI::OpenCL, t);
}
EXPORT_SYM(halide_opencl_device_interface)

const struct halide_device_interface_t *halide_opengl_device_interface() {
    Target t = get_jit_target_from_environment().with_feature(Target::OpenGL);
    return get_device_interface_for_device_api(DeviceAPI::GLSL, t);
}
EXPORT_SYM(halide_opengl_device_interface)

int halide_opengl_wrap_texture(void *user_context, struct halide_buffer_t *buf, uintptr_t tex) {
    Target target(get_host_target());
    target.set_feature(Target::OpenGL);
    int (*fn)(void *user_context, struct halide_buffer_t *buf, uint ptr_ttex);
    if (lookup_runtime_routine("halide_opengl_wrap_texture", target, fn)) {
        return (*fn)(user_context, buf, tex);
    }
    return 0;
}
EXPORT_SYM(halide_opengl_wrap_texture)

uintptr_t halide_opengl_detach_texture(void *user_context, struct halide_buffer_t *buf) {
    Target target(get_host_target());
    target.set_feature(Target::OpenGL);
    uintptr_t (*fn)(void *user_context, struct halide_buffer_t *buf);
    if (lookup_runtime_routine("halide_opengl_detach_texture", target, fn)) {
        return (*fn)(user_context, buf);
    }
    return 0;
}
EXPORT_SYM(halide_opengl_detach_texture)

const struct halide_device_interface_t *halide_openglcompute_device_interface() {
    Target t = get_jit_target_from_environment().with_feature(Target::OpenGLCompute);
    return get_device_interface_for_device_api(DeviceAPI::OpenGLCompute, t);
}
EXPORT_SYM(halide_openglcompute_device_interface)

const struct halide_device_interface_t *halide_metal_device_interface() {
    Target t = get_jit_target_from_environment().with_feature(Target::Metal);
    return get_device_interface_for_device_api(DeviceAPI::Metal, t);
}
EXPORT_SYM(halide_metal_device_interface)

}
<commit_msg>Fix mangled function pointer<commit_after>#include "DeviceInterface.h"
#include "JITModule.h"
#include "Target.h"

#include "runtime/HalideRuntime.h"
#include "runtime/HalideRuntimeCuda.h"
#include "runtime/HalideRuntimeOpenCL.h"
#include "runtime/HalideRuntimeOpenGL.h"
#include "runtime/HalideRuntimeOpenGLCompute.h"

#ifdef _MSC_VER
// We want to export the functions below from Halide.dll, but we can't
// use conventional EXPORT qualifiers because these are all declared
// in HalideRuntime.h without them. Instead we use a pragma that adds
// a linker flag. Because the pragma is in a macro, we use __pragma
// instead of #pragma
#ifdef _WIN64
#define EXPORT_SYM(n) __pragma(comment(linker, "/EXPORT:" #n))
#else
#define EXPORT_SYM(n) __pragma(comment(linker, "/EXPORT:_" #n))
#endif
#else
#define EXPORT_SYM(n)
#endif

using namespace Halide;
using namespace Halide::Internal;

namespace {

template <typename fn_type>
bool lookup_runtime_routine(const std::string &name,
                            const Target &target,
                            fn_type &result) {
    std::vector<JITModule> runtime(
        JITSharedRuntime::get(nullptr, target.with_feature(Target::JIT)));

    for (size_t i = 0; i < runtime.size(); i++) {
        std::map<std::string, JITModule::Symbol>::const_iterator f =
          runtime[i].exports().find(name);
        if (f != runtime[i].exports().end()) {
            result = reinterpret_bits<fn_type>(f->second.address);
            return true;
        }
    }
    return false;
}

}

namespace Halide {
const halide_device_interface_t *get_default_device_interface_for_target(const Target &t) {
    return get_device_interface_for_device_api(DeviceAPI::Default_GPU, t);
}

const halide_device_interface_t *get_device_interface_for_device_api(const DeviceAPI &d, const Target &t) {
    if (d == DeviceAPI::Default_GPU) {
        return get_device_interface_for_device_api(get_default_device_api_for_target(t), t);
    }

    const struct halide_device_interface_t *(*fn)();
    std::string name;
    if (d == DeviceAPI::Metal) {
        name = "metal";
    } else if (d == DeviceAPI::OpenCL) {
        name = "opencl";
    } else if (d == DeviceAPI::CUDA) {
        name = "cuda";
    } else if (d == DeviceAPI::OpenGLCompute) {
        name = "openglcompute";
    } else if (d == DeviceAPI::GLSL) {
        name = "opengl";
    } else {
        return nullptr;
    }

    if (lookup_runtime_routine("halide_" + name + "_device_interface", t, fn)) {
        return (*fn)();
    } else {
        return nullptr;
    }
}

DeviceAPI get_default_device_api_for_target(const Target &target) {
    if (target.has_feature(Target::Metal)) {
        return DeviceAPI::Metal;
    } else if (target.has_feature(Target::OpenCL)) {
        return DeviceAPI::OpenCL;
    } else if (target.has_feature(Target::CUDA)) {
        return DeviceAPI::CUDA;
    } else if (target.has_feature(Target::OpenGLCompute)) {
        return DeviceAPI::OpenGLCompute;
    } else if (target.has_feature(Target::OpenGL)) {
        return DeviceAPI::GLSL;
    } else {
        return DeviceAPI::Host;
    }
}
}

extern "C" {

/** Release all data associated with the current GPU backend, in particular
 * all resources (memory, texture, context handles) allocated by Halide. Must
 * be called explicitly when using AOT compilation. */
void halide_device_release(void *user_context, const halide_device_interface_t *device_interface) {
    user_assert(user_context == nullptr) << "Cannot provide user_context to libHalide.a halide_device_release\n";
    Target target(get_host_target());
    void (*fn)(void *user_context, const halide_device_interface_t *device_interface);
    if (lookup_runtime_routine("halide_device_release", target, fn)) {
        (*fn)(user_context, device_interface);
    }
}
EXPORT_SYM(halide_device_release)

/** Copy image data from device memory to host memory. This must be called
 * explicitly to copy back the results of a GPU-based filter. */
int halide_copy_to_host(void *user_context, struct halide_buffer_t *buf) {
    user_assert(user_context == nullptr) << "Cannot provide user_context to libHalide.a halide_copy_to_host\n";
    // Skip if there is no device buffer.
    if (buf->device == 0) {
        return 0;
    }
    Target target(get_host_target());
    int (*fn)(void *user_context, struct halide_buffer_t *buf);
    if (lookup_runtime_routine("halide_copy_to_host", target, fn)) {
        return (*fn)(user_context, buf);
    }
    return -1;
}
EXPORT_SYM(halide_copy_to_host)

/** Copy image data from host memory to device memory. This should not
 * be called directly; Halide handles copying to the device
 * automatically.  If interface is nullptr and the bug has a non-zero dev
 * field, the device associated with the dev handle will be
 * used. Otherwise if the dev field is 0 and interface is nullptr, an
 * error is returned. */
int halide_copy_to_device(void *user_context, struct halide_buffer_t *buf,
                          const halide_device_interface_t *device_interface) {
    user_assert(user_context == nullptr) << "Cannot provide user_context to libHalide.a halide_copy_to_device\n";
    Target target(get_host_target());
    int (*fn)(void *user_context, struct halide_buffer_t *buf, const halide_device_interface_t *device_interface);
    if (lookup_runtime_routine("halide_copy_to_device", target, fn)) {
        return (*fn)(user_context, buf, device_interface);
    }
    return -1;
}
EXPORT_SYM(halide_copy_to_device)

/** Wait for current GPU operations to complete. Calling this explicitly
 * should rarely be necessary, except maybe for profiling. */
int halide_device_sync(void *user_context, struct halide_buffer_t *buf) {
    user_assert(user_context == nullptr) << "Cannot provide user_context to libHalide.a halide_device_sync\n";
    Target target(get_host_target());
    int (*fn)(void *user_context, struct halide_buffer_t *buf);
    if (lookup_runtime_routine("halide_device_sync", target, fn)) {
      return (*fn)(user_context, buf);
    }
    return -1;
}
EXPORT_SYM(halide_device_sync)

/** Allocate device memory to back a halide_buffer_t. */
int halide_device_malloc(void *user_context, struct halide_buffer_t *buf, const halide_device_interface_t *device_interface) {
    user_assert(user_context == nullptr) << "Cannot provide user_context to libHalide.a halide_device_malloc\n";
    Target target(get_host_target());
    int (*fn)(void *user_context, struct halide_buffer_t *buf, const halide_device_interface_t *device_interface);
    if (lookup_runtime_routine("halide_device_malloc", target, fn)) {
      return (*fn)(user_context, buf, device_interface);
    }
    return -1;
}
EXPORT_SYM(halide_device_malloc)

int halide_device_free(void *user_context, struct halide_buffer_t *buf) {
    user_assert(user_context == nullptr) << "Cannot provide user_context to libHalide.a halide_device_free\n";
    // Skip if there is no device buffer.
    if (buf->device == 0) {
        return 0;
    }
    Target target(get_host_target());
    int (*fn)(void *user_context, struct halide_buffer_t *buf);
    if (lookup_runtime_routine("halide_device_free", target, fn)) {
        return (*fn)(user_context, buf);
    }
    if (buf->device != 0) {
        return -1;
    } else {
        return 0;
    }
}
EXPORT_SYM(halide_device_free)

int halide_weak_device_free(void *user_context, struct halide_buffer_t *buf) {
    return halide_device_free(user_context, buf);
}
EXPORT_SYM(halide_weak_device_free)

const struct halide_device_interface_t *halide_cuda_device_interface() {
    Target t = get_jit_target_from_environment().with_feature(Target::CUDA);
    return get_device_interface_for_device_api(DeviceAPI::CUDA, t);
}
EXPORT_SYM(halide_cuda_device_interface)

const struct halide_device_interface_t *halide_opencl_device_interface() {
    Target t = get_jit_target_from_environment().with_feature(Target::OpenCL);
    return get_device_interface_for_device_api(DeviceAPI::OpenCL, t);
}
EXPORT_SYM(halide_opencl_device_interface)

const struct halide_device_interface_t *halide_opengl_device_interface() {
    Target t = get_jit_target_from_environment().with_feature(Target::OpenGL);
    return get_device_interface_for_device_api(DeviceAPI::GLSL, t);
}
EXPORT_SYM(halide_opengl_device_interface)

int halide_opengl_wrap_texture(void *user_context, struct halide_buffer_t *buf, uintptr_t tex) {
    Target target(get_host_target());
    target.set_feature(Target::OpenGL);
    int (*fn)(void *, struct halide_buffer_t *, uintptr_t);
    if (lookup_runtime_routine("halide_opengl_wrap_texture", target, fn)) {
        return (*fn)(user_context, buf, tex);
    }
    return 0;
}
EXPORT_SYM(halide_opengl_wrap_texture)

uintptr_t halide_opengl_detach_texture(void *user_context, struct halide_buffer_t *buf) {
    Target target(get_host_target());
    target.set_feature(Target::OpenGL);
    uintptr_t (*fn)(void *, struct halide_buffer_t *);
    if (lookup_runtime_routine("halide_opengl_detach_texture", target, fn)) {
        return (*fn)(user_context, buf);
    }
    return 0;
}
EXPORT_SYM(halide_opengl_detach_texture)

const struct halide_device_interface_t *halide_openglcompute_device_interface() {
    Target t = get_jit_target_from_environment().with_feature(Target::OpenGLCompute);
    return get_device_interface_for_device_api(DeviceAPI::OpenGLCompute, t);
}
EXPORT_SYM(halide_openglcompute_device_interface)

const struct halide_device_interface_t *halide_metal_device_interface() {
    Target t = get_jit_target_from_environment().with_feature(Target::Metal);
    return get_device_interface_for_device_api(DeviceAPI::Metal, t);
}
EXPORT_SYM(halide_metal_device_interface)

}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2011-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include "FunctionContext.hpp"
#include "Variable.hpp"
#include "Utils.hpp"
#include "VisitorUtils.hpp"
#include "Type.hpp"
#include "Options.hpp"
#include "logging.hpp"

#include "ast/GetConstantValue.hpp"

using namespace eddic;

FunctionContext::FunctionContext(std::shared_ptr<Context> parent, std::shared_ptr<GlobalContext> global_context, Platform platform, std::shared_ptr<Configuration> configuration) :
        Context(parent, global_context), platform(platform) {
    //TODO Should not be done here
    if(configuration->option_defined("fomit-frame-pointer")){
        currentParameter = INT->size(platform);
    } else {
        currentParameter = 2 * INT->size(platform);
    }
}

int FunctionContext::size() const {
    int size = -currentPosition;

    if(size == static_cast<int>(INT->size(platform))){
        return 0;
    }

    return size;
}

int FunctionContext::stack_position(){
    return currentPosition;
}

void FunctionContext::set_stack_position(int current){
    currentPosition = current;
}

std::shared_ptr<Variable> FunctionContext::newParameter(const std::string& variable, std::shared_ptr<const Type> type){
    Position position(PositionType::PARAMETER, currentParameter);

    currentParameter += type->size(platform);

    return std::make_shared<Variable>(variable, type, position);
}

std::shared_ptr<Variable> FunctionContext::newVariable(const std::string& variable, std::shared_ptr<const Type> type){
    auto var = std::make_shared<Variable>(variable, type, Position(PositionType::VARIABLE));

    storage.push_back(var);

    return var;
}

Storage FunctionContext::stored_variables(){
    return storage;
}

std::shared_ptr<Variable> FunctionContext::addVariable(const std::string& variable, std::shared_ptr<const Type> type){
    return variables[variable] = newVariable(variable, type);
}

std::shared_ptr<Variable> FunctionContext::newVariable(std::shared_ptr<Variable> source){
    std::string name = "g_" + source->name() + "_" + toString(temporary++);

    if(source->position().is_temporary()){
        Position position(PositionType::TEMPORARY);

        auto var = std::make_shared<Variable>(name, source->type(), position);
        storage.push_back(var);
        return variables[name] = var;
    } else {
        return addVariable(name, source->type());
    }
}

std::shared_ptr<Variable> FunctionContext::addVariable(const std::string& variable, std::shared_ptr<const Type> type, ast::Value& value){
    assert(type->is_const());

    Position position(PositionType::CONST);

    auto val = visit(ast::GetConstantValue(), value);

    auto var = std::make_shared<Variable>(variable, type, position, val);
    return variables[variable] = var;
}

std::shared_ptr<Variable> FunctionContext::generate_variable(const std::string& prefix, std::shared_ptr<const Type> type){
    std::string name = prefix + "_" + toString(generated++);
    return addVariable(name, type);
}

std::shared_ptr<Variable> FunctionContext::addParameter(const std::string& parameter, std::shared_ptr<const Type> type){
    return variables[parameter] = newParameter(parameter, type);
}

std::shared_ptr<Variable> FunctionContext::new_temporary(std::shared_ptr<const Type> type){
    eddic_assert((type->is_standard_type() && type != STRING) || type->is_pointer(), "Invalid temporary");

    Position position(PositionType::TEMPORARY);

    std::string name = "t_" + toString(temporary++);
    auto var = std::make_shared<Variable>(name, type, position);
    storage.push_back(var);
    return variables[name] = var;
}

std::shared_ptr<Variable> FunctionContext::new_reference(std::shared_ptr<const Type> type, std::shared_ptr<Variable> var, Offset offset){
    std::string name = "t_" + toString(temporary++);
    auto variable = std::make_shared<Variable>(name, type, var, offset);
    storage.push_back(variable);
    return variables[name] = variable;
}

void FunctionContext::allocate_in_register(std::shared_ptr<Variable> variable, unsigned int register_){
    assert(variable->position().isStack());

    Position position(PositionType::REGISTER, register_);
    variable->setPosition(position);
}

void FunctionContext::allocate_in_param_register(std::shared_ptr<Variable> variable, unsigned int register_){
    assert(variable->position().isParameter());

    Position position(PositionType::PARAM_REGISTER, register_);
    variable->setPosition(position);
}

void FunctionContext::removeVariable(std::shared_ptr<Variable> variable){
    auto iter_var = std::find(storage.begin(), storage.end(), variable);
    auto var = *iter_var;

    storage.erase(iter_var);

    log::emit<Info>("Variables") << "Remove " << variable->name() << log::endl;
}

std::shared_ptr<FunctionContext> FunctionContext::function(){
    return shared_from_this();
}
<commit_msg>Add some logging for creation of new parametesr<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2011-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include "FunctionContext.hpp"
#include "Variable.hpp"
#include "Utils.hpp"
#include "VisitorUtils.hpp"
#include "Type.hpp"
#include "Options.hpp"
#include "logging.hpp"

#include "ast/GetConstantValue.hpp"

using namespace eddic;

FunctionContext::FunctionContext(std::shared_ptr<Context> parent, std::shared_ptr<GlobalContext> global_context, Platform platform, std::shared_ptr<Configuration> configuration) :
        Context(parent, global_context), platform(platform) {
    //TODO Should not be done here
    if(configuration->option_defined("fomit-frame-pointer")){
        currentParameter = INT->size(platform);
    } else {
        currentParameter = 2 * INT->size(platform);
    }
}

int FunctionContext::size() const {
    int size = -currentPosition;

    if(size == static_cast<int>(INT->size(platform))){
        return 0;
    }

    return size;
}

int FunctionContext::stack_position(){
    return currentPosition;
}

void FunctionContext::set_stack_position(int current){
    currentPosition = current;
}

std::shared_ptr<Variable> FunctionContext::newParameter(const std::string& variable, std::shared_ptr<const Type> type){
    Position position(PositionType::PARAMETER, currentParameter);

    log::emit<Info>("Variables") << "New parameter " << variable << " at position " << currentParameter << log::endl;

    currentParameter += type->size(platform);

    return std::make_shared<Variable>(variable, type, position);
}

std::shared_ptr<Variable> FunctionContext::newVariable(const std::string& variable, std::shared_ptr<const Type> type){
    auto var = std::make_shared<Variable>(variable, type, Position(PositionType::VARIABLE));

    storage.push_back(var);

    return var;
}

Storage FunctionContext::stored_variables(){
    return storage;
}

std::shared_ptr<Variable> FunctionContext::addVariable(const std::string& variable, std::shared_ptr<const Type> type){
    return variables[variable] = newVariable(variable, type);
}

std::shared_ptr<Variable> FunctionContext::newVariable(std::shared_ptr<Variable> source){
    std::string name = "g_" + source->name() + "_" + toString(temporary++);

    if(source->position().is_temporary()){
        Position position(PositionType::TEMPORARY);

        auto var = std::make_shared<Variable>(name, source->type(), position);
        storage.push_back(var);
        return variables[name] = var;
    } else {
        return addVariable(name, source->type());
    }
}

std::shared_ptr<Variable> FunctionContext::addVariable(const std::string& variable, std::shared_ptr<const Type> type, ast::Value& value){
    assert(type->is_const());

    Position position(PositionType::CONST);

    auto val = visit(ast::GetConstantValue(), value);

    auto var = std::make_shared<Variable>(variable, type, position, val);
    return variables[variable] = var;
}

std::shared_ptr<Variable> FunctionContext::generate_variable(const std::string& prefix, std::shared_ptr<const Type> type){
    std::string name = prefix + "_" + toString(generated++);
    return addVariable(name, type);
}

std::shared_ptr<Variable> FunctionContext::addParameter(const std::string& parameter, std::shared_ptr<const Type> type){
    return variables[parameter] = newParameter(parameter, type);
}

std::shared_ptr<Variable> FunctionContext::new_temporary(std::shared_ptr<const Type> type){
    eddic_assert((type->is_standard_type() && type != STRING) || type->is_pointer(), "Invalid temporary");

    Position position(PositionType::TEMPORARY);

    std::string name = "t_" + toString(temporary++);
    auto var = std::make_shared<Variable>(name, type, position);
    storage.push_back(var);
    return variables[name] = var;
}

std::shared_ptr<Variable> FunctionContext::new_reference(std::shared_ptr<const Type> type, std::shared_ptr<Variable> var, Offset offset){
    std::string name = "t_" + toString(temporary++);
    auto variable = std::make_shared<Variable>(name, type, var, offset);
    storage.push_back(variable);
    return variables[name] = variable;
}

void FunctionContext::allocate_in_register(std::shared_ptr<Variable> variable, unsigned int register_){
    assert(variable->position().isStack());

    Position position(PositionType::REGISTER, register_);
    variable->setPosition(position);
}

void FunctionContext::allocate_in_param_register(std::shared_ptr<Variable> variable, unsigned int register_){
    assert(variable->position().isParameter());

    Position position(PositionType::PARAM_REGISTER, register_);
    variable->setPosition(position);
}

void FunctionContext::removeVariable(std::shared_ptr<Variable> variable){
    auto iter_var = std::find(storage.begin(), storage.end(), variable);
    auto var = *iter_var;

    storage.erase(iter_var);

    log::emit<Info>("Variables") << "Remove " << variable->name() << log::endl;
}

std::shared_ptr<FunctionContext> FunctionContext::function(){
    return shared_from_this();
}
<|endoftext|>


<commit_before>/***************************************************************************
 *   Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"
#ifndef _PreComp_
# include <QApplication>
# include <QGenericReturnArgument>
# include <QKeyEvent>
# include <QMessageBox>
# include <QMetaObject>
# include <QStatusBar>
# include <QThread>
# include <QTime>
# include <QTimer>
#endif

#include "ProgressBar.h"
#include "MainWindow.h"
#include "WaitCursor.h"

using namespace Gui;

namespace Gui {
struct SequencerPrivate
{
    ProgressBar* bar;
    WaitCursor* waitCursor;
    QTime measureTime;
    QTime progressTime;
    QString text;
    bool guiThread;
};

struct ProgressBarPrivate
{
    QTimer* delayShowTimer;
    int minimumDuration;
    int observeEventFilter;

    bool isModalDialog(QObject* o) const
    {
        QWidget* parent = qobject_cast<QWidget*>(o);
        while (parent) {
            QMessageBox* dlg = qobject_cast<QMessageBox*>(parent);
            if (dlg && dlg->isModal())
                return true;
            parent = parent->parentWidget();
        }

        return false;
    }
};
}

Sequencer* Sequencer::_pclSingleton = 0;

Sequencer* Sequencer::instance()
{
    // not initialized?
    if (!_pclSingleton)
    {
        _pclSingleton = new Sequencer();
    }

    return _pclSingleton;
}

Sequencer::Sequencer ()
{
    d = new SequencerPrivate;
    d->bar = 0;
    d->waitCursor = 0;
    d->guiThread = true;
}

Sequencer::~Sequencer ()
{
    delete d;
}

void Sequencer::pause()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread)
        return;

    // allow key handling of dialog and restore cursor
    d->bar->leaveControlEvents();
    d->waitCursor->restoreCursor();
    QApplication::setOverrideCursor(Qt::ArrowCursor);
}

void Sequencer::resume()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread)
        return;

    QApplication::restoreOverrideCursor();
    d->waitCursor->setWaitCursor();
    // must be called as last to get control before WaitCursor
    d->bar->enterControlEvents(); // grab again
}

void Sequencer::startStep()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread) {
        d->guiThread = false;
        d->bar->setRange(0, (int)nTotalSteps);
        if (nTotalSteps == 0) {
            d->progressTime.start();
        }
        d->measureTime.start();
        QMetaObject::invokeMethod(d->bar, "aboutToShow", Qt::QueuedConnection);
    }
    else {
        d->guiThread = true;
        d->bar->setRange(0, (int)nTotalSteps);
        if (nTotalSteps == 0) {
            d->progressTime.start();
        }

        d->measureTime.start();
        d->waitCursor = new Gui::WaitCursor;
        d->bar->enterControlEvents();
        d->bar->aboutToShow();
    }
}

void Sequencer::nextStep(bool canAbort)
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread) {
        setValue((int)nProgress+1);
    }
    else {
        if (wasCanceled() && canAbort) {
            // restore cursor
            pause();
            bool ok = d->bar->canAbort();
            // continue and show up wait cursor if needed
            resume();

            // force to abort the operation
            if ( ok ) {
                abort();
            } else {
                rejectCancel();
                setValue((int)nProgress+1);
            }
        }
        else {
            setValue((int)nProgress+1);
        }
    }
}

void Sequencer::setProgress(size_t step)
{
    d->bar->show();
    setValue((int)step);
}

void Sequencer::setValue(int step)
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    // if number of total steps is unknown then increment only by one
    if (nTotalSteps == 0) {
        int elapsed = d->progressTime.elapsed();
        // allow an update every 500 milliseconds only
        if (elapsed > 500) {
            d->progressTime.restart();
            if (thr != currentThread) {
                QMetaObject::invokeMethod(d->bar, "setValue", Qt::/*Blocking*/QueuedConnection,
                    QGenericReturnArgument(), Q_ARG(int,d->bar->value()+1));
            }
            else {
                d->bar->setValue(d->bar->value()+1);
                qApp->processEvents();
            }
        }
    }
    else {
        if (thr != currentThread) {
            QMetaObject::invokeMethod(d->bar, "setValue", Qt::/*Blocking*/QueuedConnection,
                QGenericReturnArgument(), Q_ARG(int,step));
            if (d->bar->isVisible())
                showRemainingTime();
        }
        else {
            int elapsed = d->progressTime.restart();
            if (elapsed > 500) {
                d->bar->setValue(step);
                if (d->bar->isVisible())
                    showRemainingTime();
                d->bar->resetObserveEventFilter();
                qApp->processEvents();
            }
        }
    }
}

void Sequencer::showRemainingTime()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread

    int elapsed = d->measureTime.elapsed();
    int progress = d->bar->value();
    int totalSteps = d->bar->maximum() - d->bar->minimum();

    QString txt = d->text;
    // More than 5 percent complete or more than 5 secs have elapsed.
    if (progress * 20 > totalSteps || elapsed > 5000) {
        int rest = (int) ( (double) totalSteps/progress * elapsed ) - elapsed;

        // more than 1 secs have elapsed and at least 100 ms are remaining
        if (elapsed > 1000 && rest > 100) {
            QTime time( 0,0, 0);
            time = time.addSecs( rest/1000 );
            QString remain = Gui::ProgressBar::tr("Remaining: %1").arg(time.toString());
            QString status = QString::fromLatin1("%1\t[%2]").arg(txt).arg(remain);

            if (thr != currentThread) {
                QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
                    Qt::/*Blocking*/QueuedConnection,
                    QGenericReturnArgument(),
                    Q_ARG(QString,status));
            }
            else {
                getMainWindow()->showMessage(status);
            }
        }
    }
}

void Sequencer::resetData()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread) {
        QMetaObject::invokeMethod(d->bar, "reset", Qt::QueuedConnection);
        QMetaObject::invokeMethod(d->bar, "hide", Qt::QueuedConnection);
        QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
            Qt::/*Blocking*/QueuedConnection,
            QGenericReturnArgument(),
            Q_ARG(QString,QString()));
        QMetaObject::invokeMethod(getMainWindow(), "setPaneText",
            Qt::/*Blocking*/QueuedConnection,
            QGenericReturnArgument(),
            Q_ARG(int,1),
            Q_ARG(QString,QString()));
    }
    else {
        d->bar->reset();
        // Note: Under Qt 4.1.4 this forces to run QWindowsStyle::eventFilter() twice
        // handling the same event thus a warning is printed. Possibly, this is a bug
        // in Qt. The message is QEventDispatcherUNIX::unregisterTimer: invalid argument.
        d->bar->hide();
        delete d->waitCursor;
        d->waitCursor = 0;
        d->bar->leaveControlEvents();
        getMainWindow()->setPaneText(1, QString());
        getMainWindow()->showMessage(QString());
    }

    SequencerBase::resetData();
}

void Sequencer::abort()
{
    //resets
    resetData();
    Base::AbortException exc("Aborting...");
    throw exc;
}

void Sequencer::setText (const char* pszTxt)
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread

    // print message to the statusbar
    d->text = pszTxt ? QString::fromUtf8(pszTxt) : QLatin1String("");
    if (thr != currentThread) {
        QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
            Qt::/*Blocking*/QueuedConnection,
            QGenericReturnArgument(),
            Q_ARG(QString,d->text));
    }
    else {
        getMainWindow()->showMessage(d->text);
    }
}

bool Sequencer::isBlocking() const
{
    return d->guiThread;
}

QProgressBar* Sequencer::getProgressBar(QWidget* parent)
{
    if (!d->bar)
        d->bar = new ProgressBar(this, parent);
    return d->bar;
}

// -------------------------------------------------------

/* TRANSLATOR Gui::ProgressBar */

ProgressBar::ProgressBar (Sequencer* s, QWidget * parent)
    : QProgressBar(parent), sequencer(s)
{
    d = new Gui::ProgressBarPrivate;
    d->minimumDuration = 2000; // 2 seconds
    d->delayShowTimer = new QTimer(this);
    d->delayShowTimer->setSingleShot(true);
    connect(d->delayShowTimer, SIGNAL(timeout()), this, SLOT(delayedShow()));
    d->observeEventFilter = 0;

    setFixedWidth(120);

    // write percentage to the center
    setAlignment(Qt::AlignHCenter);
    hide();
}

ProgressBar::~ProgressBar ()
{
    disconnect(d->delayShowTimer, SIGNAL(timeout()), this, SLOT(delayedShow()));
    delete d->delayShowTimer;
    delete d;
}

int ProgressBar::minimumDuration() const
{
    return d->minimumDuration;
}

void ProgressBar::setMinimumDuration (int ms)
{
    if (value() == 0)
    {
        d->delayShowTimer->stop();
        d->delayShowTimer->start(ms);
    }

    d->minimumDuration = ms;
}

void ProgressBar::aboutToShow()
{
    // delay showing the bar
    d->delayShowTimer->start(d->minimumDuration);
}

void ProgressBar::delayedShow()
{
    if (!isVisible() && !sequencer->wasCanceled() && sequencer->isRunning())
        show();
}

bool ProgressBar::canAbort() const
{
    int ret = QMessageBox::question(getMainWindow(),tr("Aborting"),
    tr("Do you really want to abort the operation?"),  QMessageBox::Yes,
    QMessageBox::No|QMessageBox::Default);

    return (ret == QMessageBox::Yes) ? true : false;
}

void ProgressBar::showEvent(QShowEvent* e)
{
    QProgressBar::showEvent(e);
    d->delayShowTimer->stop();
}

void ProgressBar::hideEvent(QHideEvent* e)
{
    QProgressBar::hideEvent(e);
    d->delayShowTimer->stop();
}

void ProgressBar::resetObserveEventFilter()
{
    d->observeEventFilter = 0;
}

void ProgressBar::enterControlEvents()
{
    qApp->installEventFilter(this);

    // Make sure that we get the key events, otherwise the Inventor viewer usurps the key events
    // This also disables accelerators.
    grabKeyboard();
}

void ProgressBar::leaveControlEvents()
{
    qApp->removeEventFilter(this);

    // relase the keyboard again
    releaseKeyboard();
}

bool ProgressBar::eventFilter(QObject* o, QEvent* e)
{
    if (sequencer->isRunning() && e != 0) {
        switch ( e->type() )
        {
        // check for ESC
        case QEvent::KeyPress:
            {
                QKeyEvent* ke = (QKeyEvent*)e;
                if (ke->key() == Qt::Key_Escape) {
                    // eventFilter() was called from the application 50 times without performing a new step (app could hang)
                    if (d->observeEventFilter > 50) {
                        // tries to unlock the application if it hangs (propably due to incorrect usage of Base::Sequencer)
                        if (ke->modifiers() & (Qt::ControlModifier | Qt::AltModifier)) {
                            sequencer->resetData();
                            return true;
                        }
                    }

                    // cancel the operation
                    sequencer->tryToCancel();
                }

                return true;
            }   break;

        // ignore alle these events
        case QEvent::KeyRelease:
        case QEvent::Enter:
        case QEvent::Leave:
        case QEvent::MouseButtonDblClick:
        case QEvent::ContextMenu:
            {
                return true;
            }   break;

        // special case if the main window's close button was pressed
        case QEvent::Close:
            {
                // avoid to exit while app is working
                // note: all other widget types are allowed to be closed anyway
                if (o == getMainWindow()) {
                    e->ignore();
                    return true;
                }
            }   break;

        // do a system beep and ignore the event
        case QEvent::MouseButtonPress:
            {
                if (d->isModalDialog(o))
                    return false;
                QApplication::beep();
                return true;
            }   break;

        default:
            {
            }   break;
        }

        d->observeEventFilter++;
    }

    return QProgressBar::eventFilter(o, e);
}

#include "moc_ProgressBar.cpp"
<commit_msg>Fix ProgressBar rendering following STEP reader enhancement<commit_after>/***************************************************************************
 *   Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"
#ifndef _PreComp_
# include <QApplication>
# include <QGenericReturnArgument>
# include <QKeyEvent>
# include <QMessageBox>
# include <QMetaObject>
# include <QStatusBar>
# include <QThread>
# include <QTime>
# include <QTimer>
#endif

#include "ProgressBar.h"
#include "MainWindow.h"
#include "WaitCursor.h"

using namespace Gui;

namespace Gui {
struct SequencerPrivate
{
    ProgressBar* bar;
    WaitCursor* waitCursor;
    QTime measureTime;
    QTime progressTime;
    QString text;
    bool guiThread;
};

struct ProgressBarPrivate
{
    QTimer* delayShowTimer;
    int minimumDuration;
    int observeEventFilter;

    bool isModalDialog(QObject* o) const
    {
        QWidget* parent = qobject_cast<QWidget*>(o);
        while (parent) {
            QMessageBox* dlg = qobject_cast<QMessageBox*>(parent);
            if (dlg && dlg->isModal())
                return true;
            parent = parent->parentWidget();
        }

        return false;
    }
};
}

Sequencer* Sequencer::_pclSingleton = 0;

Sequencer* Sequencer::instance()
{
    // not initialized?
    if (!_pclSingleton)
    {
        _pclSingleton = new Sequencer();
    }

    return _pclSingleton;
}

Sequencer::Sequencer ()
{
    d = new SequencerPrivate;
    d->bar = 0;
    d->waitCursor = 0;
    d->guiThread = true;
}

Sequencer::~Sequencer ()
{
    delete d;
}

void Sequencer::pause()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread)
        return;

    // allow key handling of dialog and restore cursor
    d->bar->leaveControlEvents();
    d->waitCursor->restoreCursor();
    QApplication::setOverrideCursor(Qt::ArrowCursor);
}

void Sequencer::resume()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread)
        return;

    QApplication::restoreOverrideCursor();
    d->waitCursor->setWaitCursor();
    // must be called as last to get control before WaitCursor
    d->bar->enterControlEvents(); // grab again
}

void Sequencer::startStep()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread) {
        d->guiThread = false;
        d->bar->setRange(0, (int)nTotalSteps);
        if (nTotalSteps == 0) {
            d->progressTime.start();
        }
        d->measureTime.start();
        QMetaObject::invokeMethod(d->bar, "aboutToShow", Qt::QueuedConnection);
    }
    else {
        d->guiThread = true;
        d->bar->setRange(0, (int)nTotalSteps);
        if (nTotalSteps == 0) {
            d->progressTime.start();
        }

        d->measureTime.start();
        d->waitCursor = new Gui::WaitCursor;
        d->bar->enterControlEvents();
        d->bar->aboutToShow();
    }
}

void Sequencer::nextStep(bool canAbort)
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread) {
        setValue((int)nProgress+1);
    }
    else {
        if (wasCanceled() && canAbort) {
            // restore cursor
            pause();
            bool ok = d->bar->canAbort();
            // continue and show up wait cursor if needed
            resume();

            // force to abort the operation
            if ( ok ) {
                abort();
            } else {
                rejectCancel();
                setValue((int)nProgress+1);
            }
        }
        else {
            setValue((int)nProgress+1);
        }
    }
}

void Sequencer::setProgress(size_t step)
{
    d->bar->show();
    setValue((int)step);
}

void Sequencer::setValue(int step)
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    // if number of total steps is unknown then increment only by one
    if (nTotalSteps == 0) {
        int elapsed = d->progressTime.elapsed();
        // allow an update every 500 milliseconds only
        if (elapsed > 500) {
            d->progressTime.restart();
            if (thr != currentThread) {
                QMetaObject::invokeMethod(d->bar, "setValue", Qt::/*Blocking*/QueuedConnection,
                    QGenericReturnArgument(), Q_ARG(int,d->bar->value()+1));
            }
            else {
                d->bar->setValue(d->bar->value()+1);
                qApp->processEvents();
            }
        }
    }
    else {
        int elapsed = d->progressTime.elapsed();
        if (elapsed > 100) {
            d->progressTime.restart();
            if (thr != currentThread) {
                QMetaObject::invokeMethod(d->bar, "setValue", Qt::/*Blocking*/QueuedConnection,
                QGenericReturnArgument(), Q_ARG(int,step));
                if (d->bar->isVisible())
                    showRemainingTime();
            }
            else {
                d->bar->setValue(step);
                if (d->bar->isVisible())
                    showRemainingTime();
                d->bar->resetObserveEventFilter();
                qApp->processEvents();
            }
        }
    }
}

void Sequencer::showRemainingTime()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread

    int elapsed = d->measureTime.elapsed();
    int progress = d->bar->value();
    int totalSteps = d->bar->maximum() - d->bar->minimum();

    QString txt = d->text;
    // More than 5 percent complete or more than 5 secs have elapsed.
    if (progress * 20 > totalSteps || elapsed > 5000) {
        int rest = (int) ( (double) totalSteps/progress * elapsed ) - elapsed;

        // more than 1 secs have elapsed and at least 100 ms are remaining
        if (elapsed > 1000 && rest > 100) {
            QTime time( 0,0, 0);
            time = time.addSecs( rest/1000 );
            QString remain = Gui::ProgressBar::tr("Remaining: %1").arg(time.toString());
            QString status = QString::fromLatin1("%1\t[%2]").arg(txt).arg(remain);

            if (thr != currentThread) {
                QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
                    Qt::/*Blocking*/QueuedConnection,
                    QGenericReturnArgument(),
                    Q_ARG(QString,status));
            }
            else {
                getMainWindow()->showMessage(status);
            }
        }
    }
}

void Sequencer::resetData()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread) {
        QMetaObject::invokeMethod(d->bar, "reset", Qt::QueuedConnection);
        QMetaObject::invokeMethod(d->bar, "hide", Qt::QueuedConnection);
        QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
            Qt::/*Blocking*/QueuedConnection,
            QGenericReturnArgument(),
            Q_ARG(QString,QString()));
        QMetaObject::invokeMethod(getMainWindow(), "setPaneText",
            Qt::/*Blocking*/QueuedConnection,
            QGenericReturnArgument(),
            Q_ARG(int,1),
            Q_ARG(QString,QString()));
    }
    else {
        d->bar->reset();
        // Note: Under Qt 4.1.4 this forces to run QWindowsStyle::eventFilter() twice
        // handling the same event thus a warning is printed. Possibly, this is a bug
        // in Qt. The message is QEventDispatcherUNIX::unregisterTimer: invalid argument.
        d->bar->hide();
        delete d->waitCursor;
        d->waitCursor = 0;
        d->bar->leaveControlEvents();
        getMainWindow()->setPaneText(1, QString());
        getMainWindow()->showMessage(QString());
    }

    SequencerBase::resetData();
}

void Sequencer::abort()
{
    //resets
    resetData();
    Base::AbortException exc("Aborting...");
    throw exc;
}

void Sequencer::setText (const char* pszTxt)
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread

    // print message to the statusbar
    d->text = pszTxt ? QString::fromUtf8(pszTxt) : QLatin1String("");
    if (thr != currentThread) {
        QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
            Qt::/*Blocking*/QueuedConnection,
            QGenericReturnArgument(),
            Q_ARG(QString,d->text));
    }
    else {
        getMainWindow()->showMessage(d->text);
    }
}

bool Sequencer::isBlocking() const
{
    return d->guiThread;
}

QProgressBar* Sequencer::getProgressBar(QWidget* parent)
{
    if (!d->bar)
        d->bar = new ProgressBar(this, parent);
    return d->bar;
}

// -------------------------------------------------------

/* TRANSLATOR Gui::ProgressBar */

ProgressBar::ProgressBar (Sequencer* s, QWidget * parent)
    : QProgressBar(parent), sequencer(s)
{
    d = new Gui::ProgressBarPrivate;
    d->minimumDuration = 2000; // 2 seconds
    d->delayShowTimer = new QTimer(this);
    d->delayShowTimer->setSingleShot(true);
    connect(d->delayShowTimer, SIGNAL(timeout()), this, SLOT(delayedShow()));
    d->observeEventFilter = 0;

    setFixedWidth(120);

    // write percentage to the center
    setAlignment(Qt::AlignHCenter);
    hide();
}

ProgressBar::~ProgressBar ()
{
    disconnect(d->delayShowTimer, SIGNAL(timeout()), this, SLOT(delayedShow()));
    delete d->delayShowTimer;
    delete d;
}

int ProgressBar::minimumDuration() const
{
    return d->minimumDuration;
}

void ProgressBar::setMinimumDuration (int ms)
{
    if (value() == 0)
    {
        d->delayShowTimer->stop();
        d->delayShowTimer->start(ms);
    }

    d->minimumDuration = ms;
}

void ProgressBar::aboutToShow()
{
    // delay showing the bar
    d->delayShowTimer->start(d->minimumDuration);
}

void ProgressBar::delayedShow()
{
    if (!isVisible() && !sequencer->wasCanceled() && sequencer->isRunning())
        show();
}

bool ProgressBar::canAbort() const
{
    int ret = QMessageBox::question(getMainWindow(),tr("Aborting"),
    tr("Do you really want to abort the operation?"),  QMessageBox::Yes,
    QMessageBox::No|QMessageBox::Default);

    return (ret == QMessageBox::Yes) ? true : false;
}

void ProgressBar::showEvent(QShowEvent* e)
{
    QProgressBar::showEvent(e);
    d->delayShowTimer->stop();
}

void ProgressBar::hideEvent(QHideEvent* e)
{
    QProgressBar::hideEvent(e);
    d->delayShowTimer->stop();
}

void ProgressBar::resetObserveEventFilter()
{
    d->observeEventFilter = 0;
}

void ProgressBar::enterControlEvents()
{
    qApp->installEventFilter(this);

    // Make sure that we get the key events, otherwise the Inventor viewer usurps the key events
    // This also disables accelerators.
    grabKeyboard();
}

void ProgressBar::leaveControlEvents()
{
    qApp->removeEventFilter(this);

    // relase the keyboard again
    releaseKeyboard();
}

bool ProgressBar::eventFilter(QObject* o, QEvent* e)
{
    if (sequencer->isRunning() && e != 0) {
        switch ( e->type() )
        {
        // check for ESC
        case QEvent::KeyPress:
            {
                QKeyEvent* ke = (QKeyEvent*)e;
                if (ke->key() == Qt::Key_Escape) {
                    // eventFilter() was called from the application 50 times without performing a new step (app could hang)
                    if (d->observeEventFilter > 50) {
                        // tries to unlock the application if it hangs (propably due to incorrect usage of Base::Sequencer)
                        if (ke->modifiers() & (Qt::ControlModifier | Qt::AltModifier)) {
                            sequencer->resetData();
                            return true;
                        }
                    }

                    // cancel the operation
                    sequencer->tryToCancel();
                }

                return true;
            }   break;

        // ignore alle these events
        case QEvent::KeyRelease:
        case QEvent::Enter:
        case QEvent::Leave:
        case QEvent::MouseButtonDblClick:
        case QEvent::ContextMenu:
            {
                return true;
            }   break;

        // special case if the main window's close button was pressed
        case QEvent::Close:
            {
                // avoid to exit while app is working
                // note: all other widget types are allowed to be closed anyway
                if (o == getMainWindow()) {
                    e->ignore();
                    return true;
                }
            }   break;

        // do a system beep and ignore the event
        case QEvent::MouseButtonPress:
            {
                if (d->isModalDialog(o))
                    return false;
                QApplication::beep();
                return true;
            }   break;

        default:
            {
            }   break;
        }

        d->observeEventFilter++;
    }

    return QProgressBar::eventFilter(o, e);
}

#include "moc_ProgressBar.cpp"
<|endoftext|>


<commit_before>#include <iostream>
#include <sstream>
#include <vector>

#include <hs/hs_compile.h>

#include "HyperscanEngine.h"

using namespace regexbench;

static int onMatch(unsigned int, unsigned long long,
                   unsigned long long, unsigned int, void *ctx) {
  size_t &nmatches = *static_cast<size_t *>(ctx);
  nmatches++;
  return 0;
}

HyperscanEngine::HyperscanEngine()
    : db(nullptr), scratch(nullptr),
      platform{HS_TUNE_FAMILY_GENERIC, 0, 0, 0}, nsessions(0) {
}

HyperscanEngine::~HyperscanEngine() {
  hs_free_database(db);
  hs_free_scratch(scratch);
}

void HyperscanEngine::compile(const std::vector<Rule> &rules) {
  std::vector<const char *> exps;
  std::vector<unsigned> flags;
  std::vector<unsigned> ids;
  for (const auto &rule : rules) {
    exps.push_back(rule.getRegexp().data());
    ids.push_back(static_cast<unsigned>(rule.getID()));
    unsigned flag = HS_FLAG_ALLOWEMPTY;
    if (rule.isSet(MOD_CASELESS)) flag |= HS_FLAG_CASELESS;
    if (rule.isSet(MOD_MULTILINE)) flag |= HS_FLAG_MULTILINE;
    if (rule.isSet(MOD_DOTALL)) flag |= HS_FLAG_DOTALL;
    flags.push_back(flag);
  }

  hs_compile_error_t *err;
  auto mode = HS_MODE_BLOCK;

  if (nsessions)
    mode = HS_MODE_STREAM;
  auto result = hs_compile_multi(exps.data(), flags.data(), ids.data(),
                                 static_cast<unsigned>(exps.size()),
                                 HS_MODE_STREAM, &platform, &db, &err);
  if (result != HS_SUCCESS) {
    std::stringstream msg;
    msg << err->message << " (ruleid: " << err->expression << ')';
    std::runtime_error error(msg.str());
    hs_free_compile_error(err);
    throw error;
  }

  result = hs_alloc_scratch(db, &scratch);
  if (result != HS_SUCCESS)
    throw std::bad_alloc();
}

HyperscanEngineStream::~HyperscanEngineStream() {
  for (size_t i = 0; i < nsessions; i++) {
    hs_close_stream(streams[i], scratch, onMatch, nullptr);
  }
}

size_t HyperscanEngine::match(const char *data, size_t len, size_t) {
  size_t nmatches = 0;
  hs_scan(db, data, static_cast<unsigned>(len), 0, scratch,
          onMatch, &nmatches);
  return nmatches > 0;
}

void HyperscanEngineStream::init(size_t nsessions_) {
  nsessions = nsessions_;
  streams.reset(new hs_stream_t*[nsessions]);

  for (size_t i = 0; i < nsessions; i++) {
    hs_open_stream(db, 0, &streams[i]);
  }
}

size_t HyperscanEngineStream::match(const char *data, size_t len, size_t sid) {
  size_t nmatches = 0;
  auto ret = hs_scan_stream(streams[sid], data, static_cast<unsigned>(len), 0, scratch,
                            onMatch, &nmatches);
  return nmatches > 0;
}
<commit_msg>Remove debugging code<commit_after>#include <sstream>
#include <vector>

#include <hs/hs_compile.h>

#include "HyperscanEngine.h"

using namespace regexbench;

static int onMatch(unsigned int, unsigned long long,
                   unsigned long long, unsigned int, void *ctx) {
  size_t &nmatches = *static_cast<size_t *>(ctx);
  nmatches++;
  return 0;
}

HyperscanEngine::HyperscanEngine()
    : db(nullptr), scratch(nullptr),
      platform{HS_TUNE_FAMILY_GENERIC, 0, 0, 0}, nsessions(0) {
}

HyperscanEngine::~HyperscanEngine() {
  hs_free_database(db);
  hs_free_scratch(scratch);
}

void HyperscanEngine::compile(const std::vector<Rule> &rules) {
  std::vector<const char *> exps;
  std::vector<unsigned> flags;
  std::vector<unsigned> ids;
  for (const auto &rule : rules) {
    exps.push_back(rule.getRegexp().data());
    ids.push_back(static_cast<unsigned>(rule.getID()));
    unsigned flag = HS_FLAG_ALLOWEMPTY;
    if (rule.isSet(MOD_CASELESS)) flag |= HS_FLAG_CASELESS;
    if (rule.isSet(MOD_MULTILINE)) flag |= HS_FLAG_MULTILINE;
    if (rule.isSet(MOD_DOTALL)) flag |= HS_FLAG_DOTALL;
    flags.push_back(flag);
  }

  hs_compile_error_t *err;
  auto mode = HS_MODE_BLOCK;

  if (nsessions)
    mode = HS_MODE_STREAM;
  auto result = hs_compile_multi(exps.data(), flags.data(), ids.data(),
                                 static_cast<unsigned>(exps.size()),
                                 HS_MODE_STREAM, &platform, &db, &err);
  if (result != HS_SUCCESS) {
    std::stringstream msg;
    msg << err->message << " (ruleid: " << err->expression << ')';
    std::runtime_error error(msg.str());
    hs_free_compile_error(err);
    throw error;
  }

  result = hs_alloc_scratch(db, &scratch);
  if (result != HS_SUCCESS)
    throw std::bad_alloc();
}

HyperscanEngineStream::~HyperscanEngineStream() {
  for (size_t i = 0; i < nsessions; i++) {
    hs_close_stream(streams[i], scratch, onMatch, nullptr);
  }
}

size_t HyperscanEngine::match(const char *data, size_t len, size_t) {
  size_t nmatches = 0;
  hs_scan(db, data, static_cast<unsigned>(len), 0, scratch,
          onMatch, &nmatches);
  return nmatches > 0;
}

void HyperscanEngineStream::init(size_t nsessions_) {
  nsessions = nsessions_;
  streams.reset(new hs_stream_t*[nsessions]);

  for (size_t i = 0; i < nsessions; i++) {
    hs_open_stream(db, 0, &streams[i]);
  }
}

size_t HyperscanEngineStream::match(const char *data, size_t len, size_t sid) {
  size_t nmatches = 0;
  auto ret = hs_scan_stream(streams[sid], data, static_cast<unsigned>(len), 0, scratch,
                            onMatch, &nmatches);
  return nmatches > 0;
}
<|endoftext|>


<commit_before>
// LineBlockTracer.cpp

// Implements the cLineBlockTracer class representing a cBlockTracer that traces along a straight line between two points

#include "Globals.h"
#include "LineBlockTracer.h"
#include "Vector3.h"
#include "World.h"
#include "Chunk.h"
#include "BoundingBox.h"

cLineBlockTracer::cLineBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks) :
	super(a_World, a_Callbacks),
	m_StartX(0.0),
	m_StartY(0.0),
	m_StartZ(0.0),
	m_EndX(0.0),
	m_EndY(0.0),
	m_EndZ(0.0),
	m_DiffX(0.0),
	m_DiffY(0.0),
	m_DiffZ(0.0),
	m_DirX(0),
	m_DirY(0),
	m_DirZ(0),
	m_CurrentX(0),
	m_CurrentY(0),
	m_CurrentZ(0),
	m_CurrentFace(BLOCK_FACE_NONE)
{
}

bool cLineBlockTracer::Trace(cWorld & a_World, cBlockTracer::cCallbacks & a_Callbacks, const Vector3d & a_Start, const Vector3d & a_End)
{
	cLineBlockTracer Tracer(a_World, a_Callbacks);
	return Tracer.Trace(a_Start.x, a_Start.y, a_Start.z, a_End.x, a_End.y, a_End.z);
}

bool cLineBlockTracer::LineOfSightTrace(cWorld & a_World, const Vector3d & a_Start, const Vector3d & a_End, int a_Sight)
{
	static class LineOfSightCallbacks:
		public cLineBlockTracer::cCallbacks
	{
		bool m_IsAirOpaque;
		bool m_IsWaterOpaque;
		bool m_IsLavaOpaque;
	public:
		LineOfSightCallbacks(bool a_IsAirOpaque, bool a_IsWaterOpaque, bool a_IsLavaOpaque):
			m_IsAirOpaque(a_IsAirOpaque),
			m_IsWaterOpaque(a_IsWaterOpaque),
			m_IsLavaOpaque(a_IsLavaOpaque)
		{}

		virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, eBlockFace a_EntryFace) override
		{
			switch (a_BlockType)
			{
				case E_BLOCK_AIR:              return m_IsAirOpaque;
				case E_BLOCK_LAVA:             return m_IsLavaOpaque;
				case E_BLOCK_STATIONARY_LAVA:  return m_IsLavaOpaque;
				case E_BLOCK_STATIONARY_WATER: return m_IsWaterOpaque;
				case E_BLOCK_WATER:            return m_IsWaterOpaque;
				default: return true;
			}
		}
	} callbacks(
		(a_Sight & losAir) == 0,
		(a_Sight & losWater) == 0,
		(a_Sight & losLava) == 0
	);
	return Trace(a_World, callbacks, a_Start, a_End);
}

bool cLineBlockTracer::FirstSolidHitTrace(
	cWorld & a_World,
	const Vector3d & a_Start, const Vector3d & a_End,
	Vector3d & a_HitCoords,
	Vector3i & a_HitBlockCoords, eBlockFace & a_HitBlockFace
)
{
	class cSolidHitCallbacks:
		public cCallbacks
	{
	public:
		cSolidHitCallbacks(const Vector3d & a_CBStart, const Vector3d & a_CBEnd, Vector3d & a_CBHitCoords, Vector3i & a_CBHitBlockCoords, eBlockFace & a_CBHitBlockFace):
			m_Start(a_CBStart),
			m_End(a_CBEnd),
			m_HitCoords(a_CBHitCoords),
			m_HitBlockCoords(a_CBHitBlockCoords),
			m_HitBlockFace(a_CBHitBlockFace)
		{
		}

		virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, eBlockFace a_EntryFace) override
		{
			if (!cBlockInfo::IsSolid(a_BlockType))
			{
				return false;
			}

			// We hit a solid block, calculate the exact hit coords and abort trace:
			m_HitBlockCoords.Set(a_BlockX, a_BlockY, a_BlockZ);
			m_HitBlockFace = a_EntryFace;
			cBoundingBox bb(a_BlockX, a_BlockX + 1, a_BlockY, a_BlockY + 1, a_BlockZ, a_BlockZ + 1);  // Bounding box of the block hit
			double LineCoeff = 0;  // Used to calculate where along the line an intersection with the bounding box occurs
			eBlockFace Face;  // Face hit
			VERIFY(bb.CalcLineIntersection(m_Start, m_End, LineCoeff, Face));
			m_HitCoords = m_Start + (m_End - m_Start) * LineCoeff;  // Point where projectile goes into the hit block
			return true;
		}

	protected:
		const Vector3d & m_Start;
		const Vector3d & m_End;
		Vector3d & m_HitCoords;
		Vector3i & m_HitBlockCoords;
		eBlockFace & m_HitBlockFace;
	} callbacks(a_Start, a_End, a_HitCoords, a_HitBlockCoords, a_HitBlockFace);
	return !Trace(a_World, callbacks, a_Start, a_End);
}

bool cLineBlockTracer::Trace(cWorld & a_World, cBlockTracer::cCallbacks &a_Callbacks, double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ)
{
	cLineBlockTracer Tracer(a_World, a_Callbacks);
	return Tracer.Trace(a_StartX, a_StartY, a_StartZ, a_EndX, a_EndY, a_EndZ);
}

bool cLineBlockTracer::Trace(double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ)
{
	// Initialize the member veriables:
	m_StartX = a_StartX;
	m_StartY = a_StartY;
	m_StartZ = a_StartZ;
	m_EndX = a_EndX;
	m_EndY = a_EndY;
	m_EndZ = a_EndZ;
	m_DirX = (m_StartX < m_EndX) ? 1 : -1;
	m_DirY = (m_StartY < m_EndY) ? 1 : -1;
	m_DirZ = (m_StartZ < m_EndZ) ? 1 : -1;
	m_CurrentFace = BLOCK_FACE_NONE;

	// Check the start coords, adjust into the world:
	if (m_StartY < 0)
	{
		if (m_EndY < 0)
		{
			// Nothing to trace
			m_Callbacks->OnNoMoreHits();
			return true;
		}
		FixStartBelowWorld();
		m_Callbacks->OnIntoWorld(m_StartX, m_StartY, m_StartZ);
	}
	else if (m_StartY >= cChunkDef::Height)
	{
		if (m_EndY >= cChunkDef::Height)
		{
			m_Callbacks->OnNoMoreHits();
			return true;
		}
		FixStartAboveWorld();
		m_Callbacks->OnIntoWorld(m_StartX, m_StartY, m_StartZ);
	}

	m_CurrentX = FloorC(m_StartX);
	m_CurrentY = FloorC(m_StartY);
	m_CurrentZ = FloorC(m_StartZ);

	m_DiffX = m_EndX - m_StartX;
	m_DiffY = m_EndY - m_StartY;
	m_DiffZ = m_EndZ - m_StartZ;

	// The actual trace is handled with ChunkMapCS locked by calling our Item() for the specified chunk
	int BlockX = FloorC(m_StartX);
	int BlockZ = FloorC(m_StartZ);
	int ChunkX, ChunkZ;
	cChunkDef::BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ);
	return m_World->DoWithChunk(ChunkX, ChunkZ, *this);
}

void cLineBlockTracer::FixStartAboveWorld(void)
{
	// We must set the start Y to less than cChunkDef::Height so that it is considered inside the world later on
	// Therefore we use an EPS-offset from the height, as small as reasonably possible.
	const double Height = static_cast<double>(cChunkDef::Height) - 0.00001;
	CalcXZIntersection(Height, m_StartX, m_StartZ);
	m_StartY = Height;
}

void cLineBlockTracer::FixStartBelowWorld(void)
{
	CalcXZIntersection(0, m_StartX, m_StartZ);
	m_StartY = 0;
}

void cLineBlockTracer::CalcXZIntersection(double a_Y, double & a_IntersectX, double & a_IntersectZ)
{
	double Ratio = (m_StartY - a_Y) / (m_StartY - m_EndY);
	a_IntersectX = m_StartX + (m_EndX - m_StartX) * Ratio;
	a_IntersectZ = m_StartZ + (m_EndZ - m_StartZ) * Ratio;
}

bool cLineBlockTracer::MoveToNextBlock(void)
{
	// Find out which of the current block's walls gets hit by the path:
	static const double EPS = 0.00001;
	enum
	{
		dirNONE,
		dirX,
		dirY,
		dirZ,
	} Direction = dirNONE;

	// Calculate the next YZ wall hit:
	double Coeff = 1;
	if (std::abs(m_DiffX) > EPS)
	{
		double DestX = (m_DirX > 0) ? (m_CurrentX + 1) : m_CurrentX;
		double CoeffX = (DestX - m_StartX) / m_DiffX;
		if (CoeffX <= 1)  // We need to include equality for the last block in the trace
		{
			Coeff = CoeffX;
			Direction = dirX;
		}
	}

	// If the next XZ wall hit is closer, use it instead:
	if (std::abs(m_DiffY) > EPS)
	{
		double DestY = (m_DirY > 0) ? (m_CurrentY + 1) : m_CurrentY;
		double CoeffY = (DestY - m_StartY) / m_DiffY;
		if (CoeffY <= Coeff)  // We need to include equality for the last block in the trace
		{
			Coeff = CoeffY;
			Direction = dirY;
		}
	}

	// If the next XY wall hit is closer, use it instead:
	if (std::abs(m_DiffZ) > EPS)
	{
		double DestZ = (m_DirZ > 0) ? (m_CurrentZ + 1) : m_CurrentZ;
		double CoeffZ = (DestZ - m_StartZ) / m_DiffZ;
		if (CoeffZ <= Coeff)  // We need to include equality for the last block in the trace
		{
			Direction = dirZ;
		}
	}

	// Based on the wall hit, adjust the current coords
	switch (Direction)
	{
		case dirX:    m_CurrentX += m_DirX; m_CurrentFace = (m_DirX > 0) ? BLOCK_FACE_XM : BLOCK_FACE_XP; break;
		case dirY:    m_CurrentY += m_DirY; m_CurrentFace = (m_DirY > 0) ? BLOCK_FACE_YM : BLOCK_FACE_YP; break;
		case dirZ:    m_CurrentZ += m_DirZ; m_CurrentFace = (m_DirZ > 0) ? BLOCK_FACE_ZM : BLOCK_FACE_ZP; break;
		case dirNONE: return false;
	}
	return true;
}

bool cLineBlockTracer::Item(cChunk * a_Chunk)
{
	ASSERT((m_CurrentY >= 0) && (m_CurrentY < cChunkDef::Height));  // This should be provided by FixStartAboveWorld() / FixStartBelowWorld()

	// This is the actual line tracing loop.
	for (;;)
	{
		// Report the current block through the callbacks:
		if (a_Chunk == nullptr)
		{
			m_Callbacks->OnNoChunk();
			return false;
		}

		// Move to next block
		if (!MoveToNextBlock())
		{
			// We've reached the end
			m_Callbacks->OnNoMoreHits();
			return true;
		}

		if ((m_CurrentY < 0) || (m_CurrentY >= cChunkDef::Height))
		{
			// We've gone out of the world, that's the end of this trace
			double IntersectX, IntersectZ;
			CalcXZIntersection(m_CurrentY, IntersectX, IntersectZ);
			if (m_Callbacks->OnOutOfWorld(IntersectX, m_CurrentY, IntersectZ))
			{
				// The callback terminated the trace
				return false;
			}
			m_Callbacks->OnNoMoreHits();
			return true;
		}

		// Update the current chunk
		a_Chunk = a_Chunk->GetNeighborChunk(m_CurrentX, m_CurrentZ);
		if (a_Chunk == nullptr)
		{
			m_Callbacks->OnNoChunk();
			return false;
		}

		if (a_Chunk->IsValid())
		{
			BLOCKTYPE BlockType;
			NIBBLETYPE BlockMeta;
			int RelX = m_CurrentX - a_Chunk->GetPosX() * cChunkDef::Width;
			int RelZ = m_CurrentZ - a_Chunk->GetPosZ() * cChunkDef::Width;
			a_Chunk->GetBlockTypeMeta(RelX, m_CurrentY, RelZ, BlockType, BlockMeta);
			if (m_Callbacks->OnNextBlock(m_CurrentX, m_CurrentY, m_CurrentZ, BlockType, BlockMeta, m_CurrentFace))
			{
				// The callback terminated the trace
				return false;
			}
		}
		else if (m_Callbacks->OnNextBlockNoData(m_CurrentX, m_CurrentY, m_CurrentZ, m_CurrentFace))
		{
			// The callback terminated the trace
			return false;
		}
	}
}

<commit_msg>LineBlockTracer: Fixed a math rounding error failure.<commit_after>
// LineBlockTracer.cpp

// Implements the cLineBlockTracer class representing a cBlockTracer that traces along a straight line between two points

#include "Globals.h"
#include "LineBlockTracer.h"
#include "Vector3.h"
#include "World.h"
#include "Chunk.h"
#include "BoundingBox.h"

cLineBlockTracer::cLineBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks) :
	super(a_World, a_Callbacks),
	m_StartX(0.0),
	m_StartY(0.0),
	m_StartZ(0.0),
	m_EndX(0.0),
	m_EndY(0.0),
	m_EndZ(0.0),
	m_DiffX(0.0),
	m_DiffY(0.0),
	m_DiffZ(0.0),
	m_DirX(0),
	m_DirY(0),
	m_DirZ(0),
	m_CurrentX(0),
	m_CurrentY(0),
	m_CurrentZ(0),
	m_CurrentFace(BLOCK_FACE_NONE)
{
}

bool cLineBlockTracer::Trace(cWorld & a_World, cBlockTracer::cCallbacks & a_Callbacks, const Vector3d & a_Start, const Vector3d & a_End)
{
	cLineBlockTracer Tracer(a_World, a_Callbacks);
	return Tracer.Trace(a_Start.x, a_Start.y, a_Start.z, a_End.x, a_End.y, a_End.z);
}

bool cLineBlockTracer::LineOfSightTrace(cWorld & a_World, const Vector3d & a_Start, const Vector3d & a_End, int a_Sight)
{
	static class LineOfSightCallbacks:
		public cLineBlockTracer::cCallbacks
	{
		bool m_IsAirOpaque;
		bool m_IsWaterOpaque;
		bool m_IsLavaOpaque;
	public:
		LineOfSightCallbacks(bool a_IsAirOpaque, bool a_IsWaterOpaque, bool a_IsLavaOpaque):
			m_IsAirOpaque(a_IsAirOpaque),
			m_IsWaterOpaque(a_IsWaterOpaque),
			m_IsLavaOpaque(a_IsLavaOpaque)
		{}

		virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, eBlockFace a_EntryFace) override
		{
			switch (a_BlockType)
			{
				case E_BLOCK_AIR:              return m_IsAirOpaque;
				case E_BLOCK_LAVA:             return m_IsLavaOpaque;
				case E_BLOCK_STATIONARY_LAVA:  return m_IsLavaOpaque;
				case E_BLOCK_STATIONARY_WATER: return m_IsWaterOpaque;
				case E_BLOCK_WATER:            return m_IsWaterOpaque;
				default: return true;
			}
		}
	} callbacks(
		(a_Sight & losAir) == 0,
		(a_Sight & losWater) == 0,
		(a_Sight & losLava) == 0
	);
	return Trace(a_World, callbacks, a_Start, a_End);
}

bool cLineBlockTracer::FirstSolidHitTrace(
	cWorld & a_World,
	const Vector3d & a_Start, const Vector3d & a_End,
	Vector3d & a_HitCoords,
	Vector3i & a_HitBlockCoords, eBlockFace & a_HitBlockFace
)
{
	class cSolidHitCallbacks:
		public cCallbacks
	{
	public:
		cSolidHitCallbacks(const Vector3d & a_CBStart, const Vector3d & a_CBEnd, Vector3d & a_CBHitCoords, Vector3i & a_CBHitBlockCoords, eBlockFace & a_CBHitBlockFace):
			m_Start(a_CBStart),
			m_End(a_CBEnd),
			m_HitCoords(a_CBHitCoords),
			m_HitBlockCoords(a_CBHitBlockCoords),
			m_HitBlockFace(a_CBHitBlockFace)
		{
		}

		virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, eBlockFace a_EntryFace) override
		{
			if (!cBlockInfo::IsSolid(a_BlockType))
			{
				return false;
			}

			// We hit a solid block, calculate the exact hit coords and abort trace:
			m_HitBlockCoords.Set(a_BlockX, a_BlockY, a_BlockZ);
			m_HitBlockFace = a_EntryFace;
			cBoundingBox bb(a_BlockX, a_BlockX + 1, a_BlockY, a_BlockY + 1, a_BlockZ, a_BlockZ + 1);  // Bounding box of the block hit
			double LineCoeff = 0;  // Used to calculate where along the line an intersection with the bounding box occurs
			eBlockFace Face;  // Face hit
			if (!bb.CalcLineIntersection(m_Start, m_End, LineCoeff, Face))
			{
				// Math rounding errors have caused the calculation to miss the block completely, assume immediate hit
				LineCoeff = 0;
			}
			m_HitCoords = m_Start + (m_End - m_Start) * LineCoeff;  // Point where projectile goes into the hit block
			return true;
		}

	protected:
		const Vector3d & m_Start;
		const Vector3d & m_End;
		Vector3d & m_HitCoords;
		Vector3i & m_HitBlockCoords;
		eBlockFace & m_HitBlockFace;
	} callbacks(a_Start, a_End, a_HitCoords, a_HitBlockCoords, a_HitBlockFace);
	return !Trace(a_World, callbacks, a_Start, a_End);
}

bool cLineBlockTracer::Trace(cWorld & a_World, cBlockTracer::cCallbacks &a_Callbacks, double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ)
{
	cLineBlockTracer Tracer(a_World, a_Callbacks);
	return Tracer.Trace(a_StartX, a_StartY, a_StartZ, a_EndX, a_EndY, a_EndZ);
}

bool cLineBlockTracer::Trace(double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ)
{
	// Initialize the member veriables:
	m_StartX = a_StartX;
	m_StartY = a_StartY;
	m_StartZ = a_StartZ;
	m_EndX = a_EndX;
	m_EndY = a_EndY;
	m_EndZ = a_EndZ;
	m_DirX = (m_StartX < m_EndX) ? 1 : -1;
	m_DirY = (m_StartY < m_EndY) ? 1 : -1;
	m_DirZ = (m_StartZ < m_EndZ) ? 1 : -1;
	m_CurrentFace = BLOCK_FACE_NONE;

	// Check the start coords, adjust into the world:
	if (m_StartY < 0)
	{
		if (m_EndY < 0)
		{
			// Nothing to trace
			m_Callbacks->OnNoMoreHits();
			return true;
		}
		FixStartBelowWorld();
		m_Callbacks->OnIntoWorld(m_StartX, m_StartY, m_StartZ);
	}
	else if (m_StartY >= cChunkDef::Height)
	{
		if (m_EndY >= cChunkDef::Height)
		{
			m_Callbacks->OnNoMoreHits();
			return true;
		}
		FixStartAboveWorld();
		m_Callbacks->OnIntoWorld(m_StartX, m_StartY, m_StartZ);
	}

	m_CurrentX = FloorC(m_StartX);
	m_CurrentY = FloorC(m_StartY);
	m_CurrentZ = FloorC(m_StartZ);

	m_DiffX = m_EndX - m_StartX;
	m_DiffY = m_EndY - m_StartY;
	m_DiffZ = m_EndZ - m_StartZ;

	// The actual trace is handled with ChunkMapCS locked by calling our Item() for the specified chunk
	int BlockX = FloorC(m_StartX);
	int BlockZ = FloorC(m_StartZ);
	int ChunkX, ChunkZ;
	cChunkDef::BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ);
	return m_World->DoWithChunk(ChunkX, ChunkZ, *this);
}

void cLineBlockTracer::FixStartAboveWorld(void)
{
	// We must set the start Y to less than cChunkDef::Height so that it is considered inside the world later on
	// Therefore we use an EPS-offset from the height, as small as reasonably possible.
	const double Height = static_cast<double>(cChunkDef::Height) - 0.00001;
	CalcXZIntersection(Height, m_StartX, m_StartZ);
	m_StartY = Height;
}

void cLineBlockTracer::FixStartBelowWorld(void)
{
	CalcXZIntersection(0, m_StartX, m_StartZ);
	m_StartY = 0;
}

void cLineBlockTracer::CalcXZIntersection(double a_Y, double & a_IntersectX, double & a_IntersectZ)
{
	double Ratio = (m_StartY - a_Y) / (m_StartY - m_EndY);
	a_IntersectX = m_StartX + (m_EndX - m_StartX) * Ratio;
	a_IntersectZ = m_StartZ + (m_EndZ - m_StartZ) * Ratio;
}

bool cLineBlockTracer::MoveToNextBlock(void)
{
	// Find out which of the current block's walls gets hit by the path:
	static const double EPS = 0.00001;
	enum
	{
		dirNONE,
		dirX,
		dirY,
		dirZ,
	} Direction = dirNONE;

	// Calculate the next YZ wall hit:
	double Coeff = 1;
	if (std::abs(m_DiffX) > EPS)
	{
		double DestX = (m_DirX > 0) ? (m_CurrentX + 1) : m_CurrentX;
		double CoeffX = (DestX - m_StartX) / m_DiffX;
		if (CoeffX <= 1)  // We need to include equality for the last block in the trace
		{
			Coeff = CoeffX;
			Direction = dirX;
		}
	}

	// If the next XZ wall hit is closer, use it instead:
	if (std::abs(m_DiffY) > EPS)
	{
		double DestY = (m_DirY > 0) ? (m_CurrentY + 1) : m_CurrentY;
		double CoeffY = (DestY - m_StartY) / m_DiffY;
		if (CoeffY <= Coeff)  // We need to include equality for the last block in the trace
		{
			Coeff = CoeffY;
			Direction = dirY;
		}
	}

	// If the next XY wall hit is closer, use it instead:
	if (std::abs(m_DiffZ) > EPS)
	{
		double DestZ = (m_DirZ > 0) ? (m_CurrentZ + 1) : m_CurrentZ;
		double CoeffZ = (DestZ - m_StartZ) / m_DiffZ;
		if (CoeffZ <= Coeff)  // We need to include equality for the last block in the trace
		{
			Direction = dirZ;
		}
	}

	// Based on the wall hit, adjust the current coords
	switch (Direction)
	{
		case dirX:    m_CurrentX += m_DirX; m_CurrentFace = (m_DirX > 0) ? BLOCK_FACE_XM : BLOCK_FACE_XP; break;
		case dirY:    m_CurrentY += m_DirY; m_CurrentFace = (m_DirY > 0) ? BLOCK_FACE_YM : BLOCK_FACE_YP; break;
		case dirZ:    m_CurrentZ += m_DirZ; m_CurrentFace = (m_DirZ > 0) ? BLOCK_FACE_ZM : BLOCK_FACE_ZP; break;
		case dirNONE: return false;
	}
	return true;
}

bool cLineBlockTracer::Item(cChunk * a_Chunk)
{
	ASSERT((m_CurrentY >= 0) && (m_CurrentY < cChunkDef::Height));  // This should be provided by FixStartAboveWorld() / FixStartBelowWorld()

	// This is the actual line tracing loop.
	for (;;)
	{
		// Report the current block through the callbacks:
		if (a_Chunk == nullptr)
		{
			m_Callbacks->OnNoChunk();
			return false;
		}

		// Move to next block
		if (!MoveToNextBlock())
		{
			// We've reached the end
			m_Callbacks->OnNoMoreHits();
			return true;
		}

		if ((m_CurrentY < 0) || (m_CurrentY >= cChunkDef::Height))
		{
			// We've gone out of the world, that's the end of this trace
			double IntersectX, IntersectZ;
			CalcXZIntersection(m_CurrentY, IntersectX, IntersectZ);
			if (m_Callbacks->OnOutOfWorld(IntersectX, m_CurrentY, IntersectZ))
			{
				// The callback terminated the trace
				return false;
			}
			m_Callbacks->OnNoMoreHits();
			return true;
		}

		// Update the current chunk
		a_Chunk = a_Chunk->GetNeighborChunk(m_CurrentX, m_CurrentZ);
		if (a_Chunk == nullptr)
		{
			m_Callbacks->OnNoChunk();
			return false;
		}

		if (a_Chunk->IsValid())
		{
			BLOCKTYPE BlockType;
			NIBBLETYPE BlockMeta;
			int RelX = m_CurrentX - a_Chunk->GetPosX() * cChunkDef::Width;
			int RelZ = m_CurrentZ - a_Chunk->GetPosZ() * cChunkDef::Width;
			a_Chunk->GetBlockTypeMeta(RelX, m_CurrentY, RelZ, BlockType, BlockMeta);
			if (m_Callbacks->OnNextBlock(m_CurrentX, m_CurrentY, m_CurrentZ, BlockType, BlockMeta, m_CurrentFace))
			{
				// The callback terminated the trace
				return false;
			}
		}
		else if (m_Callbacks->OnNextBlockNoData(m_CurrentX, m_CurrentY, m_CurrentZ, m_CurrentFace))
		{
			// The callback terminated the trace
			return false;
		}
	}
}

<|endoftext|>