<commit_before>#include "rust_internal.h"

struct
command_line_args : public dom_owned<command_line_args>
{
    rust_dom *dom;
    int argc;
    char **argv;

    // vec[str] passed to rust_task::start.
    rust_vec *args;

    command_line_args(rust_dom *dom,
                      int sys_argc,
                      char **sys_argv)
        : dom(dom),
          argc(sys_argc),
          argv(sys_argv),
          args(NULL)
    {
#if defined(__WIN32__)
        LPCWSTR cmdline = GetCommandLineW();
        LPWSTR *wargv = CommandLineToArgvW(cmdline, &argc);
        dom->win32_require("CommandLineToArgvW", wargv != NULL);
        argv = (char **) dom->malloc(sizeof(char*) * argc);
        for (int i = 0; i < argc; ++i) {
            int n_chars = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1,
                                              NULL, 0, NULL, NULL);
            dom->win32_require("WideCharToMultiByte(0)", n_chars != 0);
            argv[i] = (char *) dom->malloc(n_chars);
            n_chars = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1,
                                          argv[i], n_chars, NULL, NULL);
            dom->win32_require("WideCharToMultiByte(1)", n_chars != 0);
        }
        LocalFree(wargv);
#endif
        size_t vec_fill = sizeof(rust_str *) * argc;
        size_t vec_alloc = next_power_of_two(sizeof(rust_vec) + vec_fill);
        void *mem = dom->malloc(vec_alloc);
        args = new (mem) rust_vec(dom, vec_alloc, 0, NULL);
        rust_str **strs = (rust_str**) &args->data[0];
        for (int i = 0; i < argc; ++i) {
            size_t str_fill = strlen(argv[i]) + 1;
            size_t str_alloc = next_power_of_two(sizeof(rust_str) + str_fill);
            mem = dom->malloc(str_alloc);
            strs[i] = new (mem) rust_str(dom, str_alloc, str_fill,
                                         (uint8_t const *)argv[i]);
        }
        args->fill = vec_fill;
        // If the caller has a declared args array, they may drop; but
        // we don't know if they have such an array. So we pin the args
        // array here to ensure it survives to program-shutdown.
        args->ref();
    }

    ~command_line_args() {
        if (args) {
            // Drop the args we've had pinned here.
            rust_str **strs = (rust_str**) &args->data[0];
            for (int i = 0; i < argc; ++i)
                dom->free(strs[i]);
            dom->free(args);
        }

#ifdef __WIN32__
        for (int i = 0; i < argc; ++i) {
            dom->free(argv[i]);
        }
        dom->free(argv);
#endif
    }
};

/**
 * Main entry point into the Rust runtime. Here we create a Rust service,
 * initialize the kernel, create the root domain and run it.
 */

extern "C" CDECL int
rust_start(uintptr_t main_fn, rust_crate const *crate, int argc,
           char **argv, void* crate_map) {

    // Only when we're on rustc is the last argument passed
    if (!crate->get_image_base())
        update_log_settings(crate_map, getenv("RUST_LOG"));
    rust_srv *srv = new rust_srv();
    rust_kernel *kernel = new rust_kernel(srv);
    kernel->start();
    rust_handle<rust_dom> *handle = kernel->create_domain(crate, "main");
    rust_dom *dom = handle->referent();
    command_line_args *args = new (dom) command_line_args(dom, argc, argv);

    DLOG(dom, rust_log::DOM, "startup: %d args in 0x%" PRIxPTR,
             args->argc, (uintptr_t)args->args);
    for (int i = 0; i < args->argc; i++) {
        DLOG(dom, rust_log::DOM,
            "startup: arg[%d] = '%s'", i, args->argv[i]);
    }

    if (dom->_log.is_tracing(rust_log::DWARF)) {
        rust_crate_reader create_reader(dom, crate);
    }

    uintptr_t main_args[4] = {0, 0, 0, (uintptr_t)args->args};
    dom->root_task->start(crate->get_exit_task_glue(),
                          crate->abi_tag, main_fn,
                          (uintptr_t)&main_args, sizeof(main_args));

    int ret = dom->start_main_loop();
    delete args;
    kernel->destroy_domain(dom);
    kernel->join_all_domains();
    delete kernel;
    delete srv;

#if !defined(__WIN32__)
    // Don't take down the process if the main thread exits without an
    // error.
    if (!ret)
        pthread_exit(NULL);
#endif
    return ret;
}

//
// Local Variables:
// mode: C++
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<commit_msg>Temporarily turn off logging initialization<commit_after>#include "rust_internal.h"

struct
command_line_args : public dom_owned<command_line_args>
{
    rust_dom *dom;
    int argc;
    char **argv;

    // vec[str] passed to rust_task::start.
    rust_vec *args;

    command_line_args(rust_dom *dom,
                      int sys_argc,
                      char **sys_argv)
        : dom(dom),
          argc(sys_argc),
          argv(sys_argv),
          args(NULL)
    {
#if defined(__WIN32__)
        LPCWSTR cmdline = GetCommandLineW();
        LPWSTR *wargv = CommandLineToArgvW(cmdline, &argc);
        dom->win32_require("CommandLineToArgvW", wargv != NULL);
        argv = (char **) dom->malloc(sizeof(char*) * argc);
        for (int i = 0; i < argc; ++i) {
            int n_chars = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1,
                                              NULL, 0, NULL, NULL);
            dom->win32_require("WideCharToMultiByte(0)", n_chars != 0);
            argv[i] = (char *) dom->malloc(n_chars);
            n_chars = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1,
                                          argv[i], n_chars, NULL, NULL);
            dom->win32_require("WideCharToMultiByte(1)", n_chars != 0);
        }
        LocalFree(wargv);
#endif
        size_t vec_fill = sizeof(rust_str *) * argc;
        size_t vec_alloc = next_power_of_two(sizeof(rust_vec) + vec_fill);
        void *mem = dom->malloc(vec_alloc);
        args = new (mem) rust_vec(dom, vec_alloc, 0, NULL);
        rust_str **strs = (rust_str**) &args->data[0];
        for (int i = 0; i < argc; ++i) {
            size_t str_fill = strlen(argv[i]) + 1;
            size_t str_alloc = next_power_of_two(sizeof(rust_str) + str_fill);
            mem = dom->malloc(str_alloc);
            strs[i] = new (mem) rust_str(dom, str_alloc, str_fill,
                                         (uint8_t const *)argv[i]);
        }
        args->fill = vec_fill;
        // If the caller has a declared args array, they may drop; but
        // we don't know if they have such an array. So we pin the args
        // array here to ensure it survives to program-shutdown.
        args->ref();
    }

    ~command_line_args() {
        if (args) {
            // Drop the args we've had pinned here.
            rust_str **strs = (rust_str**) &args->data[0];
            for (int i = 0; i < argc; ++i)
                dom->free(strs[i]);
            dom->free(args);
        }

#ifdef __WIN32__
        for (int i = 0; i < argc; ++i) {
            dom->free(argv[i]);
        }
        dom->free(argv);
#endif
    }
};

/**
 * Main entry point into the Rust runtime. Here we create a Rust service,
 * initialize the kernel, create the root domain and run it.
 */

extern "C" CDECL int
rust_start(uintptr_t main_fn, rust_crate const *crate, int argc,
           char **argv, void* crate_map) {

    // FIXME commented out until I figure out how to detect rustboot in a sane
    // way
    /* if (NOT_USING_RUSTBOOT)
          update_log_settings(crate_map, getenv("RUST_LOG"));*/
    rust_srv *srv = new rust_srv();
    rust_kernel *kernel = new rust_kernel(srv);
    kernel->start();
    rust_handle<rust_dom> *handle = kernel->create_domain(crate, "main");
    rust_dom *dom = handle->referent();
    command_line_args *args = new (dom) command_line_args(dom, argc, argv);

    DLOG(dom, rust_log::DOM, "startup: %d args in 0x%" PRIxPTR,
             args->argc, (uintptr_t)args->args);
    for (int i = 0; i < args->argc; i++) {
        DLOG(dom, rust_log::DOM,
            "startup: arg[%d] = '%s'", i, args->argv[i]);
    }

    if (dom->_log.is_tracing(rust_log::DWARF)) {
        rust_crate_reader create_reader(dom, crate);
    }

    uintptr_t main_args[4] = {0, 0, 0, (uintptr_t)args->args};
    dom->root_task->start(crate->get_exit_task_glue(),
                          crate->abi_tag, main_fn,
                          (uintptr_t)&main_args, sizeof(main_args));

    int ret = dom->start_main_loop();
    delete args;
    kernel->destroy_domain(dom);
    kernel->join_all_domains();
    delete kernel;
    delete srv;

#if !defined(__WIN32__)
    // Don't take down the process if the main thread exits without an
    // error.
    if (!ret)
        pthread_exit(NULL);
#endif
    return ret;
}

//
// Local Variables:
// mode: C++
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<|endoftext|>


<commit_before>// Copyright 2008 Paul Hodge

#include "branch.h"
#include "debug.h"
#include "function.h"
#include "introspection.h"
#include "runtime.h"
#include "syntax.h"
#include "type.h"
#include "values.h"

