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

#include <vector>
#include <unordered_map>

#include "SymbolTable.hpp"
#include "Options.hpp"
#include "Type.hpp"
#include "FunctionContext.hpp"
#include "iterators.hpp"
#include "VisitorUtils.hpp"

#include "mtac/inlining.hpp"
#include "mtac/Printer.hpp"
#include "mtac/Utils.hpp"

using namespace eddic;

namespace {

typedef std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<Variable>> VariableClones;
typedef std::unordered_map<std::shared_ptr<mtac::BasicBlock>, std::shared_ptr<mtac::BasicBlock>> BBClones;

struct VariableReplace : public boost::static_visitor<> {
    VariableClones& clones;

    VariableReplace(VariableClones& clones) : clones(clones) {}

    template<typename Value>
    void update_usage(Value& value){
        if(auto* ptr = boost::get<std::shared_ptr<Variable>>(&value)){
            if(clones.find(*ptr) != clones.end()){
                value = clones[*ptr];
            }
        }
    }

    template<typename Opt>
    void update_usage_optional(Opt& opt){
        if(opt){
            update_usage(*opt);
        }
    }

    void operator()(std::shared_ptr<mtac::Quadruple> quadruple){
        if(clones.find(quadruple->result) != clones.end()){
            quadruple->result = clones[quadruple->result];
        }

        update_usage_optional(quadruple->arg1);
        update_usage_optional(quadruple->arg2);
    }

    void operator()(std::shared_ptr<mtac::Param> param){
        update_usage(param->arg);
    }

    void operator()(std::shared_ptr<mtac::IfFalse> if_false){
        update_usage(if_false->arg1);
        update_usage_optional(if_false->arg2);
    }

    void operator()(std::shared_ptr<mtac::If> if_){
        update_usage(if_->arg1);
        update_usage_optional(if_->arg2);
    }

    void operator()(std::shared_ptr<mtac::Call> call_){
        if(call_->return_ && clones.find(call_->return_) != clones.end()){
            call_->return_ = clones[call_->return_];
        }

        if(call_->return2_ && clones.find(call_->return2_) != clones.end()){
            call_->return2_ = clones[call_->return2_];
        }
    }

    template<typename T>
    void operator()(T&){
        //NOP
    }
};

struct BBReplace : public boost::static_visitor<> {
    BBClones& clones;

    BBReplace(BBClones& clones) : clones(clones) {}

    void operator()(std::shared_ptr<mtac::IfFalse> if_){
        if(clones.find(if_->block) != clones.end()){
            if_->block = clones[if_->block];
        }
    }

    void operator()(std::shared_ptr<mtac::If> if_){
        if(clones.find(if_->block) != clones.end()){
            if_->block = clones[if_->block];
        }
    }

    void operator()(std::shared_ptr<mtac::Goto> goto_){
        if(clones.find(goto_->block) != clones.end()){
            goto_->block = clones[goto_->block];
        }
    }

    template<typename T>
    void operator()(T&){
        //NOP
    }
};

void clone(std::vector<mtac::Statement>& sources, std::vector<mtac::Statement>& destination){
    for(auto& statement : sources){
        if(auto* ptr = boost::get<std::shared_ptr<mtac::Quadruple>>(&statement)){
            auto quadruple = *ptr;

            auto copy = std::make_shared<mtac::Quadruple>();

            copy->result = quadruple->result;
            copy->arg1 = quadruple->arg1;
            copy->arg2 = quadruple->arg2;
            copy->op = quadruple->op;

            destination.push_back(copy);
        } else if(auto* ptr = boost::get<std::shared_ptr<mtac::Param>>(&statement)){
            auto param = *ptr;

            auto copy = std::make_shared<mtac::Param>();

            copy->arg = param->arg;
            copy->param = param->param;
            copy->std_param = param->std_param;
            copy->function = param->function;
            copy->address = param->address;
            copy->memberNames = param->memberNames;

            destination.push_back(copy);
        } else if(auto* ptr = boost::get<std::shared_ptr<mtac::IfFalse>>(&statement)){
            auto if_ = *ptr;

            auto copy = std::make_shared<mtac::IfFalse>();

            copy->op = if_->op;
            copy->arg1 = if_->arg1;
            copy->arg2 = if_->arg2;
            copy->label = if_->label;
            copy->block = if_->block;

            destination.push_back(copy);
        } else if(auto* ptr = boost::get<std::shared_ptr<mtac::If>>(&statement)){
            auto if_ = *ptr;

            auto copy = std::make_shared<mtac::If>();

            copy->op = if_->op;
            copy->arg1 = if_->arg1;
            copy->arg2 = if_->arg2;
            copy->label = if_->label;
            copy->block = if_->block;

            destination.push_back(copy);
        } else if(auto* ptr = boost::get<std::shared_ptr<mtac::Goto>>(&statement)){
            auto goto_ = *ptr;

            auto copy = std::make_shared<mtac::Goto>(goto_->label, goto_->type);
            copy->block = goto_->block;
            destination.push_back(copy);
        } else if(auto* ptr = boost::get<std::shared_ptr<mtac::Call>>(&statement)){
            auto call = *ptr;

            destination.push_back(std::make_shared<mtac::Call>(call->function, call->functionDefinition, call->return_, call->return2_));
        } else if(auto* ptr = boost::get<std::string>(&statement)){
            destination.push_back(*ptr);
        }

        //No need to copy NOP
    }
}

template<typename Iterator>
BBClones clone(std::shared_ptr<mtac::Function> source_function, std::shared_ptr<mtac::Function> dest_function, Iterator bit){
    BBClones bb_clones;

    for(auto block : source_function->getBasicBlocks()){
        //Copy all basic blocks except ENTRY and EXIT
        if(block->index >= 0){
            auto new_bb = std::make_shared<mtac::BasicBlock>(dest_function->getBasicBlocks().size() + 1);
            new_bb->context = block->context;
            clone(block->statements, new_bb->statements);

            bb_clones[block] = new_bb;

            bit.insert(new_bb);

            ++bit;
        }
    }

    return bb_clones;
}

template<typename Iterator>
VariableClones copy_parameters(std::shared_ptr<mtac::Function> source_function, std::shared_ptr<mtac::Function> dest_function, Iterator bit){
    VariableClones variable_clones;

    auto source_definition = source_function->definition;
    auto dest_definition = dest_function->definition;

    if(source_definition->parameters.size() > 0){
        //Param are in the previous block
        --bit;

        auto pit = (*bit)->statements.end() - 1;

        for(int i = source_definition->parameters.size() - 1; i >= 0;){
            auto statement = *pit;

            if(auto* ptr = boost::get<std::shared_ptr<mtac::Param>>(&statement)){
                auto quadruple = std::make_shared<mtac::Quadruple>();
                std::shared_ptr<Variable> dest_var;

                auto src_var = (*ptr)->param;

                if((*ptr)->memberNames.empty()){
                    auto type = src_var->type();

                    dest_var = dest_definition->context->new_temporary(type);

                    if(type == INT || type == BOOL){
                        quadruple->op = mtac::Operator::ASSIGN;
                    } else if(type->is_pointer()){
                        quadruple->op = mtac::Operator::PASSIGN;
                    } else {
                        quadruple->op = mtac::Operator::FASSIGN;
                    }

                    quadruple->arg1 = (*ptr)->arg;
                } else {
                    auto object_var = boost::get<std::shared_ptr<Variable>>((*ptr)->arg);
                    dest_var = dest_definition->context->new_temporary(INT);

                    quadruple->op = mtac::Operator::PDOT;
                    quadruple->arg1 = object_var;
                    quadruple->arg2 = mtac::compute_member_offset(object_var, (*ptr)->memberNames);
                }

                variable_clones[src_var] = dest_var;
                quadruple->result = dest_var;

                *pit = quadruple;

                --i;
            }

            --pit;
        }
    }

    return variable_clones;
}

template<typename Iterator>
void adapt_instructions(VariableClones& variable_clones, BBClones& bb_clones, std::shared_ptr<mtac::Call> call, Iterator bit){
    VariableReplace variable_replacer(variable_clones);
    BBReplace bb_replacer(bb_clones);

    auto basic_block = *bit;
    --bit;

    auto cloned_bb = bb_clones.size();

    while(cloned_bb > 0){
        auto new_bb = *bit;

        auto ssit = iterate(new_bb->statements);

        while(ssit.has_next()){
            auto statement = *ssit;

            visit(variable_replacer, statement);
            visit(bb_replacer, statement);

            if(auto* ret_ptr = boost::get<std::shared_ptr<mtac::Quadruple>>(&statement)){
                auto quadruple = *ret_ptr;

                if(quadruple->op == mtac::Operator::RETURN){
                    auto goto_ = std::make_shared<mtac::Goto>();
                    goto_->block = basic_block;

                    if(!call->return_){
                        //If the caller does not care about the return value, return has no effect
                        ssit.erase();
                        ssit.insert(goto_);

                        continue;
                    } else {
                        mtac::Operator op;
                        if(call->functionDefinition->returnType == FLOAT){
                            op = mtac::Operator::FASSIGN;
                        } else if(call->functionDefinition->returnType->is_pointer()){
                            op = mtac::Operator::PASSIGN;
                        } else {
                            op = mtac::Operator::ASSIGN;
                        }

                        if(call->return2_){
                            ssit.insert(std::make_shared<mtac::Quadruple>(call->return2_, *quadruple->arg2, op));

                            ++ssit;
                        }

                        quadruple->op = op;
                        quadruple->result = call->return_;
                        quadruple->arg2.reset();

                        ++ssit;

                        ssit.insert(goto_);

                        continue;
                    }
                }
            }

            ++ssit;
        }

        --cloned_bb;
        --bit;
    }
}

bool can_be_inlined(std::shared_ptr<mtac::Function> function){
    //The main function cannot be inlined
    if(function->getName() == "_F4main" || function->getName() == "_F4mainAS"){
        return false;
    }

    for(auto& param : function->definition->parameters){
        if(param.paramType != INT && param.paramType != FLOAT && param.paramType != BOOL && !(param.paramType->is_pointer()/* && param.name == "this")*/)){
            return false;
        }
    }

    if(mtac::is_recursive(function)){
        return false;
    }

    return true;
}

bool will_inline(std::shared_ptr<mtac::Function> function){
    if(can_be_inlined(function)){
        //function called once
        if(symbols.referenceCount(function->getName()) == 1){
            return true;
        } else {
            auto size = function->size();

            //Inline little functions
            if(size < 10){
                return true;
            }
        }
    }

    return false;
}

} //end of anonymous namespace

