<commit_before>/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 2020-2021. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * %CopyrightEnd%
 */

#include <algorithm>
#include <float.h>

#include "beam_asm.hpp"
using namespace asmjit;

#ifdef BEAMASM_DUMP_SIZES
#    include <mutex>

typedef std::pair<Uint64, Uint64> op_stats;

static std::unordered_map<char *, op_stats> sizes;
static std::mutex size_lock;

extern "C" void beamasm_dump_sizes() {
    std::lock_guard<std::mutex> lock(size_lock);

    std::vector<std::pair<char *, op_stats>> flat(sizes.cbegin(), sizes.cend());
    double total_size = 0.0;

    for (const auto &op : flat) {
        total_size += op.second.second;
    }

    /* Sort instructions by total size, in descending order. */
    std::sort(
            flat.begin(),
            flat.end(),
            [](std::pair<char *, op_stats> &a, std::pair<char *, op_stats> &b) {
                return a.second.second > b.second.second;
            });

    for (const auto &op : flat) {
        fprintf(stderr,
                "%34s:\t%zu\t%f\t%zu\t%zu\r\n",
                op.first,
                op.second.second,
                op.second.second / total_size,
                op.second.first,
                op.second.first ? (op.second.second / op.second.first) : 0);
    }
}
#endif

ErtsCodePtr BeamModuleAssembler::getCode(BeamLabel label) {
    ASSERT(label < rawLabels.size() + 1);
    return (ErtsCodePtr)getCode(rawLabels[label]);
}

ErtsCodePtr BeamModuleAssembler::getLambda(unsigned index) {
    const auto &lambda = lambdas[index];
    return (ErtsCodePtr)getCode(lambda.trampoline);
}

BeamModuleAssembler::BeamModuleAssembler(BeamGlobalAssembler *ga,
                                         Eterm mod,
                                         int num_labels,
                                         int num_functions,
                                         const BeamFile *file)
        : BeamModuleAssembler(ga, mod, num_labels, file) {
    codeHeader = a.newLabel();
    a.align(kAlignCode, 8);
    a.bind(codeHeader);

    embed_zeros(sizeof(BeamCodeHeader) +
                sizeof(ErtsCodeInfo *) * num_functions);

    /* Shared trampoline for function_clause errors, which can't jump straight
     * to `i_func_info_shared` due to size restrictions. */
    funcInfo = a.newLabel();
    a.align(kAlignCode, 8);
    a.bind(funcInfo);
    abs_jmp(ga->get_i_func_info_shared());

    /* Shared trampoline for yielding on function ingress. */
    yieldEnter = a.newLabel();
    a.align(kAlignCode, 8);
    a.bind(yieldEnter);
    abs_jmp(ga->get_i_test_yield_shared());

    /* Shared trampoline for yielding on function return. */
    yieldReturn = a.newLabel();
    a.align(kAlignCode, 8);
    a.bind(yieldReturn);
    abs_jmp(ga->get_dispatch_return());

    /* Setup the early_nif/breakpoint trampoline. */
    genericBPTramp = a.newLabel();
    a.align(kAlignCode, 16);
    a.bind(genericBPTramp);
    {
        a.ret();

        a.align(kAlignCode, 16);
        ASSERT(a.offset() - code.labelOffsetFromBase(genericBPTramp) == 16 * 1);
        abs_jmp(ga->get_call_nif_early());

        a.align(kAlignCode, 16);
        ASSERT(a.offset() - code.labelOffsetFromBase(genericBPTramp) == 16 * 2);
        aligned_call(ga->get_generic_bp_local());
        a.ret();

        a.align(kAlignCode, 16);
        ASSERT(a.offset() - code.labelOffsetFromBase(genericBPTramp) == 16 * 3);
        aligned_call(ga->get_generic_bp_local());
        abs_jmp(ga->get_call_nif_early());
    }
}

Label BeamModuleAssembler::embed_vararg_rodata(const Span<ArgVal> &args,
                                               int y_offset) {
    Label label = a.newLabel();

#if !defined(NATIVE_ERLANG_STACK)
    y_offset = CP_SIZE;
#endif

    a.section(rodata);
    a.bind(label);

    for (const ArgVal &arg : args) {
        union {
            BeamInstr as_beam;
            char as_char[1];
        } data;

        a.align(kAlignData, 8);
        switch (arg.getType()) {
        case ArgVal::XReg:
            data.as_beam = make_loader_x_reg(arg.getValue());
            a.embed(&data.as_char, sizeof(data.as_beam));
            break;
        case ArgVal::YReg:
            data.as_beam = make_loader_y_reg(arg.getValue() + y_offset);
            a.embed(&data.as_char, sizeof(data.as_beam));
            break;
        case ArgVal::Literal:
            make_word_patch(literals[arg.getValue()].patches);
            break;
        case ArgVal::Label:
            a.embedLabel(resolve_beam_label(arg));
            break;
        case ArgVal::Immediate:
        case ArgVal::Word:
            data.as_beam = arg.getValue();
            a.embed(&data.as_char, sizeof(data.as_beam));
            break;
        default:
            erts_fprintf(stderr, "tag: %li\n", arg.getType());
            ERTS_ASSERT(!"error");
        }
    }

    a.section(code.textSection());

    return label;
}

void BeamModuleAssembler::emit_i_nif_padding() {
    const size_t minimum_size = sizeof(UWord[BEAM_NATIVE_MIN_FUNC_SZ]);
    size_t prev_func_start, diff;

    prev_func_start = code.labelOffsetFromBase(rawLabels[functions.back() + 1]);
    diff = a.offset() - prev_func_start;

    if (diff < minimum_size) {
        embed_zeros(minimum_size - diff);
    }
}

void BeamModuleAssembler::emit_i_breakpoint_trampoline() {
    /* This little prologue is used by nif loading and tracing to insert
     * alternative instructions. The call is filled with a relative call to a
     * trampoline in the module header and then the jmp target is zeroed so that
     * it effectively becomes a nop */
    Label next = a.newLabel();

    a.short_().jmp(next);

    /* We embed a zero byte here, which is used to flag whether to make an early
     * nif call, call a breakpoint handler, or both. */
    a.embedUInt8(ERTS_ASM_BP_FLAG_NONE);

    if (genericBPTramp.isValid()) {
        a.call(genericBPTramp);
    } else {
        /* NIF or BIF stub; we're not going to use this trampoline as-is, but
         * we need to reserve space for it. */
        a.ud2();
    }

    a.align(kAlignCode, 8);
    a.bind(next);
    ASSERT((a.offset() - code.labelOffsetFromBase(currLabel)) ==
           BEAM_ASM_FUNC_PROLOGUE_SIZE);
}

static void i_emit_nyi(char *msg) {
    erts_exit(ERTS_ERROR_EXIT, "NYI: %s\n", msg);
}

void BeamModuleAssembler::emit_nyi(const char *msg) {
    emit_enter_runtime();

    a.mov(ARG1, imm(msg));
    runtime_call<1>(i_emit_nyi);

    /* Never returns */
}

void BeamModuleAssembler::emit_nyi() {
    emit_nyi("<unspecified>");
}

bool BeamModuleAssembler::emit(unsigned specific_op, const Span<ArgVal> &args) {
    comment(opc[specific_op].name);

#ifdef BEAMASM_DUMP_SIZES
    uint64_t before = a.offset();
#endif

#define InstrCnt()
    switch (specific_op) {
#include "beamasm_emit.h"
    default:
        ERTS_ASSERT(0 && "Invalid instruction");
        break;
    }

    if (getOffset() == last_error_offset) {
        /*
         * The previous PC where an exception may occur is equal to the
         * current offset, which is also the offset of the next
         * instruction. If the next instruction happens to be a
         * line instruction, the location for the exception will
         * be that line instruction, which is probably wrong.
         * To avoid that, bump the instruction offset.
         */
        a.nop();
    }

#ifdef BEAMASM_DUMP_SIZES
    {
        std::lock_guard<std::mutex> lock(size_lock);

        sizes[opc[specific_op].name].first++;
        sizes[opc[specific_op].name].second += a.offset() - before;
    }
#endif

    return true;
}

/*
 * Here follows meta instructions.
 */

void BeamGlobalAssembler::emit_i_func_info_shared() {
    /* Pop the ErtsCodeInfo address into ARG1 and mask out the offset added by
     * the call instruction. */
    a.pop(ARG1);
    a.and_(ARG1, ~0x7);

    a.lea(ARG1, x86::qword_ptr(ARG1, offsetof(ErtsCodeInfo, mfa)));

    a.mov(x86::qword_ptr(c_p, offsetof(Process, freason)), EXC_FUNCTION_CLAUSE);
    a.mov(x86::qword_ptr(c_p, offsetof(Process, current)), ARG1);

    mov_imm(ARG2, 0);
    mov_imm(ARG4, 0);
    a.jmp(labels[raise_exception_shared]);
}

void BeamModuleAssembler::emit_i_func_info(const ArgVal &Label,
                                           const ArgVal &Module,
                                           const ArgVal &Function,
                                           const ArgVal &Arity) {
    ErtsCodeInfo info;

    functions.push_back(Label.getValue());

    info.mfa.module = Module.getValue();
    info.mfa.function = Function.getValue();
    info.mfa.arity = Arity.getValue();
    info.u.gen_bp = NULL;

    comment("%T:%T/%d", info.mfa.module, info.mfa.function, info.mfa.arity);

    /* This is an ErtsCodeInfo structure that has a valid x86 opcode as its `op`
     * field, which *calls* the funcInfo trampoline so we can trace it back to
     * this particular function.
     *
     * We make a relative call to a trampoline in the module header because this
     * needs to fit into a word, and an directly call to `i_func_info_shared`
     * would be too large. */
    if (funcInfo.isValid()) {
        a.call(funcInfo);
    } else {
        a.nop();
    }

    a.align(kAlignCode, sizeof(UWord));
    a.embed(&info.u.gen_bp, sizeof(info.u.gen_bp));
    a.embed(&info.mfa, sizeof(info.mfa));
}

void BeamModuleAssembler::emit_label(const ArgVal &Label) {
    ASSERT(Label.isLabel());

    currLabel = rawLabels[Label.getValue()];
    a.bind(currLabel);
}

void BeamModuleAssembler::emit_aligned_label(const ArgVal &Label,
                                             const ArgVal &Alignment) {
    ASSERT(Alignment.isWord());
    a.align(kAlignCode, Alignment.getValue());
    emit_label(Label);
}

void BeamModuleAssembler::emit_on_load() {
    on_load = currLabel;
}

void BeamModuleAssembler::emit_int_code_end() {
    /* This label is used to figure out the end of the last function */
    code_end = a.newLabel();
    a.bind(code_end);

    emit_nyi("int_code_end");
}

void BeamModuleAssembler::emit_line(const ArgVal &) {
    /*
     * There is no need to align the line instruction. In the loaded
     * code, the type of the pointer will be void* and that pointer
     * will only be used in comparisons.
     */
}

void BeamModuleAssembler::emit_func_line(const ArgVal &Loc) {
    emit_line(Loc);
}

void BeamModuleAssembler::emit_empty_func_line() {
}

/*
 * Here follows stubs for instructions that should never be called.
 */

void BeamModuleAssembler::emit_i_debug_breakpoint() {
    emit_nyi("i_debug_breakpoint should never be called");
}

void BeamModuleAssembler::emit_i_generic_breakpoint() {
    emit_nyi("i_generic_breakpoint should never be called");
}

void BeamModuleAssembler::emit_trace_jump(const ArgVal &) {
    emit_nyi("trace_jump should never be called");
}

void BeamModuleAssembler::emit_call_error_handler() {
    emit_nyi("call_error_handler should never be called");
}

