<commit_before>/*################################################################################
  ##
  ##   Copyright (C) 2016-2022 Keith O'Hara
  ##
  ##   This file is part of the OptimLib C++ library.
  ##
  ##   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.
  ##
  ################################################################################*/

/*
 * Particle Swarm Optimization (PSO)
 */

#include "optim.hpp"

// [OPTIM_BEGIN]
optimlib_inline
bool
optim::internal::pso_impl(
    ColVec_t& init_out_vals,
    std::function<fp_t (const ColVec_t& vals_inp, ColVec_t* grad_out, void* opt_data)> opt_objfn,
    void* opt_data,
    algo_settings_t* settings_inp
)
{
    bool success = false;

    const size_t n_vals = BMO_MATOPS_SIZE(init_out_vals);

    //
    // PSO settings

    algo_settings_t settings;

    if (settings_inp) {
        settings = *settings_inp;
    }

    const int print_level = settings.print_level;

    const uint_t conv_failure_switch = settings.conv_failure_switch;
    const fp_t rel_objfn_change_tol = settings.rel_objfn_change_tol;

    const bool center_particle = settings.pso_settings.center_particle;

    const size_t n_pop = (center_particle) ? settings.pso_settings.n_pop + 1 : settings.pso_settings.n_pop;
    const size_t n_gen = settings.pso_settings.n_gen;
    const size_t check_freq = settings.pso_settings.check_freq;

    const uint_t inertia_method = settings.pso_settings.inertia_method;

    fp_t par_w = settings.pso_settings.par_initial_w;
    const fp_t par_w_max = settings.pso_settings.par_w_max;
    const fp_t par_w_min = settings.pso_settings.par_w_min;
    const fp_t par_damp = settings.pso_settings.par_w_damp;

    const uint_t velocity_method = settings.pso_settings.velocity_method;

    fp_t par_c_cog = settings.pso_settings.par_c_cog;
    fp_t par_c_soc = settings.pso_settings.par_c_soc;

    const fp_t par_initial_c_cog = settings.pso_settings.par_initial_c_cog;
    const fp_t par_final_c_cog = settings.pso_settings.par_final_c_cog;
    const fp_t par_initial_c_soc = settings.pso_settings.par_initial_c_soc;
    const fp_t par_final_c_soc = settings.pso_settings.par_final_c_soc;

    const bool return_position_mat = settings.pso_settings.return_position_mat;

    const bool vals_bound = settings.vals_bound;

    const ColVec_t lower_bounds = settings.lower_bounds;
    const ColVec_t upper_bounds = settings.upper_bounds;

    const ColVecInt_t bounds_type = determine_bounds_type(vals_bound, n_vals, lower_bounds, upper_bounds);

    ColVec_t par_initial_lb = ( BMO_MATOPS_SIZE(settings.pso_settings.initial_lb) == n_vals ) ? settings.pso_settings.initial_lb : BMO_MATOPS_ARRAY_ADD_SCALAR(init_out_vals, -0.5);
    ColVec_t par_initial_ub = ( BMO_MATOPS_SIZE(settings.pso_settings.initial_ub) == n_vals ) ? settings.pso_settings.initial_ub : BMO_MATOPS_ARRAY_ADD_SCALAR(init_out_vals,  0.5);

    sampling_bounds_check(vals_bound, n_vals, bounds_type, lower_bounds, upper_bounds, par_initial_lb, par_initial_ub);

    // parallelization setup

    int omp_n_threads = 1;

#ifdef OPTIM_USE_OPENMP
    if (settings.pso_settings.omp_n_threads > 0) {
        omp_n_threads = settings.pso_settings.omp_n_threads;
    } else {
        omp_n_threads = std::max(1, static_cast<int>(omp_get_max_threads()) / 2); // OpenMP often detects the number of virtual/logical cores, not physical cores
    }
#endif

    // random sampling setup

    rand_engine_t rand_engine(settings.rng_seed_value);
    std::vector<rand_engine_t> rand_engines_vec;

    for (int i = 0; i < omp_n_threads; ++i) {
        size_t seed_val = generate_seed_value(i, omp_n_threads, rand_engine);
        rand_engines_vec.push_back(rand_engine_t(seed_val));
    }

    // lambda function for box constraints

    std::function<fp_t (const ColVec_t& vals_inp, ColVec_t* grad_out, void* box_data)> box_objfn \
    = [opt_objfn, vals_bound, bounds_type, lower_bounds, upper_bounds] (const ColVec_t& vals_inp, ColVec_t* grad_out, void* opt_data) \
    -> fp_t
    {
        if (vals_bound) {
            ColVec_t vals_inv_trans = inv_transform(vals_inp, bounds_type, lower_bounds, upper_bounds);

            return opt_objfn(vals_inv_trans,nullptr,opt_data);
        } else {
            return opt_objfn(vals_inp,nullptr,opt_data);
        }
    };

    //
    // initialize

    ColVec_t rand_vec(n_vals);
    ColVec_t objfn_vals(n_pop);
    Mat_t P(n_pop,n_vals);

#ifdef OPTIM_USE_OPENMP
    #pragma omp parallel for num_threads(omp_n_threads) firstprivate(rand_vec)
#endif
    for (size_t i = 0; i < n_pop; ++i) {
        size_t thread_num = 0;

#ifdef OPTIM_USE_OPENMP
        thread_num = omp_get_thread_num();
#endif

        if (center_particle && i == n_pop - 1) {
            P.row(i) = BMO_MATOPS_COLWISE_SUM( BMO_MATOPS_MIDDLE_ROWS(P, 0, n_pop-2) ) / static_cast<fp_t>(n_pop-1); // center vector
        } else {
            bmo::stats::internal::runif_vec_inplace<fp_t>(n_vals, rand_engines_vec[thread_num], rand_vec);

            P.row(i) = BMO_MATOPS_TRANSPOSE( par_initial_lb + BMO_MATOPS_HADAMARD_PROD( (par_initial_ub - par_initial_lb), rand_vec ) );
        }

        fp_t prop_objfn_val = opt_objfn(BMO_MATOPS_TRANSPOSE(P.row(i)), nullptr, opt_data);

        if (!std::isfinite(prop_objfn_val)) {
            prop_objfn_val = inf;
        }

        objfn_vals(i) = prop_objfn_val;

        if (vals_bound) {
            P.row(i) = transform<RowVec_t>(P.row(i), bounds_type, lower_bounds, upper_bounds);
        }
    }

    ColVec_t best_vals = objfn_vals;
    Mat_t best_vecs = P;

    fp_t min_objfn_val_running = BMO_MATOPS_MIN_VAL(objfn_vals);
    fp_t min_objfn_val_check = min_objfn_val_running;

    RowVec_t best_sol_running = P.row( bmo::index_min(objfn_vals) );

    //
    // begin loop

    size_t iter = 0;
    fp_t rel_objfn_change = 2.0*rel_objfn_change_tol;

    RowVec_t rand_vec_1(n_vals);
    RowVec_t rand_vec_2(n_vals);
    Mat_t V = BMO_MATOPS_ZERO_MAT(n_pop,n_vals);

    while (rel_objfn_change > rel_objfn_change_tol && iter < n_gen) {
        ++iter;

        //
        // parameter updating

        if (inertia_method == 1) {
            par_w = par_w_min + (par_w_max - par_w_min) * (iter + 1) / n_gen;
        } else {
            par_w *= par_damp;
        }

        if (velocity_method == 2) {
            par_c_cog = par_initial_c_cog - (par_initial_c_cog - par_final_c_cog) * (iter + 1) / n_gen;
            par_c_soc = par_initial_c_soc - (par_initial_c_soc - par_final_c_soc) * (iter + 1) / n_gen;
        }

        //
        // population loop

#ifdef OPTIM_USE_OPENMP
        #pragma omp parallel for num_threads(omp_n_threads) firstprivate(rand_vec_1,rand_vec_2)
#endif
        for (size_t i=0; i < n_pop; ++i) {
            size_t thread_num = 0;

#ifdef OPTIM_USE_OPENMP
            thread_num = omp_get_thread_num();
#endif

            if ( !(center_particle && i == n_pop - 1) ) {
                bmo::stats::internal::runif_vec_inplace<fp_t>(n_vals, rand_engines_vec[thread_num], rand_vec_1);
                bmo::stats::internal::runif_vec_inplace<fp_t>(n_vals, rand_engines_vec[thread_num], rand_vec_2);

                // RowVec_t rand_vec_1 = bmo::stats::runif_vec<fp_t, RowVec_t>(n_vals, rand_engines_vec[thread_num]);
                // RowVec_t rand_vec_2 = bmo::stats::runif_vec<fp_t, RowVec_t>(n_vals, rand_engines_vec[thread_num]);

                V.row(i) = par_w * V.row(i) + par_c_cog * BMO_MATOPS_HADAMARD_PROD( rand_vec_1, (best_vecs.row(i) - P.row(i)) ) \
                    + par_c_soc * BMO_MATOPS_HADAMARD_PROD( rand_vec_2, (best_sol_running - P.row(i)) );

                P.row(i) += V.row(i);
            } else {
                P.row(i) = BMO_MATOPS_COLWISE_SUM( BMO_MATOPS_MIDDLE_ROWS(P, 0, n_pop - 2) ) / static_cast<fp_t>(n_pop - 1); // center vector
            }

            //

            fp_t prop_objfn_val = box_objfn( BMO_MATOPS_TRANSPOSE(P.row(i)), nullptr, opt_data);

            if (!std::isfinite(prop_objfn_val)) {
                prop_objfn_val = inf;
            }

            objfn_vals(i) = prop_objfn_val;

            if (objfn_vals(i) < best_vals(i)) {
                best_vals(i) = objfn_vals(i);
                best_vecs.row(i) = P.row(i);
            }
        }

        size_t min_objfn_val_index = bmo::index_min(best_vals);
        fp_t min_objfn_val = best_vals(min_objfn_val_index);

        //

        if (min_objfn_val < min_objfn_val_running) {
            min_objfn_val_running = min_objfn_val;
            best_sol_running = best_vecs.row( min_objfn_val_index );
        }

        if (iter % check_freq == 0) {
            rel_objfn_change = std::abs(min_objfn_val_running - min_objfn_val_check) / (OPTIM_FPN_SMALL_NUMBER + std::abs(min_objfn_val_running));

            if (min_objfn_val_running < min_objfn_val_check) {
                min_objfn_val_check = min_objfn_val_running;
            }
        }

        //

        OPTIM_PSO_TRACE(iter, rel_objfn_change, min_objfn_val_running, min_objfn_val_check, best_sol_running, P);
    }

    //

    if (return_position_mat) {
        if (vals_bound) {
#ifdef OPTIM_USE_OPENMP
            #pragma omp parallel for num_threads(omp_n_threads)
#endif
            for (size_t i = 0; i < n_pop; ++i) {
                P.row(i) = inv_transform<RowVec_t>(P.row(i), bounds_type, lower_bounds, upper_bounds);
            }
        }

        settings_inp->pso_settings.position_mat = P;
    }

    //

    if (vals_bound) {
        best_sol_running = inv_transform( best_sol_running, bounds_type, lower_bounds, upper_bounds);
    }

    error_reporting(init_out_vals, BMO_MATOPS_TRANSPOSE(best_sol_running), opt_objfn, opt_data,
                    success, rel_objfn_change, rel_objfn_change_tol, iter, n_gen,
                    conv_failure_switch, settings_inp);

    //

    return success;
}

optimlib_inline
bool
optim::pso(
    ColVec_t& init_out_vals,
    std::function<fp_t (const ColVec_t& vals_inp, ColVec_t* grad_out, void* opt_data)> opt_objfn,
    void* opt_data
)
{
    return internal::pso_impl(init_out_vals,opt_objfn,opt_data,nullptr);
}

optimlib_inline
bool
optim::pso(
    ColVec_t& init_out_vals,
    std::function<fp_t (const ColVec_t& vals_inp, ColVec_t* grad_out, void* opt_data)> opt_objfn,
    void* opt_data,
    algo_settings_t& settings
)
{
    return internal::pso_impl(init_out_vals,opt_objfn,opt_data,&settings);
}
<commit_msg>fix PSO center particle bug<commit_after>/*################################################################################
  ##
  ##   Copyright (C) 2016-2022 Keith O'Hara
  ##
  ##   This file is part of the OptimLib C++ library.
  ##
  ##   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.
  ##
  ################################################################################*/

/*
 * Particle Swarm Optimization (PSO)
 */

#include "optim.hpp"