bool mtac::inline_functions(std::shared_ptr<mtac::Program> program){
    if(option_defined("fno-inline-functions")){
        return false;
    }

    if(option_defined("finline-functions")){
        bool optimized = false;

        for(auto source_function : program->functions){
            if(!will_inline(source_function)){
                continue;
            }

            auto source_definition = source_function->definition;

            for(auto dest_function : program->functions){
                if(dest_function == source_function){
                    continue;
                }

                //If the function has already been inlined
                if(symbols.referenceCount(dest_function->getName()) <= 0){
                    continue;
                }

                for(auto bit = iterate(dest_function->getBasicBlocks()); bit.has_next(); ++bit){
                    auto basic_block = *bit;

                    auto it = iterate(basic_block->statements);

                    while(it.has_next()){
                        if(auto* ptr = boost::get<std::shared_ptr<mtac::Call>>(&*it)){
                            auto call = *ptr;

                            if(call->functionDefinition == source_definition){
                                //Copy the parameters
                                auto variable_clones = copy_parameters(source_function, dest_function, bit);

                                //Allocate storage for the local variables of the inlined function
                                for(auto variable_pair : source_definition->context->stored_variables()){
                                    auto variable = variable_pair.second;
                                    variable_clones[variable] = dest_function->definition->context->newVariable(variable);
                                }

                                //Clone all the source basic blocks in the dest function
                                auto bb_clones = clone(source_function, dest_function, bit);

                                //Fix all the instructions (clones and return)
                                adapt_instructions(variable_clones, bb_clones, call, bit);

                                //Erase the original call
                                it.erase();

                                symbols.removeReference(source_function->getName());
                                optimized = true;

                                continue;
                            }
                        }

                        ++it;
                    }
                }
            }
        }

        return optimized;
    } else {
        return false;
    }
}
<commit_msg>Clone statements with static visitors<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <vector>
#include <unordered_map>

#include "SymbolTable.hpp"
#include "Options.hpp"
#include "Type.hpp"
#include "FunctionContext.hpp"
#include "iterators.hpp"
#include "VisitorUtils.hpp"

#include "mtac/inlining.hpp"
#include "mtac/Printer.hpp"
#include "mtac/Utils.hpp"

using namespace eddic;

namespace {

typedef std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<Variable>> VariableClones;
typedef std::unordered_map<std::shared_ptr<mtac::BasicBlock>, std::shared_ptr<mtac::BasicBlock>> BBClones;

struct VariableReplace : public boost::static_visitor<> {
    VariableClones& clones;

    VariableReplace(VariableClones& clones) : clones(clones) {}

    template<typename Value>
    void update_usage(Value& value){
        if(auto* ptr = boost::get<std::shared_ptr<Variable>>(&value)){
            if(clones.find(*ptr) != clones.end()){
                value = clones[*ptr];
            }
        }
    }

    template<typename Opt>
    void update_usage_optional(Opt& opt){
        if(opt){
            update_usage(*opt);
        }
    }

    void operator()(std::shared_ptr<mtac::Quadruple> quadruple){
        if(clones.find(quadruple->result) != clones.end()){
            quadruple->result = clones[quadruple->result];
        }

        update_usage_optional(quadruple->arg1);
        update_usage_optional(quadruple->arg2);
    }

    void operator()(std::shared_ptr<mtac::Param> param){
        update_usage(param->arg);
    }

    void operator()(std::shared_ptr<mtac::IfFalse> if_false){
        update_usage(if_false->arg1);
        update_usage_optional(if_false->arg2);
    }

    void operator()(std::shared_ptr<mtac::If> if_){
        update_usage(if_->arg1);
        update_usage_optional(if_->arg2);
    }

    void operator()(std::shared_ptr<mtac::Call> call_){
        if(call_->return_ && clones.find(call_->return_) != clones.end()){
            call_->return_ = clones[call_->return_];
        }

        if(call_->return2_ && clones.find(call_->return2_) != clones.end()){
            call_->return2_ = clones[call_->return2_];
        }
    }

    template<typename T>
    void operator()(T&){
        //NOP
    }
};

struct BBReplace : public boost::static_visitor<> {
    BBClones& clones;

    BBReplace(BBClones& clones) : clones(clones) {}

    void operator()(std::shared_ptr<mtac::IfFalse> if_){
        if(clones.find(if_->block) != clones.end()){
            if_->block = clones[if_->block];
        }
    }

    void operator()(std::shared_ptr<mtac::If> if_){
        if(clones.find(if_->block) != clones.end()){
            if_->block = clones[if_->block];
        }
    }

    void operator()(std::shared_ptr<mtac::Goto> goto_){
        if(clones.find(goto_->block) != clones.end()){
            goto_->block = clones[goto_->block];
        }
    }

    template<typename T>
    void operator()(T&){
        //NOP
    }
};

struct StatementClone : public boost::static_visitor<mtac::Statement> {
    mtac::Statement operator()(std::shared_ptr<mtac::Quadruple> quadruple){
        auto copy = std::make_shared<mtac::Quadruple>();

        copy->result = quadruple->result;
        copy->arg1 = quadruple->arg1;
        copy->arg2 = quadruple->arg2;
        copy->op = quadruple->op;

        return copy;
    }

    mtac::Statement operator()(std::shared_ptr<mtac::Param> param){
        auto copy = std::make_shared<mtac::Param>();

        copy->arg = param->arg;
        copy->param = param->param;
        copy->std_param = param->std_param;
        copy->function = param->function;
        copy->address = param->address;
        copy->memberNames = param->memberNames;

        return copy;
    }

    mtac::Statement operator()(std::shared_ptr<mtac::IfFalse> if_){
        auto copy = std::make_shared<mtac::IfFalse>();

        copy->op = if_->op;
        copy->arg1 = if_->arg1;
        copy->arg2 = if_->arg2;
        copy->label = if_->label;
        copy->block = if_->block;

        return copy;
    }

    mtac::Statement operator()(std::shared_ptr<mtac::If> if_){
        auto copy = std::make_shared<mtac::If>();

        copy->op = if_->op;
        copy->arg1 = if_->arg1;
        copy->arg2 = if_->arg2;
        copy->label = if_->label;
        copy->block = if_->block;

        return copy;
    }

    mtac::Statement operator()(std::shared_ptr<mtac::Call> call){
        return std::make_shared<mtac::Call>(call->function, call->functionDefinition, call->return_, call->return2_);
    }

    mtac::Statement operator()(std::shared_ptr<mtac::Goto> goto_){
        auto copy = std::make_shared<mtac::Goto>(goto_->label, goto_->type);
        copy->block = goto_->block;
        return copy;
    }

    mtac::Statement operator()(std::shared_ptr<mtac::NoOp>){
        return std::make_shared<mtac::NoOp>();
    }

    mtac::Statement operator()(std::string& str){
        return str;
    }
};

void clone(std::vector<mtac::Statement>& sources, std::vector<mtac::Statement>& destination){
    StatementClone cloner;

    for(auto& statement : sources){
        destination.push_back(visit(cloner, statement));
    }
}

template<typename Iterator>
BBClones clone(std::shared_ptr<mtac::Function> source_function, std::shared_ptr<mtac::Function> dest_function, Iterator bit){
    BBClones bb_clones;

    for(auto block : source_function->getBasicBlocks()){
        //Copy all basic blocks except ENTRY and EXIT
        if(block->index >= 0){
            auto new_bb = std::make_shared<mtac::BasicBlock>(dest_function->getBasicBlocks().size() + 1);
            new_bb->context = block->context;

            clone(block->statements, new_bb->statements);

            bb_clones[block] = new_bb;

            bit.insert(new_bb);

            ++bit;
        }
    }

    return bb_clones;
}

template<typename Iterator>
VariableClones copy_parameters(std::shared_ptr<mtac::Function> source_function, std::shared_ptr<mtac::Function> dest_function, Iterator bit){
    VariableClones variable_clones;

    auto source_definition = source_function->definition;
    auto dest_definition = dest_function->definition;

    if(source_definition->parameters.size() > 0){
        //Param are in the previous block
        --bit;

        auto pit = (*bit)->statements.end() - 1;

        for(int i = source_definition->parameters.size() - 1; i >= 0;){
            auto statement = *pit;

            if(auto* ptr = boost::get<std::shared_ptr<mtac::Param>>(&statement)){
                auto quadruple = std::make_shared<mtac::Quadruple>();
                std::shared_ptr<Variable> dest_var;

                auto src_var = (*ptr)->param;

                if((*ptr)->memberNames.empty()){
                    auto type = src_var->type();

                    dest_var = dest_definition->context->new_temporary(type);

                    if(type == INT || type == BOOL){
                        quadruple->op = mtac::Operator::ASSIGN;
                    } else if(type->is_pointer()){
                        quadruple->op = mtac::Operator::PASSIGN;
                    } else {
                        quadruple->op = mtac::Operator::FASSIGN;
                    }

                    quadruple->arg1 = (*ptr)->arg;
                } else {
                    auto object_var = boost::get<std::shared_ptr<Variable>>((*ptr)->arg);
                    dest_var = dest_definition->context->new_temporary(INT);

                    quadruple->op = mtac::Operator::PDOT;
                    quadruple->arg1 = object_var;
                    quadruple->arg2 = mtac::compute_member_offset(object_var, (*ptr)->memberNames);
                }

                variable_clones[src_var] = dest_var;
                quadruple->result = dest_var;

                *pit = quadruple;

                --i;
            }

            --pit;
        }
    }

    return variable_clones;
}

template<typename Iterator>
void adapt_instructions(VariableClones& variable_clones, BBClones& bb_clones, std::shared_ptr<mtac::Call> call, Iterator bit){
    VariableReplace variable_replacer(variable_clones);
    BBReplace bb_replacer(bb_clones);

    auto basic_block = *bit;
    --bit;

    auto cloned_bb = bb_clones.size();

    while(cloned_bb > 0){
        auto new_bb = *bit;

        auto ssit = iterate(new_bb->statements);

        while(ssit.has_next()){
            auto statement = *ssit;

            visit(variable_replacer, statement);
            visit(bb_replacer, statement);

            if(auto* ret_ptr = boost::get<std::shared_ptr<mtac::Quadruple>>(&statement)){
                auto quadruple = *ret_ptr;

                if(quadruple->op == mtac::Operator::RETURN){
                    auto goto_ = std::make_shared<mtac::Goto>();
                    goto_->block = basic_block;

                    if(!call->return_){
                        //If the caller does not care about the return value, return has no effect
                        ssit.erase();
                        ssit.insert(goto_);

                        continue;
                    } else {
                        mtac::Operator op;
                        if(call->functionDefinition->returnType == FLOAT){
                            op = mtac::Operator::FASSIGN;
                        } else if(call->functionDefinition->returnType->is_pointer()){
                            op = mtac::Operator::PASSIGN;
                        } else {
                            op = mtac::Operator::ASSIGN;
                        }

                        if(call->return2_){
                            ssit.insert(std::make_shared<mtac::Quadruple>(call->return2_, *quadruple->arg2, op));

                            ++ssit;
                        }

                        quadruple->op = op;
                        quadruple->result = call->return_;
                        quadruple->arg2.reset();

                        ++ssit;

                        ssit.insert(goto_);

                        continue;
                    }
                }
            }

            ++ssit;
        }

        --cloned_bb;
        --bit;
    }
}

bool can_be_inlined(std::shared_ptr<mtac::Function> function){
    //The main function cannot be inlined
    if(function->getName() == "_F4main" || function->getName() == "_F4mainAS"){
        return false;
    }

    for(auto& param : function->definition->parameters){
        if(param.paramType != INT && param.paramType != FLOAT && param.paramType != BOOL && !(param.paramType->is_pointer()/* && param.name == "this")*/)){
            return false;
        }
    }

    if(mtac::is_recursive(function)){
        return false;
    }

    return true;
}

bool will_inline(std::shared_ptr<mtac::Function> function){
    if(can_be_inlined(function)){
        //function called once
        if(symbols.referenceCount(function->getName()) == 1){
            return true;
        } else {
            auto size = function->size();

            //Inline little functions
            if(size < 10){
                return true;
            }
        }
    }

    return false;
}

} //end of anonymous namespace