unsigned BeamModuleAssembler::patchCatches(char *rw_base) {
    unsigned catch_no = BEAM_CATCHES_NIL;

    for (const auto &c : catches) {
        const auto &patch = c.patch;
        ErtsCodePtr handler;

        handler = (ErtsCodePtr)getCode(c.handler);
        catch_no = beam_catches_cons(handler, catch_no, nullptr);

        /* Patch the `mov` instruction with the catch tag */
        auto offset = code.labelOffsetFromBase(patch.where);
        auto where = (unsigned *)&rw_base[offset + patch.ptr_offs];

        ASSERT(0x7fffffff == *where);
        Eterm catch_term = make_catch(catch_no);

        /* With the current tag scheme, more than 33 million
         * catches can exist at once. */
        ERTS_ASSERT(catch_term >> 31 == 0);
        *where = (unsigned)catch_term;
    }

    return catch_no;
}

void BeamModuleAssembler::patchImport(char *rw_base,
                                      unsigned index,
                                      BeamInstr I) {
    for (const auto &patch : imports[index].patches) {
        auto offset = code.labelOffsetFromBase(patch.where);
        auto where = (Eterm *)&rw_base[offset + patch.ptr_offs];

        ASSERT(LLONG_MAX == *where);
        *where = I + patch.val_offs;
    }
}

void BeamModuleAssembler::patchLambda(char *rw_base,
                                      unsigned index,
                                      BeamInstr I) {
    for (const auto &patch : lambdas[index].patches) {
        auto offset = code.labelOffsetFromBase(patch.where);
        auto where = (Eterm *)&rw_base[offset + patch.ptr_offs];

        ASSERT(LLONG_MAX == *where);
        *where = I + patch.val_offs;
    }
}

void BeamModuleAssembler::patchLiteral(char *rw_base,
                                       unsigned index,
                                       Eterm lit) {
    for (const auto &patch : literals[index].patches) {
        auto offset = code.labelOffsetFromBase(patch.where);
        auto where = (Eterm *)&rw_base[offset + patch.ptr_offs];

        ASSERT(LLONG_MAX == *where);
        *where = lit + patch.val_offs;
    }
}

void BeamModuleAssembler::patchStrings(char *rw_base,
                                       const byte *string_table) {
    for (const auto &patch : strings) {
        auto offset = code.labelOffsetFromBase(patch.where);
        auto where = (const byte **)&rw_base[offset + 2];

        ASSERT(LLONG_MAX == (Eterm)*where);
        *where = string_table + patch.val_offs;
    }
}
<commit_msg>jit: Clean up func_info<commit_after>/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 2020-2021. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * %CopyrightEnd%
 */

#include <algorithm>
#include <float.h>

#include "beam_asm.hpp"
using namespace asmjit;

#ifdef BEAMASM_DUMP_SIZES
#    include <mutex>

typedef std::pair<Uint64, Uint64> op_stats;

static std::unordered_map<char *, op_stats> sizes;
static std::mutex size_lock;

extern "C" void beamasm_dump_sizes() {
    std::lock_guard<std::mutex> lock(size_lock);

    std::vector<std::pair<char *, op_stats>> flat(sizes.cbegin(), sizes.cend());
    double total_size = 0.0;

    for (const auto &op : flat) {
        total_size += op.second.second;
    }

    /* Sort instructions by total size, in descending order. */
    std::sort(
            flat.begin(),
            flat.end(),
            [](std::pair<char *, op_stats> &a, std::pair<char *, op_stats> &b) {
                return a.second.second > b.second.second;
            });

    for (const auto &op : flat) {
        fprintf(stderr,
                "%34s:\t%zu\t%f\t%zu\t%zu\r\n",
                op.first,
                op.second.second,
                op.second.second / total_size,
                op.second.first,
                op.second.first ? (op.second.second / op.second.first) : 0);
    }
}
#endif

ErtsCodePtr BeamModuleAssembler::getCode(BeamLabel label) {
    ASSERT(label < rawLabels.size() + 1);
    return (ErtsCodePtr)getCode(rawLabels[label]);
}

ErtsCodePtr BeamModuleAssembler::getLambda(unsigned index) {
    const auto &lambda = lambdas[index];
    return (ErtsCodePtr)getCode(lambda.trampoline);
}

BeamModuleAssembler::BeamModuleAssembler(BeamGlobalAssembler *ga,
                                         Eterm mod,
                                         int num_labels,
                                         int num_functions,
                                         const BeamFile *file)
        : BeamModuleAssembler(ga, mod, num_labels, file) {
    codeHeader = a.newLabel();
    a.align(kAlignCode, 8);
    a.bind(codeHeader);

    embed_zeros(sizeof(BeamCodeHeader) +
                sizeof(ErtsCodeInfo *) * num_functions);

    /* Shared trampoline for function_clause errors, which can't jump straight
     * to `i_func_info_shared` due to size restrictions. */
    funcInfo = a.newLabel();
    a.align(kAlignCode, 8);
    a.bind(funcInfo);
    abs_jmp(ga->get_i_func_info_shared());

    /* Shared trampoline for yielding on function ingress. */
    yieldEnter = a.newLabel();
    a.align(kAlignCode, 8);
    a.bind(yieldEnter);
    abs_jmp(ga->get_i_test_yield_shared());

    /* Shared trampoline for yielding on function return. */
    yieldReturn = a.newLabel();
    a.align(kAlignCode, 8);
    a.bind(yieldReturn);
    abs_jmp(ga->get_dispatch_return());

    /* Setup the early_nif/breakpoint trampoline. */
    genericBPTramp = a.newLabel();
    a.align(kAlignCode, 16);
    a.bind(genericBPTramp);
    {
        a.ret();

        a.align(kAlignCode, 16);
        ASSERT(a.offset() - code.labelOffsetFromBase(genericBPTramp) == 16 * 1);
        abs_jmp(ga->get_call_nif_early());

        a.align(kAlignCode, 16);
        ASSERT(a.offset() - code.labelOffsetFromBase(genericBPTramp) == 16 * 2);
        aligned_call(ga->get_generic_bp_local());
        a.ret();

        a.align(kAlignCode, 16);
        ASSERT(a.offset() - code.labelOffsetFromBase(genericBPTramp) == 16 * 3);
        aligned_call(ga->get_generic_bp_local());
        abs_jmp(ga->get_call_nif_early());
    }
}

Label BeamModuleAssembler::embed_vararg_rodata(const Span<ArgVal> &args,
                                               int y_offset) {
    Label label = a.newLabel();

#if !defined(NATIVE_ERLANG_STACK)
    y_offset = CP_SIZE;
#endif

    a.section(rodata);
    a.bind(label);

    for (const ArgVal &arg : args) {
        union {
            BeamInstr as_beam;
            char as_char[1];
        } data;

        a.align(kAlignData, 8);
        switch (arg.getType()) {
        case ArgVal::XReg:
            data.as_beam = make_loader_x_reg(arg.getValue());
            a.embed(&data.as_char, sizeof(data.as_beam));
            break;
        case ArgVal::YReg:
            data.as_beam = make_loader_y_reg(arg.getValue() + y_offset);
            a.embed(&data.as_char, sizeof(data.as_beam));
            break;
        case ArgVal::Literal:
            make_word_patch(literals[arg.getValue()].patches);
            break;
        case ArgVal::Label:
            a.embedLabel(resolve_beam_label(arg));
            break;
        case ArgVal::Immediate:
        case ArgVal::Word:
            data.as_beam = arg.getValue();
            a.embed(&data.as_char, sizeof(data.as_beam));
            break;
        default:
            erts_fprintf(stderr, "tag: %li\n", arg.getType());
            ERTS_ASSERT(!"error");
        }
    }

    a.section(code.textSection());

    return label;
}

void BeamModuleAssembler::emit_i_nif_padding() {
    const size_t minimum_size = sizeof(UWord[BEAM_NATIVE_MIN_FUNC_SZ]);
    size_t prev_func_start, diff;

    prev_func_start = code.labelOffsetFromBase(rawLabels[functions.back() + 1]);
    diff = a.offset() - prev_func_start;

    if (diff < minimum_size) {
        embed_zeros(minimum_size - diff);
    }
}

void BeamModuleAssembler::emit_i_breakpoint_trampoline() {
    /* This little prologue is used by nif loading and tracing to insert
     * alternative instructions. The call is filled with a relative call to a
     * trampoline in the module header and then the jmp target is zeroed so that
     * it effectively becomes a nop */
    Label next = a.newLabel();

    a.short_().jmp(next);

    /* We embed a zero byte here, which is used to flag whether to make an early
     * nif call, call a breakpoint handler, or both. */
    a.embedUInt8(ERTS_ASM_BP_FLAG_NONE);

    if (genericBPTramp.isValid()) {
        a.call(genericBPTramp);
    } else {
        /* NIF or BIF stub; we're not going to use this trampoline as-is, but
         * we need to reserve space for it. */
        a.ud2();
    }

    a.align(kAlignCode, 8);
    a.bind(next);
    ASSERT((a.offset() - code.labelOffsetFromBase(currLabel)) ==
           BEAM_ASM_FUNC_PROLOGUE_SIZE);
}

static void i_emit_nyi(char *msg) {
    erts_exit(ERTS_ERROR_EXIT, "NYI: %s\n", msg);
}

void BeamModuleAssembler::emit_nyi(const char *msg) {
    emit_enter_runtime();

    a.mov(ARG1, imm(msg));
    runtime_call<1>(i_emit_nyi);

    /* Never returns */
}

void BeamModuleAssembler::emit_nyi() {
    emit_nyi("<unspecified>");
}

bool BeamModuleAssembler::emit(unsigned specific_op, const Span<ArgVal> &args) {
    comment(opc[specific_op].name);

#ifdef BEAMASM_DUMP_SIZES
    uint64_t before = a.offset();
#endif

#define InstrCnt()
    switch (specific_op) {
#include "beamasm_emit.h"
    default:
        ERTS_ASSERT(0 && "Invalid instruction");
        break;
    }

    if (getOffset() == last_error_offset) {
        /*
         * The previous PC where an exception may occur is equal to the
         * current offset, which is also the offset of the next
         * instruction. If the next instruction happens to be a
         * line instruction, the location for the exception will
         * be that line instruction, which is probably wrong.
         * To avoid that, bump the instruction offset.
         */
        a.nop();
    }

#ifdef BEAMASM_DUMP_SIZES
    {
        std::lock_guard<std::mutex> lock(size_lock);

        sizes[opc[specific_op].name].first++;
        sizes[opc[specific_op].name].second += a.offset() - before;
    }
#endif

    return true;
}

/*
 * Here follows meta instructions.
 */

void BeamGlobalAssembler::emit_i_func_info_shared() {
    /* Pop the ErtsCodeInfo address into ARG1 and mask out the offset added by
     * the call instruction. */
    a.pop(ARG1);
    a.and_(ARG1, imm(~0x7));

    a.add(ARG1, imm(offsetof(ErtsCodeInfo, mfa)));

    a.mov(x86::qword_ptr(c_p, offsetof(Process, freason)), EXC_FUNCTION_CLAUSE);
    a.mov(x86::qword_ptr(c_p, offsetof(Process, current)), ARG1);

    mov_imm(ARG2, 0);
    mov_imm(ARG4, 0);
    a.jmp(labels[raise_exception_shared]);
}