namespace circa {

Term* create_term(Branch* branch, Term* function, RefList const& inputs)
{
    assert_good_pointer(function);

    if (!is_function(function))
        throw std::runtime_error("in create_term, 2nd arg to create_term must be a function");

    Term* term = new Term();

    if (branch != NULL)
        branch->append(term);

    term->function = function;
    term->needsUpdate = true;

    Function& functionData = as_function(function);

    Term* outputType = functionData.outputType;

    if (outputType == NULL)
        throw std::runtime_error("outputType is NULL");

    if (!is_type(outputType))
        throw std::runtime_error(outputType->name + " is not a type");

    change_type(term, outputType);

    // Create state (if a state type is defined)
    Term* stateType = functionData.stateType;
    if (stateType != NULL) {
        if (!is_type(stateType))
            throw std::runtime_error(stateType->name + " is not a type");
        term->state = create_value(NULL, stateType);
    }

    for (unsigned int i=0; i < inputs.count(); i++)
        set_input(term, i, inputs[i]);

    return term;
}

bool check_valid_type(Function &func, int index, Term* term)
{
    Term* expectedType = func.inputTypes[index];

    if (expectedType == ANY_TYPE)
        return true;

    return term->type == expectedType;
}

void evaluate_term(Term* term)
{
    if (term == NULL)
        throw std::runtime_error("term is NULL");

    term->clearError();

    // Check function
    if (term->function == NULL) {
        error_occured(term, "Function is NULL");
        return;
    }

    if (!is_function(term->function)) {
        error_occured(term, "term->function is not a function");
        return;
    }

    Function& func = as_function(term->function);

    if (func.evaluate == NULL)
        return;

    // Check each input. Make sure:
    //  1) it is not null
    //  2) it is up-to-date
    //  3) it has a non-null value
    //  4) it has no errors
    //  5) it has the correct type
    for (unsigned int inputIndex=0; inputIndex < term->inputs.count(); inputIndex++)
    {
        int effectiveIndex = inputIndex;
        if (func.variableArgs)
            effectiveIndex = 0;

        Term* input = term->inputs[inputIndex];
        Function::InputProperties inputProps = func.getInputProperties(effectiveIndex);

        if (input == NULL && !inputProps.meta) {
            std::stringstream message;
            message << "Input " << inputIndex << " is NULL";
            error_occured(term, message.str());
            return;
        }

        if (!is_value_alloced(input) && !inputProps.meta) {
            std::stringstream message;
            message << "Input " << inputIndex << " has NULL value";
            error_occured(term, message.str());
            return;
        }

        if (input->hasError() && !inputProps.meta) {
            std::stringstream message;
            message << "Input " << inputIndex << " has an error";
            error_occured(term, message.str());
            return;
        }

        // Check type
        if (!check_valid_type(func, effectiveIndex, input)) {
            std::stringstream message;
            message << "Runtime type error: input " << inputIndex << " has type "
                << as_type(input->type).name;
            error_occured(term, message.str());
            return;
        }

        // Possibly evaluate this input if needed
        if (!inputProps.meta && input->needsUpdate) {
            assert(term != input); // prevent infinite recursion
            evaluate_term(input);
        }
    }

    // Make sure we have an allocated value. Allocate one if necessary
    if (!is_value_alloced(term))
        alloc_value(term);

    // Execute the function
    try {
        func.evaluate(term);
        term->needsUpdate = false;
    }
    catch (std::exception const& err)
    {
        error_occured(term, err.what());
    }
}

void evaluate_branch(Branch& branch, Term* errorListener)
{
    int count = branch.numTerms();
    for (int index=0; index < count; index++) {
		Term* term = branch.get(index);
        evaluate_term(term);

        if (term->hasError()) {
            std::stringstream out;
            out << "On term " << term_to_raw_string(term) << "\n" << term->getErrorMessage();
            error_occured(errorListener, out.str());
            return;
        }
    }

    // TODO: Instead of calling this function, stateful terms should have an
    // 'assign' function generated at the bottom of the branch, which puts the
    // result back into the stateful-value term.
    persist_results_for_stateful_terms(branch);
}

void error_occured(Term* errorTerm, std::string const& message)
{
    if (errorTerm == NULL)
        throw std::runtime_error(message);

    errorTerm->pushError(message);
}

void set_input(Term* term, int index, Term* input)
{
    assert_good_pointer(term);

    // Term* previousInput = term->inputs.get(index);

    term->inputs.setAt(index, input);

    // Update syntax hints
    if (input == NULL)
        term->syntaxHints.getInputSyntax(index).unknownStyle();
    else if (input->name == "")
        term->syntaxHints.getInputSyntax(index).bySource();
    else
        term->syntaxHints.getInputSyntax(index).byName(input->name);
}

Term* create_duplicate(Branch* branch, Term* source, bool copyBranches)
{
    Term* term = create_term(branch, source->function, source->inputs);

    term->name = source->name;

    if (copyBranches)
        copy_value(source, term);
    else
        copy_value_but_dont_copy_inner_branch(source,term);

    duplicate_branch(source->properties, term->properties);
    term->syntaxHints = source->syntaxHints;

    if (source->state != NULL) {
        if (copyBranches && !has_inner_branch(source))
            copy_value(source->state, term->state);
    }

    return term;
}

Term* possibly_coerce_term(Branch* branch, Term* original, Term* expectedType)
{
    // (In the future, we will have more complicated coersion rules)

    // Ignore NULL
    if (original == NULL)
        return original;

    // Coerce from int to float
    if (original->type == INT_TYPE && expectedType == FLOAT_TYPE) {
        return apply(branch, INT_TO_FLOAT_FUNC, RefList(original));
    }

    return original;
}

Term* apply(Branch* branch, Term* function, RefList const& _inputs, std::string const& name)
{
    // Make a local copy of _inputs
    RefList inputs = _inputs;

    // Evaluate this function if needed
    if (function->needsUpdate)
        evaluate_term(function);

    // Check if 'function' is actually a type
    if (is_type(function))
    {
        if (inputs.count() != 0)
            throw std::runtime_error("Inputs in constructor function is not yet supported");

        function = get_value_function(function);
    }

    // If 'function' is not really a function, see if we can treat it like a function
    else if (!is_function(function)) {

        Type& type = as_type(function->type);

        if (!type.memberFunctions.contains(""))
            throw std::runtime_error(std::string("Term ") + function->toString()
                    + " is not a type, and has no default function");

        RefList memberFunctionInputs;
        memberFunctionInputs.append(function);
        memberFunctionInputs.appendAll(inputs);

        function = type.memberFunctions[""];
        inputs = memberFunctionInputs;
    }

    Function& functionData = as_function(function);

    // Possibly coerce inputs
    for (unsigned int i=0; i < inputs.count(); i++) {
        inputs.setAt(i, possibly_coerce_term(branch, inputs[i],
                functionData.inputType(i)));
    }

    // Create the term
    Term* result = create_term(branch, function, inputs);

    if (name != "" && branch != NULL)
        branch->bindName(result, name);

    return result;
}

Term* apply(Branch* branch,
                     std::string const& functionName,
                     RefList const& inputs)
{
    Term* function = find_named(branch,functionName);
    if (function == NULL)
        throw std::runtime_error(std::string("function not found: ")+functionName);

    return apply(branch, function, inputs);
}

Term* eval_function(Branch& branch, Term* function, RefList const& inputs)
{
    Term* result = apply(&branch, function, inputs);
    evaluate_term(result);
    return result;
}

Term* eval_function(Branch& branch, std::string const& functionName,
        RefList const &inputs)
{
    Term* function = find_named(&branch,functionName);
    if (function == NULL)
        throw std::runtime_error(std::string("function not found: ")+functionName);

    return eval_function(branch, function, inputs);
}

} // namespace circa
<commit_msg>Cosmetic changes<commit_after>// Copyright 2008 Paul Hodge

#include "branch.h"
#include "debug.h"
#include "function.h"
#include "introspection.h"
#include "runtime.h"
#include "syntax.h"
#include "type.h"
#include "values.h"

namespace circa {

Term* create_term(Branch* branch, Term* function, RefList const& inputs)
{
    assert_good_pointer(function);

    if (!is_function(function))
        throw std::runtime_error("in create_term, 2nd arg to create_term must be a function");

    Term* term = new Term();

    if (branch != NULL)
        branch->append(term);

    term->function = function;
    term->needsUpdate = true;

    Function& functionData = as_function(function);

    Term* outputType = functionData.outputType;

    if (outputType == NULL)
        throw std::runtime_error("outputType is NULL");

    if (!is_type(outputType))
        throw std::runtime_error(outputType->name + " is not a type");

    change_type(term, outputType);

    // Create state (if a state type is defined)
    Term* stateType = functionData.stateType;
    if (stateType != NULL) {
        if (!is_type(stateType))
            throw std::runtime_error(stateType->name + " is not a type");
        term->state = create_value(NULL, stateType);
    }

    for (unsigned int i=0; i < inputs.count(); i++)
        set_input(term, i, inputs[i]);

    return term;
}

bool check_valid_type(Function &func, int index, Term* term)
{
    Term* expectedType = func.inputTypes[index];

    if (expectedType == ANY_TYPE)
        return true;

    return term->type == expectedType;
}

void evaluate_term(Term* term)
{
    if (term == NULL)
        throw std::runtime_error("term is NULL");

    term->clearError();

    // Check function
    if (term->function == NULL) {
        error_occured(term, "Function is NULL");
        return;
    }

    if (!is_function(term->function)) {
        error_occured(term, "term->function is not a function");
        return;
    }

    Function& func = as_function(term->function);

    if (func.evaluate == NULL)
        return;

    // Check each input. Make sure:
    //  1) it is not null
    //  2) it is up-to-date
    //  3) it has a non-null value
    //  4) it has no errors
    //  5) it has the correct type
    for (unsigned int inputIndex=0; inputIndex < term->inputs.count(); inputIndex++)
    {
        int effectiveIndex = inputIndex;
        if (func.variableArgs)
            effectiveIndex = 0;

        Term* input = term->inputs[inputIndex];
        Function::InputProperties inputProps = func.getInputProperties(effectiveIndex);

        if (input == NULL && !inputProps.meta) {
            std::stringstream message;
            message << "Input " << inputIndex << " is NULL";
            error_occured(term, message.str());
            return;
        }

        if (!is_value_alloced(input) && !inputProps.meta) {
            std::stringstream message;
            message << "Input " << inputIndex << " has NULL value";
            error_occured(term, message.str());
            return;
        }

        if (input->hasError() && !inputProps.meta) {
            std::stringstream message;
            message << "Input " << inputIndex << " has an error";
            error_occured(term, message.str());
            return;
        }

        // Check type
        if (!check_valid_type(func, effectiveIndex, input)) {
            std::stringstream message;
            message << "Runtime type error: input " << inputIndex << " has type "
                << as_type(input->type).name;
            error_occured(term, message.str());
            return;
        }

        // Possibly evaluate this input if needed
        if (!inputProps.meta && input->needsUpdate) {
            assert(term != input); // prevent infinite recursion
            evaluate_term(input);
        }
    }

    // Make sure we have an allocated value. Allocate one if necessary
    if (!is_value_alloced(term))
        alloc_value(term);

    // Execute the function
    try {
        func.evaluate(term);
        term->needsUpdate = false;
    }
    catch (std::exception const& err)
    {
        error_occured(term, err.what());
    }
}

void evaluate_branch(Branch& branch, Term* errorListener)
{
    int count = branch.numTerms();
    for (int index=0; index < count; index++) {
		Term* term = branch.get(index);
        evaluate_term(term);

        if (term->hasError()) {
            std::stringstream out;
            out << "On term " << term_to_raw_string(term) << "\n" << term->getErrorMessage();
            error_occured(errorListener, out.str());
            return;
        }
    }

    // TODO: Instead of calling this function, stateful terms should have an
    // 'assign' function generated at the bottom of the branch, which puts the
    // result back into the stateful-value term.
    persist_results_for_stateful_terms(branch);
}

void error_occured(Term* errorTerm, std::string const& message)
{
    if (errorTerm == NULL)
        throw std::runtime_error(message);

    errorTerm->pushError(message);
}

void set_input(Term* term, int index, Term* input)
{
    assert_good_pointer(term);

    term->inputs.setAt(index, input);

    // Update syntax hints
    if (input == NULL)
        term->syntaxHints.getInputSyntax(index).unknownStyle();
    else if (input->name == "")
        term->syntaxHints.getInputSyntax(index).bySource();
    else
        term->syntaxHints.getInputSyntax(index).byName(input->name);
}

Term* create_duplicate(Branch* branch, Term* source, bool copyBranches)
{
    Term* term = create_term(branch, source->function, source->inputs);

    term->name = source->name;

    if (copyBranches)
        copy_value(source, term);
    else
        copy_value_but_dont_copy_inner_branch(source,term);

    duplicate_branch(source->properties, term->properties);
    term->syntaxHints = source->syntaxHints;

    if (source->state != NULL) {
        if (copyBranches && !has_inner_branch(source))
            copy_value(source->state, term->state);
    }

    return term;
}

Term* possibly_coerce_term(Branch* branch, Term* original, Term* expectedType)
{
    // (In the future, we will have more complicated coersion rules)

    // Ignore NULL
    if (original == NULL)
        return original;

    // Coerce from int to float
    if (original->type == INT_TYPE && expectedType == FLOAT_TYPE) {
        return apply(branch, INT_TO_FLOAT_FUNC, RefList(original));
    }

    return original;
}

Term* apply(Branch* branch, Term* function, RefList const& _inputs, std::string const& name)
{
    // Make a local copy of _inputs
    RefList inputs = _inputs;

    // Evaluate this function if needed
    if (function->needsUpdate)
        evaluate_term(function);

    // Check if 'function' is actually a type
    if (is_type(function))
    {
        if (inputs.count() != 0)
            throw std::runtime_error("Inputs in constructor function is not yet supported");

        function = get_value_function(function);
    }

    // If 'function' is not really a function, see if we can treat it like a function
    else if (!is_function(function)) {

        Type& type = as_type(function->type);

        if (!type.memberFunctions.contains(""))
            throw std::runtime_error(std::string("Term ") + function->toString()
                    + " is not a type, and has no default function");

        RefList memberFunctionInputs;
        memberFunctionInputs.append(function);
        memberFunctionInputs.appendAll(inputs);

        function = type.memberFunctions[""];
        inputs = memberFunctionInputs;
    }

    Function& functionData = as_function(function);

    // Possibly coerce inputs
    for (unsigned int i=0; i < inputs.count(); i++) {
        inputs.setAt(i, possibly_coerce_term(branch, inputs[i],
                functionData.inputType(i)));
    }

    // Create the term
    Term* result = create_term(branch, function, inputs);

    // Bind name, if given
    if (name != "" && branch != NULL)
        branch->bindName(result, name);

    return result;
}

Term* apply(Branch* branch, std::string const& functionName, RefList const& inputs)
{
    Term* function = find_named(branch,functionName);
    if (function == NULL)
        throw std::runtime_error("function not found: "+functionName);

    return apply(branch, function, inputs);
}

Term* eval_function(Branch& branch, Term* function, RefList const& inputs)
{
    Term* result = apply(&branch, function, inputs);
    evaluate_term(result);
    return result;
}

Term* eval_function(Branch& branch, std::string const& functionName,
        RefList const &inputs)
{
    Term* function = find_named(&branch,functionName);
    if (function == NULL)
        throw std::runtime_error("function not found: "+functionName);

    return eval_function(branch, function, inputs);
}

} // namespace circa
<|endoftext|>