bool mtac::inline_functions(std::shared_ptr<mtac::Program> program){
    if(option_defined("fno-inline-functions")){
        return false;
    }

    if(option_defined("finline-functions")){
        bool optimized = false;

        for(auto source_function : program->functions){
            if(!will_inline(source_function)){
                continue;
            }

            auto source_definition = source_function->definition;

            for(auto dest_function : program->functions){
                if(dest_function == source_function){
                    continue;
                }

                //If the function has already been inlined
                if(symbols.referenceCount(dest_function->getName()) <= 0){
                    continue;
                }

                for(auto bit = iterate(dest_function->getBasicBlocks()); bit.has_next(); ++bit){
                    auto basic_block = *bit;

                    auto it = iterate(basic_block->statements);

                    while(it.has_next()){
                        if(auto* ptr = boost::get<std::shared_ptr<mtac::Call>>(&*it)){
                            auto call = *ptr;

                            if(call->functionDefinition == source_definition){
                                //Copy the parameters
                                auto variable_clones = copy_parameters(source_function, dest_function, bit);

                                //Allocate storage for the local variables of the inlined function
                                for(auto variable_pair : source_definition->context->stored_variables()){
                                    auto variable = variable_pair.second;
                                    variable_clones[variable] = dest_function->definition->context->newVariable(variable);
                                }

                                //Clone all the source basic blocks in the dest function
                                auto bb_clones = clone(source_function, dest_function, bit);

                                //Fix all the instructions (clones and return)
                                adapt_instructions(variable_clones, bb_clones, call, bit);

                                //Erase the original call
                                it.erase();

                                symbols.removeReference(source_function->getName());
                                optimized = true;

                                continue;
                            }
                        }

                        ++it;
                    }
                }
            }
        }

        return optimized;
    } else {
        return false;
    }
}
<|endoftext|>


<commit_before>#include "node-s2nconfig.h"

#include <node.h>
#include <s2n.h>

using namespace v8;

Persistent<Function> S2NConfig::constructor;

S2NConfig::S2NConfig() {
  s2n_config *s2nconfig = s2n_config_new();
  if(!s2nconfig) {
    NanThrowError("Error getting new S2N config");
  }
}

S2NConfig::~S2NConfig() {
  s2n_config_free(s2nconfig);
  free(&version);
  free(&certificate);
  free(&privateKey);
  free(&dhparams);
}

void S2NConfig::Init(Handle<Object> exports) {
  NanScope();

  // Prepare constructor template
  Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New);
  tpl->SetClassName(NanNew("S2NConfig"));
  tpl->InstanceTemplate()->SetInternalFieldCount(1);

  NODE_SET_PROTOTYPE_METHOD(tpl, "setStatusRequestType", SetStatusRequestType);
  NODE_SET_PROTOTYPE_METHOD(tpl, "setCipherPreferences", SetCipherPreferences);
  NODE_SET_PROTOTYPE_METHOD(tpl, "addCertChainAndKey", AddCertChainAndKey);
  NODE_SET_PROTOTYPE_METHOD(tpl, "addDhParams", AddDhParams);

  NanAssignPersistent(constructor, tpl->GetFunction());
  exports->Set(NanNew("S2NConfig"), tpl->GetFunction());

}

NAN_METHOD(S2NConfig::New) {
  NanScope();

  if (args.IsConstructCall()) {
    S2NConfig* obj = new S2NConfig();
    obj->Wrap(args.This());
    NanReturnValue(args.This());
  } else {
    const int argc = 1;
    Local<Value> argv[argc] = { args[0] };
    Local<Function> cons = NanNew<Function>(constructor);
    NanReturnValue(cons->NewInstance(argc, argv));
  }
}

NAN_METHOD(S2NConfig::SetStatusRequestType) {

  NanScope();

  if(args.Length() < 1 || !args[0]->IsNumber()) {
    NanThrowTypeError("Wrong arguments");
    NanReturnUndefined();
  }

  // TODO: check type is valid
  s2n_status_request_type type = static_cast<s2n_status_request_type>(args[0]->IntegerValue());

  S2NConfig* self = ObjectWrap::Unwrap<S2NConfig>(args.Holder());

  int result = s2n_config_set_status_request_type(self->s2nconfig, type);

  if(result < 0) {
    std::string err(s2n_strerror(s2n_errno, "EN"));
    std::string error("setting config status request type: ");
    error.append(err);
    NanThrowError(error.c_str());
    NanReturnUndefined();
  }

  NanReturnValue(NanNew(true));

}

NAN_METHOD(S2NConfig::SetCipherPreferences) {

  NanScope();

  if(args.Length() < 1 || !args[0]->IsString()) {
    NanThrowTypeError("Wrong arguments");
    NanReturnUndefined();
  }

  S2NConfig* self = ObjectWrap::Unwrap<S2NConfig>(args.Holder());

  std::string arg0 = *NanUtf8String(args[0]);

  self->version = arg0.c_str();

  int result = s2n_config_set_cipher_preferences(self->s2nconfig, self->version);

  if(result < 0) {
    std::string err(s2n_strerror(s2n_errno, "EN"));
    std::string error("setting config cipher preferences: ");
    error.append(err);
    NanThrowError(error.c_str());
    NanReturnUndefined();
  }

  NanReturnValue(NanNew(true));

}

NAN_METHOD(S2NConfig::AddCertChainAndKey) {

  NanScope();

  if(args.Length() < 1) {
    NanThrowTypeError("Wrong number of arguments");
    NanReturnUndefined();
  }

  if(!args[0]->IsString()) {
    NanThrowTypeError("Wrong arguments");
    NanReturnUndefined();
  }

  S2NConfig* self = ObjectWrap::Unwrap<S2NConfig>(args.Holder());

  std::string arg0 = *NanUtf8String(args[0]);
  std::string arg1 = *NanUtf8String(args[1]);

  self->certificate = strdup(arg0.c_str());
  self->privateKey  = strdup(arg1.c_str());

  int result = s2n_config_add_cert_chain_and_key(self->s2nconfig, self->certificate, self->privateKey);

  if(result < 0) {
    std::string err(s2n_strerror(s2n_errno, "EN"));
    std::string error("adding certificate chain and key: ");
    error.append(err);
    NanThrowError(error.c_str());
    NanReturnUndefined();
  }

  NanReturnValue(NanNew(true));

}

NAN_METHOD(S2NConfig::AddDhParams) {

  NanScope();

  if(args.Length() < 1) {
    NanThrowTypeError("Wrong number of arguments");
    NanReturnUndefined();
  }

  if(!args[0]->IsString()) {
    NanThrowTypeError("Wrong arguments");
    NanReturnUndefined();
  }

  S2NConfig* self = ObjectWrap::Unwrap<S2NConfig>(args.Holder());

  std::string arg0 = *NanUtf8String(args[0]);

  self->dhparams = strdup(arg0.c_str());

  int result = s2n_config_add_dhparams(self->s2nconfig, self->dhparams);

  if(result < 0) {
    std::string err(s2n_strerror(s2n_errno, "EN"));
    std::string error("adding DH params: ");
    error.append(err);
    NanThrowError(error.c_str());
    NanReturnUndefined();
  }

  NanReturnValue(NanNew(true));

}
<commit_msg>wrong number of args for addCertChainAndKey<commit_after>#include "node-s2nconfig.h"

#include <node.h>
#include <s2n.h>

using namespace v8;

Persistent<Function> S2NConfig::constructor;

S2NConfig::S2NConfig() {
  s2n_config *s2nconfig = s2n_config_new();
  if(!s2nconfig) {
    NanThrowError("Error getting new S2N config");
  }
}

S2NConfig::~S2NConfig() {
  s2n_config_free(s2nconfig);
  free(&version);
  free(&certificate);
  free(&privateKey);
  free(&dhparams);
}

void S2NConfig::Init(Handle<Object> exports) {
  NanScope();

  // Prepare constructor template
  Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New);
  tpl->SetClassName(NanNew("S2NConfig"));
  tpl->InstanceTemplate()->SetInternalFieldCount(1);

  NODE_SET_PROTOTYPE_METHOD(tpl, "setStatusRequestType", SetStatusRequestType);
  NODE_SET_PROTOTYPE_METHOD(tpl, "setCipherPreferences", SetCipherPreferences);
  NODE_SET_PROTOTYPE_METHOD(tpl, "addCertChainAndKey", AddCertChainAndKey);
  NODE_SET_PROTOTYPE_METHOD(tpl, "addDhParams", AddDhParams);

  NanAssignPersistent(constructor, tpl->GetFunction());
  exports->Set(NanNew("S2NConfig"), tpl->GetFunction());

}

NAN_METHOD(S2NConfig::New) {
  NanScope();

  if (args.IsConstructCall()) {
    S2NConfig* obj = new S2NConfig();
    obj->Wrap(args.This());
    NanReturnValue(args.This());
  } else {
    const int argc = 1;
    Local<Value> argv[argc] = { args[0] };
    Local<Function> cons = NanNew<Function>(constructor);
    NanReturnValue(cons->NewInstance(argc, argv));
  }
}

NAN_METHOD(S2NConfig::SetStatusRequestType) {

  NanScope();

  if(args.Length() < 1 || !args[0]->IsNumber()) {
    NanThrowTypeError("Wrong arguments");
    NanReturnUndefined();
  }

  // TODO: check type is valid
  s2n_status_request_type type = static_cast<s2n_status_request_type>(args[0]->IntegerValue());

  S2NConfig* self = ObjectWrap::Unwrap<S2NConfig>(args.Holder());

  int result = s2n_config_set_status_request_type(self->s2nconfig, type);

  if(result < 0) {
    std::string err(s2n_strerror(s2n_errno, "EN"));
    std::string error("setting config status request type: ");
    error.append(err);
    NanThrowError(error.c_str());
    NanReturnUndefined();
  }

  NanReturnValue(NanNew(true));

}