void BeamModuleAssembler::emit_i_func_info(const ArgVal &Label,
                                           const ArgVal &Module,
                                           const ArgVal &Function,
                                           const ArgVal &Arity) {
    ErtsCodeInfo info;

    functions.push_back(Label.getValue());

    info.mfa.module = Module.getValue();
    info.mfa.function = Function.getValue();
    info.mfa.arity = Arity.getValue();
    info.u.gen_bp = NULL;

    comment("%T:%T/%d", info.mfa.module, info.mfa.function, info.mfa.arity);

    /* This is an ErtsCodeInfo structure that has a valid x86 opcode as its `op`
     * field, which *calls* the funcInfo trampoline so we can trace it back to
     * this particular function.
     *
     * We make a relative call to a trampoline in the module header because this
     * needs to fit into a word, and an directly call to `i_func_info_shared`
     * would be too large. */
    if (funcInfo.isValid()) {
        a.call(funcInfo);
    } else {
        a.nop();
    }

    a.align(kAlignCode, sizeof(UWord));
    a.embed(&info.u.gen_bp, sizeof(info.u.gen_bp));
    a.embed(&info.mfa, sizeof(info.mfa));
}

void BeamModuleAssembler::emit_label(const ArgVal &Label) {
    ASSERT(Label.isLabel());

    currLabel = rawLabels[Label.getValue()];
    a.bind(currLabel);
}

void BeamModuleAssembler::emit_aligned_label(const ArgVal &Label,
                                             const ArgVal &Alignment) {
    ASSERT(Alignment.isWord());
    a.align(kAlignCode, Alignment.getValue());
    emit_label(Label);
}

void BeamModuleAssembler::emit_on_load() {
    on_load = currLabel;
}

void BeamModuleAssembler::emit_int_code_end() {
    /* This label is used to figure out the end of the last function */
    code_end = a.newLabel();
    a.bind(code_end);

    emit_nyi("int_code_end");
}

void BeamModuleAssembler::emit_line(const ArgVal &) {
    /*
     * There is no need to align the line instruction. In the loaded
     * code, the type of the pointer will be void* and that pointer
     * will only be used in comparisons.
     */
}

void BeamModuleAssembler::emit_func_line(const ArgVal &Loc) {
    emit_line(Loc);
}

void BeamModuleAssembler::emit_empty_func_line() {
}

/*
 * Here follows stubs for instructions that should never be called.
 */

void BeamModuleAssembler::emit_i_debug_breakpoint() {
    emit_nyi("i_debug_breakpoint should never be called");
}

void BeamModuleAssembler::emit_i_generic_breakpoint() {
    emit_nyi("i_generic_breakpoint should never be called");
}

void BeamModuleAssembler::emit_trace_jump(const ArgVal &) {
    emit_nyi("trace_jump should never be called");
}

void BeamModuleAssembler::emit_call_error_handler() {
    emit_nyi("call_error_handler should never be called");
}

unsigned BeamModuleAssembler::patchCatches(char *rw_base) {
    unsigned catch_no = BEAM_CATCHES_NIL;

    for (const auto &c : catches) {
        const auto &patch = c.patch;
        ErtsCodePtr handler;

        handler = (ErtsCodePtr)getCode(c.handler);
        catch_no = beam_catches_cons(handler, catch_no, nullptr);

        /* Patch the `mov` instruction with the catch tag */
        auto offset = code.labelOffsetFromBase(patch.where);
        auto where = (unsigned *)&rw_base[offset + patch.ptr_offs];

        ASSERT(0x7fffffff == *where);
        Eterm catch_term = make_catch(catch_no);

        /* With the current tag scheme, more than 33 million
         * catches can exist at once. */
        ERTS_ASSERT(catch_term >> 31 == 0);
        *where = (unsigned)catch_term;
    }

    return catch_no;
}

void BeamModuleAssembler::patchImport(char *rw_base,
                                      unsigned index,
                                      BeamInstr I) {
    for (const auto &patch : imports[index].patches) {
        auto offset = code.labelOffsetFromBase(patch.where);
        auto where = (Eterm *)&rw_base[offset + patch.ptr_offs];

        ASSERT(LLONG_MAX == *where);
        *where = I + patch.val_offs;
    }
}

void BeamModuleAssembler::patchLambda(char *rw_base,
                                      unsigned index,
                                      BeamInstr I) {
    for (const auto &patch : lambdas[index].patches) {
        auto offset = code.labelOffsetFromBase(patch.where);
        auto where = (Eterm *)&rw_base[offset + patch.ptr_offs];

        ASSERT(LLONG_MAX == *where);
        *where = I + patch.val_offs;
    }
}

void BeamModuleAssembler::patchLiteral(char *rw_base,
                                       unsigned index,
                                       Eterm lit) {
    for (const auto &patch : literals[index].patches) {
        auto offset = code.labelOffsetFromBase(patch.where);
        auto where = (Eterm *)&rw_base[offset + patch.ptr_offs];

        ASSERT(LLONG_MAX == *where);
        *where = lit + patch.val_offs;
    }
}

void BeamModuleAssembler::patchStrings(char *rw_base,
                                       const byte *string_table) {
    for (const auto &patch : strings) {
        auto offset = code.labelOffsetFromBase(patch.where);
        auto where = (const byte **)&rw_base[offset + 2];

        ASSERT(LLONG_MAX == (Eterm)*where);
        *where = string_table + patch.val_offs;
    }
}
<|endoftext|>


<commit_before>#include "configure.hpp"
#include <silicium/file_operations.hpp>
#include <silicium/cmake.hpp>
#include <fstream>

namespace
{
	Si::absolute_path build_configure(
		Si::absolute_path const &application_source,
		Si::absolute_path const &temporary,
		Si::optional<Si::absolute_path> const &boost_root,
		Si::Sink<char, Si::success>::interface &output)
	{
		Si::absolute_path const cdm = Si::parent(
			Si::parent(*Si::absolute_path::create(__FILE__)).or_throw(
				[]{ throw std::runtime_error("Could not find parent directory of this file: " __FILE__); }
			)
		).or_throw(
			[]{ throw std::runtime_error("Could not find the cdm directory"); }
		);
		Si::absolute_path const original_main_cpp = cdm / Si::relative_path("configure_cmdline/main.cpp");
		Si::absolute_path const source = temporary / Si::relative_path("source");
		Si::recreate_directories(source, Si::throw_);
		Si::absolute_path const copied_main_cpp = source / Si::relative_path("main.cpp");
		Si::copy(original_main_cpp, copied_main_cpp, Si::throw_);
		{
			Si::absolute_path const cmakeLists = source / Si::relative_path("CMakeLists.txt");
			std::ofstream cmakeListsFile(cmakeLists.c_str());
			cmakeListsFile << "cmake_minimum_required(VERSION 2.8)\n";
			cmakeListsFile << "project(configure_cmdline_generated)\n";
			cmakeListsFile << "if(UNIX)\n";
			cmakeListsFile << "  execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)\n";
			cmakeListsFile << "  if(GCC_VERSION VERSION_GREATER 4.7)\n";
			cmakeListsFile << "    add_definitions(-std=c++1y)\n";
			cmakeListsFile << "  else()\n";
			cmakeListsFile << "    add_definitions(-std=c++0x)\n";
			cmakeListsFile << "  endif()\n";
			cmakeListsFile << "endif()\n";
			cmakeListsFile << "if(MSVC)\n";
			cmakeListsFile << "	set(Boost_USE_STATIC_LIBS ON)\n";
			cmakeListsFile << "endif()\n";
			cmakeListsFile << "find_package(Boost REQUIRED filesystem coroutine program_options thread context system)\n";
			cmakeListsFile << "set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO\")\n";
			cmakeListsFile << "include_directories(SYSTEM ${SILICIUM_INCLUDE_DIR} ${Boost_INCLUDE_DIR} ${CDM_CONFIGURE_INCLUDE_DIRS})\n";
			cmakeListsFile << "link_directories(${Boost_LIBRARY_DIR})\n";
			cmakeListsFile << "add_executable(configure main.cpp)\n";
			cmakeListsFile << "target_link_libraries(configure ${Boost_LIBRARIES})\n";
			if (!cmakeListsFile)
			{
				throw std::runtime_error(("Could not generate " + Si::to_utf8_string(cmakeLists)).c_str());
			}
		}
		Si::absolute_path const build = temporary / Si::relative_path("build");
		Si::recreate_directories(build, Si::throw_);
		{
			std::vector<Si::os_string> arguments;
			{
				Si::absolute_path const repository_root = Si::parent(cdm).or_throw([] { throw std::runtime_error("Could not find the silicium directory"); });
				Si::absolute_path const silicium = repository_root / Si::relative_path("dependencies/silicium");
				arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("-DSILICIUM_INCLUDE_DIR=") + to_os_string(silicium));
			}
			if (boost_root)
			{
				arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("-DBOOST_ROOT=") + to_os_string(*boost_root));
			}
			Si::absolute_path const modules = cdm / Si::relative_path("modules");
			arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("-DCDM_CONFIGURE_INCLUDE_DIRS=") + Si::to_os_string(application_source) + SILICIUM_SYSTEM_LITERAL(";") + Si::to_os_string(modules));
			arguments.emplace_back(Si::to_os_string(source));
#ifdef _MSC_VER
			arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("-G \"Visual Studio 12 2013\""));
#endif
			if (Si::run_process(Si::cmake_exe, arguments, build, output).get() != 0)
			{
				throw std::runtime_error("Could not CMake-configure the cdm configure executable");
			}
		}
		{
			std::vector<Si::os_string> arguments;
			arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("--build"));
			arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("."));
			if (Si::run_process(Si::cmake_exe, arguments, build, output).get() != 0)
			{
				throw std::runtime_error("Could not CMake --build the cdm configure executable");
			}
		}
		Si::absolute_path built_executable = build / Si::relative_path(
#ifdef _MSC_VER
			SILICIUM_SYSTEM_LITERAL("Debug/")
#endif
			SILICIUM_SYSTEM_LITERAL("configure")
#ifdef _WIN32
			SILICIUM_SYSTEM_LITERAL(".exe")
#endif
		);
		return built_executable;
	}

	void run_configure(
		Si::absolute_path const &configure_executable,
		Si::absolute_path const &module_permanent,
		Si::absolute_path const &application_source,
		Si::absolute_path const &application_build_dir,
		Si::Sink<char, Si::success>::interface &output)
	{
		Si::create_directories(module_permanent, Si::throw_);
		Si::create_directories(application_build_dir, Si::throw_);
		std::vector<Si::os_string> arguments;
		arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("-m"));
		arguments.emplace_back(Si::to_os_string(module_permanent));
		arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("-a"));
		arguments.emplace_back(Si::to_os_string(application_source));
		arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("-b"));
		arguments.emplace_back(Si::to_os_string(application_build_dir));
		int const rc = Si::run_process(configure_executable, arguments, application_build_dir, output).get();
		if (rc != 0)
		{
			throw std::runtime_error("Could not configure the application: " + boost::lexical_cast<std::string>(rc));
		}
	}
}

namespace cdm
{
	void do_configure(
		Si::absolute_path const &temporary,
		Si::absolute_path const &module_permanent,
		Si::absolute_path const &application_source,
		Si::absolute_path const &application_build_dir,
		Si::optional<Si::absolute_path> const &boost_root,
		Si::Sink<char, Si::success>::interface &output
	)
	{
		Si::absolute_path const configure_executable = build_configure(application_source, temporary, boost_root, output);
		run_configure(configure_executable, module_permanent, application_source, application_build_dir, output);
	}
}
<commit_msg>the Boost.Context flag is only required for VC++<commit_after>#include "configure.hpp"
#include <silicium/file_operations.hpp>
#include <silicium/cmake.hpp>
#include <fstream>