<commit_before>#include <Halide.h>
#include <stdio.h>
#include <math.h>

using namespace Halide;

// A version of pow that tracks usage so we can check how many times it was called.
#ifdef _MSC_VER
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif

int call_count = 0;
extern "C" DLLEXPORT float my_powf(float x, float y) {
    call_count++;
    // We have to read from call_count, or for some reason apple clang removes it entirely.
    assert(call_count != -1);
    return powf(x, y);
}
HalideExtern_2(float, my_powf, float, float);

int main(int argc, char **argv) {
    // Brighten some tiles of an image, where that region is given by
    // a lower-res bitmap.

    ImageParam bitmap(Bool(), 2);
    ImageParam image(Float(32), 2);
    const int tile_size = 16;

    Var x("x"), y("y"), xi("xi"), yi("yi"), t("t");

    // Brighten the image
    Func brighter("brighter");
    brighter(x, y) = my_powf(image(x, y), 0.8f);

    // Select either the brighter or the input depending on the bitmap
    Func output("output");
    output(x, y) = select(bitmap(x/tile_size, y/tile_size), brighter(x, y), image(x, y));

    // Compute the output in tiles of the appropriate size
    output.tile(x, y, xi, yi, tile_size, tile_size);

    // Vectorize within tiles. We would also parallelize across tiles,
    // but that introduces a race condition in the call_count.
    output.vectorize(xi, 4).fuse(x, y, t);

    // Compute brighter per output tile
    brighter.compute_at(output, t);

    // Assert that the output is a whole number of tiles. Otherwise
    // the tiles in the schedule don't match up to the tiles in the
    // algorithm, and you can't safely skip any work.
    output.bound(x, 0, (image.extent(0)/tile_size)*tile_size);
    output.bound(y, 0, (image.extent(1)/tile_size)*tile_size);
    output.compile_jit();

    Image<bool> bitmap_buf(10, 10);
    bitmap_buf(5, 5) = 1;
    bitmap.set(bitmap_buf);

    Image<float> image_buf = lambda(x, y, (sin(x+y)+1)/2).realize(10 * tile_size, 10 * tile_size);
    image.set(image_buf);

    call_count = 0;
    Image<float> result = output.realize(10 * tile_size, 10 * tile_size);

    // Force a reload of call_count
    my_powf(1, 1);
    call_count--;

    // Check the right number of calls to powf occurred
    if (call_count != tile_size*tile_size) {
        printf("call_count = %d instead of %d\n", call_count, tile_size * tile_size);
        //return -1;
    }

    // Check the output is correct
    for (int y = 0; y < result.height(); y++) {
        for (int x = 0; x < result.width(); x++) {
            bool active = bitmap_buf(x/tile_size, y/tile_size);
            float correct = active ? my_powf(image_buf(x, y), 0.8f) : image_buf(x, y);
            if (fabs(correct - result(x, y)) > 0.001f) {
                printf("result(%d, %d) = %f instead of %f\n",
                       x, y, result(x, y), correct);
                return -1;
            }
        }
    }

    printf("Success!\n");
    return 0;
}
<commit_msg>Change process_some_tiles to actually work.<commit_after>#include <Halide.h>
#include <stdio.h>
#include <math.h>

using namespace Halide;

// A version of pow that tracks usage so we can check how many times it was called.
#ifdef _MSC_VER
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif

int call_count = 0;
extern "C" DLLEXPORT float my_powf(float x, float y) {
    call_count++;
    // We have to read from call_count, or for some reason apple clang removes it entirely.
    assert(call_count != -1);
    return powf(x, y);
}
HalideExtern_2(float, my_powf, float, float);

int main(int argc, char **argv) {
    // Brighten some tiles of an image, where that region is given by
    // a lower-res bitmap.

    ImageParam bitmap(Bool(), 2);
    ImageParam image(Float(32), 2);
    const int tile_size = 16;

    Var x("x"), y("y"), xi("xi"), yi("yi"), t("t");

    // Break the input into tiles.
    Func tiled("tiled");
    tiled(xi, yi, x, y) = image(x*tile_size + xi, y*tile_size + yi);

    // Brighten each tile of the image
    Func brighter("brighter");
    brighter(xi, yi, x, y) = my_powf(tiled(xi, yi, x, y), 0.8f);

    // Select either the brighter tile or the input tile depending on the bitmap
    Func output_tiles("output_tiles");
    output_tiles(xi, yi, x, y) = select(bitmap(x, y), brighter(xi, yi, x, y), tiled(xi, yi, x, y));

    // Collapse back down into 2D
    Func output("output");
    output(x, y) = output_tiles(x % tile_size, y % tile_size,
                                x / tile_size, y / tile_size);

    // Compute the output in tiles of the appropriate size to simplify
    // the mod and div above. Not important for the stage skipping behavior.
    output.bound(x, 0, (image.extent(0)/tile_size)*tile_size)
          .bound(y, 0, (image.extent(1)/tile_size)*tile_size)
          .tile(x, y, xi, yi, tile_size, tile_size);

    // Vectorize within tiles. We would also parallelize across tiles,
    // but that introduces a race condition in the call_count.
    output.vectorize(xi, 4);

    // Compute brighter per tile of output_tiles. This puts it inside
    // the loop over x and y, which makes the condition in the select
    // a constant. This is the important part of the schedule!
    brighter.compute_at(output_tiles, x);

    // Schedule output_tiles per output tile. This choice is unimportant.
    output_tiles.compute_at(output, x);

    output.compile_jit();

    Image<bool> bitmap_buf(10, 10);
    bitmap_buf(5, 5) = 1;
    bitmap.set(bitmap_buf);

    Image<float> image_buf = lambda(x, y, (sin(x+y)+1)/2).realize(10 * tile_size, 10 * tile_size);
    image.set(image_buf);

    call_count = 0;
    Image<float> result = output.realize(10 * tile_size, 10 * tile_size);

    // Force a reload of call_count
    my_powf(1, 1);
    call_count--;

    // Check the right number of calls to powf occurred
    if (call_count != tile_size*tile_size) {
        printf("call_count = %d instead of %d\n", call_count, tile_size * tile_size);
        return -1;
    }

    // Check the output is correct
    for (int y = 0; y < result.height(); y++) {
        for (int x = 0; x < result.width(); x++) {
            bool active = bitmap_buf(x/tile_size, y/tile_size);
            float correct = active ? my_powf(image_buf(x, y), 0.8f) : image_buf(x, y);
            if (fabs(correct - result(x, y)) > 0.001f) {
                printf("result(%d, %d) = %f instead of %f\n",
                       x, y, result(x, y), correct);
                return -1;
            }
        }
    }

    printf("Success!\n");
    return 0;
}
<|endoftext|>


<commit_before>#include <stdio.h>

#include "HalideRuntime.h"
#include "HalideBuffer.h"
#include <assert.h>
#include <string.h>

#include "cxx_mangling.h"
#include "cxx_mangling_gpu.h"

using namespace Halide;

int32_t extract_value_global(int32_t *arg) {
    return *arg;
}

namespace HalideTest {

int32_t extract_value_ns(const int32_t *arg) {
    return *arg;
}

}