NAN_METHOD(S2NConfig::SetCipherPreferences) {

  NanScope();

  if(args.Length() < 1 || !args[0]->IsString()) {
    NanThrowTypeError("Wrong arguments");
    NanReturnUndefined();
  }

  S2NConfig* self = ObjectWrap::Unwrap<S2NConfig>(args.Holder());

  std::string arg0 = *NanUtf8String(args[0]);

  self->version = arg0.c_str();

  int result = s2n_config_set_cipher_preferences(self->s2nconfig, self->version);

  if(result < 0) {
    std::string err(s2n_strerror(s2n_errno, "EN"));
    std::string error("setting config cipher preferences: ");
    error.append(err);
    NanThrowError(error.c_str());
    NanReturnUndefined();
  }

  NanReturnValue(NanNew(true));

}

NAN_METHOD(S2NConfig::AddCertChainAndKey) {

  NanScope();

  if(args.Length() < 2) {
    NanThrowTypeError("Wrong number of arguments");
    NanReturnUndefined();
  }

  if(!args[0]->IsString() || !args[1]->IsString()) {
    NanThrowTypeError("Wrong arguments");
    NanReturnUndefined();
  }

  S2NConfig* self = ObjectWrap::Unwrap<S2NConfig>(args.Holder());

  std::string arg0 = *NanUtf8String(args[0]);
  std::string arg1 = *NanUtf8String(args[1]);

  self->certificate = strdup(arg0.c_str());
  self->privateKey  = strdup(arg1.c_str());

  int result = s2n_config_add_cert_chain_and_key(self->s2nconfig, self->certificate, self->privateKey);

  if(result < 0) {
    std::string err(s2n_strerror(s2n_errno, "EN"));
    std::string error("adding certificate chain and key: ");
    error.append(err);
    NanThrowError(error.c_str());
    NanReturnUndefined();
  }

  NanReturnValue(NanNew(true));

}

NAN_METHOD(S2NConfig::AddDhParams) {

  NanScope();

  if(args.Length() < 1) {
    NanThrowTypeError("Wrong number of arguments");
    NanReturnUndefined();
  }

  if(!args[0]->IsString()) {
    NanThrowTypeError("Wrong arguments");
    NanReturnUndefined();
  }

  S2NConfig* self = ObjectWrap::Unwrap<S2NConfig>(args.Holder());

  std::string arg0 = *NanUtf8String(args[0]);

  self->dhparams = strdup(arg0.c_str());

  int result = s2n_config_add_dhparams(self->s2nconfig, self->dhparams);

  if(result < 0) {
    std::string err(s2n_strerror(s2n_errno, "EN"));
    std::string error("adding DH params: ");
    error.append(err);
    NanThrowError(error.c_str());
    NanReturnUndefined();
  }

  NanReturnValue(NanNew(true));

}
<|endoftext|>


<commit_before>#include <sge/nodes/tilemap.hpp>
#include <sge/engine.hpp>
#include <iostream>

using namespace nlohmann;
using namespace std;

namespace sge
{
    vector<string> TileMapNode::mro() const
    {
        auto _mro = PositionNode::mro();
        _mro.push_back("TileMapNode");
        return _mro;
    }

    void TileMapNode::set_tilemap(const string &assetname)
    {
        JSONDescriptor tmap_d(assetname);

        if (tilemap == nullptr)
        {
            tilemap = engine.assets().load<JSON>(tmap_d);
        }
        else if (tilemap->descriptor()->name() != tmap_d.name())
        {
            tilemap.reset();
            tilemap = engine.assets().load<JSON>(tmap_d);
        }

        if (tilemap != nullptr)
        {
            string tileset_assetname = tilemap->asset()["/tileset/resource"_json_pointer];
            ImageDescriptor tset_d(tileset_assetname);

            if (tileset == nullptr)
            {
                tileset = engine.assets().load<Image, ImageDescriptor>(tset_d);
            }
            else if (tileset->descriptor() ->name() != tset_d.name())
            {
                tileset.reset();
                tileset = engine.assets().load<Image, ImageDescriptor>(tset_d);
            }
        }
    }

    SDL_Rect TileMapNode::get_viewport() const
    {
        return viewport;
    }

    void TileMapNode::set_viewport(SDL_Rect vport)
    {
        set_viewport(vport.x, vport.y, vport.w, vport.h);
    }

    void TileMapNode::set_viewport(int x, int y, int w, int h)
    {
        viewport.x = max(0, x);
        viewport.y = max(0, y);
        viewport.w = max(0, w);
        viewport.h = max(0, h);
    }

    void TileMapNode::ready()
    {
        set_draw(true);
    }

    void TileMapNode::draw()
    {
        if (tilemap == nullptr || tileset == nullptr)
        {
            return;
        }

        SDL_Surface *tset = tileset->asset();
        json tmap = tilemap->asset();

        SDL_Texture *tex = SDL_CreateTextureFromSurface(engine.renderer(), tset);

        if (tex != NULL)
        {
            SDL_Point pos = get_absolute_pos();

            int mw = tmap["/map/width"_json_pointer];
            int mh = tmap["/map/height"_json_pointer];
            int tw = tmap["/tileset/width"_json_pointer];
            int th = tmap["/tileset/height"_json_pointer];
            int ts = tmap["/tileset/spacing"_json_pointer];

            SDL_Rect tileviewport;
            tileviewport.x = max(0, viewport.x / tw);
            tileviewport.y = max(0, viewport.y / th);
            tileviewport.w = min(mw, viewport.w / tw + (viewport.w % tw ? 1 : 0));
            tileviewport.h = min(mh, viewport.h / th + (viewport.h % th ? 1 : 0));

            for(int x = tileviewport.x; x < tileviewport.w; x++)
            {
                for(int y = tileviewport.y; y < tileviewport.h; y++)
                {
                    ostringstream spath;
                    spath << "/map/tiles/" << y << "/" << x;
                    string path = spath.str();

                    string tile = tmap[json::json_pointer(path)];

                    if (!tile.empty())
                    {
                        path = "/tiles/" + tile + "/0";
                        int tilex = tmap[json::json_pointer(path)];

                        path = "/tiles/" + tile + "/1";
                        int tiley = tmap[json::json_pointer(path)];

                        SDL_Rect src;
                        src.x = (tilex + ts) * tw;
                        src.y = (tiley + ts) * th;
                        src.w = tw;
                        src.h = th;

                        SDL_Rect dest;
                        dest.x = pos.x + x * tw;
                        dest.y = pos.y + y * th;
                        dest.w = tw;
                        dest.h = th;

                        if (x == tileviewport.x)
                        {
                            src.x += (viewport.x - tileviewport.x);
                            dest.x += (viewport.x - tileviewport.x);
                        }
                        else if (x == (tileviewport.w - 1))
                        {
                            src.w -= (tileviewport.w * tw - viewport.w);
                            dest.w -= (tileviewport.w * tw - viewport.w);
                        }

                        if (y == tileviewport.y)
                        {
                            src.y += (viewport.y - tileviewport.y);
                            dest.y += (viewport.y - tileviewport.y);
                        }
                        else if (y == (tileviewport.h - 1))
                        {
                            src.h -= (tileviewport.h * th - viewport.h);
                            dest.h -= (tileviewport.h * th - viewport.h);
                        }

                        if (SDL_RenderCopy(engine.renderer(), tex, &src, &dest) != 0)
                        {
                            cerr << "[TileMapNode][ERROR] SDL: " << SDL_GetError() << endl;
                        }
                    }
                }
            }

            SDL_DestroyTexture(tex);
        }
        else
        {
            cerr << "[TileMapNode][ERROR] SDL: " << SDL_GetError() << endl;
        }
    }
}
<commit_msg>fix TileMapNode::draw()<commit_after>#include <sge/nodes/tilemap.hpp>
#include <sge/engine.hpp>
#include <iostream>

using namespace nlohmann;
using namespace std;

namespace sge
{
    vector<string> TileMapNode::mro() const
    {
        auto _mro = PositionNode::mro();
        _mro.push_back("TileMapNode");
        return _mro;
    }

    void TileMapNode::set_tilemap(const string &assetname)
    {
        JSONDescriptor tmap_d(assetname);

        if (tilemap == nullptr)
        {
            tilemap = engine.assets().load<JSON>(tmap_d);
        }
        else if (tilemap->descriptor()->name() != tmap_d.name())
        {
            tilemap.reset();
            tilemap = engine.assets().load<JSON>(tmap_d);
        }

        if (tilemap != nullptr)
        {
            string tileset_assetname = tilemap->asset()["/tileset/resource"_json_pointer];
            ImageDescriptor tset_d(tileset_assetname);

            if (tileset == nullptr)
            {
                tileset = engine.assets().load<Image, ImageDescriptor>(tset_d);
            }
            else if (tileset->descriptor() ->name() != tset_d.name())
            {
                tileset.reset();
                tileset = engine.assets().load<Image, ImageDescriptor>(tset_d);
            }
        }
    }

    SDL_Rect TileMapNode::get_viewport() const
    {
        return viewport;
    }

    void TileMapNode::set_viewport(SDL_Rect vport)
    {
        set_viewport(vport.x, vport.y, vport.w, vport.h);
    }

    void TileMapNode::set_viewport(int x, int y, int w, int h)
    {
        viewport.x = max(0, x);
        viewport.y = max(0, y);
        viewport.w = max(0, w);
        viewport.h = max(0, h);
    }

    void TileMapNode::ready()
    {
        set_draw(true);
    }