namespace
{
	Si::absolute_path build_configure(
		Si::absolute_path const &application_source,
		Si::absolute_path const &temporary,
		Si::optional<Si::absolute_path> const &boost_root,
		Si::Sink<char, Si::success>::interface &output)
	{
		Si::absolute_path const cdm = Si::parent(
			Si::parent(*Si::absolute_path::create(__FILE__)).or_throw(
				[]{ throw std::runtime_error("Could not find parent directory of this file: " __FILE__); }
			)
		).or_throw(
			[]{ throw std::runtime_error("Could not find the cdm directory"); }
		);
		Si::absolute_path const original_main_cpp = cdm / Si::relative_path("configure_cmdline/main.cpp");
		Si::absolute_path const source = temporary / Si::relative_path("source");
		Si::recreate_directories(source, Si::throw_);
		Si::absolute_path const copied_main_cpp = source / Si::relative_path("main.cpp");
		Si::copy(original_main_cpp, copied_main_cpp, Si::throw_);
		{
			Si::absolute_path const cmakeLists = source / Si::relative_path("CMakeLists.txt");
			std::ofstream cmakeListsFile(cmakeLists.c_str());
			cmakeListsFile << "cmake_minimum_required(VERSION 2.8)\n";
			cmakeListsFile << "project(configure_cmdline_generated)\n";
			cmakeListsFile << "if(UNIX)\n";
			cmakeListsFile << "  execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)\n";
			cmakeListsFile << "  if(GCC_VERSION VERSION_GREATER 4.7)\n";
			cmakeListsFile << "    add_definitions(-std=c++1y)\n";
			cmakeListsFile << "  else()\n";
			cmakeListsFile << "    add_definitions(-std=c++0x)\n";
			cmakeListsFile << "  endif()\n";
			cmakeListsFile << "endif()\n";
			cmakeListsFile << "if(MSVC)\n";
			cmakeListsFile << "	set(Boost_USE_STATIC_LIBS ON)\n";
			cmakeListsFile << "	set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO\")\n";
			cmakeListsFile << "endif()\n";
			cmakeListsFile << "find_package(Boost REQUIRED filesystem coroutine program_options thread context system)\n";
			cmakeListsFile << "include_directories(SYSTEM ${SILICIUM_INCLUDE_DIR} ${Boost_INCLUDE_DIR} ${CDM_CONFIGURE_INCLUDE_DIRS})\n";
			cmakeListsFile << "link_directories(${Boost_LIBRARY_DIR})\n";
			cmakeListsFile << "add_executable(configure main.cpp)\n";
			cmakeListsFile << "target_link_libraries(configure ${Boost_LIBRARIES})\n";
			if (!cmakeListsFile)
			{
				throw std::runtime_error(("Could not generate " + Si::to_utf8_string(cmakeLists)).c_str());
			}
		}
		Si::absolute_path const build = temporary / Si::relative_path("build");
		Si::recreate_directories(build, Si::throw_);
		{
			std::vector<Si::os_string> arguments;
			{
				Si::absolute_path const repository_root = Si::parent(cdm).or_throw([] { throw std::runtime_error("Could not find the silicium directory"); });
				Si::absolute_path const silicium = repository_root / Si::relative_path("dependencies/silicium");
				arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("-DSILICIUM_INCLUDE_DIR=") + to_os_string(silicium));
			}
			if (boost_root)
			{
				arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("-DBOOST_ROOT=") + to_os_string(*boost_root));
			}
			Si::absolute_path const modules = cdm / Si::relative_path("modules");
			arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("-DCDM_CONFIGURE_INCLUDE_DIRS=") + Si::to_os_string(application_source) + SILICIUM_SYSTEM_LITERAL(";") + Si::to_os_string(modules));
			arguments.emplace_back(Si::to_os_string(source));
#ifdef _MSC_VER
			arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("-G \"Visual Studio 12 2013\""));
#endif
			if (Si::run_process(Si::cmake_exe, arguments, build, output).get() != 0)
			{
				throw std::runtime_error("Could not CMake-configure the cdm configure executable");
			}
		}
		{
			std::vector<Si::os_string> arguments;
			arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("--build"));
			arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("."));
			if (Si::run_process(Si::cmake_exe, arguments, build, output).get() != 0)
			{
				throw std::runtime_error("Could not CMake --build the cdm configure executable");
			}
		}
		Si::absolute_path built_executable = build / Si::relative_path(
#ifdef _MSC_VER
			SILICIUM_SYSTEM_LITERAL("Debug/")
#endif
			SILICIUM_SYSTEM_LITERAL("configure")
#ifdef _WIN32
			SILICIUM_SYSTEM_LITERAL(".exe")
#endif
		);
		return built_executable;
	}

	void run_configure(
		Si::absolute_path const &configure_executable,
		Si::absolute_path const &module_permanent,
		Si::absolute_path const &application_source,
		Si::absolute_path const &application_build_dir,
		Si::Sink<char, Si::success>::interface &output)
	{
		Si::create_directories(module_permanent, Si::throw_);
		Si::create_directories(application_build_dir, Si::throw_);
		std::vector<Si::os_string> arguments;
		arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("-m"));
		arguments.emplace_back(Si::to_os_string(module_permanent));
		arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("-a"));
		arguments.emplace_back(Si::to_os_string(application_source));
		arguments.emplace_back(SILICIUM_SYSTEM_LITERAL("-b"));
		arguments.emplace_back(Si::to_os_string(application_build_dir));
		int const rc = Si::run_process(configure_executable, arguments, application_build_dir, output).get();
		if (rc != 0)
		{
			throw std::runtime_error("Could not configure the application: " + boost::lexical_cast<std::string>(rc));
		}
	}
}

namespace cdm
{
	void do_configure(
		Si::absolute_path const &temporary,
		Si::absolute_path const &module_permanent,
		Si::absolute_path const &application_source,
		Si::absolute_path const &application_build_dir,
		Si::optional<Si::absolute_path> const &boost_root,
		Si::Sink<char, Si::success>::interface &output
	)
	{
		Si::absolute_path const configure_executable = build_configure(application_source, temporary, boost_root, output);
		run_configure(configure_executable, module_permanent, application_source, application_build_dir, output);
	}
}
<|endoftext|>


<commit_before><commit_msg>Add flag to ignore unknown disk id warnings<commit_after><|endoftext|>


<commit_before>#ifndef __STRESS_CLIENT_DISTR_HPP__
#define __STRESS_CLIENT_DISTR_HPP__

#include <stdio.h>
#include <string>
#include <string.h>
#include <stdint.h>
#include "utils.hpp"

/* payload_t represents just a string of characters. It's designed so that its internal
buffer can be reused over and over. */

struct payload_t {

    /* "first" is the buffer and "second" is how many bytes of the buffer are in use. The
    names are a holdover from when payload_t was defined as a std::pair<char*, size_t>. */
    char *first;
    size_t second;
    int buffer_size;

    payload_t() :
        first(NULL), second(0), buffer_size(0) { }
    payload_t(int bs) :
        first(new char[bs]), second(0), buffer_size(bs) { }
    payload_t(const payload_t &p) :
        first(new char[p.buffer_size]), second(p.second), buffer_size(p.buffer_size) {
        memcpy(first, p.first, second);
    }
    payload_t &operator=(const payload_t &p) {
        delete[] first;
        first = new char[p.buffer_size];
        second = p.second;
        buffer_size = p.buffer_size;
        memcpy(first, p.first, second);
    }
    ~payload_t() {
        delete[] first;
    }

    /* If the payload's buffer has less than this much space, then expand the
    payload's buffer. The contents of the payload's buffer after this operation
    are undefined. */
    void grow_to(int size) {
        if (buffer_size < size) {
            delete[] first;
            first = new char[size];
            buffer_size = size;
        }
    }
};

inline void append(payload_t *p, payload_t *other) {
    p->second += other->second;
    memcpy(p->first + p->second, other->first, other->second);
}

inline void prepend(payload_t *p, payload_t *other) {
    p->second += other->second;
    memmove(p->first + p->second, p->first, p->second);
    memcpy(p->first, other->first, other->second);
}

/* Defines a distribution of values, from min to max. */
struct distr_t {
public:
    distr_t(int _min, int _max)
        : min(_min), max(_max)
        {}

    distr_t()
        : min(8), max(16)
        {}

    void parse(const char *const_str) {
        std::string param(const_str);
        size_t hyphenIndex = param.find("-");

        // Make sure the parameter only contains numerals and the (optional) hyphen
        if (param.find_first_not_of("0123456789-") != std::string::npos) {
            fprintf(stderr, "Invalid distr format (use NUM or MIN-MAX), brackets and +/- signs are not supported\n");
            exit(-1);
        }

        if (hyphenIndex == std::string::npos) {
            min = max = atoi(param.c_str());
            return;
        }

        // Protection against more than one hyphen
        if (param.rfind("-") != hyphenIndex) {
            fprintf(stderr, "Invalid distr format (use NUM or MIN-MAX), +/- signs are not supported\n");
            exit(-1);
        }

        min = atoi(param.substr(0, hyphenIndex).c_str());
        max = atoi(param.substr(hyphenIndex + 1, std::string::npos).c_str());

        if (min > max) {
            fprintf(stderr, "Invalid distr format (use NUM or MIN-MAX, where MIN <= MAX)\n");
            exit(-1);
        }
    }

    void print() {
        printf("%d-%d", min, max);
    }

public:
    int min, max;
};

#endif  // __STRESS_CLIENT_DISTR_HPP__

<commit_msg>Fixed a previous bug that was causing the stress client to send garbage instead of strings that only contained A.<commit_after>#ifndef __STRESS_CLIENT_DISTR_HPP__
#define __STRESS_CLIENT_DISTR_HPP__

#include <stdio.h>
#include <string>
#include <string.h>
#include <stdint.h>
#include "utils.hpp"

/* payload_t represents just a string of characters. It's designed so that its internal
buffer can be reused over and over. */

struct payload_t {

    /* "first" is the buffer and "second" is how many bytes of the buffer are in use. The
    names are a holdover from when payload_t was defined as a std::pair<char*, size_t>. */
    char *first;
    size_t second;
    int buffer_size;

    payload_t() :
        first(NULL), second(0), buffer_size(0) { }
    payload_t(int bs) :
        first(new char[bs]), second(0), buffer_size(bs) { }
    payload_t(const payload_t &p) :
        first(new char[p.buffer_size]), second(p.second), buffer_size(p.buffer_size) {
        memcpy(first, p.first, buffer_size);
    }
    payload_t &operator=(const payload_t &p) {
        delete[] first;
        first = new char[p.buffer_size];
        second = p.second;
        buffer_size = p.buffer_size;
        memcpy(first, p.first, buffer_size);
    }
    ~payload_t() {
        delete[] first;
    }

    /* If the payload's buffer has less than this much space, then expand the
    payload's buffer. The contents of the payload's buffer after this operation
    are undefined. */
    void grow_to(int size) {
        if (buffer_size < size) {
            delete[] first;
            first = new char[size];
            buffer_size = size;
        }
    }
};

inline void append(payload_t *p, payload_t *other) {
    p->second += other->second;
    memcpy(p->first + p->second, other->first, other->second);
}

inline void prepend(payload_t *p, payload_t *other) {
    p->second += other->second;
    memmove(p->first + p->second, p->first, p->second);
    memcpy(p->first, other->first, other->second);
}

/* Defines a distribution of values, from min to max. */
struct distr_t {
public:
    distr_t(int _min, int _max)
        : min(_min), max(_max)
        {}

    distr_t()
        : min(8), max(16)
        {}

    void parse(const char *const_str) {
        std::string param(const_str);
        size_t hyphenIndex = param.find("-");

        // Make sure the parameter only contains numerals and the (optional) hyphen
        if (param.find_first_not_of("0123456789-") != std::string::npos) {
            fprintf(stderr, "Invalid distr format (use NUM or MIN-MAX), brackets and +/- signs are not supported\n");
            exit(-1);
        }

        if (hyphenIndex == std::string::npos) {
            min = max = atoi(param.c_str());
            return;
        }

        // Protection against more than one hyphen
        if (param.rfind("-") != hyphenIndex) {
            fprintf(stderr, "Invalid distr format (use NUM or MIN-MAX), +/- signs are not supported\n");
            exit(-1);
        }

        min = atoi(param.substr(0, hyphenIndex).c_str());
        max = atoi(param.substr(hyphenIndex + 1, std::string::npos).c_str());

        if (min > max) {
            fprintf(stderr, "Invalid distr format (use NUM or MIN-MAX, where MIN <= MAX)\n");
            exit(-1);
        }
    }