int main(int argc, char **argv) {
    Buffer<uint8_t> input(100);

    for (int32_t i = 0; i < 100; i++) {
        input(i) = i;
    }

    Buffer<double> result(100);

    const halide_filter_metadata_t *m = HalideTest::cxx_mangling_metadata();
    assert(m != NULL);
    printf("Name is: %s\n", m->name);
    assert(strcmp(m->name, "cxx_mangling") == 0);

    int ptr_arg = 42;
    int *int_ptr = &ptr_arg;
    const int *const_int_ptr = &ptr_arg;
    void *void_ptr = nullptr;
    const void *const_void_ptr = nullptr;
    std::string *string_ptr = nullptr;
    const std::string *const_string_ptr = nullptr;

    // Don't bother calling this (we haven't linked in the CUDA support it needs),
    // just force a reference to ensure it is linked in.
    auto f = HalideTest::cxx_mangling_gpu;
    printf("HalideTest::cxx_mangling is at: %p\n", (void*) f);

    int r = HalideTest::cxx_mangling(input, -1, 0xff, -1, 0xffff, -1, 0xffffffff,
                                    -1, 0xffffffffffffffffLL, true, 42.0, 4239.0f,
                                    int_ptr, const_int_ptr, void_ptr, const_void_ptr,
                                    string_ptr, const_string_ptr, result);
    if (r != 0) {
        fprintf(stderr, "Failure!\n");
        exit(1);
    }
    printf("Success!\n");
    return 0;
}
<commit_msg>Fix ambiguity in test<commit_after>#include <stdio.h>

#include "HalideRuntime.h"
#include "HalideBuffer.h"
#include <assert.h>
#include <string.h>

#include "cxx_mangling.h"
#include "cxx_mangling_gpu.h"

using namespace Halide;

int32_t extract_value_global(int32_t *arg) {
    return *arg;
}

namespace HalideTest {

int32_t extract_value_ns(const int32_t *arg) {
    return *arg;
}

}

int main(int argc, char **argv) {
    Buffer<uint8_t> input(100);

    for (int32_t i = 0; i < 100; i++) {
        input(i) = i;
    }

    Buffer<double> result(100);

    const halide_filter_metadata_t *m = HalideTest::cxx_mangling_metadata();
    assert(m != NULL);
    printf("Name is: %s\n", m->name);
    assert(strcmp(m->name, "cxx_mangling") == 0);

    int ptr_arg = 42;
    int *int_ptr = &ptr_arg;
    const int *const_int_ptr = &ptr_arg;
    void *void_ptr = nullptr;
    const void *const_void_ptr = nullptr;
    std::string *string_ptr = nullptr;
    const std::string *const_string_ptr = nullptr;

    // Don't bother calling this (we haven't linked in the CUDA support it needs),
    // just force a reference to ensure it is linked in.
    int (*f)(halide_buffer_t *,
             int8_t, uint8_t,
             int16_t, uint16_t,
             int32_t, uint32_t,
             int64_t, uint64_t,
             bool,
             float, double,
             int32_t *, int32_t const *,
             void *, void const *,
             void *, void const *,
             halide_buffer_t *) = HalideTest::cxx_mangling_gpu;

    printf("HalideTest::cxx_mangling is at: %p\n", (void*) f);

    int r = HalideTest::cxx_mangling(input, -1, 0xff, -1, 0xffff, -1, 0xffffffff,
                                    -1, 0xffffffffffffffffLL, true, 42.0, 4239.0f,
                                    int_ptr, const_int_ptr, void_ptr, const_void_ptr,
                                    string_ptr, const_string_ptr, result);
    if (r != 0) {
        fprintf(stderr, "Failure!\n");
        exit(1);
    }
    printf("Success!\n");
    return 0;
}
<|endoftext|>


<commit_before>// Avoid deprecation warnings
#define HALIDE_ALLOW_DEPRECATED

#include "HalideRuntime.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "old_buffer_t.h"

int &get_pixel(buffer_t *buf, int x, int y) {
    return *((int *)(buf->host) +
             (x - buf->min[0]) * buf->stride[0] +
             (y - buf->min[1]) * buf->stride[1]);
}

extern "C" int extern_stage(buffer_t *in2, buffer_t *f, buffer_t *out) {
    if (in2->host == nullptr) {
        in2->extent[0] = out->extent[0];
        in2->min[0] = out->min[0];
        in2->extent[1] = out->extent[1];
        in2->min[1] = out->min[1] + 7;
    }
    if (f->host == nullptr) {
        f->extent[0] = out->extent[0];
        f->min[0] = out->min[0];
        f->extent[1] = out->extent[1];
        f->min[1] = out->min[1];
    }
    if (f->host && in2->host && out->host) {
        for (int y = 0; y < out->extent[1]; y++) {
            for (int x = 0; x < out->extent[0]; x++) {
                get_pixel(out, x, y) = get_pixel(in2, x, y + 7) + get_pixel(f, x, y);
            }
        }
    }
    return 0;
}

int main(int argc, char **argv) {
    buffer_t in1 = {0}, in2 = {0}, out = {0};
    int scalar_param = 4;

    out.host = (uint8_t *)malloc(60*40*sizeof(int));
    out.extent[0] = 60;
    out.extent[1] = 40;
    out.stride[0] = 1;
    out.stride[1] = 60;

    // Check bounds inference works
    int err = old_buffer_t_old_buffer_t(&in1, &in2, scalar_param, &out);
    if (err != 0) {
        printf("Pipeline returned non-zero exit status in bounds query mode: %d\n", err);
    }

    buffer_t correct_in1 = {0, NULL, {62, 44, 0, 0}, {1, 62, 0, 0}, {-1, -1, 0, 0}, 4, false, false, {0}};
    buffer_t correct_in2 = {0, NULL, {60, 47, 0, 0}, {1, 60, 0, 0}, {0, 0, 0, 0}, 4, false, false, {0}};

    if (memcmp(&correct_in1, &in1, sizeof(buffer_t))) {
        printf("Bounds inference gave wrong result for input 1\n");
        return -1;
    }

    if (memcmp(&correct_in2, &in2, sizeof(buffer_t))) {
        printf("Bounds inference gave wrong result for input 2\n");
        return -1;
    }

    // Allocate the inputs
    in1.host = (uint8_t *)malloc(in1.extent[0] * in1.extent[1] * in1.elem_size);
    in2.host = (uint8_t *)malloc(in2.extent[0] * in2.extent[1] * in2.elem_size);

    memset(in1.host, 1, in1.extent[0] * in1.extent[1] * in1.elem_size);
    memset(in2.host, 2, in2.extent[0] * in2.extent[1] * in2.elem_size);

    // Run the pipeline for real
    err = old_buffer_t_old_buffer_t(&in1, &in2, scalar_param, &out);
    if (err != 0) {
        printf("Pipeline returned non-zero exit status: %d\n", err);
    }

    for (int y = 0; y < out.extent[1]; y++) {
        for (int x = 0; x < out.extent[0]; x++) {
            int result = get_pixel(&out, x, y);
            int correct = 0x01010101 * 2 + 0x02020202 * 2 + scalar_param;
            if (result != correct) {
                printf("out(%d, %d) = %d instead of %d\n", x, y, result, correct);
                return -1;
            }
        }
    }

    printf("Success!\n");
    return 0;
}
<commit_msg>Fix test<commit_after>// Avoid deprecation warnings
#define HALIDE_ALLOW_DEPRECATED

#include "HalideRuntime.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "old_buffer_t.h"

int &get_pixel(buffer_t *buf, int x, int y) {
    return *((int *)(buf->host) +
             (x - buf->min[0]) * buf->stride[0] +
             (y - buf->min[1]) * buf->stride[1]);
}

extern "C" int extern_stage(buffer_t *in2, buffer_t *f, buffer_t *out) {
    if (in2->host == nullptr) {
        in2->extent[0] = out->extent[0];
        in2->min[0] = out->min[0];
        in2->extent[1] = out->extent[1];
        in2->min[1] = out->min[1] + 7;
    }
    if (f->host == nullptr) {
        f->extent[0] = out->extent[0];
        f->min[0] = out->min[0];
        f->extent[1] = out->extent[1];
        f->min[1] = out->min[1];
    }
    if (f->host && in2->host && out->host) {
        for (int y = 0; y < out->extent[1]; y++) {
            for (int x = 0; x < out->extent[0]; x++) {
                get_pixel(out, x, y) = get_pixel(in2, x, y + 7) + get_pixel(f, x, y);
            }
        }
    }
    return 0;
}

int main(int argc, char **argv) {
    buffer_t in1 = {0}, in2 = {0}, out = {0};
    int scalar_param = 4;

    out.host = (uint8_t *)malloc(60*40*sizeof(int));
    out.extent[0] = 60;
    out.extent[1] = 40;
    out.stride[0] = 1;
    out.stride[1] = 60;
    out.elem_size = 4;

    // Check bounds inference works
    int err = old_buffer_t_old_buffer_t(&in1, &in2, scalar_param, &out);
    if (err != 0) {
        printf("Pipeline returned non-zero exit status in bounds query mode: %d\n", err);
    }

    buffer_t correct_in1 = {0, NULL, {62, 44, 0, 0}, {1, 62, 0, 0}, {-1, -1, 0, 0}, 4, false, false, {0}};
    buffer_t correct_in2 = {0, NULL, {60, 47, 0, 0}, {1, 60, 0, 0}, {0, 0, 0, 0}, 4, false, false, {0}};

    if (memcmp(&correct_in1, &in1, sizeof(buffer_t))) {
        printf("Bounds inference gave wrong result for input 1\n");
        return -1;
    }

    if (memcmp(&correct_in2, &in2, sizeof(buffer_t))) {
        printf("Bounds inference gave wrong result for input 2\n");
        return -1;
    }

    // Allocate the inputs
    in1.host = (uint8_t *)malloc(in1.extent[0] * in1.extent[1] * in1.elem_size);
    in2.host = (uint8_t *)malloc(in2.extent[0] * in2.extent[1] * in2.elem_size);

    memset(in1.host, 1, in1.extent[0] * in1.extent[1] * in1.elem_size);
    memset(in2.host, 2, in2.extent[0] * in2.extent[1] * in2.elem_size);

    // Run the pipeline for real
    err = old_buffer_t_old_buffer_t(&in1, &in2, scalar_param, &out);
    if (err != 0) {
        printf("Pipeline returned non-zero exit status: %d\n", err);
    }

    for (int y = 0; y < out.extent[1]; y++) {
        for (int x = 0; x < out.extent[0]; x++) {
            int result = get_pixel(&out, x, y);
            int correct = 0x01010101 * 2 + 0x02020202 * 2 + scalar_param;
            if (result != correct) {
                printf("out(%d, %d) = %d instead of %d\n", x, y, result, correct);
                return -1;
            }
        }
    }

    printf("Success!\n");
    return 0;
}
<|endoftext|>