    void TileMapNode::draw()
    {
        if (tilemap == nullptr || tileset == nullptr)
        {
            return;
        }

        SDL_Surface *tset = tileset->asset();
        json &tmap = tilemap->asset();

        SDL_Texture *tex = SDL_CreateTextureFromSurface(engine.renderer(), tset);

        if (tex != NULL)
        {
            SDL_Point pos = get_absolute_pos();

            int mw = tmap["/map/width"_json_pointer];
            int mh = tmap["/map/height"_json_pointer];
            int tw = tmap["/tileset/width"_json_pointer];
            int th = tmap["/tileset/height"_json_pointer];
            int ts = tmap["/tileset/spacing"_json_pointer];

            SDL_Rect tileviewport;
            tileviewport.x = max(0, viewport.x / tw);
            tileviewport.y = max(0, viewport.y / th);
            tileviewport.w = min(mw, viewport.w / tw + (viewport.w % tw ? 1 : 0));
            tileviewport.h = min(mh, viewport.h / th + (viewport.h % th ? 1 : 0));

            for(int x = tileviewport.x; x < tileviewport.w; x++)
            {
                for(int y = tileviewport.y; y < tileviewport.h; y++)
                {
                    ostringstream spath;
                    spath << "/map/tiles/" << y << "/" << x;
                    string path = spath.str();

                    string tile = tmap[json::json_pointer(path)];

                    if (!tile.empty())
                    {
                        path = "/tiles/" + tile + "/0";
                        int tilex = tmap[json::json_pointer(path)];

                        path = "/tiles/" + tile + "/1";
                        int tiley = tmap[json::json_pointer(path)];

                        SDL_Rect src;
                        src.x = tilex * (ts + tw);
                        src.y = tiley * (ts + th);
                        src.w = tw;
                        src.h = th;

                        SDL_Rect dest;
                        dest.x = pos.x + x * tw;
                        dest.y = pos.y + y * th;
                        dest.w = tw;
                        dest.h = th;

                        if (x == tileviewport.x)
                        {
                            src.x += (viewport.x - tileviewport.x);
                            dest.x += (viewport.x - tileviewport.x);
                        }
                        else if (x == (tileviewport.w - 1))
                        {
                            src.w -= (tileviewport.w * tw - viewport.w);
                            dest.w -= (tileviewport.w * tw - viewport.w);
                        }

                        if (y == tileviewport.y)
                        {
                            src.y += (viewport.y - tileviewport.y);
                            dest.y += (viewport.y - tileviewport.y);
                        }
                        else if (y == (tileviewport.h - 1))
                        {
                            src.h -= (tileviewport.h * th - viewport.h);
                            dest.h -= (tileviewport.h * th - viewport.h);
                        }

                        if (SDL_RenderCopy(engine.renderer(), tex, &src, &dest) != 0)
                        {
                            cerr << "[TileMapNode][ERROR] SDL: " << SDL_GetError() << endl;
                        }
                    }
                }
            }

            SDL_DestroyTexture(tex);
        }
        else
        {
            cerr << "[TileMapNode][ERROR] SDL: " << SDL_GetError() << endl;
        }
    }
}
<|endoftext|>


<commit_before>/*
* Copyright (c) 2003-2009 Rony Shapiro <ronys@users.sourceforge.net>.
* All rights reserved. Use of the code is allowed under the
* Artistic License 2.0 terms, as specified in the LICENSE file
* distributed with this code, or available from
* http://www.opensource.org/licenses/artistic-license-2.0.php
*/

/**
 * \file Linux-specific implementation of file.h
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <cassert>
#include <fstream>

#include <dirent.h>
#include <fnmatch.h>
#include <malloc.h> // for free

#include "../file.h"
#include "../env.h"
#include "corelib.h"
#include "StringXStream.h"

using namespace std;

const TCHAR *pws_os::PathSeparator = _T("/");

bool pws_os::FileExists(const stringT &filename)
{
  struct stat statbuf;
  int status;
#ifndef UNICODE
  status = ::stat(filename.c_str(), &statbuf);
#else
  size_t N = wcstombs(NULL, filename.c_str(), 0) + 1;
  char *fn = new char[N];
  wcstombs(fn, filename.c_str(), N);
  status = ::stat(fn, &statbuf);
  delete[] fn;
#endif /* UNICODE */
  return (status == 0);
}

bool pws_os::FileExists(const stringT &filename, bool &bReadOnly)
{
  int status;
  bool retval = false;
  bReadOnly = false;
#ifndef UNICODE
  status = ::access(filename.c_str(), R_OK);
  if (status == 0) {
    bReadOnly = (::access(filename.c_str(), W_OK) != 0);
    retval = true;
  }
#else
  size_t N = wcstombs(NULL, filename.c_str(), 0) + 1;
  char *fn = new char[N];
  wcstombs(fn, filename.c_str(), N);
  status = ::access(fn, R_OK);
  if (status == 0) {
    bReadOnly = (::access(fn, W_OK) != 0);
    retval = true;
  }
  delete[] fn;
#endif /* UNICODE */
  return retval;
}

bool pws_os::RenameFile(const stringT &oldname, const stringT &newname)
{
  int status;
#ifndef UNICODE
  status = ::rename(oldname.c_str(), newname.c_str());
#else
  size_t oldN = wcstombs(NULL, oldname.c_str(), 0) + 1;
  char *oldfn = new char[oldN];
  wcstombs(oldfn, oldname.c_str(), oldN);
  size_t newN = wcstombs(NULL, newname.c_str(), 0) + 1;
  char *newfn = new char[newN];
  wcstombs(newfn, newname.c_str(), newN);
  status = ::rename(oldfn, newfn);
  delete[] oldfn;
  delete[] newfn;
#endif /* UNICODE */
  return (status == 0);
}

bool pws_os::CopyAFile(const stringT &from, const stringT &to)
{
  const char *szfrom = NULL;
  const char *szto = NULL;
  bool retval = false;
#ifndef UNICODE
  szfrom = from.c_str();
  szto = to.c_str();
#else
  size_t fromsize = wcstombs(NULL, from.c_str(), 0) + 1;
  szfrom = new char[fromsize];
  wcstombs(const_cast<char *>(szfrom), from.c_str(), fromsize);
  size_t tosize = wcstombs(NULL, to.c_str(), 0) + 1;
  assert(tosize > 0);
  szto = new char[tosize];
  wcstombs(const_cast<char *>(szto), to.c_str(), tosize);
#endif /* UNICODE */
  // can we read the source?
  bool readable = ::access(szfrom, R_OK) == 0;
  if (!readable) {
    retval = false;
  } else { // creates dirs as needed

    string cto(szto);
    string::size_type start = (cto[0] == '/') ? 1 : 0;
    string::size_type stop;
    do {
      stop = cto.find_first_of("/", start);
      if (stop != stringT::npos)
        ::mkdir(cto.substr(start, stop).c_str(), 0700); // fail if already there - who cares?
      start = stop + 1;
    } while (stop != stringT::npos);

    ifstream src(szfrom, ios_base::in|ios_base::binary);
    ofstream dst(szto, ios_base::out|ios_base::binary);
    const size_t BUFSIZE = 2048;
    char buf[BUFSIZE];
    size_t readBytes;

    do {
      src.read(buf, BUFSIZE);
      readBytes = src.gcount();
      dst.write(buf, readBytes);
    } while(readBytes != 0);
    retval = true;
  }
#ifdef UNICODE
  delete[] szfrom;
  delete[] szto;
#endif
  return retval;
}

bool pws_os::DeleteAFile(const stringT &filename)
{
#ifndef UNICODE
  const char *szfn =  filename.c_str();
#else
  size_t fnsize = wcstombs(NULL, filename.c_str(), 0) + 1;
  assert(fnsize > 0);
  const char *szfn = new char[fnsize];
  wcstombs(const_cast<char *>(szfn), filename.c_str(), fnsize);
#endif /* UNICODE */

  bool retval = (::unlink(szfn) == 0);
#ifdef UNICODE
  delete[] szfn;
#endif
  return retval;
}

static string filterString;

static int filterFunc(const struct dirent *de)
{
  return fnmatch(filterString.c_str(), de->d_name, 0) == 0;
}

void pws_os::FindFiles(const stringT &filter, vector<stringT> &res)
{
  if (filter.empty())
    return;
  // filter is a full path with a filter file name.
  const char *szfilter;
#ifdef UNICODE
  size_t fltsize = wcstombs(NULL, filter.c_str(), 0) + 1;
  assert(fltsize > 0);
  szfilter = new char[fltsize];
  wcstombs(const_cast<char *>(szfilter), filter.c_str(), fltsize);
#else
  szfilter = filter.c_str();
#endif /* UNICODE */
  string cfilter(szfilter);
#ifdef UNICODE
  delete[] szfilter;
#endif
  // start by splitting it up
  string dir;
  string::size_type last_slash = cfilter.find_last_of("/");
  if (last_slash != string::npos) {
    dir = cfilter.substr(0, last_slash);
    filterString = cfilter.substr(last_slash + 1);
  } else {
    dir = ".";
    filterString = cfilter;
  }
  res.clear();
  struct dirent **namelist;
  int nMatches = scandir(dir.c_str(), &namelist,
                         filterFunc, alphasort);
  if (nMatches <= 0)
    return;
  while (nMatches-- != 0) {
#ifndef UNICODE
    res.push_back(namelist[nMatches]->d_name);
#else
    size_t wname_len = ::mbstowcs(NULL,
                                  namelist[nMatches]->d_name,
                                  0) + 1;
    wchar_t *wname = new wchar_t[wname_len];
    mbstowcs(wname, namelist[nMatches]->d_name, wname_len);
    res.push_back(wname);
    delete[] wname;
#endif
    free(namelist[nMatches]);
  }
  free(namelist);
}

static stringT GetLockFileName(const stringT &filename)
{
  assert(!filename.empty());
  // derive lock filename from filename
  stringT retval(filename, 0, filename.find_last_of(TCHAR('.')));
  retval += _T(".plk");
  return retval;
}

bool pws_os::LockFile(const stringT &filename, stringT &locker,
                      HANDLE &lockFileHandle, int &LockCount)
{
  const stringT lock_filename = GetLockFileName(filename);
  stringT s_locker;
#ifndef UNICODE
  const char *lfn = lock_filename.c_str();
#else
  size_t lfs = wcstombs(NULL, lock_filename.c_str(), lock_filename.length()) + 1;
  char *lfn = new char[lfs];
  wcstombs(lfn, lock_filename.c_str(), lock_filename.length());
#endif
  int fh = open(lfn, (O_CREAT | O_EXCL | O_WRONLY),
                 (S_IREAD | S_IWRITE));
#ifdef UNICODE
  delete[] lfn;
#endif

  if (fh == -1) { // failed to open exclusively. Already locked, or ???
    switch (errno) {
    case EACCES:
      // Tried to open read-only file for writing, or file's
      // sharing mode does not allow specified operations, or given path is directory
        LoadAString(locker, IDSC_NOLOCKACCESS);
      break;
    case EEXIST: // filename already exists
      {
        // read locker data ("user@machine:nnnnnnnn") from file
          istringstreamT is(lock_filename);
          stringT lockerStr;
          if (is >> lockerStr) {
            locker = lockerStr;
          }
      } // EEXIST block
        break;
    case EINVAL: // Invalid oflag or pmode argument
      LoadAString(locker, IDSC_INTERNALLOCKERROR);
      break;
    case EMFILE: // No more file handles available (too many open files)
      LoadAString(locker, IDSC_SYSTEMLOCKERROR);
      break;
    case ENOENT: //File or path not found
      LoadAString(locker, IDSC_LOCKFILEPATHNF);
      break;
    default:
      LoadAString(locker, IDSC_LOCKUNKNOWNERROR);
      break;
    } // switch (errno)
    return false;
  } else { // valid filehandle, write our info
    int numWrit;
    const stringT user = pws_os::getusername();
    const stringT host = pws_os::gethostname();
    const stringT pid = pws_os::getprocessid();

    numWrit = write(fh, user.c_str(), user.length() * sizeof(TCHAR));
    numWrit += write(fh, _T("@"), sizeof(TCHAR));
    numWrit += write(fh, host.c_str(), host.length() * sizeof(TCHAR));
    numWrit += write(fh, _T(":"), sizeof(TCHAR));
    numWrit += write(fh, pid.c_str(), pid.length() * sizeof(TCHAR));
    ASSERT(numWrit > 0);
    close(fh);
    return true;
  }
}