    void print() {
        printf("%d-%d", min, max);
    }

public:
    int min, max;
};

#endif  // __STRESS_CLIENT_DISTR_HPP__

<|endoftext|>


<commit_before>#include "xs_mesh_homogenized.hpp"

#include <iomanip>
#include <iostream>
#include <sstream>

using std::cout;
using std::cin;
using std::endl;

namespace mocc {
    XSMeshHomogenized::XSMeshHomogenized( const CoreMesh& mesh ):
        mesh_( mesh )
    {
        // Set up the non-xs part of the xs mesh
        eubounds_ = mesh_.mat_lib().g_bounds();
        ng_ = eubounds_.size();

        regions_ = std::vector<XSMeshRegion>( mesh_.n_pin() );

        int ipin = 0;
        int first_reg = 0;
        for( const auto &pin: mesh_ ) {
            // Use the lexicographically-ordered pin index as the xs mesh index.
            // This is to put the indexing in a way that works best for the Sn
            // sweeper as it is implemented now. This is really brittle, and
            // should be replaced with some sort of Sn Mesh object, which both
            // the XS Mesh and the Sn sweeper will use to handle indexing.
            auto pos = mesh.pin_position(ipin);
            int ireg = mesh.index_lex( pos );
            int ixsreg = mesh_.index_lex( pos );
            regions_[ixsreg] = this->homogenize_region( ireg, *pin );
            ipin++;
            first_reg += pin->n_reg();
        }
    }

    /**
     * Update the XS mesh, incorporating a new estimate of the scalar flux.
     */
    void XSMeshHomogenized::update( const ArrayF &flux ) {
        int ipin = 0;
        int first_reg = 0;
        for( const auto &pin: mesh_ ) {
            int ireg = mesh_.index_lex( mesh_.pin_position(ipin) );
            int ixsreg = ireg;
            regions_[ixsreg] = this->homogenize_region_flux( ireg, first_reg,
                    *pin, flux);

            ipin++;
            first_reg += pin->n_reg();
        }
        return;
    }

    XSMeshRegion XSMeshHomogenized::homogenize_region( int i,
            const Pin& pin) const {
        VecI fsrs( 1, i );
        VecF xstr( ng_, 0.0 );
        VecF xsnf( ng_, 0.0 );
        VecF xskf( ng_, 0.0 );
        VecF xsch( ng_, 0.0 );

        std::vector<VecF> scat( ng_, VecF(ng_, 0.0) );

        auto mat_lib = mesh_.mat_lib();
        auto &pin_mesh = pin.mesh();
        auto vols = pin_mesh.vols();

        for( size_t ig=0; ig<ng_; ig++ ) {
            int ireg = 0;
            int ixsreg = 0;
            real_t fvol = 0.0;
            for( auto &mat_id: pin.mat_ids() ) {
                auto mat = mat_lib.get_material_by_id(mat_id);
                const ScatteringRow& scat_row = mat.xssc().to(ig);
                int gmin = scat_row.min_g;
                int gmax = scat_row.max_g;
                real_t fsrc = 0.0;
                for( size_t igg=0; igg<ng_; igg++ ) {
                    fsrc += mat.xsnf()[igg];
                }
                for( size_t i=0; i<pin_mesh.n_fsrs(ixsreg); i++ ) {
                    fvol += vols[ireg] * fsrc;
                    xstr[ig] += vols[ireg] * mat.xstr()[ig];
                    xsnf[ig] += vols[ireg] * mat.xsnf()[ig];
                    xskf[ig] += vols[ireg] * mat.xskf()[ig];
                    xsch[ig] += vols[ireg] * fsrc * mat.xsch()[ig];

                    for( int igg=gmin; igg<=gmax; igg++ ) {
                        scat[ig][igg] += scat_row.from[igg-gmin] * vols[ireg];
                    }
                    ireg++;
                }
                ixsreg++;
            }

            xstr[ig] /= pin.vol();
            xsnf[ig] /= pin.vol();
            xskf[ig] /= pin.vol();
            xsch[ig] /= fvol;

            for( auto &s: scat[ig] ) {
                s /= pin.vol();
            }

        }

        ScatteringMatrix scat_mat(scat);

        return XSMeshRegion( fsrs, xstr, xsnf, xsch, xskf, scat_mat );
    }

    XSMeshRegion XSMeshHomogenized::homogenize_region_flux( int i,
            int first_reg, const Pin& pin, const ArrayF &flux ) const {

        size_t n_reg = mesh_.n_reg();

        // Set the FSRs to be one element, representing the coarse mesh index to
        // which this \ref XSMeshRegion belongs.
        VecI fsrs( 1, i );
        VecF xstr( ng_, 0.0 );
        VecF xsnf( ng_, 0.0 );
        VecF xskf( ng_, 0.0 );
        VecF xsch( ng_, 0.0 );

        std::vector<VecF> scat( ng_, VecF(ng_, 0.0) );

        auto mat_lib = mesh_.mat_lib();
        auto &pin_mesh = pin.mesh();
        auto vols = pin_mesh.vols();

        // Precompute the fission source in each region, since it is the
        // wieghting factor for chi
        VecF fs( pin_mesh.n_reg(), 0.0 );
        {
            int ixsreg = 0;
            for( auto &mat_id: pin.mat_ids() ) {
                auto mat = mat_lib.get_material_by_id(mat_id);
                for( size_t ig=0; ig<ng_; ig++ ) {
                    int ireg = first_reg; // pin-local region index
                    int ireg_local = 0;
                    for( size_t i=0; i<pin_mesh.n_fsrs(ixsreg); i++ ) {
                        fs[ireg_local] += mat.xsnf()[ig] * flux[ireg + ig*n_reg] *
                            vols[ireg_local];
                        ireg++;
                        ireg_local++;
                    }
                }
                ixsreg++;
            }
        }

        real_t fs_sum = 0.0;
        for( auto &v: fs ) {
            fs_sum += v;
        }

        for( size_t ig=0; ig<ng_; ig++ ) {
            real_t fluxvolsum = 0.0;
            VecF scatsum(ng_, 0.0);
            int ireg = first_reg; // global region index
            int ireg_local = 0; // pin-local refion index
            int ixsreg = 0;
            for( auto &mat_id: pin.mat_ids() ) {
                auto mat = mat_lib.get_material_by_id(mat_id);
                const ScatteringRow& scat_row = mat.xssc().to(ig);
                int gmin = scat_row.min_g;
                int gmax = scat_row.max_g;
                for( size_t i=0; i<pin_mesh.n_fsrs(ixsreg); i++ ) {
                    real_t v = vols[ireg_local];
                    real_t flux_i = flux[ireg + ig*n_reg];
                    fluxvolsum += v * flux_i;
                    xstr[ig] += v * flux_i * mat.xstr()[ig];
                    xsnf[ig] += v * flux_i * mat.xsnf()[ig];
                    xskf[ig] += v * flux_i * mat.xskf()[ig];
                    xsch[ig] += fs[ireg_local] * mat.xsch()[ig];

                    for( int igg=0; igg<ng_; igg++ ){
                        real_t fluxgg = flux[ireg + igg*n_reg];
                        fluxgg = 1.0;
                        scatsum[igg] += fluxgg * v;
                        if( (igg >= gmin) && (igg <= gmax) ) {
                            real_t scgg = scat_row.from[igg-gmin];
                            scat[ig][igg] += scgg * v * fluxgg;
                        }
                    }
                    ireg++;
                    ireg_local++;
                }
                ixsreg++;
            }

            for( size_t igg=0; igg<ng_; igg++ ) {
                if( scat[ig][igg] > 0.0 ) {
                    scat[ig][igg] /= scatsum[igg];
                }
            }

            xstr[ig] /= fluxvolsum;
            xsnf[ig] /= fluxvolsum;
            xskf[ig] /= fluxvolsum;
            xsch[ig] /= fs_sum;
        }

        ScatteringMatrix scat_mat(scat);

        return XSMeshRegion( fsrs, xstr, xsnf, xsch, xskf, scat_mat );
    }

    void XSMeshHomogenized::output( H5::CommonFG *file ) const {
        file->createGroup( "/xsmesh" );
        file->createGroup( "/xsmesh/xstr" );

        auto d = mesh_.dimensions();
        std::reverse(d.begin(), d.end());

        for( size_t ig=0; ig<ng_; ig++ ) {
            // Transport cross section
            VecF xstr( this->size(), 0.0 );
            int i = 0;
            for( auto xsr: regions_ ) {
                xstr[i] = xsr.xsmactr()[ig];
                i++;
            }
            std::stringstream setname;
            setname << "/xsmesh/xstr/" << ig;
            HDF::Write( file, setname.str(), xstr, d );
        }
        return;
    }

}
<commit_msg>Finish proper scattering xs homogenization<commit_after>#include "xs_mesh_homogenized.hpp"

#include <iomanip>
#include <iostream>
#include <sstream>

using std::cout;
using std::cin;
using std::endl;

namespace mocc {
    XSMeshHomogenized::XSMeshHomogenized( const CoreMesh& mesh ):
        mesh_( mesh )
    {
        // Set up the non-xs part of the xs mesh
        eubounds_ = mesh_.mat_lib().g_bounds();
        ng_ = eubounds_.size();

        regions_ = std::vector<XSMeshRegion>( mesh_.n_pin() );

        int ipin = 0;
        int first_reg = 0;
        for( const auto &pin: mesh_ ) {
            // Use the lexicographically-ordered pin index as the xs mesh index.
            // This is to put the indexing in a way that works best for the Sn
            // sweeper as it is implemented now. This is really brittle, and
            // should be replaced with some sort of Sn Mesh object, which both
            // the XS Mesh and the Sn sweeper will use to handle indexing.
            auto pos = mesh.pin_position(ipin);
            int ireg = mesh.index_lex( pos );
            int ixsreg = mesh_.index_lex( pos );
            regions_[ixsreg] = this->homogenize_region( ireg, *pin );
            ipin++;
            first_reg += pin->n_reg();
        }
    }

    /**
     * Update the XS mesh, incorporating a new estimate of the scalar flux.
     */
    void XSMeshHomogenized::update( const ArrayF &flux ) {
        int ipin = 0;
        int first_reg = 0;
        for( const auto &pin: mesh_ ) {
            int ireg = mesh_.index_lex( mesh_.pin_position(ipin) );
            int ixsreg = ireg;
            regions_[ixsreg] = this->homogenize_region_flux( ireg, first_reg,
                    *pin, flux);

            ipin++;
            first_reg += pin->n_reg();
        }
        return;
    }