<commit_before>/**
 * Copyright (C) 2017 Jung-Sang Ahn <jungsang.ahn@gmail.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 <stdlib.h>

#include "skiplist.h"

//#define __SL_DEBUG
#ifdef __SL_DEBUG
    #include "skiplist_debug.h"
#else
    #define __SLD_RT_INS(e, n, t, c)
    #define __SLD_NC_INS(n, nn, t, c)
    #define __SLD_RT_RMV(e, n, t, c)
    #define __SLD_NC_RMV(n, nn, t, c)
    #define __SLD_BM(n)
    #define __SLD_ASSERT(cond)
#endif

static inline void _sl_node_init(SkiplistNode *node,
                                 size_t top_layer)
{
    if (top_layer > UINT8_MAX) {
        top_layer = UINT8_MAX;
    }

    node->isFullyLinked = false;
    node->beingModified = false;
    node->removed = false;

    if (node->topLayer != top_layer ||
        node->next == nullptr) {

        node->topLayer = top_layer;

        if (node->next) {
            delete[] node->next;
        }
        node->next = new std::atomic<SkiplistNode*>[top_layer+1];
    }
}

void skiplist_init(SkiplistRaw *slist,
                   skiplist_cmp_t *cmp_func) {
    _sl_node_init(&slist->head, slist->maxLayer);
    _sl_node_init(&slist->tail, slist->maxLayer);

    size_t layer;
    for (layer = 0; layer < slist->maxLayer; ++layer) {
        slist->head.next[layer] = &slist->tail;
        slist->tail.next[layer] = nullptr;
    }

    slist->head.isFullyLinked = slist->tail.isFullyLinked = true;
    slist->cmpFunc = cmp_func;
}

static inline int _sl_cmp(SkiplistRaw *slist,
                          SkiplistNode *a,
                          SkiplistNode *b)
{
    if (a == b) {
        return 0;
    }
    if (a == &slist->head ||
        b == &slist->tail) {
        return -1;
    }
    if (a == &slist->tail ||
        b == &slist->head) {
        return 1;
    }
    return slist->cmpFunc(a, b, slist->aux);
}

static inline bool _sl_valid_node(SkiplistNode *node) {
    return !node->removed && node->isFullyLinked;
}

static inline SkiplistNode* _sl_next(SkiplistRaw *slist,
                                     SkiplistNode *cur_node,
                                     int layer)
{
    SkiplistNode *next_node = cur_node->next[layer];
    while ( next_node && !_sl_valid_node(next_node) ) {
        next_node = next_node->next[layer];
    }
    return next_node;
}

static inline size_t _sl_decide_top_layer(SkiplistRaw *slist)
{
    size_t layer = 0;
    while (layer+1 < slist->maxLayer) {
        // coin filp
        if (rand() % slist->fanout == 0) {
            // grow: 1/fanout probability
            layer++;
        } else {
            // stop: 1 - 1/fanout probability
            break;
        }
    }
    return layer;
}

static inline void _sl_clr_flags(SkiplistNode** node_arr,
                                 int start_layer,
                                 int top_layer)
{
    int layer;
    for (layer = start_layer; layer <= top_layer; ++layer) {
        if ( layer == top_layer ||
             node_arr[layer] != node_arr[layer+1] ) {
            __SLD_ASSERT(node_arr[layer]->beingModified == true);
            node_arr[layer]->beingModified = false;
        }
    }
}

static inline bool _sl_valid_prev_next(SkiplistNode *prev,
                                       SkiplistNode *next) {
    return _sl_valid_node(prev) && _sl_valid_node(next);
}

void skiplist_insert(SkiplistRaw *slist,
                     SkiplistNode *node)
{
    int top_layer = _sl_decide_top_layer(slist);

    // init node before insertion
    _sl_node_init(node, top_layer);

    SkiplistNode* prevs[SKIPLIST_MAX_LAYER];
    SkiplistNode* nexts[SKIPLIST_MAX_LAYER];

insert_retry:
    int cmp = 0;
    int cur_layer = 0;
    int layer;
    SkiplistNode *cur_node = &slist->head;

    for (cur_layer = slist->maxLayer-1; cur_layer >= 0; --cur_layer) {
        do {
            SkiplistNode *next_node = _sl_next(slist, cur_node, cur_layer);
            cmp = _sl_cmp(slist, node, next_node);
            if (cmp > 0) {
                // cur_node->next < node
                // => move to next node
                cur_node = next_node;
                continue;
            }
            // node <= cur_node->next

            if (cur_layer <= top_layer) {
                prevs[cur_layer] = cur_node;
                nexts[cur_layer] = next_node;

                // both 'prev' and 'next' should be fully linked before
                // insertion, and no other thread should not modify 'prev'
                // at the same time.

                int error_code = 0;
                int locked_layer = cur_layer + 1;

                // check if prev node is duplicated with upper layer
                if (cur_layer < top_layer &&
                    prevs[cur_layer] == prevs[cur_layer+1]) {
                    // duplicate
                    // => which means that 'beingModified' flag is already true
                    // => do nothing
                } else {
                    bool expected = false;
                    if (prevs[cur_layer]->beingModified.
                            compare_exchange_strong(expected, true)) {
                        locked_layer = cur_layer;
                    } else {
                        error_code = -1;
                    }
                }

                if (error_code == 0 &&
                    !_sl_valid_prev_next(prevs[cur_layer], nexts[cur_layer])) {
                    error_code = -2;
                }

                if (error_code != 0) {
                    __SLD_RT_INS(error_code, node, top_layer, cur_layer);
                    _sl_clr_flags(prevs, locked_layer, top_layer);
                    goto insert_retry;
                }

                // set current node's pointers
                node->next[cur_layer] = nexts[cur_layer];

                if (_sl_next(slist, cur_node, cur_layer) != next_node) {
                    __SLD_NC_INS(cur_node, next_node, top_layer, cur_layer);
                    // clear including the current layer
                    // as we already set modification flag above.
                    _sl_clr_flags(prevs, cur_layer, top_layer);
                    goto insert_retry;
                }
            }

            if (cur_layer) {
                // non-bottom layer => go down
                break;
            }

            // bottom layer => insertion succeeded
            // change prev/next nodes' prev/next pointers from 0 ~ top_layer
            for (layer = 0; layer <= top_layer; ++layer) {
                prevs[layer]->next[layer] = node;
            }

            // now this node is fully linked
            node->isFullyLinked = true;

            // modification is done for all layers
            _sl_clr_flags(prevs, 0, top_layer);
            return;

        } while (cur_node != &slist->tail);
    }
}

SkiplistNode* skiplist_find(SkiplistRaw *slist,
                            SkiplistNode *query)
{
    int cmp = 0;
    int cur_layer = 0;
    SkiplistNode *cur_node = &slist->head;

    for (cur_layer = slist->maxLayer-1 ; cur_layer >= 0; --cur_layer) {
        do {
            SkiplistNode *next_node = _sl_next(slist, cur_node, cur_layer);

            cmp = _sl_cmp(slist, query, next_node);
            if (cmp > 0) {
                // next_node < query
                // => move to next node
                cur_node = next_node;
                continue;
            } else if (cmp == 0) {
                // query == next_node .. return
                return next_node;
            }

            // query < next_node
            if (cur_layer) {
                // non-bottom layer => go down
                break;
            }

            // bottom layer => means that exact match doesn't exist.
            return nullptr;

        } while (cur_node != &slist->tail);
    }

    return nullptr;
}

SkiplistNode* skiplist_find_smaller(SkiplistRaw *slist,
                                    SkiplistNode *query)
{
    int cmp = 0;
    int cur_layer = 0;
    SkiplistNode *cur_node = &slist->head;

    for (cur_layer = slist->maxLayer-1 ; cur_layer >= 0; --cur_layer) {
        do {
            SkiplistNode *next_node = _sl_next(slist, cur_node, cur_layer);

            cmp = _sl_cmp(slist, query, next_node);
            if (cmp > 0) {
                // next_node < query
                // => move to next node
                cur_node = next_node;
                continue;
            }

            // otherwise: query <= next_node
            if (cur_layer) {
                // non-bottom layer => go down
                break;
            }

            // bottom layer => return cur_node
            return cur_node;

        } while (cur_node != &slist->tail);
    }

    return nullptr;
}

int skiplist_erase_node(SkiplistRaw *slist,
                        SkiplistNode *node)
{
    int top_layer = node->topLayer;

    if (node->removed) {
        // already removed
        return -1;
    }

    SkiplistNode* prevs[SKIPLIST_MAX_LAYER];
    SkiplistNode* nexts[SKIPLIST_MAX_LAYER];

    bool expected = false;
    if (!node->beingModified.compare_exchange_strong(expected, true)) {
        // already being modified .. fail
        __SLD_BM(node);
        return -2;
    }

    // clear removed flag first, so that reader cannot read this node.
    node->removed = true;

erase_node_retry:
    if (!node->isFullyLinked) {
        // already unlinked .. remove is done by other thread
        return -3;
    }

    int cmp = 0;
    int cur_layer = slist->maxLayer - 1;
    SkiplistNode *cur_node = &slist->head;

    for (; cur_layer >= 0; --cur_layer) {
        do {
            SkiplistNode *next_node = _sl_next(slist, cur_node, cur_layer);

            cmp = _sl_cmp(slist, node, next_node);
            if (cmp > 0) {
                // 'next_node' < 'node'
                // => move to next node
                cur_node = next_node;
                continue;
            }
            // otherwise: 'node' <= 'next_node'

            if (cur_layer <= top_layer) {
                prevs[cur_layer] = cur_node;
                // note: 'next_node' should not be 'node',
                //       as 'removed' flag is already set.
                __SLD_ASSERT(next_node != node);
                nexts[cur_layer] = next_node;

                // check if prev node duplicates with upper layer
                int error_code = 0;
                int locked_layer = cur_layer + 1;
                if (cur_layer < top_layer &&
                    prevs[cur_layer] == prevs[cur_layer+1]) {
                    // duplicate with upper layer
                    // => which means that 'beingModified' flag is already true
                    // => do nothing.
                } else {
                    expected = false;
                    if (prevs[cur_layer]->beingModified.
                            compare_exchange_strong(expected, true)) {
                        locked_layer = cur_layer;
                    } else {
                        error_code = -1;
                    }
                }

                if (error_code == 0 &&
                    !_sl_valid_prev_next(prevs[cur_layer], nexts[cur_layer])) {
                    error_code = -2;
                }

                if (error_code != 0) {
                    __SLD_RT_RMV(error_code, node, top_layer, cur_layer);
                    _sl_clr_flags(prevs, locked_layer, top_layer);
                    goto erase_node_retry;
                }

                if (_sl_next(slist, cur_node, cur_layer) != nexts[cur_layer]) {
                    __SLD_NC_RMV(cur_node, nexts[cur_layer], top_layer, cur_layer);
                    _sl_clr_flags(prevs, cur_layer, top_layer);
                    goto erase_node_retry;
                }
            }

            // go down
            break;

        } while (cur_node != &slist->tail);
    }

    // bottom layer => removal succeeded.
    // change prev nodes' next pointer from 0 ~ top_layer
    for (cur_layer = 0; cur_layer <= top_layer; ++cur_layer) {
        prevs[cur_layer]->next[cur_layer] = nexts[cur_layer];
    }

    // now this node is unlinked
    node->isFullyLinked = false;

    // modification is done for all layers
    _sl_clr_flags(prevs, 0, top_layer);

    node->beingModified = false;
    return 0;
}

int skiplist_erase(SkiplistRaw *slist,
                   SkiplistNode *query)
{
    SkiplistNode *found = skiplist_find(slist, query);
    if (!found) {
        // key not found
        return -4;
    }

    int ret = 0;
    do {
        ret = skiplist_erase_node(slist, found);
        // if ret == -2, other thread is accessing the same node
        // at the same time. try again.
    } while (ret == -2);
    return ret;
}

SkiplistNode* skiplist_next(SkiplistRaw *slist,
                            SkiplistNode *node) {
    SkiplistNode *next = _sl_next(slist, node, 0);
    if (next == &slist->tail) {
        return nullptr;
    }
    return  next;
}

SkiplistNode* skiplist_prev(SkiplistRaw *slist,
                            SkiplistNode *node) {
    SkiplistNode *prev = skiplist_find_smaller(slist, node);
    if (prev == &slist->head) {
        return nullptr;
    }
    return  prev;
}

SkiplistNode* skiplist_begin(SkiplistRaw *slist) {
    SkiplistNode *next = _sl_next(slist, &slist->head, 0);
    if (next == &slist->tail) {
        return nullptr;
    }
    return next;
}

SkiplistNode* skiplist_end(SkiplistRaw *slist) {
    return skiplist_prev(slist, &slist->tail);
}

<commit_msg>Fix whitespaces<commit_after>/**
 * Copyright (C) 2017 Jung-Sang Ahn <jungsang.ahn@gmail.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 <stdlib.h>

#include "skiplist.h"

//#define __SL_DEBUG
#ifdef __SL_DEBUG
    #include "skiplist_debug.h"
#else
    #define __SLD_RT_INS(e, n, t, c)
    #define __SLD_NC_INS(n, nn, t, c)
    #define __SLD_RT_RMV(e, n, t, c)
    #define __SLD_NC_RMV(n, nn, t, c)
    #define __SLD_BM(n)
    #define __SLD_ASSERT(cond)
#endif

static inline void _sl_node_init(SkiplistNode *node,
                                 size_t top_layer)
{
    if (top_layer > UINT8_MAX) {
        top_layer = UINT8_MAX;
    }

    node->isFullyLinked = false;
    node->beingModified = false;
    node->removed = false;

    if (node->topLayer != top_layer ||
        node->next == nullptr) {

        node->topLayer = top_layer;

        if (node->next) {
            delete[] node->next;
        }
        node->next = new std::atomic<SkiplistNode*>[top_layer+1];
    }
}

void skiplist_init(SkiplistRaw *slist,
                   skiplist_cmp_t *cmp_func) {
    _sl_node_init(&slist->head, slist->maxLayer);
    _sl_node_init(&slist->tail, slist->maxLayer);

    size_t layer;
    for (layer = 0; layer < slist->maxLayer; ++layer) {
        slist->head.next[layer] = &slist->tail;
        slist->tail.next[layer] = nullptr;
    }

    slist->head.isFullyLinked = slist->tail.isFullyLinked = true;
    slist->cmpFunc = cmp_func;
}

static inline int _sl_cmp(SkiplistRaw *slist,
                          SkiplistNode *a,
                          SkiplistNode *b)
{
    if (a == b) {
        return 0;
    }
    if (a == &slist->head ||
        b == &slist->tail) {
        return -1;
    }
    if (a == &slist->tail ||
        b == &slist->head) {
        return 1;
    }
    return slist->cmpFunc(a, b, slist->aux);
}

static inline bool _sl_valid_node(SkiplistNode *node) {
    return !node->removed && node->isFullyLinked;
}

static inline SkiplistNode* _sl_next(SkiplistRaw *slist,
                                     SkiplistNode *cur_node,
                                     int layer)
{
    SkiplistNode *next_node = cur_node->next[layer];
    while ( next_node && !_sl_valid_node(next_node) ) {
        next_node = next_node->next[layer];
    }
    return next_node;
}

static inline size_t _sl_decide_top_layer(SkiplistRaw *slist)
{
    size_t layer = 0;
    while (layer+1 < slist->maxLayer) {
        // coin filp
        if (rand() % slist->fanout == 0) {
            // grow: 1/fanout probability
            layer++;
        } else {
            // stop: 1 - 1/fanout probability
            break;
        }
    }
    return layer;
}

static inline void _sl_clr_flags(SkiplistNode** node_arr,
                                 int start_layer,
                                 int top_layer)
{
    int layer;
    for (layer = start_layer; layer <= top_layer; ++layer) {
        if ( layer == top_layer ||
             node_arr[layer] != node_arr[layer+1] ) {
            __SLD_ASSERT(node_arr[layer]->beingModified == true);
            node_arr[layer]->beingModified = false;
        }
    }
}

static inline bool _sl_valid_prev_next(SkiplistNode *prev,
                                       SkiplistNode *next) {
    return _sl_valid_node(prev) && _sl_valid_node(next);
}

void skiplist_insert(SkiplistRaw *slist,
                     SkiplistNode *node)
{
    int top_layer = _sl_decide_top_layer(slist);

    // init node before insertion
    _sl_node_init(node, top_layer);

    SkiplistNode* prevs[SKIPLIST_MAX_LAYER];
    SkiplistNode* nexts[SKIPLIST_MAX_LAYER];

insert_retry:
    int cmp = 0;
    int cur_layer = 0;
    int layer;
    SkiplistNode *cur_node = &slist->head;

    for (cur_layer = slist->maxLayer-1; cur_layer >= 0; --cur_layer) {
        do {
            SkiplistNode *next_node = _sl_next(slist, cur_node, cur_layer);
            cmp = _sl_cmp(slist, node, next_node);
            if (cmp > 0) {
                // next_node < node
                // => move to next node
                cur_node = next_node;
                continue;
            }
            // otherwise: node <= next_node

            if (cur_layer <= top_layer) {
                prevs[cur_layer] = cur_node;
                nexts[cur_layer] = next_node;

                // both 'prev' and 'next' should be fully linked before
                // insertion, and no other thread should not modify 'prev'
                // at the same time.

                int error_code = 0;
                int locked_layer = cur_layer + 1;

                // check if prev node is duplicated with upper layer
                if (cur_layer < top_layer &&
                    prevs[cur_layer] == prevs[cur_layer+1]) {
                    // duplicate
                    // => which means that 'beingModified' flag is already true
                    // => do nothing
                } else {
                    bool expected = false;
                    if (prevs[cur_layer]->beingModified.
                            compare_exchange_strong(expected, true)) {
                        locked_layer = cur_layer;
                    } else {
                        error_code = -1;
                    }
                }

                if (error_code == 0 &&
                    !_sl_valid_prev_next(prevs[cur_layer], nexts[cur_layer])) {
                    error_code = -2;
                }

                if (error_code != 0) {
                    __SLD_RT_INS(error_code, node, top_layer, cur_layer);
                    _sl_clr_flags(prevs, locked_layer, top_layer);
                    goto insert_retry;
                }

                // set current node's pointers
                node->next[cur_layer] = nexts[cur_layer];

                if (_sl_next(slist, cur_node, cur_layer) != next_node) {
                    __SLD_NC_INS(cur_node, next_node, top_layer, cur_layer);
                    // clear including the current layer
                    // as we already set modification flag above.
                    _sl_clr_flags(prevs, cur_layer, top_layer);
                    goto insert_retry;
                }
            }

            if (cur_layer) {
                // non-bottom layer => go down
                break;
            }

            // bottom layer => insertion succeeded
            // change prev/next nodes' prev/next pointers from 0 ~ top_layer
            for (layer = 0; layer <= top_layer; ++layer) {
                prevs[layer]->next[layer] = node;
            }

            // now this node is fully linked
            node->isFullyLinked = true;

            // modification is done for all layers
            _sl_clr_flags(prevs, 0, top_layer);
            return;

        } while (cur_node != &slist->tail);
    }
}

SkiplistNode* skiplist_find(SkiplistRaw *slist,
                            SkiplistNode *query)
{
    int cmp = 0;
    int cur_layer = 0;
    SkiplistNode *cur_node = &slist->head;

    for (cur_layer = slist->maxLayer-1; cur_layer >= 0; --cur_layer) {
        do {
            SkiplistNode *next_node = _sl_next(slist, cur_node, cur_layer);
            cmp = _sl_cmp(slist, query, next_node);
            if (cmp > 0) {
                // next_node < query
                // => move to next node
                cur_node = next_node;
                continue;
            } else if (cmp == 0) {
                // query == next_node .. return
                return next_node;
            }

            // query < next_node
            if (cur_layer) {
                // non-bottom layer => go down
                break;
            }

            // bottom layer => means that exact match doesn't exist.
            return nullptr;

        } while (cur_node != &slist->tail);
    }

    return nullptr;
}

SkiplistNode* skiplist_find_smaller(SkiplistRaw *slist,
                                    SkiplistNode *query)
{
    int cmp = 0;
    int cur_layer = 0;
    SkiplistNode *cur_node = &slist->head;

    for (cur_layer = slist->maxLayer-1; cur_layer >= 0; --cur_layer) {
        do {
            SkiplistNode *next_node = _sl_next(slist, cur_node, cur_layer);
            cmp = _sl_cmp(slist, query, next_node);
            if (cmp > 0) {
                // next_node < query
                // => move to next node
                cur_node = next_node;
                continue;
            }

            // otherwise: query <= next_node
            if (cur_layer) {
                // non-bottom layer => go down
                break;
            }

            // bottom layer => return cur_node
            return cur_node;

        } while (cur_node != &slist->tail);
    }

    return nullptr;
}

int skiplist_erase_node(SkiplistRaw *slist,
                        SkiplistNode *node)
{
    int top_layer = node->topLayer;

    if (node->removed) {
        // already removed
        return -1;
    }

    SkiplistNode* prevs[SKIPLIST_MAX_LAYER];
    SkiplistNode* nexts[SKIPLIST_MAX_LAYER];

    bool expected = false;
    if (!node->beingModified.compare_exchange_strong(expected, true)) {
        // already being modified .. fail
        __SLD_BM(node);
        return -2;
    }

    // clear removed flag first, so that reader cannot read this node.
    node->removed = true;

erase_node_retry:
    if (!node->isFullyLinked) {
        // already unlinked .. remove is done by other thread
        return -3;
    }

    int cmp = 0;
    int cur_layer = slist->maxLayer - 1;
    SkiplistNode *cur_node = &slist->head;

    for (; cur_layer >= 0; --cur_layer) {
        do {
            SkiplistNode *next_node = _sl_next(slist, cur_node, cur_layer);

            cmp = _sl_cmp(slist, node, next_node);
            if (cmp > 0) {
                // 'next_node' < 'node'
                // => move to next node
                cur_node = next_node;
                continue;
            }
            // otherwise: 'node' <= 'next_node'

            if (cur_layer <= top_layer) {
                prevs[cur_layer] = cur_node;
                // note: 'next_node' should not be 'node',
                //       as 'removed' flag is already set.
                __SLD_ASSERT(next_node != node);
                nexts[cur_layer] = next_node;

                // check if prev node duplicates with upper layer
                int error_code = 0;
                int locked_layer = cur_layer + 1;
                if (cur_layer < top_layer &&
                    prevs[cur_layer] == prevs[cur_layer+1]) {
                    // duplicate with upper layer
                    // => which means that 'beingModified' flag is already true
                    // => do nothing.
                } else {
                    expected = false;
                    if (prevs[cur_layer]->beingModified.
                            compare_exchange_strong(expected, true)) {
                        locked_layer = cur_layer;
                    } else {
                        error_code = -1;
                    }
                }

                if (error_code == 0 &&
                    !_sl_valid_prev_next(prevs[cur_layer], nexts[cur_layer])) {
                    error_code = -2;
                }

                if (error_code != 0) {
                    __SLD_RT_RMV(error_code, node, top_layer, cur_layer);
                    _sl_clr_flags(prevs, locked_layer, top_layer);
                    goto erase_node_retry;
                }

                if (_sl_next(slist, cur_node, cur_layer) != nexts[cur_layer]) {
                    __SLD_NC_RMV(cur_node, nexts[cur_layer], top_layer, cur_layer);
                    _sl_clr_flags(prevs, cur_layer, top_layer);
                    goto erase_node_retry;
                }
            }

            // go down
            break;

        } while (cur_node != &slist->tail);
    }

    // bottom layer => removal succeeded.
    // change prev nodes' next pointer from 0 ~ top_layer
    for (cur_layer = 0; cur_layer <= top_layer; ++cur_layer) {
        prevs[cur_layer]->next[cur_layer] = nexts[cur_layer];
    }

    // now this node is unlinked
    node->isFullyLinked = false;

    // modification is done for all layers
    _sl_clr_flags(prevs, 0, top_layer);

    node->beingModified = false;
    return 0;
}

int skiplist_erase(SkiplistRaw *slist,
                   SkiplistNode *query)
{
    SkiplistNode *found = skiplist_find(slist, query);
    if (!found) {
        // key not found
        return -4;
    }

    int ret = 0;
    do {
        ret = skiplist_erase_node(slist, found);
        // if ret == -2, other thread is accessing the same node
        // at the same time. try again.
    } while (ret == -2);
    return ret;
}

SkiplistNode* skiplist_next(SkiplistRaw *slist,
                            SkiplistNode *node) {
    SkiplistNode *next = _sl_next(slist, node, 0);
    if (next == &slist->tail) {
        return nullptr;
    }
    return next;
}

SkiplistNode* skiplist_prev(SkiplistRaw *slist,
                            SkiplistNode *node) {
    SkiplistNode *prev = skiplist_find_smaller(slist, node);
    if (prev == &slist->head) {
        return nullptr;
    }
    return prev;
}

SkiplistNode* skiplist_begin(SkiplistRaw *slist) {
    SkiplistNode *next = _sl_next(slist, &slist->head, 0);
    if (next == &slist->tail) {
        return nullptr;
    }
    return next;
}

SkiplistNode* skiplist_end(SkiplistRaw *slist) {
    return skiplist_prev(slist, &slist->tail);
}

<|endoftext|>


<commit_before>/**
 * @brief Test cases for materialization node.
 *
 * Copyright(c) 2015, CMU
 */