void pws_os::UnlockFile(const stringT &filename,
                        HANDLE &lockFileHandle, int &LockCount)
{
  stringT lock_filename = GetLockFileName(filename);
#ifndef UNICODE
  const char *lfn = lock_filename.c_str();
#else
  size_t lfs = wcstombs(NULL, lock_filename.c_str(), lock_filename.length()) + 1;
  char *lfn = new char[lfs];
  wcstombs(lfn, lock_filename.c_str(), lock_filename.length());
#endif
  unlink(lfn);
#ifdef UNICODE
  delete[] lfn;
#endif
}

bool pws_os::IsLockedFile(const stringT &filename)
{
  const stringT lock_filename = GetLockFileName(filename);
  return pws_os::FileExists(lock_filename);
}

std::FILE *pws_os::FOpen(const stringT &filename, const TCHAR *mode)
{
  const char *cfname = NULL;
  const char *cmode = NULL;
#ifdef UNICODE
  size_t fnsize = wcstombs(NULL, filename.c_str(), 0) + 1;
  assert(fnsize > 0);
  cfname = new char[fnsize];
  wcstombs(const_cast<char *>(cfname), filename.c_str(), fnsize);

  size_t modesize = wcstombs(NULL, mode, 0) + 1;
  assert(modesize > 0);
  cmode = new char[modesize];
  wcstombs(const_cast<char *>(cmode), mode, modesize);
#else
  cfname = filename.c_str();
  cmode = mode;
#endif /* UNICODE */
  FILE *retval = ::fopen(cfname, cmode);
#ifdef UNICODE
  delete[] cfname;
  delete[] cmode;
#endif
  return retval;
}

long pws_os::fileLength(std::FILE *fp)
{
  int fd = fileno(fp);
  if (fd == -1)
    return -1;
  struct stat st;
  if (fstat(fd, &st) == -1)
    return -1;
  return st.st_size;
}

<commit_msg>wxWidgets - another silly off-by-one error - lock files now named correctly<commit_after>/*
* Copyright (c) 2003-2009 Rony Shapiro <ronys@users.sourceforge.net>.
* All rights reserved. Use of the code is allowed under the
* Artistic License 2.0 terms, as specified in the LICENSE file
* distributed with this code, or available from
* http://www.opensource.org/licenses/artistic-license-2.0.php
*/

/**
 * \file Linux-specific implementation of file.h
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <cassert>
#include <fstream>

#include <dirent.h>
#include <fnmatch.h>
#include <malloc.h> // for free

#include "../file.h"
#include "../env.h"
#include "corelib.h"
#include "StringXStream.h"

using namespace std;

const TCHAR *pws_os::PathSeparator = _T("/");

bool pws_os::FileExists(const stringT &filename)
{
  struct stat statbuf;
  int status;
#ifndef UNICODE
  status = ::stat(filename.c_str(), &statbuf);
#else
  size_t N = wcstombs(NULL, filename.c_str(), 0) + 1;
  char *fn = new char[N];
  wcstombs(fn, filename.c_str(), N);
  status = ::stat(fn, &statbuf);
  delete[] fn;
#endif /* UNICODE */
  return (status == 0);
}

bool pws_os::FileExists(const stringT &filename, bool &bReadOnly)
{
  int status;
  bool retval = false;
  bReadOnly = false;
#ifndef UNICODE
  status = ::access(filename.c_str(), R_OK);
  if (status == 0) {
    bReadOnly = (::access(filename.c_str(), W_OK) != 0);
    retval = true;
  }
#else
  size_t N = wcstombs(NULL, filename.c_str(), 0) + 1;
  char *fn = new char[N];
  wcstombs(fn, filename.c_str(), N);
  status = ::access(fn, R_OK);
  if (status == 0) {
    bReadOnly = (::access(fn, W_OK) != 0);
    retval = true;
  }
  delete[] fn;
#endif /* UNICODE */
  return retval;
}

bool pws_os::RenameFile(const stringT &oldname, const stringT &newname)
{
  int status;
#ifndef UNICODE
  status = ::rename(oldname.c_str(), newname.c_str());
#else
  size_t oldN = wcstombs(NULL, oldname.c_str(), 0) + 1;
  char *oldfn = new char[oldN];
  wcstombs(oldfn, oldname.c_str(), oldN);
  size_t newN = wcstombs(NULL, newname.c_str(), 0) + 1;
  char *newfn = new char[newN];
  wcstombs(newfn, newname.c_str(), newN);
  status = ::rename(oldfn, newfn);
  delete[] oldfn;
  delete[] newfn;
#endif /* UNICODE */
  return (status == 0);
}

bool pws_os::CopyAFile(const stringT &from, const stringT &to)
{
  const char *szfrom = NULL;
  const char *szto = NULL;
  bool retval = false;
#ifndef UNICODE
  szfrom = from.c_str();
  szto = to.c_str();
#else
  size_t fromsize = wcstombs(NULL, from.c_str(), 0) + 1;
  szfrom = new char[fromsize];
  wcstombs(const_cast<char *>(szfrom), from.c_str(), fromsize);
  size_t tosize = wcstombs(NULL, to.c_str(), 0) + 1;
  assert(tosize > 0);
  szto = new char[tosize];
  wcstombs(const_cast<char *>(szto), to.c_str(), tosize);
#endif /* UNICODE */
  // can we read the source?
  bool readable = ::access(szfrom, R_OK) == 0;
  if (!readable) {
    retval = false;
  } else { // creates dirs as needed

    string cto(szto);
    string::size_type start = (cto[0] == '/') ? 1 : 0;
    string::size_type stop;
    do {
      stop = cto.find_first_of("/", start);
      if (stop != stringT::npos)
        ::mkdir(cto.substr(start, stop).c_str(), 0700); // fail if already there - who cares?
      start = stop + 1;
    } while (stop != stringT::npos);

    ifstream src(szfrom, ios_base::in|ios_base::binary);
    ofstream dst(szto, ios_base::out|ios_base::binary);
    const size_t BUFSIZE = 2048;
    char buf[BUFSIZE];
    size_t readBytes;

    do {
      src.read(buf, BUFSIZE);
      readBytes = src.gcount();
      dst.write(buf, readBytes);
    } while(readBytes != 0);
    retval = true;
  }
#ifdef UNICODE
  delete[] szfrom;
  delete[] szto;
#endif
  return retval;
}

bool pws_os::DeleteAFile(const stringT &filename)
{
#ifndef UNICODE
  const char *szfn =  filename.c_str();
#else
  size_t fnsize = wcstombs(NULL, filename.c_str(), 0) + 1;
  assert(fnsize > 0);
  const char *szfn = new char[fnsize];
  wcstombs(const_cast<char *>(szfn), filename.c_str(), fnsize);
#endif /* UNICODE */

  bool retval = (::unlink(szfn) == 0);
#ifdef UNICODE
  delete[] szfn;
#endif
  return retval;
}

static string filterString;

static int filterFunc(const struct dirent *de)
{
  return fnmatch(filterString.c_str(), de->d_name, 0) == 0;
}

void pws_os::FindFiles(const stringT &filter, vector<stringT> &res)
{
  if (filter.empty())
    return;
  // filter is a full path with a filter file name.
  const char *szfilter;
#ifdef UNICODE
  size_t fltsize = wcstombs(NULL, filter.c_str(), 0) + 1;
  assert(fltsize > 0);
  szfilter = new char[fltsize];
  wcstombs(const_cast<char *>(szfilter), filter.c_str(), fltsize);
#else
  szfilter = filter.c_str();
#endif /* UNICODE */
  string cfilter(szfilter);
#ifdef UNICODE
  delete[] szfilter;
#endif
  // start by splitting it up
  string dir;
  string::size_type last_slash = cfilter.find_last_of("/");
  if (last_slash != string::npos) {
    dir = cfilter.substr(0, last_slash);
    filterString = cfilter.substr(last_slash + 1);
  } else {
    dir = ".";
    filterString = cfilter;
  }
  res.clear();
  struct dirent **namelist;
  int nMatches = scandir(dir.c_str(), &namelist,
                         filterFunc, alphasort);
  if (nMatches <= 0)
    return;
  while (nMatches-- != 0) {
#ifndef UNICODE
    res.push_back(namelist[nMatches]->d_name);
#else
    size_t wname_len = ::mbstowcs(NULL,
                                  namelist[nMatches]->d_name,
                                  0) + 1;
    wchar_t *wname = new wchar_t[wname_len];
    mbstowcs(wname, namelist[nMatches]->d_name, wname_len);
    res.push_back(wname);
    delete[] wname;
#endif
    free(namelist[nMatches]);
  }
  free(namelist);
}

static stringT GetLockFileName(const stringT &filename)
{
  assert(!filename.empty());
  // derive lock filename from filename
  stringT retval(filename, 0, filename.find_last_of(TCHAR('.')));
  retval += _T(".plk");
  return retval;
}