    XSMeshRegion XSMeshHomogenized::homogenize_region( int i,
            const Pin& pin) const {
        VecI fsrs( 1, i );
        VecF xstr( ng_, 0.0 );
        VecF xsnf( ng_, 0.0 );
        VecF xskf( ng_, 0.0 );
        VecF xsch( ng_, 0.0 );

        std::vector<VecF> scat( ng_, VecF(ng_, 0.0) );

        auto mat_lib = mesh_.mat_lib();
        auto &pin_mesh = pin.mesh();
        auto vols = pin_mesh.vols();

        for( size_t ig=0; ig<ng_; ig++ ) {
            int ireg = 0;
            int ixsreg = 0;
            real_t fvol = 0.0;
            for( auto &mat_id: pin.mat_ids() ) {
                auto mat = mat_lib.get_material_by_id(mat_id);
                const ScatteringRow& scat_row = mat.xssc().to(ig);
                int gmin = scat_row.min_g;
                int gmax = scat_row.max_g;
                real_t fsrc = 0.0;
                for( size_t igg=0; igg<ng_; igg++ ) {
                    fsrc += mat.xsnf()[igg];
                }
                for( size_t i=0; i<pin_mesh.n_fsrs(ixsreg); i++ ) {
                    fvol += vols[ireg] * fsrc;
                    xstr[ig] += vols[ireg] * mat.xstr()[ig];
                    xsnf[ig] += vols[ireg] * mat.xsnf()[ig];
                    xskf[ig] += vols[ireg] * mat.xskf()[ig];
                    xsch[ig] += vols[ireg] * fsrc * mat.xsch()[ig];

                    for( int igg=gmin; igg<=gmax; igg++ ) {
                        scat[ig][igg] += scat_row.from[igg-gmin] * vols[ireg];
                    }
                    ireg++;
                }
                ixsreg++;
            }

            xstr[ig] /= pin.vol();
            xsnf[ig] /= pin.vol();
            xskf[ig] /= pin.vol();
            xsch[ig] /= fvol;

            for( auto &s: scat[ig] ) {
                s /= pin.vol();
            }

        }

        ScatteringMatrix scat_mat(scat);

        return XSMeshRegion( fsrs, xstr, xsnf, xsch, xskf, scat_mat );
    }

    XSMeshRegion XSMeshHomogenized::homogenize_region_flux( int i,
            int first_reg, const Pin& pin, const ArrayF &flux ) const {

        size_t n_reg = mesh_.n_reg();

        // Set the FSRs to be one element, representing the coarse mesh index to
        // which this \ref XSMeshRegion belongs.
        VecI fsrs( 1, i );
        VecF xstr( ng_, 0.0 );
        VecF xsnf( ng_, 0.0 );
        VecF xskf( ng_, 0.0 );
        VecF xsch( ng_, 0.0 );

        std::vector<VecF> scat( ng_, VecF(ng_, 0.0) );

        auto mat_lib = mesh_.mat_lib();
        auto &pin_mesh = pin.mesh();
        auto vols = pin_mesh.vols();

        // Precompute the fission source in each region, since it is the
        // wieghting factor for chi
        VecF fs( pin_mesh.n_reg(), 0.0 );
        {
            int ixsreg = 0;
            for( auto &mat_id: pin.mat_ids() ) {
                auto mat = mat_lib.get_material_by_id(mat_id);
                for( size_t ig=0; ig<ng_; ig++ ) {
                    int ireg = first_reg; // pin-local region index
                    int ireg_local = 0;
                    for( size_t i=0; i<pin_mesh.n_fsrs(ixsreg); i++ ) {
                        fs[ireg_local] += mat.xsnf()[ig] * flux[ireg + ig*n_reg] *
                            vols[ireg_local];
                        ireg++;
                        ireg_local++;
                    }
                }
                ixsreg++;
            }
        }

        real_t fs_sum = 0.0;
        for( auto &v: fs ) {
            fs_sum += v;
        }

        for( size_t ig=0; ig<ng_; ig++ ) {
            real_t fluxvolsum = 0.0;
            VecF scatsum(ng_, 0.0);
            int ireg = first_reg; // global region index
            int ireg_local = 0; // pin-local refion index
            int ixsreg = 0;
            for( auto &mat_id: pin.mat_ids() ) {
                auto mat = mat_lib.get_material_by_id(mat_id);
                const ScatteringRow& scat_row = mat.xssc().to(ig);
                int gmin = scat_row.min_g;
                int gmax = scat_row.max_g;
                for( size_t i=0; i<pin_mesh.n_fsrs(ixsreg); i++ ) {
                    real_t v = vols[ireg_local];
                    real_t flux_i = flux[ireg + ig*n_reg];
                    fluxvolsum += v * flux_i;
                    xstr[ig] += v * flux_i * mat.xstr()[ig];
                    xsnf[ig] += v * flux_i * mat.xsnf()[ig];
                    xskf[ig] += v * flux_i * mat.xskf()[ig];
                    xsch[ig] += fs[ireg_local] * mat.xsch()[ig];

                    for( int igg=0; igg<ng_; igg++ ){
                        real_t fluxgg = flux[ireg + igg*n_reg];
                        scatsum[igg] += fluxgg * v;
                        if( (igg >= gmin) && (igg <= gmax) ) {
                            real_t scgg = scat_row.from[igg-gmin];
                            scat[ig][igg] += scgg * v * fluxgg;
                        }
                    }
                    ireg++;
                    ireg_local++;
                }
                ixsreg++;
            }

            for( size_t igg=0; igg<ng_; igg++ ) {
                if( scat[ig][igg] > 0.0 ) {
                    scat[ig][igg] /= scatsum[igg];
                }
            }

            xstr[ig] /= fluxvolsum;
            xsnf[ig] /= fluxvolsum;
            xskf[ig] /= fluxvolsum;
            xsch[ig] /= fs_sum;
        }

        ScatteringMatrix scat_mat(scat);

        return XSMeshRegion( fsrs, xstr, xsnf, xsch, xskf, scat_mat );
    }

    void XSMeshHomogenized::output( H5::CommonFG *file ) const {
        file->createGroup( "/xsmesh" );
        file->createGroup( "/xsmesh/xstr" );
        file->createGroup( "/xsmesh/xsnf" );

        auto d = mesh_.dimensions();
        std::reverse(d.begin(), d.end());

        for( size_t ig=0; ig<ng_; ig++ ) {
            // Transport cross section
            VecF xstr( this->size(), 0.0 );
            VecF xsnf( this->size(), 0.0 );
            int i = 0;
            for( auto xsr: regions_ ) {
                xstr[i] = xsr.xsmactr()[ig];
                xsnf[i] = xsr.xsmacnf()[ig];
                i++;
            }
            {
                std::stringstream setname;
                setname << "/xsmesh/xstr/" << ig;
                HDF::Write( file, setname.str(), xstr, d );
            }
            {
                std::stringstream setname;
                setname << "/xsmesh/xsnf/" << ig;
                HDF::Write( file, setname.str(), xsnf, d );
            }
        }

        // scattering matrix
        VecF scat( regions_.size()*ng_*ng_, 0.0 );
        auto it = scat.begin();
        for( const auto &reg: regions_ ){
            auto reg_scat = reg.xsmacsc().as_vector();
            std::copy( reg_scat.begin(), reg_scat.end(), it );
        }
        VecI dims( 3 );
        dims[0] = regions_.size();
        dims[1] = ng_;
        dims[2] = ng_;

        HDF::Write( file, "/xsmesh/xssc", scat, dims );

        return;
    }

}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <qgraphicsvideoitem.h>

#include <qmediaobject.h>
#include <qmediaservice.h>
#include <qpaintervideosurface_p.h>
#include <qvideooutputcontrol.h>
#include <qvideorenderercontrol.h>

#include <QtMultimedia/qvideosurfaceformat.h>

QTM_BEGIN_NAMESPACE

class QGraphicsVideoItemPrivate
{
public:
    QGraphicsVideoItemPrivate()
        : q_ptr(0)
        , surface(0)
        , mediaObject(0)
        , service(0)
        , outputControl(0)
        , rendererControl(0)
    {
    }

    QGraphicsVideoItem *q_ptr;

    QPainterVideoSurface *surface;
    QMediaObject *mediaObject;
    QMediaService *service;
    QVideoOutputControl *outputControl;
    QVideoRendererControl *rendererControl;
    QRect boundingRect;

    void clearService();

    void _q_present();
    void _q_formatChanged(const QVideoSurfaceFormat &format);
    void _q_serviceDestroyed();
    void _q_mediaObjectDestroyed();
};

void QGraphicsVideoItemPrivate::clearService()
{
    if (outputControl) {
        outputControl->setOutput(QVideoOutputControl::NoOutput);
        outputControl = 0;
    }
    if (rendererControl) {
        surface->stop();
        rendererControl->setSurface(0);
        rendererControl = 0;
    }
    if (service) {
        QObject::disconnect(service, SIGNAL(destroyed()), q_ptr, SLOT(_q_serviceDestroyed()));
        service = 0;
    }
}

void QGraphicsVideoItemPrivate::_q_present()
{
    q_ptr->update(boundingRect);
}

void QGraphicsVideoItemPrivate::_q_formatChanged(const QVideoSurfaceFormat &format)
{
    q_ptr->prepareGeometryChange();

    boundingRect = QRect(QPoint(0, 0), format.sizeHint());
    boundingRect.moveCenter(QPoint(0, 0));
}

void QGraphicsVideoItemPrivate::_q_serviceDestroyed()
{
    rendererControl = 0;
    outputControl = 0;
    service = 0;

    surface->stop();
}

void QGraphicsVideoItemPrivate::_q_mediaObjectDestroyed()
{
    mediaObject = 0;

    clearService();
}

/*!
    \class QGraphicsVideoItem

    \brief The QGraphicsVideoItem class provides a graphics item which display video produced by a QMediaObject.

    \ingroup multimedia

    Attaching a QGraphicsVideoItem to a QMediaObject allows it to display
    the video or image output of that media object.  A QGraphicsVideoItem
    is attached to a media object by passing a pointer to the QMediaObject
    to the setMediaObject() function.

    \code
    player = new QMediaPlayer(this);

    QGraphicsVideoItem *item = new QGraphicsVideoItem;
    item->setMediaObject(player);
    graphicsView->scence()->addItem(item);
    graphicsView->show();

    player->setMedia(video);
    player->play();
    \endcode

    \bold {Note}: Only a single display output can be attached to a media object at one time.

    \sa QMediaObject, QMediaPlayer, QVideoWidget
*/

/*!
    Constructs a graphics item that displays video.

    The \a parent is passed to QGraphicsItem.
*/
QGraphicsVideoItem::QGraphicsVideoItem(QGraphicsItem *parent)
    : QGraphicsItem(parent)
    , d_ptr(new QGraphicsVideoItemPrivate)
{
    d_ptr->q_ptr = this;
    d_ptr->surface = new QPainterVideoSurface;
}

/*!
    Destroys a video graphics item.
*/
QGraphicsVideoItem::~QGraphicsVideoItem()
{
    if (d_ptr->outputControl)
        d_ptr->outputControl->setOutput(QVideoOutputControl::NoOutput);

    if (d_ptr->rendererControl)
        d_ptr->rendererControl->setSurface(0);

    delete d_ptr->surface;
    delete d_ptr;
}

/*!
    \property QGraphicsVideoItem::mediaObject
    \brief the media object which provides the video displayed by a graphics item.
*/

QMediaObject *QGraphicsVideoItem::mediaObject() const
{
    return d_func()->mediaObject;
}

void QGraphicsVideoItem::setMediaObject(QMediaObject *object)
{
    Q_D(QGraphicsVideoItem);

    if (object == d->mediaObject)
        return;

    d->clearService();

    if (d->mediaObject) {
        disconnect(d->mediaObject, SIGNAL(destroyed()), this, SLOT(_q_mediaObjectDestroyed()));
        d->mediaObject->unbind(this);
    }

    d->mediaObject = object;

    if (d->mediaObject) {
        d->mediaObject->bind(this);

        connect(d->mediaObject, SIGNAL(destroyed()), this, SLOT(_q_mediaObjectDestroyed()));

        d->service = d->mediaObject->service();

        if (d->service) {
            connect(d->service, SIGNAL(destroyed()), this, SLOT(_q_serviceDestroyed()));

            d->outputControl = qobject_cast<QVideoOutputControl *>(
                    d->service->control(QVideoOutputControl_iid));
            d->rendererControl = qobject_cast<QVideoRendererControl *>(
                    d->service->control(QVideoRendererControl_iid));

            if (d->outputControl != 0 && d->rendererControl != 0) {
                if (!d->surface) {
                    d->surface = new QPainterVideoSurface;

                    connect(d->surface, SIGNAL(frameChanged()), this, SLOT(_q_present()));
                    connect(d->surface, SIGNAL(surfaceFormatChanged(QVideoSurfaceFormat)),
                            this, SLOT(_q_formatChanged(QVideoSurfaceFormat)));
                    connect(d->service, SIGNAL(destroyed()), this, SLOT(_q_serviceDestroyed()));
                }

                d->rendererControl->setSurface(d->surface);

                if (isVisible())
                    d->outputControl->setOutput(QVideoOutputControl::RendererOutput);
            }
        }
    }
}