#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

#include "gtest/gtest.h"

#include "catalog/manager.h"
#include "catalog/schema.h"
#include "common/types.h"
#include "common/value_factory.h"
#include "executor/logical_tile.h"
#include "executor/logical_tile_factory.h"
#include "executor/materialization_executor.h"
#include "planner/abstract_plan_node.h"
#include "planner/materialization_node.h"
#include "storage/tile.h"
#include "storage/tile_group.h"
#include "storage/tuple.h"
#include "storage/vm_backend.h"

#include "executor/executor_tests_util.h"
#include "executor/mock_executor.h"
#include "harness.h"

using ::testing::IsNull;
using ::testing::NotNull;
using ::testing::Return;

namespace nstore {
namespace test {

namespace {

/**
 * @brief Populates the tiles in the given tile-group in a specific manner.
 * @param tile_group Tile-group to populate with values.
 * @param num_rows Number of tuples to insert.
 *
 * This is a convenience function for the test cases.
 */
void PopulateTiles(storage::TileGroup *tile_group, int num_rows) {
  // Create tuple schema from tile schemas.
  std::vector<catalog::Schema> &tile_schemas = tile_group->GetTileSchemas();
  std::unique_ptr<catalog::Schema> schema(
    catalog::Schema::AppendSchemaList(tile_schemas));

  // Ensure that the tile group created by ExecutorTestsUtil is as expected.
  assert(tile_schemas.size() == 2);
  assert(schema->GetColumnCount() == 4);

  // Insert tuples into tile_group.
  const bool allocate = true;
  const txn_id_t txn_id = GetTransactionId();
  for (int i = 0; i < num_rows; i++) {
    storage::Tuple tuple(schema.get(), allocate);
    tuple.SetValue(0, ValueFactory::GetIntegerValue(10 * i));
    tuple.SetValue(1, ValueFactory::GetIntegerValue(10 * i + 1));
    tuple.SetValue(2, ValueFactory::GetTinyIntValue(10 * i + 2));
    tuple.SetValue(
        3,
        ValueFactory::GetStringValue(std::to_string(10 * i + 3),
        tile_group->GetTilePool(1)));
    tile_group->InsertTuple(txn_id, &tuple);
  }
}

} // namespace

// "Pass-through" test case. There is nothing to materialize as
// there is only one base tile in the logical tile.
TEST(MaterializationTests, SingleBaseTileTest) {
  storage::VMBackend backend;
  const int tuple_count = 9;
  std::unique_ptr<storage::TileGroup> tile_group(
      ExecutorTestsUtil::CreateSimpleTileGroup(
        &backend,
        tuple_count));

  PopulateTiles(tile_group.get(), tuple_count);

  // Create logical tile from single base tile.
  storage::Tile *source_base_tile = tile_group->GetTile(0);
  const bool own_base_tile = false;
  std::unique_ptr<executor::LogicalTile> source_tile(
      executor::LogicalTileFactory::WrapBaseTiles(
        { source_base_tile },
        own_base_tile));

  // Create materialization node for this test.
  //TODO This should be deleted soon... Should we use default copy constructor?
  std::unique_ptr<catalog::Schema> output_schema(catalog::Schema::CopySchema(
      &source_base_tile->GetSchema()));
  std::unordered_map<id_t, id_t> old_to_new_cols;

  unsigned int column_count = output_schema->GetColumnCount();
  for (id_t col = 0; col < column_count; col++) {
    // Create identity mapping.
    old_to_new_cols[col] = col;
  }
  planner::MaterializationNode node(
      std::move(old_to_new_cols),
      output_schema.release());

  // Pass them through materialization executor.
  executor::MaterializationExecutor executor(&node);
  MockExecutor child_executor;
  executor.AddChild(&child_executor);

  // Uneventful init...
  EXPECT_CALL(child_executor, SubInit())
    .WillOnce(Return(true));
  EXPECT_TRUE(executor.Init());

  // Where the main work takes place...
  EXPECT_CALL(child_executor, SubGetNextTile())
    .WillOnce(Return(source_tile.release()))
    .WillOnce(Return(nullptr));

  std::unique_ptr<executor::LogicalTile> result_logical_tile(
    executor.GetNextTile());
  EXPECT_THAT(result_logical_tile, NotNull());
  EXPECT_THAT(executor.GetNextTile(), IsNull());

  // Verify that logical tile is only made up of a single base tile.
  int num_cols = result_logical_tile->NumCols();
  EXPECT_EQ(2, num_cols);
  storage::Tile *result_base_tile = result_logical_tile->GetBaseTile(0);
  EXPECT_THAT(result_base_tile, NotNull());
  EXPECT_TRUE(source_base_tile != result_base_tile);
  EXPECT_EQ(result_logical_tile->GetBaseTile(1), result_base_tile);

  // Check that the base tile has the correct values.
  for (int i = 0; i < tuple_count; i++) {
    EXPECT_EQ(ValueFactory::GetIntegerValue(10 * i),
              result_base_tile->GetValue(i, 0));
    EXPECT_EQ(ValueFactory::GetIntegerValue(10 * i + 1),
              result_base_tile->GetValue(i, 1));

    // Double check that logical tile is functioning.
    EXPECT_EQ(result_base_tile->GetValue(i, 0),
              result_logical_tile->GetValue(0, i));
    EXPECT_EQ(result_base_tile->GetValue(i, 1),
              result_logical_tile->GetValue(1, i));
  }
}

// Materializing logical tile composed of two base tiles.
// The materialized tile's columns are reordered as well.
TEST(MaterializationTests, TwoBaseTilesWithReorderTest) {
  storage::VMBackend backend;
  const int tuple_count = 9;
  std::unique_ptr<storage::TileGroup> tile_group(
      ExecutorTestsUtil::CreateSimpleTileGroup(
        &backend,
        tuple_count));

  PopulateTiles(tile_group.get(), tuple_count);

  //TODO WaTeRmArK
  // Create logical tile from two base tiles.
  storage::Tile *source_base_tile1 = tile_group->GetTile(0);
  const bool own_base_tiles = false;
  std::unique_ptr<executor::LogicalTile> source_tile(
      executor::LogicalTileFactory::WrapBaseTiles(
          { source_base_tile1 },
          own_base_tiles));

  // Create materialization node for this test.
  //TODO This should be deleted soon... Should we use default copy constructor?
  std::unique_ptr<catalog::Schema> output_schema(catalog::Schema::CopySchema(
      &source_base_tile1->GetSchema()));
  std::unordered_map<id_t, id_t> old_to_new_cols;

  unsigned int column_count = output_schema->GetColumnCount();
  for (id_t col = 0; col < column_count; col++) {
    // Create identity mapping.
    old_to_new_cols[col] = col;
  }
  planner::MaterializationNode node(
      std::move(old_to_new_cols),
      output_schema.release());

  // Pass them through materialization executor.
  executor::MaterializationExecutor executor(&node);
  MockExecutor child_executor;
  executor.AddChild(&child_executor);

  // Uneventful init...
  EXPECT_CALL(child_executor, SubInit())
    .WillOnce(Return(true));
  EXPECT_TRUE(executor.Init());

  // Where the main work takes place...
  EXPECT_CALL(child_executor, SubGetNextTile())
    .WillOnce(Return(source_tile.release()))
    .WillOnce(Return(nullptr));

  std::unique_ptr<executor::LogicalTile> result_logical_tile(
    executor.GetNextTile());
  EXPECT_THAT(result_logical_tile, NotNull());
  EXPECT_THAT(executor.GetNextTile(), IsNull());

  // Verify that logical tile is only made up of a single base tile.
  int num_cols = result_logical_tile->NumCols();
  EXPECT_EQ(2, num_cols);
  storage::Tile *result_base_tile = result_logical_tile->GetBaseTile(0);
  EXPECT_THAT(result_base_tile, NotNull());
  EXPECT_EQ(result_logical_tile->GetBaseTile(1), result_base_tile);

  // Check that the base tile has the correct values.
  for (int i = 0; i < tuple_count; i++) {
    EXPECT_EQ(ValueFactory::GetIntegerValue(10 * i),
              result_base_tile->GetValue(i, 0));
    EXPECT_EQ(ValueFactory::GetIntegerValue(10 * i + 1),
              result_base_tile->GetValue(i, 1));

    // Double check that logical tile is functioning.
    EXPECT_EQ(result_base_tile->GetValue(i, 0),
              result_logical_tile->GetValue(0, i));
    EXPECT_EQ(result_base_tile->GetValue(i, 1),
              result_logical_tile->GetValue(1, i));
  }
}

} // namespace test
} // namespace nstore
<commit_msg>Finished materialization tests.<commit_after>/**
 * @brief Test cases for materialization node.
 *
 * Copyright(c) 2015, CMU
 */