// [OPTIM_BEGIN]
optimlib_inline
bool
optim::internal::pso_impl(
    ColVec_t& init_out_vals,
    std::function<fp_t (const ColVec_t& vals_inp, ColVec_t* grad_out, void* opt_data)> opt_objfn,
    void* opt_data,
    algo_settings_t* settings_inp
)
{
    bool success = false;

    const size_t n_vals = BMO_MATOPS_SIZE(init_out_vals);

    //
    // PSO settings

    algo_settings_t settings;

    if (settings_inp) {
        settings = *settings_inp;
    }

    const int print_level = settings.print_level;

    const uint_t conv_failure_switch = settings.conv_failure_switch;
    const fp_t rel_objfn_change_tol = settings.rel_objfn_change_tol;

    const bool center_particle = settings.pso_settings.center_particle;

    const size_t n_center = (center_particle) ? 1 : 0;
    const size_t n_pop = settings.pso_settings.n_pop;
    const size_t n_gen = settings.pso_settings.n_gen;
    const size_t check_freq = settings.pso_settings.check_freq;

    const uint_t inertia_method = settings.pso_settings.inertia_method;

    fp_t par_w = settings.pso_settings.par_initial_w;
    const fp_t par_w_max = settings.pso_settings.par_w_max;
    const fp_t par_w_min = settings.pso_settings.par_w_min;
    const fp_t par_damp = settings.pso_settings.par_w_damp;

    const uint_t velocity_method = settings.pso_settings.velocity_method;

    fp_t par_c_cog = settings.pso_settings.par_c_cog;
    fp_t par_c_soc = settings.pso_settings.par_c_soc;

    const fp_t par_initial_c_cog = settings.pso_settings.par_initial_c_cog;
    const fp_t par_final_c_cog = settings.pso_settings.par_final_c_cog;
    const fp_t par_initial_c_soc = settings.pso_settings.par_initial_c_soc;
    const fp_t par_final_c_soc = settings.pso_settings.par_final_c_soc;

    const bool return_position_mat = settings.pso_settings.return_position_mat;

    const bool vals_bound = settings.vals_bound;

    const ColVec_t lower_bounds = settings.lower_bounds;
    const ColVec_t upper_bounds = settings.upper_bounds;

    const ColVecInt_t bounds_type = determine_bounds_type(vals_bound, n_vals, lower_bounds, upper_bounds);

    ColVec_t par_initial_lb = ( BMO_MATOPS_SIZE(settings.pso_settings.initial_lb) == n_vals ) ? settings.pso_settings.initial_lb : BMO_MATOPS_ARRAY_ADD_SCALAR(init_out_vals, -0.5);
    ColVec_t par_initial_ub = ( BMO_MATOPS_SIZE(settings.pso_settings.initial_ub) == n_vals ) ? settings.pso_settings.initial_ub : BMO_MATOPS_ARRAY_ADD_SCALAR(init_out_vals,  0.5);

    sampling_bounds_check(vals_bound, n_vals, bounds_type, lower_bounds, upper_bounds, par_initial_lb, par_initial_ub);

    // parallelization setup

    int omp_n_threads = 1;

#ifdef OPTIM_USE_OPENMP
    if (settings.pso_settings.omp_n_threads > 0) {
        omp_n_threads = settings.pso_settings.omp_n_threads;
    } else {
        omp_n_threads = std::max(1, static_cast<int>(omp_get_max_threads()) / 2); // OpenMP often detects the number of virtual/logical cores, not physical cores
    }
#endif

    // random sampling setup

    rand_engine_t rand_engine(settings.rng_seed_value);
    std::vector<rand_engine_t> rand_engines_vec;

    for (int i = 0; i < omp_n_threads; ++i) {
        size_t seed_val = generate_seed_value(i, omp_n_threads, rand_engine);
        rand_engines_vec.push_back(rand_engine_t(seed_val));
    }

    // lambda function for box constraints

    std::function<fp_t (const ColVec_t& vals_inp, ColVec_t* grad_out, void* box_data)> box_objfn \
    = [opt_objfn, vals_bound, bounds_type, lower_bounds, upper_bounds] (const ColVec_t& vals_inp, ColVec_t* grad_out, void* opt_data) \
    -> fp_t
    {
        if (vals_bound) {
            ColVec_t vals_inv_trans = inv_transform(vals_inp, bounds_type, lower_bounds, upper_bounds);

            return opt_objfn(vals_inv_trans,nullptr,opt_data);
        } else {
            return opt_objfn(vals_inp,nullptr,opt_data);
        }
    };

    //
    // initialize

    ColVec_t rand_vec(n_vals);
    ColVec_t objfn_vals(n_pop);
    Mat_t P(n_pop + n_center, n_vals);

#ifdef OPTIM_USE_OPENMP
    #pragma omp parallel for num_threads(omp_n_threads) firstprivate(rand_vec)
#endif
    for (size_t i = 0; i < n_pop; ++i) {
        size_t thread_num = 0;

#ifdef OPTIM_USE_OPENMP
        thread_num = omp_get_thread_num();
#endif

        bmo::stats::internal::runif_vec_inplace<fp_t>(n_vals, rand_engines_vec[thread_num], rand_vec);

        P.row(i) = BMO_MATOPS_TRANSPOSE( par_initial_lb + BMO_MATOPS_HADAMARD_PROD( (par_initial_ub - par_initial_lb), rand_vec ) );

        fp_t prop_objfn_val = opt_objfn(BMO_MATOPS_TRANSPOSE(P.row(i)), nullptr, opt_data);

        if (!std::isfinite(prop_objfn_val)) {
            prop_objfn_val = inf;
        }

        objfn_vals(i) = prop_objfn_val;

        if (vals_bound) {
            P.row(i) = transform<RowVec_t>(P.row(i), bounds_type, lower_bounds, upper_bounds);
        }
    }

    if (center_particle) {
        // taken outside the loop due to parallelization
        P.row(n_pop) = BMO_MATOPS_COLWISE_SUM( BMO_MATOPS_MIDDLE_ROWS(P, 0, n_pop - 1) ) / static_cast<fp_t>(n_pop); // center vector
    }

    ColVec_t best_vals = objfn_vals;
    Mat_t best_vecs = P;

    fp_t min_objfn_val_running = BMO_MATOPS_MIN_VAL(objfn_vals);
    fp_t min_objfn_val_check = min_objfn_val_running;

    RowVec_t best_sol_running = P.row( bmo::index_min(objfn_vals) );

    OPTIM_PSO_TRACE(0, 0, min_objfn_val_running, min_objfn_val_check, best_sol_running, P);

    //
    // begin loop

    size_t iter = 0;
    fp_t rel_objfn_change = 2.0*rel_objfn_change_tol;

    RowVec_t rand_vec_1(n_vals);
    RowVec_t rand_vec_2(n_vals);
    Mat_t V = BMO_MATOPS_ZERO_MAT(n_pop, n_vals);

    while (rel_objfn_change > rel_objfn_change_tol && iter < n_gen) {
        ++iter;

        //
        // parameter updating

        if (inertia_method == 1) {
            par_w = par_w_min + (par_w_max - par_w_min) * (iter + 1) / n_gen;
        } else {
            par_w *= par_damp;
        }

        if (velocity_method == 2) {
            par_c_cog = par_initial_c_cog - (par_initial_c_cog - par_final_c_cog) * (iter + 1) / n_gen;
            par_c_soc = par_initial_c_soc - (par_initial_c_soc - par_final_c_soc) * (iter + 1) / n_gen;
        }

        //
        // population loop

#ifdef OPTIM_USE_OPENMP
        #pragma omp parallel for num_threads(omp_n_threads) firstprivate(rand_vec_1,rand_vec_2)
#endif
        for (size_t i=0; i < n_pop; ++i) {
            size_t thread_num = 0;

#ifdef OPTIM_USE_OPENMP
            thread_num = omp_get_thread_num();
#endif

            bmo::stats::internal::runif_vec_inplace<fp_t>(n_vals, rand_engines_vec[thread_num], rand_vec_1);
            bmo::stats::internal::runif_vec_inplace<fp_t>(n_vals, rand_engines_vec[thread_num], rand_vec_2);

            V.row(i) = par_w * V.row(i) + par_c_cog * BMO_MATOPS_HADAMARD_PROD( rand_vec_1, (best_vecs.row(i) - P.row(i)) ) \
                + par_c_soc * BMO_MATOPS_HADAMARD_PROD( rand_vec_2, (best_sol_running - P.row(i)) );

            P.row(i) += V.row(i);

            //

            fp_t prop_objfn_val = box_objfn( BMO_MATOPS_TRANSPOSE(P.row(i)), nullptr, opt_data);

            if (!std::isfinite(prop_objfn_val)) {
                prop_objfn_val = inf;
            }

            objfn_vals(i) = prop_objfn_val;

            if (objfn_vals(i) < best_vals(i)) {
                best_vals(i) = objfn_vals(i);
                best_vecs.row(i) = P.row(i);
            }
        }

        if (center_particle) {
            // taken outside the loop due to parallelization
            P.row(n_pop) = BMO_MATOPS_COLWISE_SUM( BMO_MATOPS_MIDDLE_ROWS(P, 0, n_pop - 1) ) / static_cast<fp_t>(n_pop); // center vector

            fp_t prop_objfn_val = box_objfn( BMO_MATOPS_TRANSPOSE(P.row(n_pop)), nullptr, opt_data);

            if (!std::isfinite(prop_objfn_val)) {
                prop_objfn_val = inf;
            }

            objfn_vals(n_pop) = prop_objfn_val;

            if (objfn_vals(n_pop) < best_vals(n_pop)) {
                best_vals(n_pop) = objfn_vals(n_pop);
                best_vecs.row(n_pop) = P.row(n_pop);
            }
        }

        size_t min_objfn_val_index = bmo::index_min(best_vals);
        fp_t min_objfn_val = best_vals(min_objfn_val_index);

        //

        if (min_objfn_val < min_objfn_val_running) {
            min_objfn_val_running = min_objfn_val;
            best_sol_running = best_vecs.row( min_objfn_val_index );
        }

        if (iter % check_freq == 0) {
            rel_objfn_change = std::abs(min_objfn_val_running - min_objfn_val_check) / (OPTIM_FPN_SMALL_NUMBER + std::abs(min_objfn_val_running));

            if (min_objfn_val_running < min_objfn_val_check) {
                min_objfn_val_check = min_objfn_val_running;
            }
        }

        //

        OPTIM_PSO_TRACE(iter, rel_objfn_change, min_objfn_val_running, min_objfn_val_check, best_sol_running, P);
    }

    //

    if (return_position_mat) {
        if (vals_bound) {
#ifdef OPTIM_USE_OPENMP
            #pragma omp parallel for num_threads(omp_n_threads)
#endif
            for (size_t i = 0; i < n_pop + n_center; ++i) {
                P.row(i) = inv_transform<RowVec_t>(P.row(i), bounds_type, lower_bounds, upper_bounds);
            }
        }

        settings_inp->pso_settings.position_mat = P;
    }

    //

    if (vals_bound) {
        best_sol_running = inv_transform( best_sol_running, bounds_type, lower_bounds, upper_bounds);
    }

    error_reporting(init_out_vals, BMO_MATOPS_TRANSPOSE(best_sol_running), opt_objfn, opt_data,
                    success, rel_objfn_change, rel_objfn_change_tol, iter, n_gen,
                    conv_failure_switch, settings_inp);

    //

    return success;
}

optimlib_inline
bool
optim::pso(
    ColVec_t& init_out_vals,
    std::function<fp_t (const ColVec_t& vals_inp, ColVec_t* grad_out, void* opt_data)> opt_objfn,
    void* opt_data
)
{
    return internal::pso_impl(init_out_vals,opt_objfn,opt_data,nullptr);
}

optimlib_inline
bool
optim::pso(
    ColVec_t& init_out_vals,
    std::function<fp_t (const ColVec_t& vals_inp, ColVec_t* grad_out, void* opt_data)> opt_objfn,
    void* opt_data,
    algo_settings_t& settings
)
{
    return internal::pso_impl(init_out_vals,opt_objfn,opt_data,&settings);
}
<|endoftext|>


<commit_before>//-----------------------------------------------------------------------bl-
//--------------------------------------------------------------------------
//
// GRINS - General Reacting Incompressible Navier-Stokes
//
// Copyright (C) 2014 Paul T. Bauman, Roy H. Stogner
// Copyright (C) 2010-2013 The PECOS Development Team
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the Version 2.1 GNU Lesser General
// Public License as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc. 51 Franklin Street, Fifth Floor,
// Boston, MA  02110-1301  USA
//
//-----------------------------------------------------------------------el-

#include "grins_config.h"

#include <iostream>

// GRINS
#include "grins/simulation_builder.h"
#include "grins/simulation.h"

// GRVY
#ifdef GRINS_HAVE_GRVY
#include "grvy.h"
#endif

// libMesh
#include "libmesh/parallel.h"