/*!
    \reimp
*/
QRectF QGraphicsVideoItem::boundingRect() const
{
    return d_func()->boundingRect;
}

/*!
    \reimp
*/
void QGraphicsVideoItem::paint(
        QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_D(QGraphicsVideoItem);

    Q_UNUSED(option);
    Q_UNUSED(widget);

    if (d->surface != 0) {
        d->surface->paint(painter, d->boundingRect);
        d->surface->setReady(true);
    }
}

/*!
    \reimp

    \internal
*/
QVariant QGraphicsVideoItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
    Q_D(QGraphicsVideoItem);

    if (change == ItemVisibleChange && d->outputControl != 0 && d->rendererControl != 0) {
        if (value.toBool()) {
            d->outputControl->setOutput(QVideoOutputControl::RendererOutput);

            return d->outputControl->output() == QVideoOutputControl::RendererOutput;
        } else {
            d->outputControl->setOutput(QVideoOutputControl::NoOutput);

            return value;
        }
    } else {
        return QGraphicsItem::itemChange(change, value);
    }
}

#include "moc_qgraphicsvideoitem.cpp"
QTM_END_NAMESPACE
<commit_msg>Fixed regression in QGraphicsVideoItem<commit_after>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <qgraphicsvideoitem.h>

#include <qmediaobject.h>
#include <qmediaservice.h>
#include <qpaintervideosurface_p.h>
#include <qvideooutputcontrol.h>
#include <qvideorenderercontrol.h>

#include <QtMultimedia/qvideosurfaceformat.h>

QTM_BEGIN_NAMESPACE

class QGraphicsVideoItemPrivate
{
public:
    QGraphicsVideoItemPrivate()
        : q_ptr(0)
        , surface(0)
        , mediaObject(0)
        , service(0)
        , outputControl(0)
        , rendererControl(0)
    {
    }

    QGraphicsVideoItem *q_ptr;

    QPainterVideoSurface *surface;
    QMediaObject *mediaObject;
    QMediaService *service;
    QVideoOutputControl *outputControl;
    QVideoRendererControl *rendererControl;
    QRect boundingRect;

    void clearService();

    void _q_present();
    void _q_formatChanged(const QVideoSurfaceFormat &format);
    void _q_serviceDestroyed();
    void _q_mediaObjectDestroyed();
};

void QGraphicsVideoItemPrivate::clearService()
{
    if (outputControl) {
        outputControl->setOutput(QVideoOutputControl::NoOutput);
        outputControl = 0;
    }
    if (rendererControl) {
        surface->stop();
        rendererControl->setSurface(0);
        rendererControl = 0;
    }
    if (service) {
        QObject::disconnect(service, SIGNAL(destroyed()), q_ptr, SLOT(_q_serviceDestroyed()));
        service = 0;
    }
}

void QGraphicsVideoItemPrivate::_q_present()
{
    q_ptr->update(boundingRect);
}

void QGraphicsVideoItemPrivate::_q_formatChanged(const QVideoSurfaceFormat &format)
{
    q_ptr->prepareGeometryChange();

    boundingRect = QRect(QPoint(0, 0), format.sizeHint());
    boundingRect.moveCenter(QPoint(0, 0));
}

void QGraphicsVideoItemPrivate::_q_serviceDestroyed()
{
    rendererControl = 0;
    outputControl = 0;
    service = 0;

    surface->stop();
}

void QGraphicsVideoItemPrivate::_q_mediaObjectDestroyed()
{
    mediaObject = 0;

    clearService();
}

/*!
    \class QGraphicsVideoItem

    \brief The QGraphicsVideoItem class provides a graphics item which display video produced by a QMediaObject.

    \ingroup multimedia

    Attaching a QGraphicsVideoItem to a QMediaObject allows it to display
    the video or image output of that media object.  A QGraphicsVideoItem
    is attached to a media object by passing a pointer to the QMediaObject
    to the setMediaObject() function.

    \code
    player = new QMediaPlayer(this);

    QGraphicsVideoItem *item = new QGraphicsVideoItem;
    item->setMediaObject(player);
    graphicsView->scence()->addItem(item);
    graphicsView->show();

    player->setMedia(video);
    player->play();
    \endcode

    \bold {Note}: Only a single display output can be attached to a media object at one time.

    \sa QMediaObject, QMediaPlayer, QVideoWidget
*/

/*!
    Constructs a graphics item that displays video.

    The \a parent is passed to QGraphicsItem.
*/
QGraphicsVideoItem::QGraphicsVideoItem(QGraphicsItem *parent)
    : QGraphicsItem(parent)
    , d_ptr(new QGraphicsVideoItemPrivate)
{
    d_ptr->q_ptr = this;
    d_ptr->surface = new QPainterVideoSurface;

    connect(d_ptr->surface, SIGNAL(frameChanged()), this, SLOT(_q_present()));
    connect(d_ptr->surface, SIGNAL(surfaceFormatChanged(QVideoSurfaceFormat)),
            this, SLOT(_q_formatChanged(QVideoSurfaceFormat)));
}

/*!
    Destroys a video graphics item.
*/
QGraphicsVideoItem::~QGraphicsVideoItem()
{
    if (d_ptr->outputControl)
        d_ptr->outputControl->setOutput(QVideoOutputControl::NoOutput);

    if (d_ptr->rendererControl)
        d_ptr->rendererControl->setSurface(0);

    delete d_ptr->surface;
    delete d_ptr;
}

/*!
    \property QGraphicsVideoItem::mediaObject
    \brief the media object which provides the video displayed by a graphics item.
*/

QMediaObject *QGraphicsVideoItem::mediaObject() const
{
    return d_func()->mediaObject;
}

void QGraphicsVideoItem::setMediaObject(QMediaObject *object)
{
    Q_D(QGraphicsVideoItem);

    if (object == d->mediaObject)
        return;

    d->clearService();

    if (d->mediaObject) {
        disconnect(d->mediaObject, SIGNAL(destroyed()), this, SLOT(_q_mediaObjectDestroyed()));
        d->mediaObject->unbind(this);
    }

    d->mediaObject = object;

    if (d->mediaObject) {
        d->mediaObject->bind(this);

        connect(d->mediaObject, SIGNAL(destroyed()), this, SLOT(_q_mediaObjectDestroyed()));

        d->service = d->mediaObject->service();

        if (d->service) {
            connect(d->service, SIGNAL(destroyed()), this, SLOT(_q_serviceDestroyed()));

            d->outputControl = qobject_cast<QVideoOutputControl *>(
                    d->service->control(QVideoOutputControl_iid));
            d->rendererControl = qobject_cast<QVideoRendererControl *>(
                    d->service->control(QVideoRendererControl_iid));

            if (d->outputControl != 0 && d->rendererControl != 0) {
                d->rendererControl->setSurface(d->surface);

                if (isVisible())
                    d->outputControl->setOutput(QVideoOutputControl::RendererOutput);
            }
        }
    }
}

/*!
    \reimp
*/
QRectF QGraphicsVideoItem::boundingRect() const
{
    return d_func()->boundingRect;
}

/*!
    \reimp
*/
void QGraphicsVideoItem::paint(
        QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_D(QGraphicsVideoItem);

    Q_UNUSED(option);
    Q_UNUSED(widget);

    if (d->surface != 0) {
        d->surface->paint(painter, d->boundingRect);
        d->surface->setReady(true);
    }
}

/*!
    \reimp

    \internal
*/
QVariant QGraphicsVideoItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
    Q_D(QGraphicsVideoItem);

    if (change == ItemVisibleChange && d->outputControl != 0 && d->rendererControl != 0) {
        if (value.toBool()) {
            d->outputControl->setOutput(QVideoOutputControl::RendererOutput);

            return d->outputControl->output() == QVideoOutputControl::RendererOutput;
        } else {
            d->outputControl->setOutput(QVideoOutputControl::NoOutput);

            return value;
        }
    } else {
        return QGraphicsItem::itemChange(change, value);
    }
}

#include "moc_qgraphicsvideoitem.cpp"
QTM_END_NAMESPACE
<|endoftext|>


<commit_before>/*****************************************************************************
   Copyright 2004-2008 Steve Menard

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*****************************************************************************/