bool pws_os::LockFile(const stringT &filename, stringT &locker,
                      HANDLE &lockFileHandle, int &LockCount)
{
  const stringT lock_filename = GetLockFileName(filename);
  stringT s_locker;
#ifndef UNICODE
  const char *lfn = lock_filename.c_str();
#else
  size_t lfs = wcstombs(NULL, lock_filename.c_str(), lock_filename.length()) + 1;
  char *lfn = new char[lfs];
  wcstombs(lfn, lock_filename.c_str(), lfs);
#endif
  int fh = open(lfn, (O_CREAT | O_EXCL | O_WRONLY),
                 (S_IREAD | S_IWRITE));
#ifdef UNICODE
  delete[] lfn;
#endif

  if (fh == -1) { // failed to open exclusively. Already locked, or ???
    switch (errno) {
    case EACCES:
      // Tried to open read-only file for writing, or file's
      // sharing mode does not allow specified operations, or given path is directory
        LoadAString(locker, IDSC_NOLOCKACCESS);
      break;
    case EEXIST: // filename already exists
      {
        // read locker data ("user@machine:nnnnnnnn") from file
          istringstreamT is(lock_filename);
          stringT lockerStr;
          if (is >> lockerStr) {
            locker = lockerStr;
          }
      } // EEXIST block
        break;
    case EINVAL: // Invalid oflag or pmode argument
      LoadAString(locker, IDSC_INTERNALLOCKERROR);
      break;
    case EMFILE: // No more file handles available (too many open files)
      LoadAString(locker, IDSC_SYSTEMLOCKERROR);
      break;
    case ENOENT: //File or path not found
      LoadAString(locker, IDSC_LOCKFILEPATHNF);
      break;
    default:
      LoadAString(locker, IDSC_LOCKUNKNOWNERROR);
      break;
    } // switch (errno)
    return false;
  } else { // valid filehandle, write our info
    int numWrit;
    const stringT user = pws_os::getusername();
    const stringT host = pws_os::gethostname();
    const stringT pid = pws_os::getprocessid();

    numWrit = write(fh, user.c_str(), user.length() * sizeof(TCHAR));
    numWrit += write(fh, _T("@"), sizeof(TCHAR));
    numWrit += write(fh, host.c_str(), host.length() * sizeof(TCHAR));
    numWrit += write(fh, _T(":"), sizeof(TCHAR));
    numWrit += write(fh, pid.c_str(), pid.length() * sizeof(TCHAR));
    ASSERT(numWrit > 0);
    close(fh);
    return true;
  }
}

void pws_os::UnlockFile(const stringT &filename,
                        HANDLE &lockFileHandle, int &LockCount)
{
  stringT lock_filename = GetLockFileName(filename);
#ifndef UNICODE
  const char *lfn = lock_filename.c_str();
#else
  size_t lfs = wcstombs(NULL, lock_filename.c_str(), lock_filename.length()) + 1;
  char *lfn = new char[lfs];
  wcstombs(lfn, lock_filename.c_str(), lock_filename.length());
#endif
  unlink(lfn);
#ifdef UNICODE
  delete[] lfn;
#endif
}

bool pws_os::IsLockedFile(const stringT &filename)
{
  const stringT lock_filename = GetLockFileName(filename);
  return pws_os::FileExists(lock_filename);
}

std::FILE *pws_os::FOpen(const stringT &filename, const TCHAR *mode)
{
  const char *cfname = NULL;
  const char *cmode = NULL;
#ifdef UNICODE
  size_t fnsize = wcstombs(NULL, filename.c_str(), 0) + 1;
  assert(fnsize > 0);
  cfname = new char[fnsize];
  wcstombs(const_cast<char *>(cfname), filename.c_str(), fnsize);

  size_t modesize = wcstombs(NULL, mode, 0) + 1;
  assert(modesize > 0);
  cmode = new char[modesize];
  wcstombs(const_cast<char *>(cmode), mode, modesize);
#else
  cfname = filename.c_str();
  cmode = mode;
#endif /* UNICODE */
  FILE *retval = ::fopen(cfname, cmode);
#ifdef UNICODE
  delete[] cfname;
  delete[] cmode;
#endif
  return retval;
}

long pws_os::fileLength(std::FILE *fp)
{
  int fd = fileno(fp);
  if (fd == -1)
    return -1;
  struct stat st;
  if (fstat(fd, &st) == -1)
    return -1;
  return st.st_size;
}

<|endoftext|>


<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/

#include <osg/GLExtensions>
#include <osg/Texture2D>
#include <osg/State>
#include <osg/GLU>

using namespace osg;

Texture2D::Texture2D():
            _textureWidth(0),
            _textureHeight(0),
            _numMipmapLevels(0)
{
    setUseHardwareMipMapGeneration(true);
}

Texture2D::Texture2D(Image* image):
            _textureWidth(0),
            _textureHeight(0),
            _numMipmapLevels(0)
{
    setUseHardwareMipMapGeneration(true);
    setImage(image);
}

Texture2D::Texture2D(const Texture2D& text,const CopyOp& copyop):
            Texture(text,copyop),
            _image(copyop(text._image.get())),
            _textureWidth(text._textureWidth),
            _textureHeight(text._textureHeight),
            _numMipmapLevels(text._numMipmapLevels),
            _subloadCallback(text._subloadCallback)
{
}

Texture2D::~Texture2D()
{
}

int Texture2D::compare(const StateAttribute& sa) const
{
    // check the types are equal and then create the rhs variable
    // used by the COMPARE_StateAttribute_Paramter macro's below.
    COMPARE_StateAttribute_Types(Texture2D,sa)

    if (_image!=rhs._image) // smart pointer comparison.
    {
        if (_image.valid())
        {
            if (rhs._image.valid())
            {
                int result = _image->compare(*rhs._image);
                if (result!=0) return result;
            }
            else
            {
                return 1; // valid lhs._image is greater than null.
            }
        }
        else if (rhs._image.valid())
        {
            return -1; // valid rhs._image is greater than null.
        }
    }

    int result = compareTexture(rhs);
    if (result!=0) return result;

    // compare each paramter in turn against the rhs.
#if 1
    COMPARE_StateAttribute_Parameter(_textureWidth)
    COMPARE_StateAttribute_Parameter(_textureHeight)
#endif
    COMPARE_StateAttribute_Parameter(_subloadCallback)

    return 0; // passed all the above comparison macro's, must be equal.
}

void Texture2D::setImage(Image* image)
{
    _image = image;
    _modifiedTag.setAllElementsTo(0);
}

void Texture2D::apply(State& state) const
{

    //state.setReportGLErrors(true);

    // get the contextID (user defined ID of 0 upwards) for the
    // current OpenGL context.
    const unsigned int contextID = state.getContextID();

    // get the texture object for the current contextID.
    TextureObject* textureObject = getTextureObject(contextID);

    if (textureObject != 0)
    {
        textureObject->bind();

        if (getTextureParameterDirty(state.getContextID()))
            applyTexParameters(GL_TEXTURE_2D,state);

        if (_subloadCallback.valid())
        {
            _subloadCallback->subload(*this,state);
        }
        else if (_image.valid() && getModifiedTag(contextID) != _image->getModifiedTag())
        {
            applyTexImage2D_subload(state,GL_TEXTURE_2D,_image.get(),
                                    _textureWidth, _textureHeight, _internalFormat, _numMipmapLevels);

            // update the modified tag to show that it is upto date.
            getModifiedTag(contextID) = _image->getModifiedTag();

        }

    }
    else if (_subloadCallback.valid())
    {

        _textureObjectBuffer[contextID] = textureObject = generateTextureObject(contextID,GL_TEXTURE_2D);

        textureObject->bind();

        applyTexParameters(GL_TEXTURE_2D,state);

        _subloadCallback->load(*this,state);

        textureObject->setAllocated(_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,1,0);

        // in theory the following line is redundent, but in practice
        // have found that the first frame drawn doesn't apply the textures
        // unless a second bind is called?!!
        // perhaps it is the first glBind which is not required...
        //glBindTexture( GL_TEXTURE_2D, handle );

    }
    else if (_image.valid() && _image->data())
    {

        // compute the internal texture format, this set the _internalFormat to an appropriate value.
        computeInternalFormat();

        // compute the dimensions of the texture.
        computeRequiredTextureDimensions(state,*_image,_textureWidth, _textureHeight, _numMipmapLevels);

        _textureObjectBuffer[contextID] = textureObject = generateTextureObject(
                contextID,GL_TEXTURE_2D,_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,1,0);

        textureObject->bind();

        applyTexParameters(GL_TEXTURE_2D,state);

        if (textureObject->isAllocated())
        {
            //std::cout<<"Reusing texture object"<<std::endl;
            applyTexImage2D_subload(state,GL_TEXTURE_2D,_image.get(),
                                 _textureWidth, _textureHeight, _internalFormat, _numMipmapLevels);
        }
        else
        {
            //std::cout<<"Creating new texture object"<<std::endl;
            applyTexImage2D_load(state,GL_TEXTURE_2D,_image.get(),
                                 _textureWidth, _textureHeight, _numMipmapLevels);

            textureObject->setAllocated(true);
        }

        // update the modified tag to show that it is upto date.
        getModifiedTag(contextID) = _image->getModifiedTag();

        if (_unrefImageDataAfterApply && areAllTextureObjectsLoaded() && _image->getDataVariance()==STATIC)
        {
            Texture2D* non_const_this = const_cast<Texture2D*>(this);
            non_const_this->_image = 0;
        }

        // in theory the following line is redundent, but in practice
        // have found that the first frame drawn doesn't apply the textures
        // unless a second bind is called?!!
        // perhaps it is the first glBind which is not required...
        //glBindTexture( GL_TEXTURE_2D, handle );

    }
    else
    {
        glBindTexture( GL_TEXTURE_2D, 0 );
    }
}

void Texture2D::computeInternalFormat() const
{
    if (_image.valid()) computeInternalFormatWithImage(*_image);
}

void Texture2D::copyTexImage2D(State& state, int x, int y, int width, int height )
{
    const unsigned int contextID = state.getContextID();

    if (_internalFormat==0) _internalFormat=GL_RGBA;

    // get the globj for the current contextID.
    TextureObject* textureObject = getTextureObject(contextID);

    if (textureObject)
    {
        if (width==(int)_textureWidth && height==(int)_textureHeight)
        {
            // we have a valid texture object which is the right size
            // so lets play clever and use copyTexSubImage2D instead.
            // this allows use to reuse the texture object and avoid
            // expensive memory allocations.
            copyTexSubImage2D(state,0 ,0, x, y, width, height);
            return;
        }
        // the relevent texture object is not of the right size so
        // needs to been deleted
        // remove previously bound textures.
        dirtyTextureObject();
        // note, dirtyTextureObject() dirties all the texture objects for
        // this texture, is this right?  Perhaps we should dirty just the
        // one for this context.  Note sure yet will leave till later.
        // RO July 2001.
    }

    // remove any previously assigned images as these are nolonger valid.
    _image = NULL;

    // switch off mip-mapping.
    _min_filter = LINEAR;
    _mag_filter = LINEAR;

    _textureObjectBuffer[contextID] = textureObject = generateTextureObject(contextID,GL_TEXTURE_2D);

    textureObject->bind();

    applyTexParameters(GL_TEXTURE_2D,state);
    glCopyTexImage2D( GL_TEXTURE_2D, 0, _internalFormat, x, y, width, height, 0 );

    _textureWidth = width;
    _textureHeight = height;
    _numMipmapLevels = 1;

    textureObject->setAllocated(_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,1,0);

    // inform state that this texture is the current one bound.
    state.haveAppliedAttribute(this);
}