int main(int argc, char* argv[])
{
#ifdef GRINS_USE_GRVY_TIMERS
  GRVY::GRVY_Timer_Class grvy_timer;
  grvy_timer.Init("GRINS Timer");
#endif

  // Check command line count.
  if( argc < 2 )
    {
      // TODO: Need more consistent error handling.
      std::cerr << "Error: Must specify libMesh input file." << std::endl;
      exit(1); // TODO: something more sophisticated for parallel runs?
    }

  // libMesh input file should be first argument
  std::string libMesh_input_filename = argv[1];

  // Create our GetPot object.
  GetPot libMesh_inputfile( libMesh_input_filename );

  // GetPot doesn't throw an error for a nonexistent file?
  {
    std::ifstream i(libMesh_input_filename.c_str());
    if (!i)
      {
        std::cerr << "Error: Could not read from libMesh input file "
                << libMesh_input_filename << std::endl;
        exit(1);
      }
  }

#ifdef GRINS_USE_GRVY_TIMERS
  grvy_timer.BeginTimer("Initialize Solver");
#endif

  // Initialize libMesh library.
  libMesh::LibMeshInit libmesh_init(argc, argv);

  libMesh::out << "Starting GRINS with command:\n";
  for (unsigned int i=0; i != argc; ++i)
    libMesh::out << argv[i] << ' ';
  libMesh::out << std::endl;

  GRINS::SimulationBuilder sim_builder;

  GRINS::Simulation grins( libMesh_inputfile,
			   sim_builder,
                           libmesh_init.comm() );

#ifdef GRINS_USE_GRVY_TIMERS
  grvy_timer.EndTimer("Initialize Solver");

  // Attach GRVY timer to solver
  grins.attach_grvy_timer( &grvy_timer );
#endif

grins.run();

#ifdef GRINS_USE_GRVY_TIMERS
  grvy_timer.Finalize();

  if( Parallel::Communicator_World.rank() == 0 ) grvy_timer.Summarize();
#endif

  return 0;
}
<commit_msg>Shut up comparison warning<commit_after>//-----------------------------------------------------------------------bl-
//--------------------------------------------------------------------------
//
// GRINS - General Reacting Incompressible Navier-Stokes
//
// Copyright (C) 2014 Paul T. Bauman, Roy H. Stogner
// Copyright (C) 2010-2013 The PECOS Development Team
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the Version 2.1 GNU Lesser General
// Public License as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc. 51 Franklin Street, Fifth Floor,
// Boston, MA  02110-1301  USA
//
//-----------------------------------------------------------------------el-

#include "grins_config.h"

#include <iostream>

// GRINS
#include "grins/simulation_builder.h"
#include "grins/simulation.h"

// GRVY
#ifdef GRINS_HAVE_GRVY
#include "grvy.h"
#endif

// libMesh
#include "libmesh/parallel.h"

int main(int argc, char* argv[])
{
#ifdef GRINS_USE_GRVY_TIMERS
  GRVY::GRVY_Timer_Class grvy_timer;
  grvy_timer.Init("GRINS Timer");
#endif

  // Check command line count.
  if( argc < 2 )
    {
      // TODO: Need more consistent error handling.
      std::cerr << "Error: Must specify libMesh input file." << std::endl;
      exit(1); // TODO: something more sophisticated for parallel runs?
    }

  // libMesh input file should be first argument
  std::string libMesh_input_filename = argv[1];

  // Create our GetPot object.
  GetPot libMesh_inputfile( libMesh_input_filename );

  // GetPot doesn't throw an error for a nonexistent file?
  {
    std::ifstream i(libMesh_input_filename.c_str());
    if (!i)
      {
        std::cerr << "Error: Could not read from libMesh input file "
                << libMesh_input_filename << std::endl;
        exit(1);
      }
  }

#ifdef GRINS_USE_GRVY_TIMERS
  grvy_timer.BeginTimer("Initialize Solver");
#endif

  // Initialize libMesh library.
  libMesh::LibMeshInit libmesh_init(argc, argv);

  libMesh::out << "Starting GRINS with command:\n";
  for (int i=0; i != argc; ++i)
    libMesh::out << argv[i] << ' ';
  libMesh::out << std::endl;

  GRINS::SimulationBuilder sim_builder;

  GRINS::Simulation grins( libMesh_inputfile,
			   sim_builder,
                           libmesh_init.comm() );

#ifdef GRINS_USE_GRVY_TIMERS
  grvy_timer.EndTimer("Initialize Solver");

  // Attach GRVY timer to solver
  grins.attach_grvy_timer( &grvy_timer );
#endif

grins.run();

#ifdef GRINS_USE_GRVY_TIMERS
  grvy_timer.Finalize();

  if( Parallel::Communicator_World.rank() == 0 ) grvy_timer.Summarize();
#endif

  return 0;
}
<|endoftext|>


<commit_before>#include "common.h"
#include <cassert>
#include <cmath>
#include <cstring>

#include "Memory.h"

#define NUM_BUFFER_BITS 16
#define MAX_NUM_BUFFERS ((size_t)1 << NUM_BUFFER_BITS)
#define NUM_ADDRESS_BITS ((sizeof(size_t)<<3) - NUM_BUFFER_BITS)
#define MAX_BUFFER_SIZE ((size_t)1 << NUM_ADDRESS_BITS)

#define EXTRACT_BUFFER(address) \
  (address >> NUM_ADDRESS_BITS)
#define EXTRACT_OFFSET(address) \
  (address ^ (EXTRACT_BUFFER(address) << NUM_ADDRESS_BITS))

using namespace spirsim;
using namespace std;

Memory::Memory()
{
  clear();
}

Memory::~Memory()
{
  clear();
}

size_t Memory::allocateBuffer(size_t size)
{
  // Check requested size doesn't exceed maximum
  if (size > MAX_BUFFER_SIZE)
  {
    return NULL;
  }

  // Find first unallocated buffer slot
  int b = getNextBuffer();
  if (b < 0 || b >= MAX_NUM_BUFFERS)
  {
    return NULL;
  }

  // Create buffer
  Buffer buffer = {
    false,
    size,
    new unsigned char[size]
  };

  // Initialize contents to 0
  // TODO: Solution to catch use of uninitialised data
  memset(buffer.data, 0, size);

  m_memory[b] = buffer;
  m_totalAllocated += size;

  return ((size_t)b) << NUM_ADDRESS_BITS;
}

void Memory::clear()
{
  map<int,Buffer>::iterator itr;
  for (itr = m_memory.begin(); itr != m_memory.end(); itr++)
  {
    if (!itr->second.hostPtr)
    {
      delete[] itr->second.data;
    }
  }
  m_memory.clear();
  m_freeBuffers = queue<int>();
  m_totalAllocated = 0;

  // Reserve first buffer as a makeshift 'NULL'
  m_memory[0].data = new unsigned char[0];

}

Memory* Memory::clone() const
{
  Memory *mem = new Memory();

  // Clone buffers
  map<int,Buffer>::const_iterator itr;
  for (itr = m_memory.begin(); itr != m_memory.end(); itr++)
  {
    Buffer src = itr->second;
    Buffer dest = {
      src.hostPtr,
      src.size,
      src.hostPtr ? src.data : new unsigned char[src.size]
    };
    memcpy(dest.data, src.data, src.size);
    mem->m_memory[itr->first] = dest;
  }

  // Clone state
  mem->m_freeBuffers = m_freeBuffers;
  mem->m_totalAllocated = m_totalAllocated;

  return mem;
}

size_t Memory::createHostBuffer(size_t size, void *ptr)
{
  // Check requested size doesn't exceed maximum
  if (size > MAX_BUFFER_SIZE)
  {
    return NULL;
  }

  // Find first unallocated buffer slot
  int b = getNextBuffer();
  if (b < 0 || b >= MAX_NUM_BUFFERS)
  {
    return NULL;
  }

  // Create buffer
  Buffer buffer = {
    true,
    size,
    (unsigned char*)ptr
  };

  m_memory[b] = buffer;
  m_totalAllocated += size;

  return ((size_t)b) << NUM_ADDRESS_BITS;
}

bool Memory::copy(size_t dest, size_t src, size_t size)
{
  size_t src_buffer = EXTRACT_BUFFER(src);
  size_t src_offset = EXTRACT_OFFSET(src);
  size_t dest_buffer = EXTRACT_BUFFER(dest);
  size_t dest_offset = EXTRACT_OFFSET(dest);

  // Bounds check
  if (src_buffer >= MAX_NUM_BUFFERS ||
      dest_buffer >= MAX_NUM_BUFFERS ||
      m_memory.find(src_buffer) == m_memory.end() ||
      m_memory.find(dest_buffer) == m_memory.end() ||
      src_offset+size > m_memory.at(src_buffer).size ||
      dest_offset+size > m_memory.at(dest_buffer).size)
  {
    return false;
  }

  // Copy data
  memcpy(m_memory.at(dest_buffer).data + dest_offset,
         m_memory.at(src_buffer).data + src_offset,
         size);

  return true;
}

void Memory::deallocateBuffer(size_t address)
{
  int buffer = EXTRACT_BUFFER(address);
  assert(buffer < MAX_NUM_BUFFERS && m_memory.find(buffer) != m_memory.end());

  if (!m_memory[buffer].hostPtr)
  {
    delete[] m_memory[buffer].data;
  }

  m_totalAllocated -= m_memory[buffer].size;
  m_memory.erase(buffer);
  m_freeBuffers.push(buffer);
}

void Memory::dump() const
{
  map<int,Buffer>::const_iterator itr;
  for (itr = m_memory.begin(); itr != m_memory.end(); itr++)
  {
    for (int i = 0; i < itr->second.size; i++)
    {
      if (i%4 == 0)
      {
        cout << endl << hex << uppercase
             << setw(16) << setfill(' ') << right
             << ((((size_t)itr->first)<<NUM_ADDRESS_BITS) | i) << ":";
      }
      cout << " " << hex << uppercase << setw(2) << setfill('0')
           << (int)itr->second.data[i];
    }
  }
  cout << endl;
}

size_t Memory::getMaxAllocSize()
{
  return MAX_BUFFER_SIZE;
}

int Memory::getNextBuffer()
{
  if (m_freeBuffers.empty())
  {
    return m_memory.size();
  }
  else
  {
    int b = m_freeBuffers.front();
    m_freeBuffers.pop();
    return b;
  }
}

size_t Memory::getTotalAllocated() const
{
  return m_totalAllocated;
}

bool Memory::load(unsigned char *dest, size_t address, size_t size) const
{
  size_t buffer = EXTRACT_BUFFER(address);
  size_t offset = EXTRACT_OFFSET(address);

  // Bounds check
  if (buffer >= MAX_NUM_BUFFERS ||
      m_memory.find(buffer) == m_memory.end() ||
      offset+size > m_memory.at(buffer).size)
  {
    return false;
  }

  // Load data
  memcpy(dest, m_memory.at(buffer).data + offset, size);

  return true;
}

unsigned char* Memory::mapBuffer(size_t address, size_t offset, size_t size)
{
  size_t buffer = EXTRACT_BUFFER(address);

  // Bounds check
  if (buffer >= MAX_NUM_BUFFERS ||
      m_memory.find(buffer) == m_memory.end() ||
      offset+size > m_memory[buffer].size)
  {
    return false;
  }

  return m_memory[buffer].data + offset;
}

bool Memory::store(const unsigned char *source, size_t address, size_t size)
{
  size_t buffer = EXTRACT_BUFFER(address);
  size_t offset = EXTRACT_OFFSET(address);

  // Bounds check
  if (buffer >= MAX_NUM_BUFFERS ||
      m_memory.find(buffer) == m_memory.end() ||
      offset+size > m_memory[buffer].size)
  {
    return false;
  }

  // Store data
  memcpy(m_memory[buffer].data + offset, source, size);

  return true;
}
<commit_msg>Fixed leak in Memory class for its NULL buffer.<commit_after>#include "common.h"
#include <cassert>
#include <cmath>
#include <cstring>

#include "Memory.h"

#define NUM_BUFFER_BITS 16
#define MAX_NUM_BUFFERS ((size_t)1 << NUM_BUFFER_BITS)
#define NUM_ADDRESS_BITS ((sizeof(size_t)<<3) - NUM_BUFFER_BITS)
#define MAX_BUFFER_SIZE ((size_t)1 << NUM_ADDRESS_BITS)

#define EXTRACT_BUFFER(address) \
  (address >> NUM_ADDRESS_BITS)
#define EXTRACT_OFFSET(address) \
  (address ^ (EXTRACT_BUFFER(address) << NUM_ADDRESS_BITS))

using namespace spirsim;
using namespace std;

Memory::Memory()
{
  clear();
}

Memory::~Memory()
{
  clear();

  delete[] m_memory[0].data;
}