#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

#include "gtest/gtest.h"

#include "catalog/manager.h"
#include "catalog/schema.h"
#include "common/types.h"
#include "common/value_factory.h"
#include "executor/logical_tile.h"
#include "executor/logical_tile_factory.h"
#include "executor/materialization_executor.h"
#include "planner/abstract_plan_node.h"
#include "planner/materialization_node.h"
#include "storage/tile.h"
#include "storage/tile_group.h"
#include "storage/tuple.h"
#include "storage/vm_backend.h"

#include "executor/executor_tests_util.h"
#include "executor/mock_executor.h"
#include "harness.h"

using ::testing::IsNull;
using ::testing::NotNull;
using ::testing::Return;

namespace nstore {
namespace test {

namespace {

/**
 * @brief Populates the tiles in the given tile-group in a specific manner.
 * @param tile_group Tile-group to populate with values.
 * @param num_rows Number of tuples to insert.
 *
 * This is a convenience function for the test cases.
 */
void PopulateTiles(storage::TileGroup *tile_group, int num_rows) {
  // Create tuple schema from tile schemas.
  std::vector<catalog::Schema> &tile_schemas = tile_group->GetTileSchemas();
  std::unique_ptr<catalog::Schema> schema(
    catalog::Schema::AppendSchemaList(tile_schemas));

  // Ensure that the tile group created by ExecutorTestsUtil is as expected.
  assert(tile_schemas.size() == 2);
  assert(schema->GetColumnCount() == 4);

  // Insert tuples into tile_group.
  const bool allocate = true;
  const txn_id_t txn_id = GetTransactionId();
  for (int i = 0; i < num_rows; i++) {
    storage::Tuple tuple(schema.get(), allocate);
    tuple.SetValue(0, ValueFactory::GetIntegerValue(10 * i));
    tuple.SetValue(1, ValueFactory::GetIntegerValue(10 * i + 1));
    tuple.SetValue(2, ValueFactory::GetTinyIntValue(10 * i + 2));
    tuple.SetValue(
        3,
        ValueFactory::GetStringValue(std::to_string(10 * i + 3),
        tile_group->GetTilePool(1)));
    tile_group->InsertTuple(txn_id, &tuple);
  }
}

} // namespace

// "Pass-through" test case. There is nothing to materialize as
// there is only one base tile in the logical tile.
TEST(MaterializationTests, SingleBaseTileTest) {
  storage::VMBackend backend;
  const int tuple_count = 9;
  std::unique_ptr<storage::TileGroup> tile_group(
      ExecutorTestsUtil::CreateSimpleTileGroup(
        &backend,
        tuple_count));

  PopulateTiles(tile_group.get(), tuple_count);

  // Create logical tile from single base tile.
  storage::Tile *source_base_tile = tile_group->GetTile(0);
  const bool own_base_tiles = false;
  std::unique_ptr<executor::LogicalTile> source_logical_tile(
      executor::LogicalTileFactory::WrapBaseTiles(
        { source_base_tile },
        own_base_tiles));

  // Create materialization node for this test.
  std::unique_ptr<catalog::Schema> output_schema(catalog::Schema::CopySchema(
      &source_base_tile->GetSchema()));
  std::unordered_map<id_t, id_t> old_to_new_cols;

  unsigned int column_count = output_schema->GetColumnCount();
  for (id_t col = 0; col < column_count; col++) {
    // Create identity mapping.
    old_to_new_cols[col] = col;
  }
  planner::MaterializationNode node(
      std::move(old_to_new_cols),
      output_schema.release());

  // Pass them through materialization executor.
  executor::MaterializationExecutor executor(&node);
  MockExecutor child_executor;
  executor.AddChild(&child_executor);

  // Uneventful init...
  EXPECT_CALL(child_executor, SubInit())
    .WillOnce(Return(true));
  EXPECT_TRUE(executor.Init());

  // Where the main work takes place...
  EXPECT_CALL(child_executor, SubGetNextTile())
    .WillOnce(Return(source_logical_tile.release()))
    .WillOnce(Return(nullptr));

  std::unique_ptr<executor::LogicalTile> result_logical_tile(
    executor.GetNextTile());
  EXPECT_THAT(result_logical_tile, NotNull());
  EXPECT_THAT(executor.GetNextTile(), IsNull());

  // Verify that logical tile is only made up of a single base tile.
  int num_cols = result_logical_tile->NumCols();
  EXPECT_EQ(2, num_cols);
  storage::Tile *result_base_tile = result_logical_tile->GetBaseTile(0);
  EXPECT_THAT(result_base_tile, NotNull());
  EXPECT_TRUE(source_base_tile != result_base_tile);
  EXPECT_EQ(result_logical_tile->GetBaseTile(1), result_base_tile);

  // Check that the base tile has the correct values.
  for (int i = 0; i < tuple_count; i++) {
    EXPECT_EQ(ValueFactory::GetIntegerValue(10 * i),
              result_base_tile->GetValue(i, 0));
    EXPECT_EQ(ValueFactory::GetIntegerValue(10 * i + 1),
              result_base_tile->GetValue(i, 1));

    // Double check that logical tile is functioning.
    EXPECT_EQ(result_base_tile->GetValue(i, 0),
              result_logical_tile->GetValue(0, i));
    EXPECT_EQ(result_base_tile->GetValue(i, 1),
              result_logical_tile->GetValue(1, i));
  }
}

// Materializing logical tile composed of two base tiles.
// The materialized tile's output columns are reordered.
// Also, one of the columns is dropped.
TEST(MaterializationTests, TwoBaseTilesWithReorderTest) {
  storage::VMBackend backend;
  const int tuple_count = 9;
  std::unique_ptr<storage::TileGroup> tile_group(
      ExecutorTestsUtil::CreateSimpleTileGroup(
        &backend,
        tuple_count));

  PopulateTiles(tile_group.get(), tuple_count);

  // Create logical tile from two base tiles.
  const std::vector<storage::Tile *> source_base_tiles =
    { tile_group->GetTile(0), tile_group->GetTile(1) };
  const bool own_base_tiles = false;
  std::unique_ptr<executor::LogicalTile> source_tile(
      executor::LogicalTileFactory::WrapBaseTiles(
          source_base_tiles,
          own_base_tiles));

  // Create materialization node for this test.
  // Construct output schema. We drop column 3 and reorder the others to 3,1,0.
  std::vector<catalog::ColumnInfo> output_columns;
  // Note that Column 3 in the tile group is column 1 in the second tile.
  output_columns.push_back(source_base_tiles[1]->GetSchema().GetColumnInfo(1));
  output_columns.push_back(source_base_tiles[0]->GetSchema().GetColumnInfo(1));
  output_columns.push_back(source_base_tiles[0]->GetSchema().GetColumnInfo(0));
  std::unique_ptr<catalog::Schema> output_schema(
      new catalog::Schema(output_columns));

  // Construct mapping using the ordering mentioned above.
  std::unordered_map<id_t, id_t> old_to_new_cols;
  old_to_new_cols[3] = 0;
  old_to_new_cols[1] = 1;
  old_to_new_cols[0] = 2;
  planner::MaterializationNode node(
      std::move(old_to_new_cols),
      output_schema.release());

  // Pass them through materialization executor.
  executor::MaterializationExecutor executor(&node);
  MockExecutor child_executor;
  executor.AddChild(&child_executor);

  // Uneventful init...
  EXPECT_CALL(child_executor, SubInit())
    .WillOnce(Return(true));
  EXPECT_TRUE(executor.Init());

  // Where the main work takes place...
  EXPECT_CALL(child_executor, SubGetNextTile())
    .WillOnce(Return(source_tile.release()))
    .WillOnce(Return(nullptr));

  std::unique_ptr<executor::LogicalTile> result_logical_tile(
    executor.GetNextTile());
  EXPECT_THAT(result_logical_tile, NotNull());
  EXPECT_THAT(executor.GetNextTile(), IsNull());

  // Verify that logical tile is only made up of a single base tile.
  int num_cols = result_logical_tile->NumCols();
  EXPECT_EQ(3, num_cols);
  storage::Tile *result_base_tile = result_logical_tile->GetBaseTile(0);
  EXPECT_THAT(result_base_tile, NotNull());
  EXPECT_EQ(result_base_tile, result_logical_tile->GetBaseTile(1));
  EXPECT_EQ(result_base_tile, result_logical_tile->GetBaseTile(2));

  // Check that the base tile has the correct values.
  for (int i = 0; i < tuple_count; i++) {
    // Output column 2.
    EXPECT_EQ(ValueFactory::GetIntegerValue(10 * i),
              result_base_tile->GetValue(i, 2));
    // Output column 1.
    EXPECT_EQ(ValueFactory::GetIntegerValue(10 * i + 1),
              result_base_tile->GetValue(i, 1));
    // Output column 0.
    Value string_value(
        ValueFactory::GetStringValue(std::to_string(10 * i + 3)));
    EXPECT_EQ(string_value,
              result_base_tile->GetValue(i, 0));
    string_value.FreeUninlinedData();

    // Double check that logical tile is functioning.
    EXPECT_EQ(result_base_tile->GetValue(i, 0),
              result_logical_tile->GetValue(0, i));
    EXPECT_EQ(result_base_tile->GetValue(i, 1),
              result_logical_tile->GetValue(1, i));
    EXPECT_EQ(result_base_tile->GetValue(i, 2),
              result_logical_tile->GetValue(2, i));
  }
}

} // namespace test
} // namespace nstore
<|endoftext|>