#include <jpype_python.h>
PyObject* JPypeJavaArray::findArrayClass(PyObject* obj, PyObject* args)
{
	try {
		char* cname;
		JPyArg::parseTuple(args, "s", &cname);

		JPTypeName name = JPTypeName::fromSimple(cname);
		JPArrayClass* claz = JPTypeManager::findArrayClass(name);
		if (claz == NULL)
		{
			Py_INCREF(Py_None);
			return Py_None;
		}

		PyObject* res = JPyCObject::fromVoidAndDesc((void*)claz, (void*)"jclass", NULL);

		return res;
	}
	PY_STANDARD_CATCH;

	PyErr_Clear();

	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* JPypeJavaArray::setJavaArrayClass(PyObject* self, PyObject* arg)
{
	try {
		PyObject* t;
		JPyArg::parseTuple(arg, "O", &t);
		hostEnv->setJavaArrayClass(t);

		Py_INCREF(Py_None);
		return Py_None;
	}
	PY_STANDARD_CATCH

	return NULL;
}

PyObject* JPypeJavaArray::setGetJavaArrayClassMethod(PyObject* self, PyObject* arg)
{
	try {
		PyObject* t;
		JPyArg::parseTuple(arg, "O", &t);
		hostEnv->setGetJavaArrayClassMethod(t);

		Py_INCREF(Py_None);
		return Py_None;
	}
	PY_STANDARD_CATCH

	return NULL;
}

PyObject* JPypeJavaArray::getArrayLength(PyObject* self, PyObject* arg)
{
	try {
		PyObject* arrayObject;
		JPyArg::parseTuple(arg, "O!", &PyCObject_Type, &arrayObject);
		JPArray* a = (JPArray*)JPyCObject::asVoidPtr(arrayObject);

		int res = a->getLength();
		return JPyInt::fromLong(res);

	}
	PY_STANDARD_CATCH

	return NULL;
}

PyObject* JPypeJavaArray::getArrayItem(PyObject* self, PyObject* arg)
{
	try {
		PyObject* arrayObject;
		int ndx;
		JPyArg::parseTuple(arg, "O!i", &PyCObject_Type, &arrayObject, &ndx);
		JPArray* a = (JPArray*)JPyCObject::asVoidPtr(arrayObject);

		HostRef* res = a->getItem(ndx);
		return detachRef(res);
	}
	PY_STANDARD_CATCH

	return NULL;
}

PyObject* JPypeJavaArray::getArraySlice(PyObject* self, PyObject* arg)
{
	try {
		PyObject* arrayObject;
		int lo = -1;
		int hi = -1;
		JPyArg::parseTuple(arg, "O!ii", &PyCObject_Type, &arrayObject, &lo, &hi);
		JPArray* a = (JPArray*)JPyCObject::asVoidPtr(arrayObject);
		int length = a->getLength();
		// stolen from jcc, to get nice slice support
        if (lo < 0) lo = length + lo;
        if (lo < 0) lo = 0;
        else if (lo > length) lo = length;
        if (hi < 0) hi = length + hi;
        if (hi < 0) hi = 0;
        else if (hi > length) hi = length;
        if (lo > hi) lo = hi;

		const string& name = a->getType()->getObjectType().getComponentName().getNativeName();
		switch(name[0]) {
		// for primitive types, we have fast sequence generation available
		case 'B': case 'S': case 'I': case 'J': case 'F': case 'D': case 'Z': case 'C':
			// fast
			return a->getSequenceFromRange(lo, hi);
		default: {
			// horrible slow
			vector<HostRef*> values = a->getRange(lo, hi);

			JPCleaner cleaner;
			PyObject* res = JPySequence::newList((int)values.size());
			for (unsigned int i = 0; i < values.size(); i++)
			{
				JPySequence::setItem(res, i, (PyObject*)values[i]->data());
				cleaner.add(values[i]);
			}

			return res;
		}
		}
	} PY_STANDARD_CATCH

	return NULL;
}

PyObject* JPypeJavaArray::setArraySlice(PyObject* self, PyObject* arg)
{
	try {
		PyObject* arrayObject;
		int ndx = -1;
		int ndx2 = -1;
		PyObject* val;
		JPyArg::parseTuple(arg, "O!iiO", &PyCObject_Type, &arrayObject, &ndx, &ndx2, &val);
		JPArray* a = (JPArray*)JPyCObject::asVoidPtr(arrayObject);

		Py_ssize_t len = JPyObject::length(val);
		vector<HostRef*> values;
		JPCleaner cleaner;
		for (Py_ssize_t i = 0; i < len; i++)
		{
			HostRef* v = new HostRef(JPySequence::getItem(val, i), false);
			values.push_back(v);
			cleaner.add(v);
		}

		a->setRange(ndx, ndx2, values);

		Py_INCREF(Py_None);
		return Py_None;
	}
	PY_STANDARD_CATCH

	return NULL;
}

PyObject* JPypeJavaArray::setArrayItem(PyObject* self, PyObject* arg)
{
	try {
		PyObject* arrayObject;
		int ndx;
		PyObject* value;
		JPyArg::parseTuple(arg, "O!iO", &PyCObject_Type, &arrayObject, &ndx, &value);
		JPArray* a = (JPArray*)JPyCObject::asVoidPtr(arrayObject);

		JPCleaner cleaner;
		HostRef* v = new HostRef(value);
		cleaner.add(v);

		a->setItem(ndx, v);
		Py_INCREF(Py_None);
		return Py_None;
	}
	PY_STANDARD_CATCH

	return NULL;
}

PyObject* JPypeJavaArray::newArray(PyObject* self, PyObject* arg)
{
	try {
		PyObject* arrayObject;
		int sz;
		JPyArg::parseTuple(arg, "O!i", &PyCObject_Type, &arrayObject, &sz);
		JPArrayClass* a = (JPArrayClass*)JPyCObject::asVoidPtr(arrayObject);

		JPArray* v = a->newInstance(sz);
		PyObject* res = JPyCObject::fromVoidAndDesc(v, (void*)"JPArray", PythonHostEnvironment::deleteJPArrayDestructor);

		return res;
	}
	PY_STANDARD_CATCH

	return NULL;
}

PyObject* JPypeJavaArray::setArrayValues(PyObject* self, PyObject* arg)
{
	try {
		PyObject* arrayObject;
		PyObject* values;
		JPyArg::parseTuple(arg, "O!O", &PyCObject_Type, &arrayObject, &values);
		JPArray* a = (JPArray*)JPyCObject::asVoidPtr(arrayObject);
		JPArrayClass* arrayClass = a->getClass();
		HostRef valuesRef(values);

		JPCleaner cleaner;
		jarray arr = (jarray)a->getObject();
		cleaner.addLocal(arr);
		arrayClass->getComponentType()->setArrayValues(arr, &valuesRef);

		Py_INCREF(Py_None);
		return Py_None;
	}
	PY_STANDARD_CATCH

	return NULL;
}
<commit_msg>impl setArraySlice with distinction for primitive types<commit_after>/*****************************************************************************
   Copyright 2004-2008 Steve Menard

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*****************************************************************************/

#include <jpype_python.h>

namespace { // impl detail
	inline bool is_primitive(char t) {
		switch(t) {
			case 'B': case 'S': case 'I': case 'J': case 'F': case 'D': case 'Z': case 'C':
				return true;
			default:
				return false;
		}
	}
}

PyObject* JPypeJavaArray::findArrayClass(PyObject* obj, PyObject* args)
{
	try {
		char* cname;
		JPyArg::parseTuple(args, "s", &cname);

		JPTypeName name = JPTypeName::fromSimple(cname);
		JPArrayClass* claz = JPTypeManager::findArrayClass(name);
		if (claz == NULL)
		{
			Py_INCREF(Py_None);
			return Py_None;
		}

		PyObject* res = JPyCObject::fromVoidAndDesc((void*)claz, (void*)"jclass", NULL);

		return res;
	}
	PY_STANDARD_CATCH;

	PyErr_Clear();

	Py_INCREF(Py_None);
	return Py_None;
}

PyObject* JPypeJavaArray::setJavaArrayClass(PyObject* self, PyObject* arg)
{
	try {
		PyObject* t;
		JPyArg::parseTuple(arg, "O", &t);
		hostEnv->setJavaArrayClass(t);

		Py_INCREF(Py_None);
		return Py_None;
	}
	PY_STANDARD_CATCH

	return NULL;
}

PyObject* JPypeJavaArray::setGetJavaArrayClassMethod(PyObject* self, PyObject* arg)
{
	try {
		PyObject* t;
		JPyArg::parseTuple(arg, "O", &t);
		hostEnv->setGetJavaArrayClassMethod(t);

		Py_INCREF(Py_None);
		return Py_None;
	}
	PY_STANDARD_CATCH

	return NULL;
}

PyObject* JPypeJavaArray::getArrayLength(PyObject* self, PyObject* arg)
{
	try {
		PyObject* arrayObject;
		JPyArg::parseTuple(arg, "O!", &PyCObject_Type, &arrayObject);
		JPArray* a = (JPArray*)JPyCObject::asVoidPtr(arrayObject);

		int res = a->getLength();
		return JPyInt::fromLong(res);

	}
	PY_STANDARD_CATCH

	return NULL;
}

PyObject* JPypeJavaArray::getArrayItem(PyObject* self, PyObject* arg)
{
	try {
		PyObject* arrayObject;
		int ndx;
		JPyArg::parseTuple(arg, "O!i", &PyCObject_Type, &arrayObject, &ndx);
		JPArray* a = (JPArray*)JPyCObject::asVoidPtr(arrayObject);

		HostRef* res = a->getItem(ndx);
		return detachRef(res);
	}
	PY_STANDARD_CATCH

	return NULL;
}

PyObject* JPypeJavaArray::getArraySlice(PyObject* self, PyObject* arg)
{
		PyObject* arrayObject;
		int lo = -1;
		int hi = -1;
	try
	{

		JPyArg::parseTuple(arg, "O!ii", &PyCObject_Type, &arrayObject, &lo, &hi);
		JPArray* a = (JPArray*)JPyCObject::asVoidPtr(arrayObject);
		int length = a->getLength();
		// stolen from jcc, to get nice slice support
		if (lo < 0) lo = length + lo;
		if (lo < 0) lo = 0;
		else if (lo > length) lo = length;
		if (hi < 0) hi = length + hi;
		if (hi < 0) hi = 0;
		else if (hi > length) hi = length;
		if (lo > hi) lo = hi;

		const string& name = a->getType()->getObjectType().getComponentName().getNativeName();
		if(is_primitive(name[0]))
		{
			// for primitive types, we have fast sequence generation available
			return a->getSequenceFromRange(lo, hi);
		}
		else
		{
			// slow wrapped access for non primitives
			vector<HostRef*> values = a->getRange(lo, hi);

			JPCleaner cleaner;
			PyObject* res = JPySequence::newList((int)values.size());
			for (unsigned int i = 0; i < values.size(); i++)
			{
				JPySequence::setItem(res, i, (PyObject*)values[i]->data());
				cleaner.add(values[i]);
			}

			return res;
		}
	} PY_STANDARD_CATCH

	return NULL;
}

PyObject* JPypeJavaArray::setArraySlice(PyObject* self, PyObject* arg)
{

	PyObject* arrayObject;
	int lo = -1;
	int hi = -1;
	PyObject* sequence;
	try {
		JPyArg::parseTuple(arg, "O!iiO", &PyCObject_Type, &arrayObject, &lo, &hi, &sequence);
		JPArray* a = (JPArray*)JPyCObject::asVoidPtr(arrayObject);

		Py_ssize_t length = a->getLength();

		if (lo < 0) lo = length + lo;
		if (lo < 0) lo = 0;
		else if (lo > length) lo = length;
		if (hi < 0) hi = length + hi;
		if (hi < 0) hi = 0;
		else if (hi > length) hi = length;
		if (lo > hi) lo = hi;

		const string& name = a->getType()->getObjectType().getComponentName().getNativeName();

		if(is_primitive(name[0]))
		{
			// for primitive types, we have fast setters available
			a->setRange(lo, hi, sequence);
		}
		else
		{
			// slow wrapped access for non primitive types
			vector<HostRef*> values;
			values.reserve(hi - lo);
			JPCleaner cleaner;
			for (Py_ssize_t i = 0; i < hi - lo; i++)
			{
				HostRef* v = new HostRef(JPySequence::getItem(sequence, i), false);
				values.push_back(v);
				cleaner.add(v);
			}

			a->setRange(lo, hi, values);
		}

		Py_RETURN_NONE;
	}
	PY_STANDARD_CATCH

	return NULL;
}

PyObject* JPypeJavaArray::setArrayItem(PyObject* self, PyObject* arg)
{
	try {
		PyObject* arrayObject;
		int ndx;
		PyObject* value;
		JPyArg::parseTuple(arg, "O!iO", &PyCObject_Type, &arrayObject, &ndx, &value);
		JPArray* a = (JPArray*)JPyCObject::asVoidPtr(arrayObject);

		JPCleaner cleaner;
		HostRef* v = new HostRef(value);
		cleaner.add(v);

		a->setItem(ndx, v);
		Py_INCREF(Py_None);
		return Py_None;
	}
	PY_STANDARD_CATCH

	return NULL;
}

PyObject* JPypeJavaArray::newArray(PyObject* self, PyObject* arg)
{
	try {
		PyObject* arrayObject;
		int sz;
		JPyArg::parseTuple(arg, "O!i", &PyCObject_Type, &arrayObject, &sz);
		JPArrayClass* a = (JPArrayClass*)JPyCObject::asVoidPtr(arrayObject);

		JPArray* v = a->newInstance(sz);
		PyObject* res = JPyCObject::fromVoidAndDesc(v, (void*)"JPArray", PythonHostEnvironment::deleteJPArrayDestructor);

		return res;
	}
	PY_STANDARD_CATCH

	return NULL;
}

PyObject* JPypeJavaArray::setArrayValues(PyObject* self, PyObject* arg)
{
	try {
		PyObject* arrayObject;
		PyObject* values;
		JPyArg::parseTuple(arg, "O!O", &PyCObject_Type, &arrayObject, &values);
		JPArray* a = (JPArray*)JPyCObject::asVoidPtr(arrayObject);
		JPArrayClass* arrayClass = a->getClass();
		HostRef valuesRef(values);

		JPCleaner cleaner;
		jarray arr = (jarray)a->getObject();
		cleaner.addLocal(arr);
		arrayClass->getComponentType()->setArrayValues(arr, &valuesRef);

		Py_INCREF(Py_None);
		return Py_None;
	}
	PY_STANDARD_CATCH

	return NULL;
}
<|endoftext|>