size_t Memory::allocateBuffer(size_t size)
{
  // Check requested size doesn't exceed maximum
  if (size > MAX_BUFFER_SIZE)
  {
    return NULL;
  }

  // Find first unallocated buffer slot
  int b = getNextBuffer();
  if (b < 0 || b >= MAX_NUM_BUFFERS)
  {
    return NULL;
  }

  // Create buffer
  Buffer buffer = {
    false,
    size,
    new unsigned char[size]
  };

  // Initialize contents to 0
  // TODO: Solution to catch use of uninitialised data
  memset(buffer.data, 0, size);

  m_memory[b] = buffer;
  m_totalAllocated += size;

  return ((size_t)b) << NUM_ADDRESS_BITS;
}

void Memory::clear()
{
  map<int,Buffer>::iterator itr;
  for (itr = m_memory.begin(); itr != m_memory.end(); itr++)
  {
    if (!itr->second.hostPtr)
    {
      delete[] itr->second.data;
    }
  }
  m_memory.clear();
  m_freeBuffers = queue<int>();
  m_totalAllocated = 0;

  // Reserve first buffer as a makeshift 'NULL'
  m_memory[0].data = new unsigned char[0];
}

Memory* Memory::clone() const
{
  Memory *mem = new Memory();

  // Clone buffers
  map<int,Buffer>::const_iterator itr;
  for (itr = m_memory.begin(); itr != m_memory.end(); itr++)
  {
    Buffer src = itr->second;
    Buffer dest = {
      src.hostPtr,
      src.size,
      src.hostPtr ? src.data : new unsigned char[src.size]
    };
    memcpy(dest.data, src.data, src.size);
    mem->m_memory[itr->first] = dest;
  }

  // Clone state
  mem->m_freeBuffers = m_freeBuffers;
  mem->m_totalAllocated = m_totalAllocated;

  return mem;
}

size_t Memory::createHostBuffer(size_t size, void *ptr)
{
  // Check requested size doesn't exceed maximum
  if (size > MAX_BUFFER_SIZE)
  {
    return NULL;
  }

  // Find first unallocated buffer slot
  int b = getNextBuffer();
  if (b < 0 || b >= MAX_NUM_BUFFERS)
  {
    return NULL;
  }

  // Create buffer
  Buffer buffer = {
    true,
    size,
    (unsigned char*)ptr
  };

  m_memory[b] = buffer;
  m_totalAllocated += size;

  return ((size_t)b) << NUM_ADDRESS_BITS;
}

bool Memory::copy(size_t dest, size_t src, size_t size)
{
  size_t src_buffer = EXTRACT_BUFFER(src);
  size_t src_offset = EXTRACT_OFFSET(src);
  size_t dest_buffer = EXTRACT_BUFFER(dest);
  size_t dest_offset = EXTRACT_OFFSET(dest);

  // Bounds check
  if (src_buffer >= MAX_NUM_BUFFERS ||
      dest_buffer >= MAX_NUM_BUFFERS ||
      m_memory.find(src_buffer) == m_memory.end() ||
      m_memory.find(dest_buffer) == m_memory.end() ||
      src_offset+size > m_memory.at(src_buffer).size ||
      dest_offset+size > m_memory.at(dest_buffer).size)
  {
    return false;
  }

  // Copy data
  memcpy(m_memory.at(dest_buffer).data + dest_offset,
         m_memory.at(src_buffer).data + src_offset,
         size);

  return true;
}

void Memory::deallocateBuffer(size_t address)
{
  int buffer = EXTRACT_BUFFER(address);
  assert(buffer < MAX_NUM_BUFFERS && m_memory.find(buffer) != m_memory.end());

  if (!m_memory[buffer].hostPtr)
  {
    delete[] m_memory[buffer].data;
  }

  m_totalAllocated -= m_memory[buffer].size;
  m_memory.erase(buffer);
  m_freeBuffers.push(buffer);
}

void Memory::dump() const
{
  map<int,Buffer>::const_iterator itr;
  for (itr = m_memory.begin(); itr != m_memory.end(); itr++)
  {
    for (int i = 0; i < itr->second.size; i++)
    {
      if (i%4 == 0)
      {
        cout << endl << hex << uppercase
             << setw(16) << setfill(' ') << right
             << ((((size_t)itr->first)<<NUM_ADDRESS_BITS) | i) << ":";
      }
      cout << " " << hex << uppercase << setw(2) << setfill('0')
           << (int)itr->second.data[i];
    }
  }
  cout << endl;
}

size_t Memory::getMaxAllocSize()
{
  return MAX_BUFFER_SIZE;
}

int Memory::getNextBuffer()
{
  if (m_freeBuffers.empty())
  {
    return m_memory.size();
  }
  else
  {
    int b = m_freeBuffers.front();
    m_freeBuffers.pop();
    return b;
  }
}

size_t Memory::getTotalAllocated() const
{
  return m_totalAllocated;
}

bool Memory::load(unsigned char *dest, size_t address, size_t size) const
{
  size_t buffer = EXTRACT_BUFFER(address);
  size_t offset = EXTRACT_OFFSET(address);

  // Bounds check
  if (buffer >= MAX_NUM_BUFFERS ||
      m_memory.find(buffer) == m_memory.end() ||
      offset+size > m_memory.at(buffer).size)
  {
    return false;
  }

  // Load data
  memcpy(dest, m_memory.at(buffer).data + offset, size);

  return true;
}

unsigned char* Memory::mapBuffer(size_t address, size_t offset, size_t size)
{
  size_t buffer = EXTRACT_BUFFER(address);

  // Bounds check
  if (buffer >= MAX_NUM_BUFFERS ||
      m_memory.find(buffer) == m_memory.end() ||
      offset+size > m_memory[buffer].size)
  {
    return false;
  }

  return m_memory[buffer].data + offset;
}

bool Memory::store(const unsigned char *source, size_t address, size_t size)
{
  size_t buffer = EXTRACT_BUFFER(address);
  size_t offset = EXTRACT_OFFSET(address);

  // Bounds check
  if (buffer >= MAX_NUM_BUFFERS ||
      m_memory.find(buffer) == m_memory.end() ||
      offset+size > m_memory[buffer].size)
  {
    return false;
  }

  // Store data
  memcpy(m_memory[buffer].data + offset, source, size);

  return true;
}
<|endoftext|>


<commit_before>/***********************************************************************************************************************
*                                                                                *
* SPLASH build system v0.2                                                                                *
*                                                                                *
* Copyright (c) 2016 Andrew D. Zonenberg                                                                               *
* All rights reserved.                                                                                *
*                                                                                *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the     *
* following conditions are met:                                                                                *
*                                                                                *
*    * Redistributions of source code must retain the above copyright notice, this list of conditions, and the         *
*      following disclaimer.                                                                                *
*                                                                                *
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       *
*      following disclaimer in the documentation and/or other materials provided with the distribution.                *
*                                                                                *
*    * Neither the name of the author nor the names of any contributors may be used to endorse or promote products     *
*      derived from this software without specific prior written permission.                                           *
*                                                                                *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES        *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR       *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE       *
* POSSIBILITY OF SUCH DAMAGE.                                                                                *
*                                                                                *
***********************************************************************************************************************/

#include "splashctl.h"

using namespace std;

void ShowUsage();
void ShowVersion();

//Mutex to control access to all global node lists
mutex g_toolchainListMutex;

//List of nodes (eliminate multiple splashbuild instances)
unordered_set<string> g_activeClients;

//List of compilers available on each node
//This is the authoritative pointer to nodes
map<string, vtool> g_toolchainsByNode;

//List of nodes with any compiler for a given language and target architecture
map<larch, vnode> g_nodesByLanguage;

//List of nodes with a specific compiler (by hash)
map<string, vnode> g_nodesByCompiler;

/**
	@brief Program entry point
 */