void Texture2D::copyTexSubImage2D(State& state, int xoffset, int yoffset, int x, int y, int width, int height )
{
    const unsigned int contextID = state.getContextID();

    if (_internalFormat==0) _internalFormat=GL_RGBA;

    // get the texture object for the current contextID.
    TextureObject* textureObject = getTextureObject(contextID);

    if (textureObject)
    {
        // we have a valid image
        textureObject->bind();

        applyTexParameters(GL_TEXTURE_2D,state);
        glCopyTexSubImage2D( GL_TEXTURE_2D, 0, xoffset,yoffset, x, y, width, height);

        /* Redundant, delete later */
        //glBindTexture( GL_TEXTURE_2D, handle );

        // inform state that this texture is the current one bound.
        state.haveAppliedAttribute(this);

    }
    else
    {
        // no texture object already exsits for this context so need to
        // create it upfront - simply call copyTexImage2D.
        copyTexImage2D(state,x,y,width,height);
    }
}
<commit_msg>Added support for hardware generated mipmaps into Textre2D::copy*() methods.<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/

#include <osg/GLExtensions>
#include <osg/Texture2D>
#include <osg/State>
#include <osg/Notify>
#include <osg/GLU>

using namespace osg;

Texture2D::Texture2D():
            _textureWidth(0),
            _textureHeight(0),
            _numMipmapLevels(0)
{
    setUseHardwareMipMapGeneration(true);
}

Texture2D::Texture2D(Image* image):
            _textureWidth(0),
            _textureHeight(0),
            _numMipmapLevels(0)
{
    setUseHardwareMipMapGeneration(true);
    setImage(image);
}

Texture2D::Texture2D(const Texture2D& text,const CopyOp& copyop):
            Texture(text,copyop),
            _image(copyop(text._image.get())),
            _textureWidth(text._textureWidth),
            _textureHeight(text._textureHeight),
            _numMipmapLevels(text._numMipmapLevels),
            _subloadCallback(text._subloadCallback)
{
}

Texture2D::~Texture2D()
{
}

int Texture2D::compare(const StateAttribute& sa) const
{
    // check the types are equal and then create the rhs variable
    // used by the COMPARE_StateAttribute_Paramter macro's below.
    COMPARE_StateAttribute_Types(Texture2D,sa)

    if (_image!=rhs._image) // smart pointer comparison.
    {
        if (_image.valid())
        {
            if (rhs._image.valid())
            {
                int result = _image->compare(*rhs._image);
                if (result!=0) return result;
            }
            else
            {
                return 1; // valid lhs._image is greater than null.
            }
        }
        else if (rhs._image.valid())
        {
            return -1; // valid rhs._image is greater than null.
        }
    }

    int result = compareTexture(rhs);
    if (result!=0) return result;

    // compare each paramter in turn against the rhs.
#if 1
    COMPARE_StateAttribute_Parameter(_textureWidth)
    COMPARE_StateAttribute_Parameter(_textureHeight)
#endif
    COMPARE_StateAttribute_Parameter(_subloadCallback)

    return 0; // passed all the above comparison macro's, must be equal.
}

void Texture2D::setImage(Image* image)
{
    _image = image;
    _modifiedTag.setAllElementsTo(0);
}

void Texture2D::apply(State& state) const
{

    //state.setReportGLErrors(true);

    // get the contextID (user defined ID of 0 upwards) for the
    // current OpenGL context.
    const unsigned int contextID = state.getContextID();

    // get the texture object for the current contextID.
    TextureObject* textureObject = getTextureObject(contextID);

    if (textureObject != 0)
    {
        textureObject->bind();

        if (getTextureParameterDirty(state.getContextID()))
            applyTexParameters(GL_TEXTURE_2D,state);

        if (_subloadCallback.valid())
        {
            _subloadCallback->subload(*this,state);
        }
        else if (_image.valid() && getModifiedTag(contextID) != _image->getModifiedTag())
        {
            applyTexImage2D_subload(state,GL_TEXTURE_2D,_image.get(),
                                    _textureWidth, _textureHeight, _internalFormat, _numMipmapLevels);

            // update the modified tag to show that it is upto date.
            getModifiedTag(contextID) = _image->getModifiedTag();

        }

    }
    else if (_subloadCallback.valid())
    {

        _textureObjectBuffer[contextID] = textureObject = generateTextureObject(contextID,GL_TEXTURE_2D);

        textureObject->bind();

        applyTexParameters(GL_TEXTURE_2D,state);

        _subloadCallback->load(*this,state);

        textureObject->setAllocated(_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,1,0);

        // in theory the following line is redundent, but in practice
        // have found that the first frame drawn doesn't apply the textures
        // unless a second bind is called?!!
        // perhaps it is the first glBind which is not required...
        //glBindTexture( GL_TEXTURE_2D, handle );

    }
    else if (_image.valid() && _image->data())
    {

        // compute the internal texture format, this set the _internalFormat to an appropriate value.
        computeInternalFormat();

        // compute the dimensions of the texture.
        computeRequiredTextureDimensions(state,*_image,_textureWidth, _textureHeight, _numMipmapLevels);

        _textureObjectBuffer[contextID] = textureObject = generateTextureObject(
                contextID,GL_TEXTURE_2D,_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,1,0);

        textureObject->bind();

        applyTexParameters(GL_TEXTURE_2D,state);

        if (textureObject->isAllocated())
        {
            //std::cout<<"Reusing texture object"<<std::endl;
            applyTexImage2D_subload(state,GL_TEXTURE_2D,_image.get(),
                                 _textureWidth, _textureHeight, _internalFormat, _numMipmapLevels);
        }
        else
        {
            //std::cout<<"Creating new texture object"<<std::endl;
            applyTexImage2D_load(state,GL_TEXTURE_2D,_image.get(),
                                 _textureWidth, _textureHeight, _numMipmapLevels);

            textureObject->setAllocated(true);
        }

        // update the modified tag to show that it is upto date.
        getModifiedTag(contextID) = _image->getModifiedTag();

        if (_unrefImageDataAfterApply && areAllTextureObjectsLoaded() && _image->getDataVariance()==STATIC)
        {
            Texture2D* non_const_this = const_cast<Texture2D*>(this);
            non_const_this->_image = 0;
        }

        // in theory the following line is redundent, but in practice
        // have found that the first frame drawn doesn't apply the textures
        // unless a second bind is called?!!
        // perhaps it is the first glBind which is not required...
        //glBindTexture( GL_TEXTURE_2D, handle );

    }
    else
    {
        glBindTexture( GL_TEXTURE_2D, 0 );
    }
}

void Texture2D::computeInternalFormat() const
{
    if (_image.valid()) computeInternalFormatWithImage(*_image);
}

void Texture2D::copyTexImage2D(State& state, int x, int y, int width, int height )
{
    const unsigned int contextID = state.getContextID();

    if (_internalFormat==0) _internalFormat=GL_RGBA;

    // get the globj for the current contextID.
    TextureObject* textureObject = getTextureObject(contextID);

    if (textureObject)
    {
        if (width==(int)_textureWidth && height==(int)_textureHeight)
        {
            // we have a valid texture object which is the right size
            // so lets play clever and use copyTexSubImage2D instead.
            // this allows use to reuse the texture object and avoid
            // expensive memory allocations.
            copyTexSubImage2D(state,0 ,0, x, y, width, height);
            return;
        }
        // the relevent texture object is not of the right size so
        // needs to been deleted
        // remove previously bound textures.
        dirtyTextureObject();
        // note, dirtyTextureObject() dirties all the texture objects for
        // this texture, is this right?  Perhaps we should dirty just the
        // one for this context.  Note sure yet will leave till later.
        // RO July 2001.
    }

    // remove any previously assigned images as these are nolonger valid.
    _image = NULL;

    // switch off mip-mapping.
    //
    _textureObjectBuffer[contextID] = textureObject = generateTextureObject(contextID,GL_TEXTURE_2D);

    textureObject->bind();

    applyTexParameters(GL_TEXTURE_2D,state);

    bool needHardwareMipMap = (_min_filter != LINEAR && _min_filter != NEAREST);
    bool hardwareMipMapOn = false;
    if (needHardwareMipMap)
    {
        const Extensions* extensions = getExtensions(contextID,true);
        bool generateMipMapSupported = extensions->isGenerateMipMapSupported();

        hardwareMipMapOn = _useHardwareMipMapGeneration && generateMipMapSupported;

        if (!hardwareMipMapOn)
        {
            // have to swtich off mip mapping
            notify(NOTICE)<<"Warning: Texture2D::copyTexImage2D(,,,,) switch of mip mapping as hardware support not available."<<std::endl;
            _min_filter = LINEAR;
        }
    }

    if (hardwareMipMapOn) glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_TRUE);

    glCopyTexImage2D( GL_TEXTURE_2D, 0, _internalFormat, x, y, width, height, 0 );

    if (hardwareMipMapOn) glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_FALSE);

    _textureWidth = width;
    _textureHeight = height;
    _numMipmapLevels = 1;

    textureObject->setAllocated(_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,1,0);

    // inform state that this texture is the current one bound.
    state.haveAppliedAttribute(this);
}

void Texture2D::copyTexSubImage2D(State& state, int xoffset, int yoffset, int x, int y, int width, int height )
{
    const unsigned int contextID = state.getContextID();

    if (_internalFormat==0) _internalFormat=GL_RGBA;

    // get the texture object for the current contextID.
    TextureObject* textureObject = getTextureObject(contextID);

    if (textureObject)
    {
        // we have a valid image
        textureObject->bind();

        applyTexParameters(GL_TEXTURE_2D,state);

        bool needHardwareMipMap = (_min_filter != LINEAR && _min_filter != NEAREST);
        bool hardwareMipMapOn = false;
        if (needHardwareMipMap)
        {
            const Extensions* extensions = getExtensions(contextID,true);
            bool generateMipMapSupported = extensions->isGenerateMipMapSupported();

            hardwareMipMapOn = _useHardwareMipMapGeneration && generateMipMapSupported;

            if (!hardwareMipMapOn)
            {
                // have to swtich off mip mapping
                notify(NOTICE)<<"Warning: Texture2D::copyTexImage2D(,,,,) switch of mip mapping as hardware support not available."<<std::endl;
                _min_filter = LINEAR;
            }
        }

        if (hardwareMipMapOn) glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_TRUE);

        glCopyTexSubImage2D( GL_TEXTURE_2D, 0, xoffset, yoffset, x, y, width, height);

        if (hardwareMipMapOn) glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_FALSE);

        // inform state that this texture is the current one bound.
        state.haveAppliedAttribute(this);

    }
    else
    {
        // no texture object already exsits for this context so need to
        // create it upfront - simply call copyTexImage2D.
        copyTexImage2D(state,x,y,width,height);
    }
}
<|endoftext|>