int main(int argc, char* argv[])
{
	int port = 49000;

	Severity console_verbosity = Severity::NOTICE;

	//Parse command-line arguments
	for(int i=1; i<argc; i++)
	{
		string s(argv[i]);

		//Let the logger eat its args first
		if(ParseLoggerArguments(i, argc, argv, console_verbosity))
			continue;

		else if(s == "--help")
		{
			ShowUsage();
			return 0;
		}

		else if(s == "--version")
		{
			ShowVersion();
			return 0;
		}

		//Last arg without a switch is the port number
		//TODO: mandatory arguments to introduce this?
		//TODO: sanity check that it's numeric
		else
			port = atoi(argv[i]);

	}

	//Set up logging
	g_log_sinks.emplace(g_log_sinks.begin(), new STDLogSink(console_verbosity));

	//Print header
	if(console_verbosity >= Severity::NOTICE)
	{
		ShowVersion();
		printf("\n");
	}

	//Socket server
	Socket server(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(!server.Bind(port))
		return -1;
	if(!server.Listen())
		return -1;
	while(true)
	{
		thread t(ClientThread, server.Accept().Detach());
		t.detach();
	}

	return 0;
}

void ShowVersion()
{
	printf(
		"SPLASH control server daemon by Andrew D. Zonenberg.\n"
		"\n"
		"License: 3-clause BSD\n"
		"This is free software: you are free to change and redistribute it.\n"
		"There is NO WARRANTY, to the extent permitted by law.\n");
}

void ShowUsage()
{
	printf("Usage: splashctl [control_port]\n");
	exit(0);
}
<commit_msg>splashctl: Added a bunch of comments describing future work<commit_after>/***********************************************************************************************************************
*                                                                                *
* SPLASH build system v0.2                                                                                *
*                                                                                *
* Copyright (c) 2016 Andrew D. Zonenberg                                                                               *
* All rights reserved.                                                                                *
*                                                                                *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the     *
* following conditions are met:                                                                                *
*                                                                                *
*    * Redistributions of source code must retain the above copyright notice, this list of conditions, and the         *
*      following disclaimer.                                                                                *
*                                                                                *
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       *
*      following disclaimer in the documentation and/or other materials provided with the distribution.                *
*                                                                                *
*    * Neither the name of the author nor the names of any contributors may be used to endorse or promote products     *
*      derived from this software without specific prior written permission.                                           *
*                                                                                *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES        *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR       *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE       *
* POSSIBILITY OF SUCH DAMAGE.                                                                                *
*                                                                                *
***********************************************************************************************************************/

#include "splashctl.h"

using namespace std;

void ShowUsage();
void ShowVersion();

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Global state: toolchains

//Mutex to control access to all global node lists
mutex g_toolchainListMutex;

//List of nodes (eliminate multiple splashbuild instances)
unordered_set<string> g_activeClients;

//List of compilers available on each node
//This is the authoritative pointer to nodes
map<string, vtool> g_toolchainsByNode;

//List of nodes with any compiler for a given language and target architecture
map<larch, vnode> g_nodesByLanguage;

//List of nodes with a specific compiler (by hash)
map<string, vnode> g_nodesByCompiler;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Global state: files on clients

/*
	Set of hashes for source/object files we have in the cache (need to read at app startup)

	Directory structure:
	$CACHE/
		xx/				first octet of hash, as hex
			hash/		hash of file object (may not actually be the hash of the file, includes flags etc)
				xx		the file itself (original filename)
				.hash	sha256 of the file itself (for load-time integrity checking)
				.atime	last-accessed time of the file
						We don't use filesystem atime as that's way too easy to set by accident

	Global cache:
	unordered_set<string> 	listing which hashes we have in the cache
	map<string, time_t>		mapping hashes to last-used times

	Client state:
 */

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// App code

/**
	@brief Program entry point
 */
int main(int argc, char* argv[])
{
	int port = 49000;

	Severity console_verbosity = Severity::NOTICE;

	//Parse command-line arguments
	for(int i=1; i<argc; i++)
	{
		string s(argv[i]);

		//Let the logger eat its args first
		if(ParseLoggerArguments(i, argc, argv, console_verbosity))
			continue;

		else if(s == "--help")
		{
			ShowUsage();
			return 0;
		}

		else if(s == "--version")
		{
			ShowVersion();
			return 0;
		}

		//Last arg without a switch is the port number
		//TODO: mandatory arguments to introduce this?
		//TODO: sanity check that it's numeric
		else
			port = atoi(argv[i]);

	}

	//Set up logging
	g_log_sinks.emplace(g_log_sinks.begin(), new STDLogSink(console_verbosity));

	//Print header
	if(console_verbosity >= Severity::NOTICE)
	{
		ShowVersion();
		printf("\n");
	}

	//Socket server
	Socket server(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(!server.Bind(port))
		return -1;
	if(!server.Listen())
		return -1;
	while(true)
	{
		thread t(ClientThread, server.Accept().Detach());
		t.detach();
	}

	return 0;
}

void ShowVersion()
{
	printf(
		"SPLASH control server daemon by Andrew D. Zonenberg.\n"
		"\n"
		"License: 3-clause BSD\n"
		"This is free software: you are free to change and redistribute it.\n"
		"There is NO WARRANTY, to the extent permitted by law.\n");
}

void ShowUsage()
{
	printf("Usage: splashctl [control_port]\n");
	exit(0);
}
<|endoftext|>


<commit_before>#pragma once

#include <map>
#include <string>
#include <type_traits>
#include "SDL.h"
#undef main

namespace MgCore
{

    enum class KeyCode
    {
        _KEY_NONE = 0,
        KEY_A,
        KEY_B,
        KEY_C,
        KEY_D,
        KEY_E,
        KEY_F,
        KEY_G,
        KEY_H,
        KEY_I,
        KEY_J,
        KEY_K,
        KEY_L,
        KEY_M,
        KEY_N,
        KEY_O,
        KEY_P,
        KEY_Q,
        KEY_R,
        KEY_S,
        KEY_T,
        KEY_U,
        KEY_V,
        KEY_W,
        KEY_X,
        KEY_Y,
        KEY_Z,
        KEY_1,
        KEY_2,
        KEY_3,
        KEY_4,
        KEY_5,
        KEY_6,
        KEY_7,
        KEY_8,
        KEY_9,
        KEY_RETURN,
        KEY_ESCAPE,
        KEY_BACKSPACE,
        KEY_TAB,
        KEY_SPACE,
        KEY_MINUS,
        KEY_EQUALS,
        KEY_LEFT_BRACKET,
        KEY_RIGHT_BRACKET,
        KEY_BACKSLASH,
        KEY_SEMICOLON,
        KEY_APOSTROPHE,
        KEY_GRAVE,
        KEY_COMMA,
        KEY_PERIOD,
        KEY_SLASH,
        KEY_CAPS_LOCK,
        KEY_F1,
        KEY_F2,
        KEY_F3,
        KEY_F4,
        KEY_F5,
        KEY_F6,
        KEY_F7,
        KEY_F8,
        KEY_F9,
        KEY_F10,
        KEY_F11,
        KEY_F12,
        KEY_PRINT_SCREEN,
        KEY_SCROLL_LOCK,
        KEY_PAUSE,
        KEY_INSERT,
        KEY_HOME,
        KEY_PAGE_UP,
        KEY_DELETE,
        KEY_END,
        KEY_PAGE_DOWN,
        KEY_RIGHT,
        KEY_LEFT,
        KEY_DOWN,
        KEY_UP,
        KEY_NUMLOCK_CLEAR,
        KEY_KP_DIVIDE,
        KEY_KP_MULTIPLY,
        KEY_KP_MINUS,
        KEY_KP_PLUS,
        KEY_KP_ENTER,
        KEY_KP_1,
        KEY_KP_2,
        KEY_KP_3,
        KEY_KP_4,
        KEY_KP_5,
        KEY_KP_6,
        KEY_KP_7,
        KEY_KP_8,
        KEY_KP_9,
        KEY_KP_0,
        KEY_KP_PERIOD,
        KEY_NON_US_BACKSLASH,
        KEY_LEFT_CTRL,
        KEY_LEFT_SHIFT,
        KEY_LEFT_ALT,
        KEY_LEFT_SUPER,
        KEY_RIGHT_CTRL,
        KEY_RIGHT_SHIFT,
        KEY_RIGHT_ALT,
        KEY_RIGHT_SUPER
    };

    enum class ModFlag
    {
        MOD_NONE            = 0,
        MOD_LEFT_SHIFT      = 1 << 0,
        MOD_RIGHT_SHIFT     = 1 << 1,
        MOD_LEFT_CTRL       = 1 << 2,
        MOD_RIGHT_CTRL      = 1 << 3,
        MOD_LEFT_ALT        = 1 << 4,
        MOD_RIGHT_ALT       = 1 << 5,
        MOD_LEFT_SUPER      = 1 << 6,
        MOD_RIGHT_SUPER     = 1 << 7,
        MOD_NUM             = 1 << 8,
        MOD_CAPS            = 1 << 9,
    };

    using underlying = std::underlying_type<ModFlag>::type;

    static ModFlag operator|(const ModFlag& left, const ModFlag& right)
    {
        return static_cast<ModFlag>(static_cast<underlying>(left) | static_cast<underlying>(right));
    }

    static ModFlag operator&(const ModFlag& left, const ModFlag& right)
    {
        return static_cast<ModFlag>(static_cast<underlying>(left) & static_cast<underlying>(right));
    }

    static std::map<int, ModFlag> modMap {
        {KMOD_NONE, ModFlag::MOD_NONE},
        {KMOD_LSHIFT, ModFlag::MOD_LEFT_SHIFT},
        {KMOD_RSHIFT, ModFlag::MOD_RIGHT_SHIFT},
        {KMOD_LCTRL, ModFlag::MOD_LEFT_CTRL},
        {KMOD_RCTRL, ModFlag::MOD_RIGHT_CTRL},
        {KMOD_LALT, ModFlag::MOD_LEFT_ALT},
        {KMOD_RALT, ModFlag::MOD_RIGHT_ALT},
        {KMOD_LGUI, ModFlag::MOD_LEFT_SUPER},
        {KMOD_RGUI, ModFlag::MOD_RIGHT_SUPER},
        {KMOD_NUM, ModFlag::MOD_NUM},
        {KMOD_CAPS, ModFlag::MOD_CAPS}};

    static std::map<int, KeyCode> keyMap {
        {SDL_SCANCODE_A, KeyCode::KEY_A},
        {SDL_SCANCODE_B, KeyCode::KEY_B},
        {SDL_SCANCODE_C, KeyCode::KEY_C},
        {SDL_SCANCODE_D, KeyCode::KEY_D},
        {SDL_SCANCODE_E, KeyCode::KEY_E},
        {SDL_SCANCODE_F, KeyCode::KEY_F},
        {SDL_SCANCODE_G, KeyCode::KEY_G},
        {SDL_SCANCODE_H, KeyCode::KEY_H},
        {SDL_SCANCODE_I, KeyCode::KEY_I},
        {SDL_SCANCODE_J, KeyCode::KEY_J},
        {SDL_SCANCODE_K, KeyCode::KEY_K},
        {SDL_SCANCODE_L, KeyCode::KEY_L},
        {SDL_SCANCODE_M, KeyCode::KEY_M},
        {SDL_SCANCODE_N, KeyCode::KEY_N},
        {SDL_SCANCODE_O, KeyCode::KEY_O},
        {SDL_SCANCODE_P, KeyCode::KEY_P},
        {SDL_SCANCODE_Q, KeyCode::KEY_Q},
        {SDL_SCANCODE_R, KeyCode::KEY_R},
        {SDL_SCANCODE_S, KeyCode::KEY_S},
        {SDL_SCANCODE_T, KeyCode::KEY_T},
        {SDL_SCANCODE_U, KeyCode::KEY_U},
        {SDL_SCANCODE_V, KeyCode::KEY_V},
        {SDL_SCANCODE_W, KeyCode::KEY_W},
        {SDL_SCANCODE_X, KeyCode::KEY_X},
        {SDL_SCANCODE_Y, KeyCode::KEY_Y},
        {SDL_SCANCODE_Z, KeyCode::KEY_Z},
        {SDL_SCANCODE_1, KeyCode::KEY_1},
        {SDL_SCANCODE_2, KeyCode::KEY_2},
        {SDL_SCANCODE_3, KeyCode::KEY_3},
        {SDL_SCANCODE_4, KeyCode::KEY_4},
        {SDL_SCANCODE_5, KeyCode::KEY_5},
        {SDL_SCANCODE_6, KeyCode::KEY_6},
        {SDL_SCANCODE_7, KeyCode::KEY_7},
        {SDL_SCANCODE_8, KeyCode::KEY_7},
        {SDL_SCANCODE_9, KeyCode::KEY_8},
        {SDL_SCANCODE_0, KeyCode::KEY_9},
        {SDL_SCANCODE_RETURN, KeyCode::KEY_RETURN},
        {SDL_SCANCODE_ESCAPE, KeyCode::KEY_ESCAPE},
        {SDL_SCANCODE_BACKSPACE, KeyCode::KEY_BACKSPACE},
        {SDL_SCANCODE_TAB, KeyCode::KEY_TAB},
        {SDL_SCANCODE_SPACE, KeyCode::KEY_SPACE},
        {SDL_SCANCODE_MINUS, KeyCode::KEY_MINUS},
        {SDL_SCANCODE_EQUALS, KeyCode::KEY_EQUALS},
        {SDL_SCANCODE_LEFTBRACKET, KeyCode::KEY_LEFT_BRACKET},
        {SDL_SCANCODE_RIGHTBRACKET, KeyCode::KEY_RIGHT_BRACKET},
        {SDL_SCANCODE_BACKSLASH, KeyCode::KEY_BACKSLASH},
        {SDL_SCANCODE_SEMICOLON, KeyCode::KEY_SEMICOLON},
        {SDL_SCANCODE_APOSTROPHE, KeyCode::KEY_APOSTROPHE},
        {SDL_SCANCODE_GRAVE, KeyCode::KEY_GRAVE},
        {SDL_SCANCODE_COMMA, KeyCode::KEY_COMMA},
        {SDL_SCANCODE_PERIOD, KeyCode::KEY_PERIOD},
        {SDL_SCANCODE_SLASH, KeyCode::KEY_SLASH},
        {SDL_SCANCODE_CAPSLOCK, KeyCode::KEY_CAPS_LOCK},
        {SDL_SCANCODE_F1, KeyCode::KEY_F1},
        {SDL_SCANCODE_F2, KeyCode::KEY_F2},
        {SDL_SCANCODE_F3, KeyCode::KEY_F3},
        {SDL_SCANCODE_F4, KeyCode::KEY_F4},
        {SDL_SCANCODE_F5, KeyCode::KEY_F5},
        {SDL_SCANCODE_F6, KeyCode::KEY_F6},
        {SDL_SCANCODE_F7, KeyCode::KEY_F7},
        {SDL_SCANCODE_F8, KeyCode::KEY_F8},
        {SDL_SCANCODE_F9, KeyCode::KEY_F9},
        {SDL_SCANCODE_F10, KeyCode::KEY_F10},
        {SDL_SCANCODE_F11, KeyCode::KEY_F11},
        {SDL_SCANCODE_F12, KeyCode::KEY_F12},
        {SDL_SCANCODE_PRINTSCREEN, KeyCode::KEY_PRINT_SCREEN},
        {SDL_SCANCODE_SCROLLLOCK, KeyCode::KEY_SCROLL_LOCK},
        {SDL_SCANCODE_PAUSE, KeyCode::KEY_PAUSE},
        {SDL_SCANCODE_INSERT, KeyCode::KEY_INSERT},
        {SDL_SCANCODE_HOME, KeyCode::KEY_HOME},
        {SDL_SCANCODE_PAGEUP, KeyCode::KEY_PAGE_UP},
        {SDL_SCANCODE_DELETE, KeyCode::KEY_DELETE},
        {SDL_SCANCODE_END, KeyCode::KEY_END},
        {SDL_SCANCODE_PAGEDOWN, KeyCode::KEY_PAGE_DOWN},
        {SDL_SCANCODE_RIGHT, KeyCode::KEY_RIGHT},
        {SDL_SCANCODE_LEFT, KeyCode::KEY_LEFT},
        {SDL_SCANCODE_DOWN, KeyCode::KEY_DOWN},
        {SDL_SCANCODE_UP, KeyCode::KEY_UP},
        {SDL_SCANCODE_NUMLOCKCLEAR, KeyCode::KEY_NUMLOCK_CLEAR},
        {SDL_SCANCODE_KP_DIVIDE, KeyCode::KEY_KP_DIVIDE},
        {SDL_SCANCODE_KP_MULTIPLY, KeyCode::KEY_KP_MULTIPLY},
        {SDL_SCANCODE_KP_MINUS, KeyCode::KEY_KP_MINUS},
        {SDL_SCANCODE_KP_PLUS, KeyCode::KEY_KP_PLUS},
        {SDL_SCANCODE_KP_ENTER, KeyCode::KEY_KP_ENTER},
        {SDL_SCANCODE_KP_1, KeyCode::KEY_KP_1},
        {SDL_SCANCODE_KP_2, KeyCode::KEY_KP_2},
        {SDL_SCANCODE_KP_3, KeyCode::KEY_KP_3},
        {SDL_SCANCODE_KP_4, KeyCode::KEY_KP_4},
        {SDL_SCANCODE_KP_5, KeyCode::KEY_KP_5},
        {SDL_SCANCODE_KP_6, KeyCode::KEY_KP_6},
        {SDL_SCANCODE_KP_7, KeyCode::KEY_KP_7},
        {SDL_SCANCODE_KP_8, KeyCode::KEY_KP_8},
        {SDL_SCANCODE_KP_9, KeyCode::KEY_KP_9},
        {SDL_SCANCODE_KP_0, KeyCode::KEY_KP_0},
        {SDL_SCANCODE_KP_PERIOD, KeyCode::KEY_KP_PERIOD},
        {SDL_SCANCODE_NONUSBACKSLASH, KeyCode::KEY_NON_US_BACKSLASH},
        {SDL_SCANCODE_LCTRL, KeyCode::KEY_LEFT_CTRL},
        {SDL_SCANCODE_LSHIFT, KeyCode::KEY_LEFT_SHIFT},
        {SDL_SCANCODE_LALT, KeyCode::KEY_LEFT_ALT},
        {SDL_SCANCODE_LGUI, KeyCode::KEY_LEFT_SUPER},
        {SDL_SCANCODE_RCTRL, KeyCode::KEY_RIGHT_CTRL},
        {SDL_SCANCODE_RSHIFT, KeyCode::KEY_RIGHT_SHIFT},
        {SDL_SCANCODE_RALT, KeyCode::KEY_RIGHT_ALT},
        {SDL_SCANCODE_RGUI, KeyCode::KEY_RIGHT_SUPER}
    };

    static std::map<KeyCode, std::string> keyStrMap {
        {KeyCode::KEY_CAPS_LOCK, "CAPS LOCK"},
        {KeyCode::KEY_F1, "F1"},
        {KeyCode::KEY_F2, "F2"},
        {KeyCode::KEY_F3, "F3"},
        {KeyCode::KEY_F4, "F4"},
        {KeyCode::KEY_F5, "F5"},
        {KeyCode::KEY_F6, "F6"},
        {KeyCode::KEY_F7, "F7"},
        {KeyCode::KEY_F8, "F8"},
        {KeyCode::KEY_F9, "F9"},
        {KeyCode::KEY_F10, "F10"},
        {KeyCode::KEY_F11, "F11"},
        {KeyCode::KEY_F12, "F12"},
        {KeyCode::KEY_PRINT_SCREEN, "PRINT SCREEN"},
        {KeyCode::KEY_SCROLL_LOCK, "SCROLL LOCK"},
        {KeyCode::KEY_PAUSE, "PAUSE"},
        {KeyCode::KEY_INSERT, "INSERT"},
        {KeyCode::KEY_HOME, "HOME"},
        {KeyCode::KEY_PAGE_UP, "PAGE UP"},
        {KeyCode::KEY_END, "END"},
        {KeyCode::KEY_PAGE_DOWN, "PAGE DOWN"},
        {KeyCode::KEY_RIGHT, "RIGHT"},
        {KeyCode::KEY_LEFT, "LEFT"},
        {KeyCode::KEY_DOWN, "DOWN"},
        {KeyCode::KEY_UP, "UP"},
        {KeyCode::KEY_NUMLOCK_CLEAR, "NUMLOCK CLEAR"},
        {KeyCode::KEY_LEFT_CTRL, "LEFT CTRL"},
        {KeyCode::KEY_LEFT_SHIFT, "LEFT SHIFT"},
        {KeyCode::KEY_LEFT_ALT, "LEFT ALT"},
        {KeyCode::KEY_LEFT_SUPER, "LEFT SUPER"},
        {KeyCode::KEY_RIGHT_CTRL, "RIGHT CTRL"},
        {KeyCode::KEY_RIGHT_SHIFT, "RIGHT SHIFT"},
        {KeyCode::KEY_RIGHT_ALT, "RIGHT ALT"},
        {KeyCode::KEY_RIGHT_SUPER, "RIGHT SUPER"}
    };

// def process_modkeys(value):
//  keys = []
//  for key in modmap:
//      if value & key:
//          keys.append(modmap[key])
//  return keys

// def process_key_char(value):
//  char = None
//  try:
//      char = str(chr(value))
//  except ValueError:
//      temp = value ^ (1<<30)
//      if temp in keymap:
//          char = keystrMap[keymap[temp]]
//  return char

}
<commit_msg>implement keymodifier checking function.<commit_after>#pragma once

#include <map>
#include <string>
#include <type_traits>
#include <memory>
#include "SDL.h"
#undef main

namespace MgCore
{

    enum class KeyCode
    {
        _KEY_NONE = 0,
        KEY_A,
        KEY_B,
        KEY_C,
        KEY_D,
        KEY_E,
        KEY_F,
        KEY_G,
        KEY_H,
        KEY_I,
        KEY_J,
        KEY_K,
        KEY_L,
        KEY_M,
        KEY_N,
        KEY_O,
        KEY_P,
        KEY_Q,
        KEY_R,
        KEY_S,
        KEY_T,
        KEY_U,
        KEY_V,
        KEY_W,
        KEY_X,
        KEY_Y,
        KEY_Z,
        KEY_1,
        KEY_2,
        KEY_3,
        KEY_4,
        KEY_5,
        KEY_6,
        KEY_7,
        KEY_8,
        KEY_9,
        KEY_RETURN,
        KEY_ESCAPE,
        KEY_BACKSPACE,
        KEY_TAB,
        KEY_SPACE,
        KEY_MINUS,
        KEY_EQUALS,
        KEY_LEFT_BRACKET,
        KEY_RIGHT_BRACKET,
        KEY_BACKSLASH,
        KEY_SEMICOLON,
        KEY_APOSTROPHE,
        KEY_GRAVE,
        KEY_COMMA,
        KEY_PERIOD,
        KEY_SLASH,
        KEY_CAPS_LOCK,
        KEY_F1,
        KEY_F2,
        KEY_F3,
        KEY_F4,
        KEY_F5,
        KEY_F6,
        KEY_F7,
        KEY_F8,
        KEY_F9,
        KEY_F10,
        KEY_F11,
        KEY_F12,
        KEY_PRINT_SCREEN,
        KEY_SCROLL_LOCK,
        KEY_PAUSE,
        KEY_INSERT,
        KEY_HOME,
        KEY_PAGE_UP,
        KEY_DELETE,
        KEY_END,
        KEY_PAGE_DOWN,
        KEY_RIGHT,
        KEY_LEFT,
        KEY_DOWN,
        KEY_UP,
        KEY_NUMLOCK_CLEAR,
        KEY_KP_DIVIDE,
        KEY_KP_MULTIPLY,
        KEY_KP_MINUS,
        KEY_KP_PLUS,
        KEY_KP_ENTER,
        KEY_KP_1,
        KEY_KP_2,
        KEY_KP_3,
        KEY_KP_4,
        KEY_KP_5,
        KEY_KP_6,
        KEY_KP_7,
        KEY_KP_8,
        KEY_KP_9,
        KEY_KP_0,
        KEY_KP_PERIOD,
        KEY_NON_US_BACKSLASH,
        KEY_LEFT_CTRL,
        KEY_LEFT_SHIFT,
        KEY_LEFT_ALT,
        KEY_LEFT_SUPER,
        KEY_RIGHT_CTRL,
        KEY_RIGHT_SHIFT,
        KEY_RIGHT_ALT,
        KEY_RIGHT_SUPER
    };

    enum class ModFlag
    {
        MOD_NONE            = 0,
        MOD_LEFT_SHIFT      = 1 << 0,
        MOD_RIGHT_SHIFT     = 1 << 1,
        MOD_LEFT_CTRL       = 1 << 2,
        MOD_RIGHT_CTRL      = 1 << 3,
        MOD_LEFT_ALT        = 1 << 4,
        MOD_RIGHT_ALT       = 1 << 5,
        MOD_LEFT_SUPER      = 1 << 6,
        MOD_RIGHT_SUPER     = 1 << 7,
        MOD_NUM             = 1 << 8,
        MOD_CAPS            = 1 << 9,
    };

    using underlying = std::underlying_type<ModFlag>::type;

    static ModFlag operator|(const ModFlag& left, const ModFlag& right)
    {
        return static_cast<ModFlag>(static_cast<underlying>(left) | static_cast<underlying>(right));
    }

    static ModFlag operator&(const ModFlag& left, const ModFlag& right)
    {
        return static_cast<ModFlag>(static_cast<underlying>(left) & static_cast<underlying>(right));
    }

    static std::map<int, ModFlag> modMap {
        {KMOD_NONE, ModFlag::MOD_NONE},
        {KMOD_LSHIFT, ModFlag::MOD_LEFT_SHIFT},
        {KMOD_RSHIFT, ModFlag::MOD_RIGHT_SHIFT},
        {KMOD_LCTRL, ModFlag::MOD_LEFT_CTRL},
        {KMOD_RCTRL, ModFlag::MOD_RIGHT_CTRL},
        {KMOD_LALT, ModFlag::MOD_LEFT_ALT},
        {KMOD_RALT, ModFlag::MOD_RIGHT_ALT},
        {KMOD_LGUI, ModFlag::MOD_LEFT_SUPER},
        {KMOD_RGUI, ModFlag::MOD_RIGHT_SUPER},
        {KMOD_NUM, ModFlag::MOD_NUM},
        {KMOD_CAPS, ModFlag::MOD_CAPS}};

    static std::map<int, KeyCode> keyMap {
        {SDL_SCANCODE_A, KeyCode::KEY_A},
        {SDL_SCANCODE_B, KeyCode::KEY_B},
        {SDL_SCANCODE_C, KeyCode::KEY_C},
        {SDL_SCANCODE_D, KeyCode::KEY_D},
        {SDL_SCANCODE_E, KeyCode::KEY_E},
        {SDL_SCANCODE_F, KeyCode::KEY_F},
        {SDL_SCANCODE_G, KeyCode::KEY_G},
        {SDL_SCANCODE_H, KeyCode::KEY_H},
        {SDL_SCANCODE_I, KeyCode::KEY_I},
        {SDL_SCANCODE_J, KeyCode::KEY_J},
        {SDL_SCANCODE_K, KeyCode::KEY_K},
        {SDL_SCANCODE_L, KeyCode::KEY_L},
        {SDL_SCANCODE_M, KeyCode::KEY_M},
        {SDL_SCANCODE_N, KeyCode::KEY_N},
        {SDL_SCANCODE_O, KeyCode::KEY_O},
        {SDL_SCANCODE_P, KeyCode::KEY_P},
        {SDL_SCANCODE_Q, KeyCode::KEY_Q},
        {SDL_SCANCODE_R, KeyCode::KEY_R},
        {SDL_SCANCODE_S, KeyCode::KEY_S},
        {SDL_SCANCODE_T, KeyCode::KEY_T},
        {SDL_SCANCODE_U, KeyCode::KEY_U},
        {SDL_SCANCODE_V, KeyCode::KEY_V},
        {SDL_SCANCODE_W, KeyCode::KEY_W},
        {SDL_SCANCODE_X, KeyCode::KEY_X},
        {SDL_SCANCODE_Y, KeyCode::KEY_Y},
        {SDL_SCANCODE_Z, KeyCode::KEY_Z},
        {SDL_SCANCODE_1, KeyCode::KEY_1},
        {SDL_SCANCODE_2, KeyCode::KEY_2},
        {SDL_SCANCODE_3, KeyCode::KEY_3},
        {SDL_SCANCODE_4, KeyCode::KEY_4},
        {SDL_SCANCODE_5, KeyCode::KEY_5},
        {SDL_SCANCODE_6, KeyCode::KEY_6},
        {SDL_SCANCODE_7, KeyCode::KEY_7},
        {SDL_SCANCODE_8, KeyCode::KEY_7},
        {SDL_SCANCODE_9, KeyCode::KEY_8},
        {SDL_SCANCODE_0, KeyCode::KEY_9},
        {SDL_SCANCODE_RETURN, KeyCode::KEY_RETURN},
        {SDL_SCANCODE_ESCAPE, KeyCode::KEY_ESCAPE},
        {SDL_SCANCODE_BACKSPACE, KeyCode::KEY_BACKSPACE},
        {SDL_SCANCODE_TAB, KeyCode::KEY_TAB},
        {SDL_SCANCODE_SPACE, KeyCode::KEY_SPACE},
        {SDL_SCANCODE_MINUS, KeyCode::KEY_MINUS},
        {SDL_SCANCODE_EQUALS, KeyCode::KEY_EQUALS},
        {SDL_SCANCODE_LEFTBRACKET, KeyCode::KEY_LEFT_BRACKET},
        {SDL_SCANCODE_RIGHTBRACKET, KeyCode::KEY_RIGHT_BRACKET},
        {SDL_SCANCODE_BACKSLASH, KeyCode::KEY_BACKSLASH},
        {SDL_SCANCODE_SEMICOLON, KeyCode::KEY_SEMICOLON},
        {SDL_SCANCODE_APOSTROPHE, KeyCode::KEY_APOSTROPHE},
        {SDL_SCANCODE_GRAVE, KeyCode::KEY_GRAVE},
        {SDL_SCANCODE_COMMA, KeyCode::KEY_COMMA},
        {SDL_SCANCODE_PERIOD, KeyCode::KEY_PERIOD},
        {SDL_SCANCODE_SLASH, KeyCode::KEY_SLASH},
        {SDL_SCANCODE_CAPSLOCK, KeyCode::KEY_CAPS_LOCK},
        {SDL_SCANCODE_F1, KeyCode::KEY_F1},
        {SDL_SCANCODE_F2, KeyCode::KEY_F2},
        {SDL_SCANCODE_F3, KeyCode::KEY_F3},
        {SDL_SCANCODE_F4, KeyCode::KEY_F4},
        {SDL_SCANCODE_F5, KeyCode::KEY_F5},
        {SDL_SCANCODE_F6, KeyCode::KEY_F6},
        {SDL_SCANCODE_F7, KeyCode::KEY_F7},
        {SDL_SCANCODE_F8, KeyCode::KEY_F8},
        {SDL_SCANCODE_F9, KeyCode::KEY_F9},
        {SDL_SCANCODE_F10, KeyCode::KEY_F10},
        {SDL_SCANCODE_F11, KeyCode::KEY_F11},
        {SDL_SCANCODE_F12, KeyCode::KEY_F12},
        {SDL_SCANCODE_PRINTSCREEN, KeyCode::KEY_PRINT_SCREEN},
        {SDL_SCANCODE_SCROLLLOCK, KeyCode::KEY_SCROLL_LOCK},
        {SDL_SCANCODE_PAUSE, KeyCode::KEY_PAUSE},
        {SDL_SCANCODE_INSERT, KeyCode::KEY_INSERT},
        {SDL_SCANCODE_HOME, KeyCode::KEY_HOME},
        {SDL_SCANCODE_PAGEUP, KeyCode::KEY_PAGE_UP},
        {SDL_SCANCODE_DELETE, KeyCode::KEY_DELETE},
        {SDL_SCANCODE_END, KeyCode::KEY_END},
        {SDL_SCANCODE_PAGEDOWN, KeyCode::KEY_PAGE_DOWN},
        {SDL_SCANCODE_RIGHT, KeyCode::KEY_RIGHT},
        {SDL_SCANCODE_LEFT, KeyCode::KEY_LEFT},
        {SDL_SCANCODE_DOWN, KeyCode::KEY_DOWN},
        {SDL_SCANCODE_UP, KeyCode::KEY_UP},
        {SDL_SCANCODE_NUMLOCKCLEAR, KeyCode::KEY_NUMLOCK_CLEAR},
        {SDL_SCANCODE_KP_DIVIDE, KeyCode::KEY_KP_DIVIDE},
        {SDL_SCANCODE_KP_MULTIPLY, KeyCode::KEY_KP_MULTIPLY},
        {SDL_SCANCODE_KP_MINUS, KeyCode::KEY_KP_MINUS},
        {SDL_SCANCODE_KP_PLUS, KeyCode::KEY_KP_PLUS},
        {SDL_SCANCODE_KP_ENTER, KeyCode::KEY_KP_ENTER},
        {SDL_SCANCODE_KP_1, KeyCode::KEY_KP_1},
        {SDL_SCANCODE_KP_2, KeyCode::KEY_KP_2},
        {SDL_SCANCODE_KP_3, KeyCode::KEY_KP_3},
        {SDL_SCANCODE_KP_4, KeyCode::KEY_KP_4},
        {SDL_SCANCODE_KP_5, KeyCode::KEY_KP_5},
        {SDL_SCANCODE_KP_6, KeyCode::KEY_KP_6},
        {SDL_SCANCODE_KP_7, KeyCode::KEY_KP_7},
        {SDL_SCANCODE_KP_8, KeyCode::KEY_KP_8},
        {SDL_SCANCODE_KP_9, KeyCode::KEY_KP_9},
        {SDL_SCANCODE_KP_0, KeyCode::KEY_KP_0},
        {SDL_SCANCODE_KP_PERIOD, KeyCode::KEY_KP_PERIOD},
        {SDL_SCANCODE_NONUSBACKSLASH, KeyCode::KEY_NON_US_BACKSLASH},
        {SDL_SCANCODE_LCTRL, KeyCode::KEY_LEFT_CTRL},
        {SDL_SCANCODE_LSHIFT, KeyCode::KEY_LEFT_SHIFT},
        {SDL_SCANCODE_LALT, KeyCode::KEY_LEFT_ALT},
        {SDL_SCANCODE_LGUI, KeyCode::KEY_LEFT_SUPER},
        {SDL_SCANCODE_RCTRL, KeyCode::KEY_RIGHT_CTRL},
        {SDL_SCANCODE_RSHIFT, KeyCode::KEY_RIGHT_SHIFT},
        {SDL_SCANCODE_RALT, KeyCode::KEY_RIGHT_ALT},
        {SDL_SCANCODE_RGUI, KeyCode::KEY_RIGHT_SUPER}
    };

    std::vector<ModFlag> processModifiers(int value)
    {
        std::vector<ModFlag> keys;
        for (auto const& key : modMap) {
            if (value & key.first) {
                keys.push_back(key.second);
            }
        }
        return keys;
    }

}
<|endoftext|>


<commit_before>/*
  Copyright (c) 2011 Arduino.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "UARTClass.h"

// Constructors ////////////////////////////////////////////////////////////////

UARTClass::UARTClass( Uart *pUart, IRQn_Type dwIrq, uint32_t dwId, RingBuffer *pRx_buffer, RingBuffer *pTx_buffer )
{
  _rx_buffer = pRx_buffer ;
  _tx_buffer = pTx_buffer;

  _pUart=pUart ;
  _dwIrq=dwIrq ;
  _dwId=dwId ;
}

// Public Methods //////////////////////////////////////////////////////////////

void UARTClass::begin( const uint32_t dwBaudRate )
{
	begin( dwBaudRate, UART_MR_PAR_NO | UART_MR_CHMODE_NORMAL );
}

void UARTClass::begin( const uint32_t dwBaudRate, const uint32_t config )
{
  // Configure PMC
  pmc_enable_periph_clk( _dwId ) ;

  // Disable PDC channel
  _pUart->UART_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS ;

  // Reset and disable receiver and transmitter
  _pUart->UART_CR = UART_CR_RSTRX | UART_CR_RSTTX | UART_CR_RXDIS | UART_CR_TXDIS ;

  // Configure mode
  _pUart->UART_MR = config ;

  // Configure baudrate (asynchronous, no oversampling)
  _pUart->UART_BRGR = (SystemCoreClock / dwBaudRate) >> 4 ;

  // Configure interrupts
  _pUart->UART_IDR = 0xFFFFFFFF;
  _pUart->UART_IER = UART_IER_RXRDY | UART_IER_OVRE | UART_IER_FRAME;

  // Enable UART interrupt in NVIC
  NVIC_EnableIRQ(_dwIrq);

  //make sure both ring buffers are initialized back to empty.
  _rx_buffer->_iHead = _rx_buffer->_iTail = 0;
  _tx_buffer->_iHead = _tx_buffer->_iTail = 0;

  // Enable receiver and transmitter
  _pUart->UART_CR = UART_CR_RXEN | UART_CR_TXEN ;
}

void UARTClass::end( void )
{
  // clear any received data
  _rx_buffer->_iHead = _rx_buffer->_iTail ;

  while (_tx_buffer->_iHead != _tx_buffer->_iTail); //wait for transmit data to be sent

  // Disable UART interrupt in NVIC
  NVIC_DisableIRQ( _dwIrq ) ;

  // Wait for any outstanding data to be sent
  flush();

  pmc_disable_periph_clk( _dwId ) ;
}

void UARTClass::setInterruptPriority(uint32_t priority)
{
	NVIC_SetPriority(_dwIrq, priority & 0x0F);
}

uint32_t UARTClass::getInterruptPriority()
{
	return NVIC_GetPriority(_dwIrq);
}

int UARTClass::available( void )
{
  return (uint32_t)(SERIAL_BUFFER_SIZE + _rx_buffer->_iHead - _rx_buffer->_iTail) % SERIAL_BUFFER_SIZE ;
}

int UARTClass::availableForWrite(void)
{
  int head = _tx_buffer->_iHead;
  int tail = _tx_buffer->_iTail;
  if (head >= tail) return SERIAL_BUFFER_SIZE - 1 - head + tail;
  return tail - head - 1;
}

int UARTClass::peek( void )
{
  if ( _rx_buffer->_iHead == _rx_buffer->_iTail )
    return -1 ;

  return _rx_buffer->_aucBuffer[_rx_buffer->_iTail] ;
}

int UARTClass::read( void )
{
  // if the head isn't ahead of the tail, we don't have any characters
  if ( _rx_buffer->_iHead == _rx_buffer->_iTail )
    return -1 ;

  uint8_t uc = _rx_buffer->_aucBuffer[_rx_buffer->_iTail] ;
  _rx_buffer->_iTail = (unsigned int)(_rx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE ;
  return uc ;
}

void UARTClass::flush( void )
{
  // Wait for transmission to complete
  while ((_pUart->UART_SR & UART_SR_TXRDY) != UART_SR_TXRDY)
    ;
}

size_t UARTClass::write( const uint8_t uc_data )
{
  if (((_pUart->UART_SR & UART_SR_TXRDY) != UART_SR_TXRDY) | (_tx_buffer->_iTail != _tx_buffer->_iHead)) //is the hardware currently busy?
  {
	  //if busy we buffer
	  unsigned int l = (_tx_buffer->_iHead + 1) % SERIAL_BUFFER_SIZE;
	  while (_tx_buffer->_iTail == l); //spin locks if we're about to overwrite the buffer. This continues once the data is sent

	  _tx_buffer->_aucBuffer[_tx_buffer->_iHead] = uc_data;
	  _tx_buffer->_iHead = l;
	  _pUart->UART_IER = UART_IER_TXRDY; //make sure TX interrupt is enabled
  }
  else
  {
     // Send character
     _pUart->UART_THR = uc_data ;
  }
  return 1;
}

void UARTClass::IrqHandler( void )
{
  uint32_t status = _pUart->UART_SR;

  // Did we receive data ?
  if ((status & UART_SR_RXRDY) == UART_SR_RXRDY)
    _rx_buffer->store_char(_pUart->UART_RHR);

  //Do we need to keep sending data?
  if ((status & UART_SR_TXRDY) == UART_SR_TXRDY)
  {
	  if (_tx_buffer->_iTail != _tx_buffer->_iHead) { //just in case
		_pUart->UART_THR = _tx_buffer->_aucBuffer[_tx_buffer->_iTail];
		_tx_buffer->_iTail = (unsigned int)(_tx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE;
	  }
	  else
	  {
		_pUart->UART_IDR = UART_IDR_TXRDY; //mask off transmit interrupt so we don't get it anymore
	  }
  }

  // Acknowledge errors
  if ((status & UART_SR_OVRE) == UART_SR_OVRE ||
		  (status & UART_SR_FRAME) == UART_SR_FRAME)
  {
	// TODO: error reporting outside ISR
    _pUart->UART_CR |= UART_CR_RSTSTA;
  }
}

<commit_msg>Fixed flush so that it actually is sure to flush all outstanding data.<commit_after>/*
  Copyright (c) 2011 Arduino.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "UARTClass.h"

// Constructors ////////////////////////////////////////////////////////////////

UARTClass::UARTClass( Uart *pUart, IRQn_Type dwIrq, uint32_t dwId, RingBuffer *pRx_buffer, RingBuffer *pTx_buffer )
{
  _rx_buffer = pRx_buffer ;
  _tx_buffer = pTx_buffer;

  _pUart=pUart ;
  _dwIrq=dwIrq ;
  _dwId=dwId ;
}

// Public Methods //////////////////////////////////////////////////////////////

void UARTClass::begin( const uint32_t dwBaudRate )
{
	begin( dwBaudRate, UART_MR_PAR_NO | UART_MR_CHMODE_NORMAL );
}

void UARTClass::begin( const uint32_t dwBaudRate, const uint32_t config )
{
  // Configure PMC
  pmc_enable_periph_clk( _dwId ) ;

  // Disable PDC channel
  _pUart->UART_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS ;

  // Reset and disable receiver and transmitter
  _pUart->UART_CR = UART_CR_RSTRX | UART_CR_RSTTX | UART_CR_RXDIS | UART_CR_TXDIS ;

  // Configure mode
  _pUart->UART_MR = config ;

  // Configure baudrate (asynchronous, no oversampling)
  _pUart->UART_BRGR = (SystemCoreClock / dwBaudRate) >> 4 ;

  // Configure interrupts
  _pUart->UART_IDR = 0xFFFFFFFF;
  _pUart->UART_IER = UART_IER_RXRDY | UART_IER_OVRE | UART_IER_FRAME;

  // Enable UART interrupt in NVIC
  NVIC_EnableIRQ(_dwIrq);

  //make sure both ring buffers are initialized back to empty.
  _rx_buffer->_iHead = _rx_buffer->_iTail = 0;
  _tx_buffer->_iHead = _tx_buffer->_iTail = 0;

  // Enable receiver and transmitter
  _pUart->UART_CR = UART_CR_RXEN | UART_CR_TXEN ;
}

void UARTClass::end( void )
{
  // clear any received data
  _rx_buffer->_iHead = _rx_buffer->_iTail ;

  // Wait for any outstanding data to be sent
  flush();

  // Disable UART interrupt in NVIC
  NVIC_DisableIRQ( _dwIrq ) ;

  pmc_disable_periph_clk( _dwId ) ;
}

void UARTClass::setInterruptPriority(uint32_t priority)
{
	NVIC_SetPriority(_dwIrq, priority & 0x0F);
}

uint32_t UARTClass::getInterruptPriority()
{
	return NVIC_GetPriority(_dwIrq);
}

int UARTClass::available( void )
{
  return (uint32_t)(SERIAL_BUFFER_SIZE + _rx_buffer->_iHead - _rx_buffer->_iTail) % SERIAL_BUFFER_SIZE ;
}

int UARTClass::availableForWrite(void)
{
  int head = _tx_buffer->_iHead;
  int tail = _tx_buffer->_iTail;
  if (head >= tail) return SERIAL_BUFFER_SIZE - 1 - head + tail;
  return tail - head - 1;
}

int UARTClass::peek( void )
{
  if ( _rx_buffer->_iHead == _rx_buffer->_iTail )
    return -1 ;

  return _rx_buffer->_aucBuffer[_rx_buffer->_iTail] ;
}

int UARTClass::read( void )
{
  // if the head isn't ahead of the tail, we don't have any characters
  if ( _rx_buffer->_iHead == _rx_buffer->_iTail )
    return -1 ;

  uint8_t uc = _rx_buffer->_aucBuffer[_rx_buffer->_iTail] ;
  _rx_buffer->_iTail = (unsigned int)(_rx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE ;
  return uc ;
}

void UARTClass::flush( void )
{
  while (_tx_buffer->_iHead != _tx_buffer->_iTail); //wait for transmit data to be sent
  // Wait for transmission to complete
  while ((_pUart->UART_SR & UART_SR_TXRDY) != UART_SR_TXRDY)
    ;
}

size_t UARTClass::write( const uint8_t uc_data )
{
  if (((_pUart->UART_SR & UART_SR_TXRDY) != UART_SR_TXRDY) | (_tx_buffer->_iTail != _tx_buffer->_iHead)) //is the hardware currently busy?
  {
	  //if busy we buffer
	  unsigned int l = (_tx_buffer->_iHead + 1) % SERIAL_BUFFER_SIZE;
	  while (_tx_buffer->_iTail == l); //spin locks if we're about to overwrite the buffer. This continues once the data is sent

	  _tx_buffer->_aucBuffer[_tx_buffer->_iHead] = uc_data;
	  _tx_buffer->_iHead = l;
	  _pUart->UART_IER = UART_IER_TXRDY; //make sure TX interrupt is enabled
  }
  else
  {
     // Send character
     _pUart->UART_THR = uc_data ;
  }
  return 1;
}

void UARTClass::IrqHandler( void )
{
  uint32_t status = _pUart->UART_SR;

  // Did we receive data ?
  if ((status & UART_SR_RXRDY) == UART_SR_RXRDY)
    _rx_buffer->store_char(_pUart->UART_RHR);

  //Do we need to keep sending data?
  if ((status & UART_SR_TXRDY) == UART_SR_TXRDY)
  {
	  if (_tx_buffer->_iTail != _tx_buffer->_iHead) { //just in case
		_pUart->UART_THR = _tx_buffer->_aucBuffer[_tx_buffer->_iTail];
		_tx_buffer->_iTail = (unsigned int)(_tx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE;
	  }
	  else
	  {
		_pUart->UART_IDR = UART_IDR_TXRDY; //mask off transmit interrupt so we don't get it anymore
	  }
  }

  // Acknowledge errors
  if ((status & UART_SR_OVRE) == UART_SR_OVRE ||
		  (status & UART_SR_FRAME) == UART_SR_FRAME)
  {
	// TODO: error reporting outside ISR
    _pUart->UART_CR |= UART_CR_RSTSTA;
  }
}

<|endoftext|>


<commit_before>// Copyright 2013 Google Inc. 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.

#include "line_printer.h"

#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
#endif
#else
#include <unistd.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <sys/time.h>
#endif

#include "util.h"

LinePrinter::LinePrinter() : have_blank_line_(true), console_locked_(false) {
#ifndef _WIN32
  const char* term = getenv("TERM");
  smart_terminal_ = isatty(1) && term && string(term) != "dumb";
#else
  // Disable output buffer.  It'd be nice to use line buffering but
  // MSDN says: "For some systems, [_IOLBF] provides line
  // buffering. However, for Win32, the behavior is the same as _IOFBF
  // - Full Buffering."
  setvbuf(stdout, NULL, _IONBF, 0);
  console_ = GetStdHandle(STD_OUTPUT_HANDLE);
  CONSOLE_SCREEN_BUFFER_INFO csbi;
  smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);
#endif
  supports_color_ = smart_terminal_;
  if (!supports_color_) {
    const char* clicolor_force = getenv("CLICOLOR_FORCE");
    supports_color_ = clicolor_force && string(clicolor_force) != "0";
  }
#ifdef _WIN32
  // Try enabling ANSI escape sequence support on Windows 10 terminals.
  if (supports_color_) {
    DWORD mode;
    if (GetConsoleMode(console_, &mode)) {
      SetConsoleMode(console_, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
    }
  }
#endif
}

void LinePrinter::Print(string to_print, LineType type) {
  if (console_locked_) {
    line_buffer_ = to_print;
    line_type_ = type;
    return;
  }

  if (smart_terminal_) {
    printf("\r");  // Print over previous line, if any.
    // On Windows, calling a C library function writing to stdout also handles
    // pausing the executable when the "Pause" key or Ctrl-S is pressed.
  }

  if (smart_terminal_ && type == ELIDE) {
#ifdef _WIN32
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo(console_, &csbi);

    to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X));
    // We don't want to have the cursor spamming back and forth, so instead of
    // printf use WriteConsoleOutput which updates the contents of the buffer,
    // but doesn't move the cursor position.
    COORD buf_size = { csbi.dwSize.X, 1 };
    COORD zero_zero = { 0, 0 };
    SMALL_RECT target = {
      csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y,
      static_cast<SHORT>(csbi.dwCursorPosition.X + csbi.dwSize.X - 1),
      csbi.dwCursorPosition.Y
    };
    vector<CHAR_INFO> char_data(csbi.dwSize.X);
    for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) {
      char_data[i].Char.AsciiChar = i < to_print.size() ? to_print[i] : ' ';
      char_data[i].Attributes = csbi.wAttributes;
    }
    WriteConsoleOutput(console_, &char_data[0], buf_size, zero_zero, &target);
#else
    // Limit output to width of the terminal if provided so we don't cause
    // line-wrapping.
    winsize size;
    if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == 0) && size.ws_col) {
      to_print = ElideMiddle(to_print, size.ws_col);
    }
    printf("%s", to_print.c_str());
    printf("\x1B[K");  // Clear to end of line.
    fflush(stdout);
#endif

    have_blank_line_ = false;
  } else {
    printf("%s\n", to_print.c_str());
  }
}

void LinePrinter::PrintOrBuffer(const char* data, size_t size) {
  if (console_locked_) {
    output_buffer_.append(data, size);
  } else {
    // Avoid printf and C strings, since the actual output might contain null
    // bytes like UTF-16 does (yuck).
    fwrite(data, 1, size, stdout);
  }
}

void LinePrinter::PrintOnNewLine(const string& to_print) {
  if (console_locked_ && !line_buffer_.empty()) {
    output_buffer_.append(line_buffer_);
    output_buffer_.append(1, '\n');
    line_buffer_.clear();
  }
  if (!have_blank_line_) {
    PrintOrBuffer("\n", 1);
  }
  if (!to_print.empty()) {
    PrintOrBuffer(&to_print[0], to_print.size());
  }
  have_blank_line_ = to_print.empty() || *to_print.rbegin() == '\n';
}

void LinePrinter::SetConsoleLocked(bool locked) {
  if (locked == console_locked_)
    return;

  if (locked)
    PrintOnNewLine("");

  console_locked_ = locked;

  if (!locked) {
    PrintOnNewLine(output_buffer_);
    if (!line_buffer_.empty()) {
      Print(line_buffer_, line_type_);
    }
    output_buffer_.clear();
    line_buffer_.clear();
  }
}
<commit_msg>Unset suports_color_ if SetConsoleMode fails on WIN32<commit_after>// Copyright 2013 Google Inc. 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.

#include "line_printer.h"

#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
#endif
#else
#include <unistd.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <sys/time.h>
#endif

#include "util.h"

LinePrinter::LinePrinter() : have_blank_line_(true), console_locked_(false) {
#ifndef _WIN32
  const char* term = getenv("TERM");
  smart_terminal_ = isatty(1) && term && string(term) != "dumb";
#else
  // Disable output buffer.  It'd be nice to use line buffering but
  // MSDN says: "For some systems, [_IOLBF] provides line
  // buffering. However, for Win32, the behavior is the same as _IOFBF
  // - Full Buffering."
  setvbuf(stdout, NULL, _IONBF, 0);
  console_ = GetStdHandle(STD_OUTPUT_HANDLE);
  CONSOLE_SCREEN_BUFFER_INFO csbi;
  smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);
#endif
  supports_color_ = smart_terminal_;
  if (!supports_color_) {
    const char* clicolor_force = getenv("CLICOLOR_FORCE");
    supports_color_ = clicolor_force && string(clicolor_force) != "0";
  }
#ifdef _WIN32
  // Try enabling ANSI escape sequence support on Windows 10 terminals.
  if (supports_color_) {
    DWORD mode;
    if (GetConsoleMode(console_, &mode)) {
      if (!SetConsoleMode(console_, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) {
        supports_color_ = false;
      }
    }
  }
#endif
}

void LinePrinter::Print(string to_print, LineType type) {
  if (console_locked_) {
    line_buffer_ = to_print;
    line_type_ = type;
    return;
  }

  if (smart_terminal_) {
    printf("\r");  // Print over previous line, if any.
    // On Windows, calling a C library function writing to stdout also handles
    // pausing the executable when the "Pause" key or Ctrl-S is pressed.
  }

  if (smart_terminal_ && type == ELIDE) {
#ifdef _WIN32
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo(console_, &csbi);

    to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X));
    // We don't want to have the cursor spamming back and forth, so instead of
    // printf use WriteConsoleOutput which updates the contents of the buffer,
    // but doesn't move the cursor position.
    COORD buf_size = { csbi.dwSize.X, 1 };
    COORD zero_zero = { 0, 0 };
    SMALL_RECT target = {
      csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y,
      static_cast<SHORT>(csbi.dwCursorPosition.X + csbi.dwSize.X - 1),
      csbi.dwCursorPosition.Y
    };
    vector<CHAR_INFO> char_data(csbi.dwSize.X);
    for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) {
      char_data[i].Char.AsciiChar = i < to_print.size() ? to_print[i] : ' ';
      char_data[i].Attributes = csbi.wAttributes;
    }
    WriteConsoleOutput(console_, &char_data[0], buf_size, zero_zero, &target);
#else
    // Limit output to width of the terminal if provided so we don't cause
    // line-wrapping.
    winsize size;
    if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == 0) && size.ws_col) {
      to_print = ElideMiddle(to_print, size.ws_col);
    }
    printf("%s", to_print.c_str());
    printf("\x1B[K");  // Clear to end of line.
    fflush(stdout);
#endif

    have_blank_line_ = false;
  } else {
    printf("%s\n", to_print.c_str());
  }
}

void LinePrinter::PrintOrBuffer(const char* data, size_t size) {
  if (console_locked_) {
    output_buffer_.append(data, size);
  } else {
    // Avoid printf and C strings, since the actual output might contain null
    // bytes like UTF-16 does (yuck).
    fwrite(data, 1, size, stdout);
  }
}

void LinePrinter::PrintOnNewLine(const string& to_print) {
  if (console_locked_ && !line_buffer_.empty()) {
    output_buffer_.append(line_buffer_);
    output_buffer_.append(1, '\n');
    line_buffer_.clear();
  }
  if (!have_blank_line_) {
    PrintOrBuffer("\n", 1);
  }
  if (!to_print.empty()) {
    PrintOrBuffer(&to_print[0], to_print.size());
  }
  have_blank_line_ = to_print.empty() || *to_print.rbegin() == '\n';
}

void LinePrinter::SetConsoleLocked(bool locked) {
  if (locked == console_locked_)
    return;

  if (locked)
    PrintOnNewLine("");

  console_locked_ = locked;

  if (!locked) {
    PrintOnNewLine(output_buffer_);
    if (!line_buffer_.empty()) {
      Print(line_buffer_, line_type_);
    }
    output_buffer_.clear();
    line_buffer_.clear();
  }
}
<|endoftext|>