<commit_before>//
//  Var.cpp
//  PowerTools++
//
//  Created by Hassan on 16/12/2014.
//  Copyright (c) 2014 NICTA. All rights reserved.
//

#include <stdio.h>
#include <string>
#include <cstring>
#include <iomanip>
#include <iostream>
#include "PowerTools++/var.h"
#include "PowerTools++/Constraint.h"
using namespace std;

template<typename Number> var<Number>::var():var(NULL, -1){};
template<typename Number> var<Number>::var(string name):var(NULL, name){};
template<typename Number> var<Number>::var(int idx):var(NULL, idx){};
template<typename Number> var<Number>::var(Model* model, string name):var(model, name, -1){};
template<typename Number> var<Number>::var(Model* model, int idx):var(model, "noname", idx){};
template<typename Number> var<Number>::var(string name, int idx):var(NULL, name, idx){};
template<typename Number> var<Number>::var(Model* model, string name, int idx):var(model, name, idx, -numeric_limits<Number>::infinity(), numeric_limits<Number>::infinity()){};
//@}

/** Constructor with lower Bound only */
template<typename Number> var<Number>::var(Model* model, string name, int idx, Number lb):var(NULL, name, idx, lb, numeric_limits<Number>::infinity()){};

//@{
/** Bounded variable constructor */
template<typename Number> var<Number>::var(string name, Number lb, Number ub):var(NULL, name, -1, lb, ub){};
template<typename Number> var<Number>::var(Model* model, string name, int idx, Number lb, Number ub):var(model,name,idx,lb,ub,lb,ub){};
template<typename Number> var<Number>::var(Model* model, string name, int idx, Number lb, Number ub, Number lb_off, Number ub_off){
    _name = name;
    _model = model;
    _idx = idx;
    if(typeid(Number)==typeid(bool)){
        _type = binary;
        _lb = false;
        _ub = true;
        // WARNING setting bounds of a binary variable!
    }
    else{
        if(typeid(Number)==typeid(int))
            _type = integ;
        else{
           if(typeid(Number)==typeid(float))
               _type = real;
            else
               _type = longreal;
        }
    }
    _bounded_down = true;
    _bounded_up = true;
    if (lb==-numeric_limits<Number>::infinity()) {
        _bounded_down = false;
    }
    else
    if (ub==numeric_limits<Number>::infinity()) {
        _bounded_up = false;
    }
    _lb = lb;
    _ub = ub;
    _lb_off = lb_off;
    _ub_off = ub_off;
    _val = 0.0;
};
//@}

/* Destructor */
template<typename Number> var<Number>::~var(){};

template<typename Number>var<Number>& var<Number>::operator=(Number cst) {
    _val = cst;
    return *this;
};

/* Accessors */
template<typename Number>Number var<Number>::get_lb() const{
    return _lb;
};
template<typename Number>Number var<Number>::get_ub() const{
    return _ub;
};
template<typename Number>Number var<Number>::get_lb_off() const{
    return _lb_off;
};
template<typename Number>Number var<Number>::get_ub_off() const{
    return _ub_off;
};

template<typename Number>Number var<Number>::get_value() const{
    return _val;
};

/* Modifiers */

template<typename Number>void var<Number>::init(string name, Number lb, Number ub){
    init(name, lb, ub, lb, ub);
};

template<typename Number>void var<Number>::init(string name, Number lb, Number ub, Number lb_off, Number ub_off){
    init(name);
    _bounded_down = true;
    _bounded_up = true;
    if (lb==-numeric_limits<Number>::infinity()) {
        _bounded_down = false;
    }
    else
        if (ub==numeric_limits<Number>::infinity()) {
            _bounded_up = false;
        }
    _lb = lb;
    _ub = ub;
    _lb_off = lb_off;
    _ub_off = ub_off;
}

template<typename Number>void var<Number>::init(string name){
    _name = name;
    _model = NULL;
    _cstrs.clear();
    _hess.clear();
    _idx = -1;
    if(typeid(Number)==typeid(bool)){
        _type = binary;
        _lb = false;
        _ub = true;
        _lb_off = false;
        _ub_off = true;
        _bounded_down = true;
        _bounded_up = true;
        _val = false;
    }
    else{
        if(typeid(Number)==typeid(int))
            _type = integ;
        else{
            if(typeid(Number)==typeid(float))
                _type = real;
            else
                _type = longreal;
        }
        _bounded_down = false;
        _bounded_up = false;
        _lb=-numeric_limits<Number>::infinity();
        _ub=numeric_limits<Number>::infinity();
        _lb_off=-numeric_limits<Number>::infinity();
        _ub_off=numeric_limits<Number>::infinity();
        _val = 0.0;
    }

};

template<typename Number>void var<Number>::set_lb(Number lb){
    _lb = lb;
};
template<typename Number>void var<Number>::set_ub(Number ub){
    _ub = ub;
};
template<typename Number>void var<Number>::set_val(Number val){
    _val = val;
};

/* Output */
template<typename Number> void var<Number>::print () const{
    switch (_type) {
        case integ:
            cout << "Int ";
            break;
        case binary:
            cout << "Binary ";
            break;

        default:
            cout << "Real ";
            break;
    }
    cout << _name;
    if (!_bounded_down && !_bounded_down) {
        cout << " (unbounded) = " << std::fixed << std::setprecision(6) << _val;
//        cout << "in constraints: ";
//        for (auto it:_cstrs) {
//            it.second->print();
//        }
        return;
    }
    cout << " in ";
    switch (_type) {
        case integ:
            cout << "{" << get_lb() << "," << get_ub() << "}";
            break;
        case binary:
            cout << "{" << (get_lb() ? 1 : 0) << "," << (get_ub() ? 1 : 0)  << "}";
            break;
        default:
            cout << "[" << std::fixed << std::setprecision(10) << get_lb()
                << "," << std::fixed << std::setprecision(10) << get_ub() << "]";
            break;
    }
    cout << " = " << std::fixed << std::setprecision(10) << _val;

    cout << ";" << endl;
//    cout << "in constraints: ";
//    for (auto it:_cstrs) {
//        it.second->print();
//    }
};

template class var<bool>;
template class var<int>;
template class var<float>;
template class var<>;
<commit_msg>Consistent printing of vars.<commit_after>//
//  Var.cpp
//  PowerTools++
//
//  Created by Hassan on 16/12/2014.
//  Copyright (c) 2014 NICTA. All rights reserved.
//

#include <stdio.h>
#include <string>
#include <cstring>
#include <iomanip>
#include <iostream>
#include "PowerTools++/var.h"
#include "PowerTools++/Constraint.h"
using namespace std;

template<typename Number> var<Number>::var():var(NULL, -1){};
template<typename Number> var<Number>::var(string name):var(NULL, name){};
template<typename Number> var<Number>::var(int idx):var(NULL, idx){};
template<typename Number> var<Number>::var(Model* model, string name):var(model, name, -1){};
template<typename Number> var<Number>::var(Model* model, int idx):var(model, "noname", idx){};
template<typename Number> var<Number>::var(string name, int idx):var(NULL, name, idx){};
template<typename Number> var<Number>::var(Model* model, string name, int idx):var(model, name, idx, -numeric_limits<Number>::infinity(), numeric_limits<Number>::infinity()){};
//@}

/** Constructor with lower Bound only */
template<typename Number> var<Number>::var(Model* model, string name, int idx, Number lb):var(NULL, name, idx, lb, numeric_limits<Number>::infinity()){};

//@{
/** Bounded variable constructor */
template<typename Number> var<Number>::var(string name, Number lb, Number ub):var(NULL, name, -1, lb, ub){};
template<typename Number> var<Number>::var(Model* model, string name, int idx, Number lb, Number ub):var(model,name,idx,lb,ub,lb,ub){};
template<typename Number> var<Number>::var(Model* model, string name, int idx, Number lb, Number ub, Number lb_off, Number ub_off){
    _name = name;
    _model = model;
    _idx = idx;
    if(typeid(Number)==typeid(bool)){
        _type = binary;
        _lb = false;
        _ub = true;
        // WARNING setting bounds of a binary variable!
    }
    else{
        if(typeid(Number)==typeid(int))
            _type = integ;
        else{
           if(typeid(Number)==typeid(float))
               _type = real;
            else
               _type = longreal;
        }
    }
    _bounded_down = true;
    _bounded_up = true;
    if (lb==-numeric_limits<Number>::infinity()) {
        _bounded_down = false;
    }
    else
    if (ub==numeric_limits<Number>::infinity()) {
        _bounded_up = false;
    }
    _lb = lb;
    _ub = ub;
    _lb_off = lb_off;
    _ub_off = ub_off;
    _val = 0.0;
};
//@}

/* Destructor */
template<typename Number> var<Number>::~var(){};

template<typename Number>var<Number>& var<Number>::operator=(Number cst) {
    _val = cst;
    return *this;
};

/* Accessors */
template<typename Number>Number var<Number>::get_lb() const{
    return _lb;
};
template<typename Number>Number var<Number>::get_ub() const{
    return _ub;
};
template<typename Number>Number var<Number>::get_lb_off() const{
    return _lb_off;
};
template<typename Number>Number var<Number>::get_ub_off() const{
    return _ub_off;
};

template<typename Number>Number var<Number>::get_value() const{
    return _val;
};

/* Modifiers */

template<typename Number>void var<Number>::init(string name, Number lb, Number ub){
    init(name, lb, ub, lb, ub);
};

template<typename Number>void var<Number>::init(string name, Number lb, Number ub, Number lb_off, Number ub_off){
    init(name);
    _bounded_down = true;
    _bounded_up = true;
    if (lb==-numeric_limits<Number>::infinity()) {
        _bounded_down = false;
    }
    else
        if (ub==numeric_limits<Number>::infinity()) {
            _bounded_up = false;
        }
    _lb = lb;
    _ub = ub;
    _lb_off = lb_off;
    _ub_off = ub_off;
}

template<typename Number>void var<Number>::init(string name){
    _name = name;
    _model = NULL;
    _cstrs.clear();
    _hess.clear();
    _idx = -1;
    if(typeid(Number)==typeid(bool)){
        _type = binary;
        _lb = false;
        _ub = true;
        _lb_off = false;
        _ub_off = true;
        _bounded_down = true;
        _bounded_up = true;
        _val = false;
    }
    else{
        if(typeid(Number)==typeid(int))
            _type = integ;
        else{
            if(typeid(Number)==typeid(float))
                _type = real;
            else
                _type = longreal;
        }
        _bounded_down = false;
        _bounded_up = false;
        _lb=-numeric_limits<Number>::infinity();
        _ub=numeric_limits<Number>::infinity();
        _lb_off=-numeric_limits<Number>::infinity();
        _ub_off=numeric_limits<Number>::infinity();
        _val = 0.0;
    }

};

template<typename Number>void var<Number>::set_lb(Number lb){
    _lb = lb;
};
template<typename Number>void var<Number>::set_ub(Number ub){
    _ub = ub;
};
template<typename Number>void var<Number>::set_val(Number val){
    _val = val;
};

/* Output */
template<typename Number> void var<Number>::print () const{
    switch (_type) {
        case integ:
            cout << "Int ";
            break;
        case binary:
            cout << "Binary ";
            break;

        default:
            cout << "Real ";
            break;
    }
    cout << _name;
    if (!_bounded_down && !_bounded_down) {
        cout << " (unbounded) = " << std::fixed << std::setprecision(10) << _val << ";" << std::endl;
//        cout << "in constraints: ";
//        for (auto it:_cstrs) {
//            it.second->print();
//        }
        return;
    }
    cout << " in ";
    switch (_type) {
        case integ:
            cout << "{" << get_lb() << "," << get_ub() << "}";
            break;
        case binary:
            cout << "{" << (get_lb() ? 1 : 0) << "," << (get_ub() ? 1 : 0)  << "}";
            break;
        default:
            cout << "[" << std::fixed << std::setprecision(10) << get_lb()
                << "," << std::fixed << std::setprecision(10) << get_ub() << "]";
            break;
    }
    cout << " = " << std::fixed << std::setprecision(10) << _val;

    cout << ";" << endl;
//    cout << "in constraints: ";
//    for (auto it:_cstrs) {
//        it.second->print();
//    }
};

template class var<bool>;
template class var<int>;
template class var<float>;
template class var<>;
<|endoftext|>


<commit_before>#include <ghost.h>
#include <ghost_util.h>
#include <ghost_vec.h>
#include <cmath>
#include <cstdio>
#include <iostream>
#include "ghost_complex.h"
#include <omp.h>

double conjugate(double * c) {
	return *c;
}

float conjugate(float * c) {
	return *c;
}

template <typename T>
ghost_complex<T> conjugate(ghost_complex<T>* c) {
	return ghost_complex<T>(std::real(*c),-std::imag(*c));
}

template <typename v_t> void ghost_normalizeVector_tmpl(ghost_vec_t *vec)
{
	v_t s;
	ghost_vec_dotprod_tmpl<v_t>(vec,vec,&s);
	s = (v_t)sqrt(s);
	s = (v_t)(((v_t)1.)/s);
	vec->scale(vec,&s);

#ifdef GHOST_HAVE_OPENCL
	vec->CLupload(vec);
#endif
#ifdef GHOST_HAVE_CUDA
	vec->CUupload(vec);
#endif
}

template <typename v_t> void ghost_vec_dotprod_tmpl(ghost_vec_t *vec, ghost_vec_t *vec2, void *res)
{ // the parallelization is done manually because reduction does not work with ghost_complex numbers
	if (vec->traits->nrows != vec2->traits->nrows) {
		WARNING_LOG("The input vectors of the dot product have different numbers of rows");
	}
	if (vec->traits->nvecs != vec2->traits->nvecs) {
		WARNING_LOG("The input vectors of the dot product have different numbers of columns");
	}
	ghost_vidx_t i,v;
	ghost_vidx_t nr = MIN(vec->traits->nrows,vec2->traits->nrows);

	int nthreads;
#pragma omp parallel
	nthreads = ghost_ompGetNumThreads();

	for (v=0; v<MIN(vec->traits->nvecs,vec2->traits->nvecs); v++) {
		v_t sum = 0;
		v_t partsums[nthreads];
		for (i=0; i<nthreads; i++) partsums[i] = (v_t)0.;

#pragma omp parallel for
		for (i=0; i<nr; i++) {
			partsums[ghost_ompGetThreadNum()] +=
				*(v_t *)VECVAL(vec2,vec2->val,v,i)*
				conjugate((v_t *)(VECVAL(vec,vec->val,v,i)));
		}

		for (i=0; i<nthreads; i++) sum += partsums[i];

		((v_t *)res)[v] = sum;
	}

}

template <typename v_t> void ghost_vec_vaxpy_tmpl(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale)
{
	ghost_vidx_t i,v;
	v_t *s = (v_t *)scale;
	ghost_vidx_t nr = MIN(vec->traits->nrows,vec2->traits->nrows);

	for (v=0; v<MIN(vec->traits->nvecs,vec2->traits->nvecs); v++) {
#pragma omp parallel for
		for (i=0; i<nr; i++) {
			*(v_t *)VECVAL(vec,vec->val,v,i) += *(v_t *)VECVAL(vec2,vec2->val,v,i) * s[v];
		}
	}
}

template <typename v_t> void ghost_vec_vaxpby_tmpl(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale, void *b_)
{
	ghost_vidx_t i,v;
	v_t *s = (v_t *)scale;
	v_t *b = (v_t *)b_;
	ghost_vidx_t nr = MIN(vec->traits->nrows,vec2->traits->nrows);

	for (v=0; v<MIN(vec->traits->nvecs,vec2->traits->nvecs); v++) {
#pragma omp parallel for
		for (i=0; i<nr; i++) {
			*(v_t *)VECVAL(vec,vec->val,v,i) = *(v_t *)VECVAL(vec2,vec2->val,v,i) * s[v] +
				*(v_t *)VECVAL(vec,vec->val,v,i) * b[v];
		}
	}
}

template<typename v_t> void ghost_vec_vscale_tmpl(ghost_vec_t *vec, void *scale)
{
	ghost_vidx_t i,v;
	v_t *s = (v_t *)scale;

	for (v=0; v<vec->traits->nvecs; v++) {
#pragma omp parallel for
		for (i=0; i<vec->traits->nrows; i++) {
			*(v_t *)VECVAL(vec,vec->val,v,i) *= s[v];
		}
	}
}

template <typename v_t> void ghost_vec_fromRand_tmpl(ghost_vec_t *vec)
{
	vec_malloc(vec);
	DEBUG_LOG(1,"Filling vector with random values");
	size_t sizeofdt = ghost_sizeofDataType(vec->traits->datatype);

	int i,v;

	// TODO fuse loops but preserve randomness

	for (v=0; v<vec->traits->nvecs; v++) {
#pragma omp parallel for schedule(runtime)
		for (i=0; i<vec->traits->nrows; i++) {
			*(v_t *)VECVAL(vec,vec->val,v,i) = (v_t)0;
		}
	}

	for (v=0; v<vec->traits->nvecs; v++) {
		for (i=0; i<vec->traits->nrows; i++) {
			*(v_t *)VECVAL(vec,vec->val,v,i) = (v_t)(rand()*1./RAND_MAX); // TODO imag
		}
	}
	vec->upload(vec);

}

template <typename v_t> void ghost_vec_print_tmpl(ghost_vec_t *vec)
{
	char prefix[16];
#ifdef GHOST_HAVE_MPI
	if (vec->context != NULL && vec->context->mpicomm != MPI_COMM_NULL) {
		int rank = ghost_getRank(vec->context->mpicomm);
		int ndigits = (int)floor(log10(abs(rank))) + 1;
		snprintf(prefix,4+ndigits,"PE%d: ",rank);
	} else {
		snprintf(prefix,1,"\0");
	}
#else
	snprintf(prefix,1,"\0");
#endif

	ghost_vidx_t i,v;

	std::cout.setf(std::ios::fixed, std::ios::floatfield);
	for (i=0; i<vec->traits->nrows; i++) {
		for (v=0; v<vec->traits->nvecs; v++) {
			std::cout << *(v_t *)(VECVAL(vec,vec->val,v,i)) << "\t";
		}
		std::cout << std::endl;
	}
}

extern "C" void d_ghost_printVector(ghost_vec_t *vec)
{ return ghost_vec_print_tmpl< double >(vec); }

extern "C" void s_ghost_printVector(ghost_vec_t *vec)
{ return ghost_vec_print_tmpl< float >(vec); }

extern "C" void z_ghost_printVector(ghost_vec_t *vec)
{ return ghost_vec_print_tmpl< ghost_complex<double> >(vec); }

extern "C" void c_ghost_printVector(ghost_vec_t *vec)
{ return ghost_vec_print_tmpl< ghost_complex<float> >(vec); }

extern "C" void d_ghost_normalizeVector(ghost_vec_t *vec)
{ return ghost_normalizeVector_tmpl< double >(vec); }

extern "C" void s_ghost_normalizeVector(ghost_vec_t *vec)
{ return ghost_normalizeVector_tmpl< float >(vec); }

extern "C" void z_ghost_normalizeVector(ghost_vec_t *vec)
{ return ghost_normalizeVector_tmpl< ghost_complex<double> >(vec); }

extern "C" void c_ghost_normalizeVector(ghost_vec_t *vec)
{ return ghost_normalizeVector_tmpl< ghost_complex<float> >(vec); }

extern "C" void d_ghost_vec_dotprod(ghost_vec_t *vec, ghost_vec_t *vec2, void *res)
{ return ghost_vec_dotprod_tmpl< double >(vec,vec2,res); }

extern "C" void s_ghost_vec_dotprod(ghost_vec_t *vec, ghost_vec_t *vec2, void *res)
{ return ghost_vec_dotprod_tmpl< float >(vec,vec2,res); }

extern "C" void z_ghost_vec_dotprod(ghost_vec_t *vec, ghost_vec_t *vec2, void *res)
{ return ghost_vec_dotprod_tmpl< ghost_complex<double> >(vec,vec2,res); }

extern "C" void c_ghost_vec_dotprod(ghost_vec_t *vec, ghost_vec_t *vec2, void *res)
{ return ghost_vec_dotprod_tmpl< ghost_complex<float> >(vec,vec2,res); }

extern "C" void d_ghost_vec_vscale(ghost_vec_t *vec, void *scale)
{ return ghost_vec_vscale_tmpl< double >(vec, scale); }

extern "C" void s_ghost_vec_vscale(ghost_vec_t *vec, void *scale)
{ return ghost_vec_vscale_tmpl< float  >(vec, scale); }

extern "C" void z_ghost_vec_vscale(ghost_vec_t *vec, void *scale)
{ return ghost_vec_vscale_tmpl< ghost_complex<double> >(vec, scale); }

extern "C" void c_ghost_vec_vscale(ghost_vec_t *vec, void *scale)
{ return ghost_vec_vscale_tmpl< ghost_complex<float> >(vec, scale); }

extern "C" void d_ghost_vec_vaxpy(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale)
{ return ghost_vec_vaxpy_tmpl< double >(vec, vec2, scale); }

extern "C" void s_ghost_vec_vaxpy(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale)
{ return ghost_vec_vaxpy_tmpl< float >(vec, vec2, scale); }

extern "C" void z_ghost_vec_vaxpy(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale)
{ return ghost_vec_vaxpy_tmpl< ghost_complex<double> >(vec, vec2, scale); }

extern "C" void c_ghost_vec_vaxpy(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale)
{ return ghost_vec_vaxpy_tmpl< ghost_complex<float> >(vec, vec2, scale); }

extern "C" void d_ghost_vec_vaxpby(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale, void *b)
{ return ghost_vec_vaxpby_tmpl< double >(vec, vec2, scale, b); }

extern "C" void s_ghost_vec_vaxpby(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale, void *b)
{ return ghost_vec_vaxpby_tmpl< float >(vec, vec2, scale, b); }

extern "C" void z_ghost_vec_vaxpby(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale, void *b)
{ return ghost_vec_vaxpby_tmpl< ghost_complex<double> >(vec, vec2, scale, b); }

extern "C" void c_ghost_vec_vaxpby(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale, void *b)
{ return ghost_vec_vaxpby_tmpl< ghost_complex<float> >(vec, vec2, scale, b); }

extern "C" void d_ghost_vec_fromRand(ghost_vec_t *vec)
{ return ghost_vec_fromRand_tmpl< double >(vec); }

extern "C" void s_ghost_vec_fromRand(ghost_vec_t *vec)
{ return ghost_vec_fromRand_tmpl< float >(vec); }

extern "C" void z_ghost_vec_fromRand(ghost_vec_t *vec)
{ return ghost_vec_fromRand_tmpl< ghost_complex<double> >(vec); }

extern "C" void c_ghost_vec_fromRand(ghost_vec_t *vec)
{ return ghost_vec_fromRand_tmpl< ghost_complex<float> >(vec); }

<commit_msg>also print prefix<commit_after>#include <ghost.h>
#include <ghost_util.h>
#include <ghost_vec.h>
#include <cmath>
#include <cstdio>
#include <iostream>
#include "ghost_complex.h"
#include <omp.h>

double conjugate(double * c) {
	return *c;
}

float conjugate(float * c) {
	return *c;
}

template <typename T>
ghost_complex<T> conjugate(ghost_complex<T>* c) {
	return ghost_complex<T>(std::real(*c),-std::imag(*c));
}

template <typename v_t> void ghost_normalizeVector_tmpl(ghost_vec_t *vec)
{
	v_t s;
	ghost_vec_dotprod_tmpl<v_t>(vec,vec,&s);
	s = (v_t)sqrt(s);
	s = (v_t)(((v_t)1.)/s);
	vec->scale(vec,&s);

#ifdef GHOST_HAVE_OPENCL
	vec->CLupload(vec);
#endif
#ifdef GHOST_HAVE_CUDA
	vec->CUupload(vec);
#endif
}

template <typename v_t> void ghost_vec_dotprod_tmpl(ghost_vec_t *vec, ghost_vec_t *vec2, void *res)
{ // the parallelization is done manually because reduction does not work with ghost_complex numbers
	if (vec->traits->nrows != vec2->traits->nrows) {
		WARNING_LOG("The input vectors of the dot product have different numbers of rows");
	}
	if (vec->traits->nvecs != vec2->traits->nvecs) {
		WARNING_LOG("The input vectors of the dot product have different numbers of columns");
	}
	ghost_vidx_t i,v;
	ghost_vidx_t nr = MIN(vec->traits->nrows,vec2->traits->nrows);

	int nthreads;
#pragma omp parallel
	nthreads = ghost_ompGetNumThreads();

	for (v=0; v<MIN(vec->traits->nvecs,vec2->traits->nvecs); v++) {
		v_t sum = 0;
		v_t partsums[nthreads];
		for (i=0; i<nthreads; i++) partsums[i] = (v_t)0.;

#pragma omp parallel for
		for (i=0; i<nr; i++) {
			partsums[ghost_ompGetThreadNum()] +=
				*(v_t *)VECVAL(vec2,vec2->val,v,i)*
				conjugate((v_t *)(VECVAL(vec,vec->val,v,i)));
		}

		for (i=0; i<nthreads; i++) sum += partsums[i];

		((v_t *)res)[v] = sum;
	}

}

template <typename v_t> void ghost_vec_vaxpy_tmpl(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale)
{
	ghost_vidx_t i,v;
	v_t *s = (v_t *)scale;
	ghost_vidx_t nr = MIN(vec->traits->nrows,vec2->traits->nrows);

	for (v=0; v<MIN(vec->traits->nvecs,vec2->traits->nvecs); v++) {
#pragma omp parallel for
		for (i=0; i<nr; i++) {
			*(v_t *)VECVAL(vec,vec->val,v,i) += *(v_t *)VECVAL(vec2,vec2->val,v,i) * s[v];
		}
	}
}

template <typename v_t> void ghost_vec_vaxpby_tmpl(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale, void *b_)
{
	ghost_vidx_t i,v;
	v_t *s = (v_t *)scale;
	v_t *b = (v_t *)b_;
	ghost_vidx_t nr = MIN(vec->traits->nrows,vec2->traits->nrows);

	for (v=0; v<MIN(vec->traits->nvecs,vec2->traits->nvecs); v++) {
#pragma omp parallel for
		for (i=0; i<nr; i++) {
			*(v_t *)VECVAL(vec,vec->val,v,i) = *(v_t *)VECVAL(vec2,vec2->val,v,i) * s[v] +
				*(v_t *)VECVAL(vec,vec->val,v,i) * b[v];
		}
	}
}

template<typename v_t> void ghost_vec_vscale_tmpl(ghost_vec_t *vec, void *scale)
{
	ghost_vidx_t i,v;
	v_t *s = (v_t *)scale;

	for (v=0; v<vec->traits->nvecs; v++) {
#pragma omp parallel for
		for (i=0; i<vec->traits->nrows; i++) {
			*(v_t *)VECVAL(vec,vec->val,v,i) *= s[v];
		}
	}
}

template <typename v_t> void ghost_vec_fromRand_tmpl(ghost_vec_t *vec)
{
	vec_malloc(vec);
	DEBUG_LOG(1,"Filling vector with random values");
	size_t sizeofdt = ghost_sizeofDataType(vec->traits->datatype);

	int i,v;

	// TODO fuse loops but preserve randomness

	for (v=0; v<vec->traits->nvecs; v++) {
#pragma omp parallel for schedule(runtime)
		for (i=0; i<vec->traits->nrows; i++) {
			*(v_t *)VECVAL(vec,vec->val,v,i) = (v_t)0;
		}
	}

	for (v=0; v<vec->traits->nvecs; v++) {
		for (i=0; i<vec->traits->nrows; i++) {
			*(v_t *)VECVAL(vec,vec->val,v,i) = (v_t)(rand()*1./RAND_MAX); // TODO imag
		}
	}
	vec->upload(vec);

}

template <typename v_t> void ghost_vec_print_tmpl(ghost_vec_t *vec)
{
	char prefix[16];
#ifdef GHOST_HAVE_MPI
	if (vec->context != NULL && vec->context->mpicomm != MPI_COMM_NULL) {
		int rank = ghost_getRank(vec->context->mpicomm);
		int ndigits = (int)floor(log10(abs(rank))) + 1;
		snprintf(prefix,4+ndigits,"PE%d: ",rank);
	} else {
		snprintf(prefix,1,"\0");
	}
#else
	snprintf(prefix,1,"\0");
#endif

	ghost_vidx_t i,v;

	std::cout.setf(std::ios::fixed, std::ios::floatfield);
	for (i=0; i<vec->traits->nrows; i++) {
		std::cout << prefix << "\t";
		for (v=0; v<vec->traits->nvecs; v++) {
			std::cout << *(v_t *)(VECVAL(vec,vec->val,v,i)) << "\t";
		}
		std::cout << std::endl;
	}
}

extern "C" void d_ghost_printVector(ghost_vec_t *vec)
{ return ghost_vec_print_tmpl< double >(vec); }

extern "C" void s_ghost_printVector(ghost_vec_t *vec)
{ return ghost_vec_print_tmpl< float >(vec); }

extern "C" void z_ghost_printVector(ghost_vec_t *vec)
{ return ghost_vec_print_tmpl< ghost_complex<double> >(vec); }

extern "C" void c_ghost_printVector(ghost_vec_t *vec)
{ return ghost_vec_print_tmpl< ghost_complex<float> >(vec); }

extern "C" void d_ghost_normalizeVector(ghost_vec_t *vec)
{ return ghost_normalizeVector_tmpl< double >(vec); }

extern "C" void s_ghost_normalizeVector(ghost_vec_t *vec)
{ return ghost_normalizeVector_tmpl< float >(vec); }

extern "C" void z_ghost_normalizeVector(ghost_vec_t *vec)
{ return ghost_normalizeVector_tmpl< ghost_complex<double> >(vec); }

extern "C" void c_ghost_normalizeVector(ghost_vec_t *vec)
{ return ghost_normalizeVector_tmpl< ghost_complex<float> >(vec); }

extern "C" void d_ghost_vec_dotprod(ghost_vec_t *vec, ghost_vec_t *vec2, void *res)
{ return ghost_vec_dotprod_tmpl< double >(vec,vec2,res); }

extern "C" void s_ghost_vec_dotprod(ghost_vec_t *vec, ghost_vec_t *vec2, void *res)
{ return ghost_vec_dotprod_tmpl< float >(vec,vec2,res); }

extern "C" void z_ghost_vec_dotprod(ghost_vec_t *vec, ghost_vec_t *vec2, void *res)
{ return ghost_vec_dotprod_tmpl< ghost_complex<double> >(vec,vec2,res); }

extern "C" void c_ghost_vec_dotprod(ghost_vec_t *vec, ghost_vec_t *vec2, void *res)
{ return ghost_vec_dotprod_tmpl< ghost_complex<float> >(vec,vec2,res); }

extern "C" void d_ghost_vec_vscale(ghost_vec_t *vec, void *scale)
{ return ghost_vec_vscale_tmpl< double >(vec, scale); }

extern "C" void s_ghost_vec_vscale(ghost_vec_t *vec, void *scale)
{ return ghost_vec_vscale_tmpl< float  >(vec, scale); }

extern "C" void z_ghost_vec_vscale(ghost_vec_t *vec, void *scale)
{ return ghost_vec_vscale_tmpl< ghost_complex<double> >(vec, scale); }

extern "C" void c_ghost_vec_vscale(ghost_vec_t *vec, void *scale)
{ return ghost_vec_vscale_tmpl< ghost_complex<float> >(vec, scale); }

extern "C" void d_ghost_vec_vaxpy(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale)
{ return ghost_vec_vaxpy_tmpl< double >(vec, vec2, scale); }

extern "C" void s_ghost_vec_vaxpy(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale)
{ return ghost_vec_vaxpy_tmpl< float >(vec, vec2, scale); }

extern "C" void z_ghost_vec_vaxpy(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale)
{ return ghost_vec_vaxpy_tmpl< ghost_complex<double> >(vec, vec2, scale); }

extern "C" void c_ghost_vec_vaxpy(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale)
{ return ghost_vec_vaxpy_tmpl< ghost_complex<float> >(vec, vec2, scale); }

extern "C" void d_ghost_vec_vaxpby(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale, void *b)
{ return ghost_vec_vaxpby_tmpl< double >(vec, vec2, scale, b); }

extern "C" void s_ghost_vec_vaxpby(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale, void *b)
{ return ghost_vec_vaxpby_tmpl< float >(vec, vec2, scale, b); }

extern "C" void z_ghost_vec_vaxpby(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale, void *b)
{ return ghost_vec_vaxpby_tmpl< ghost_complex<double> >(vec, vec2, scale, b); }

extern "C" void c_ghost_vec_vaxpby(ghost_vec_t *vec, ghost_vec_t *vec2, void *scale, void *b)
{ return ghost_vec_vaxpby_tmpl< ghost_complex<float> >(vec, vec2, scale, b); }

extern "C" void d_ghost_vec_fromRand(ghost_vec_t *vec)
{ return ghost_vec_fromRand_tmpl< double >(vec); }

extern "C" void s_ghost_vec_fromRand(ghost_vec_t *vec)
{ return ghost_vec_fromRand_tmpl< float >(vec); }

extern "C" void z_ghost_vec_fromRand(ghost_vec_t *vec)
{ return ghost_vec_fromRand_tmpl< ghost_complex<double> >(vec); }

extern "C" void c_ghost_vec_fromRand(ghost_vec_t *vec)
{ return ghost_vec_fromRand_tmpl< ghost_complex<float> >(vec); }

<|endoftext|>


<commit_before>/*

Copyright (c) 2007, Arvid Norberg
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 its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS BE
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.

*/

#ifndef TORRENT_BANDWIDTH_MANAGER_HPP_INCLUDED
#define TORRENT_BANDWIDTH_MANAGER_HPP_INCLUDED

#include <boost/shared_ptr.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/integer_traits.hpp>
#include <boost/thread/mutex.hpp>
#include <deque>

#include "libtorrent/socket.hpp"
#include "libtorrent/invariant_check.hpp"
#include "libtorrent/assert.hpp"
#include "libtorrent/bandwidth_limit.hpp"
#include "libtorrent/bandwidth_queue_entry.hpp"

using boost::weak_ptr;
using boost::shared_ptr;
using boost::intrusive_ptr;
using boost::bind;

//#define TORRENT_VERBOSE_BANDWIDTH_LIMIT

namespace libtorrent {

// the maximum block of bandwidth quota to
// hand out is 33kB. The block size may
// be smaller on lower limits
enum
{
	max_bandwidth_block_size = 33000,
	min_bandwidth_block_size = 400
};

const time_duration bw_window_size = seconds(1);

template<class PeerConnection, class Torrent>
struct history_entry
{
	history_entry(intrusive_ptr<PeerConnection> p, weak_ptr<Torrent> t
		, int a, ptime exp)
		: expires_at(exp), amount(a), peer(p), tor(t) {}
	ptime expires_at;
	int amount;
	intrusive_ptr<PeerConnection> peer;
	weak_ptr<Torrent> tor;
};

template<class T>
T clamp(T val, T ceiling, T floor) throw()
{
	TORRENT_ASSERT(ceiling >= floor);
	if (val >= ceiling) return ceiling;
	else if (val <= floor) return floor;
	return val;
}

template<class PeerConnection, class Torrent>
struct bandwidth_manager
{
	bandwidth_manager(io_service& ios, int channel) throw()
		: m_ios(ios)
		, m_history_timer(m_ios)
		, m_limit(bandwidth_limit::inf)
		, m_current_quota(0)
		, m_channel(channel)
		, m_in_hand_out_bandwidth(false)
		, m_abort(false)
	{}

	void throttle(int limit) throw()
	{
		mutex_t::scoped_lock l(m_mutex);
		TORRENT_ASSERT(limit >= 0);
		m_limit = limit;
	}

	int throttle() const throw()
	{
		mutex_t::scoped_lock l(m_mutex);
		return m_limit;
	}

	void close()
	{
		m_abort = true;
		m_history_timer.cancel();
	}

#ifndef NDEBUG
	bool is_queued(PeerConnection const* peer) const
	{
		for (typename queue_t::const_iterator i = m_queue.begin()
			, end(m_queue.end()); i != end; ++i)
		{
			if (i->peer.get() == peer) return true;
		}
		return false;
	}

	bool is_in_history(PeerConnection const* peer) const
	{
		for (typename history_t::const_iterator i
			= m_history.begin(), end(m_history.end()); i != end; ++i)
		{
			if (i->peer.get() == peer) return true;
		}
		return false;
	}
#endif

	// non prioritized means that, if there's a line for bandwidth,
	// others will cut in front of the non-prioritized peers.
	// this is used by web seeds
	void request_bandwidth(intrusive_ptr<PeerConnection> const& peer
		, int blk
		, bool non_prioritized) throw()
	{
		INVARIANT_CHECK;
		TORRENT_ASSERT(blk > 0);

		TORRENT_ASSERT(!peer->ignore_bandwidth_limits());

		// make sure this peer isn't already in line
		// waiting for bandwidth
		TORRENT_ASSERT(!is_queued(peer.get()));
		TORRENT_ASSERT(peer->max_assignable_bandwidth(m_channel) > 0);

		boost::shared_ptr<Torrent> t = peer->associated_torrent().lock();
		m_queue.push_back(bw_queue_entry<PeerConnection>(peer, blk, non_prioritized));
		if (!non_prioritized)
		{
			typename queue_t::reverse_iterator i = m_queue.rbegin();
			typename queue_t::reverse_iterator j(i);
			for (++j; j != m_queue.rend(); ++j)
			{
				// if the peer's torrent is not the same one
				// continue looking for a peer from the same torrent
				if (j->peer->associated_torrent().lock() != t)
					continue;
				// if we found a peer from the same torrent that
				// is prioritized, there is no point looking
				// any further.
				if (!j->non_prioritized) break;

				using std::swap;
				swap(*i, *j);
				i = j;
			}
		}
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
		std::cerr << " req_bandwidht. m_queue.size() = " << m_queue.size() << std::endl;
#endif
		if (!m_queue.empty()) hand_out_bandwidth();
	}

#ifndef NDEBUG
	void check_invariant() const
	{
		int current_quota = 0;
		for (typename history_t::const_iterator i
			= m_history.begin(), end(m_history.end()); i != end; ++i)
		{
			current_quota += i->amount;
		}

		TORRENT_ASSERT(current_quota == m_current_quota);
	}
#endif

private:

	void add_history_entry(history_entry<PeerConnection, Torrent> const& e) throw()
	{
		INVARIANT_CHECK;

		m_history.push_front(e);
		m_current_quota += e.amount;
		// in case the size > 1 there is already a timer
		// active that will be invoked, no need to set one up
		if (m_history.size() > 1) return;

		if (m_abort) return;

		asio::error_code ec;
		m_history_timer.expires_at(e.expires_at, ec);
		m_history_timer.async_wait(bind(&bandwidth_manager::on_history_expire, this, _1));
	}

	void on_history_expire(asio::error_code const& e) throw()
	{
		INVARIANT_CHECK;

		if (e) return;

		TORRENT_ASSERT(!m_history.empty());

		ptime now(time_now());
		while (!m_history.empty() && m_history.back().expires_at <= now)
		{
			history_entry<PeerConnection, Torrent> e = m_history.back();
			m_history.pop_back();
			m_current_quota -= e.amount;
			TORRENT_ASSERT(m_current_quota >= 0);
			intrusive_ptr<PeerConnection> c = e.peer;
			shared_ptr<Torrent> t = e.tor.lock();
			if (!c->is_disconnecting()) c->expire_bandwidth(m_channel, e.amount);
			if (t) t->expire_bandwidth(m_channel, e.amount);
		}

		// now, wait for the next chunk to expire
		if (!m_history.empty() && !m_abort)
		{
			asio::error_code ec;
			m_history_timer.expires_at(m_history.back().expires_at, ec);
			m_history_timer.async_wait(bind(&bandwidth_manager::on_history_expire, this, _1));
		}

		// since some bandwidth just expired, it
		// means we can hand out more (in case there
		// are still consumers in line)
		if (!m_queue.empty()) hand_out_bandwidth();
	}

	void hand_out_bandwidth()
	{
		// if we're already handing out bandwidth, just return back
		// to the loop further down on the callstack
		if (m_in_hand_out_bandwidth) return;
		m_in_hand_out_bandwidth = true;

		INVARIANT_CHECK;

		ptime now(time_now());

		mutex_t::scoped_lock l(m_mutex);
		int limit = m_limit;
		l.unlock();

		// available bandwidth to hand out
		int amount = limit - m_current_quota;

#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
		std::cerr << " hand_out_bandwidht. m_queue.size() = " << m_queue.size()
			<< " amount = " << amount
			<< " limit = " << limit
			<< " m_current_quota = " << m_current_quota << std::endl;
#endif

		if (amount <= 0)
		{
			m_in_hand_out_bandwidth = false;
			return;
		}

		queue_t q;
		queue_t tmp;
		m_queue.swap(q);
		while (!q.empty() && amount > 0)
		{
			TORRENT_ASSERT(amount == limit - m_current_quota);
			bw_queue_entry<PeerConnection> qe = q.front();
			TORRENT_ASSERT(qe.max_block_size > 0);
			q.pop_front();

			shared_ptr<Torrent> t = qe.peer->associated_torrent().lock();
			if (!t) continue;
			if (qe.peer->is_disconnecting())
			{
				t->expire_bandwidth(m_channel, qe.max_block_size);
				TORRENT_ASSERT(amount == limit - m_current_quota);
				continue;
			}

			// at this point, max_assignable may actually be zero. Since
			// the rate limit of the peer might have changed while it
			// was in the queue.
			int max_assignable = qe.peer->max_assignable_bandwidth(m_channel);
			if (max_assignable == 0)
			{
				TORRENT_ASSERT(is_in_history(qe.peer.get()));
				tmp.push_back(qe);
				continue;
			}

			// this is the limit of the block size. It depends on the throttle
			// so that it can be closer to optimal. Larger block sizes will give lower
			// granularity to the rate but will be more efficient. At high rates
			// the block sizes are bigger and at low rates, the granularity
			// is more important and block sizes are smaller

			// the minimum rate that can be given is the block size, so, the
			// block size must be smaller for lower rates. This is because
			// the history window is one second, and the block will be forgotten
			// after one second.
			int block_size = (std::min)(qe.peer->bandwidth_throttle(m_channel)
				, limit / 10);

			if (block_size < min_bandwidth_block_size)
			{
				block_size = (std::min)(int(min_bandwidth_block_size), limit);
			}
			else if (block_size > max_bandwidth_block_size)
			{
				if (limit == bandwidth_limit::inf)
				{
					block_size = max_bandwidth_block_size;
				}
				else
				{
					// try to make the block_size a divisor of
					// m_limit to make the distributions as fair
					// as possible
					// TODO: move this calculcation to where the limit
					// is changed
					block_size = limit
						/ (limit / max_bandwidth_block_size);
				}
			}
			if (block_size > qe.max_block_size) block_size = qe.max_block_size;

#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
			std::cerr << " block_size = " << block_size << " amount = " << amount << std::endl;
#endif
			if (amount < block_size / 2)
			{
				tmp.push_back(qe);
				break;
			}

			// so, hand out max_assignable, but no more than
			// the available bandwidth (amount) and no more
			// than the max_bandwidth_block_size
			int hand_out_amount = (std::min)((std::min)(block_size, max_assignable)
				, amount);
			TORRENT_ASSERT(hand_out_amount > 0);
			TORRENT_ASSERT(amount == limit - m_current_quota);
			amount -= hand_out_amount;
			TORRENT_ASSERT(hand_out_amount <= qe.max_block_size);
			t->assign_bandwidth(m_channel, hand_out_amount, qe.max_block_size);
			qe.peer->assign_bandwidth(m_channel, hand_out_amount);
			add_history_entry(history_entry<PeerConnection, Torrent>(
				qe.peer, t, hand_out_amount, now + bw_window_size));
			TORRENT_ASSERT(amount == limit - m_current_quota);
		}
		if (!q.empty()) m_queue.insert(m_queue.begin(), q.begin(), q.end());
		if (!tmp.empty()) m_queue.insert(m_queue.begin(), tmp.begin(), tmp.end());
		m_in_hand_out_bandwidth = false;
	}

	typedef boost::mutex mutex_t;
	mutable mutex_t m_mutex;

	// the io_service used for the timer
	io_service& m_ios;

	// the timer that is waiting for the entries
	// in the history queue to expire (slide out
	// of the history window)
	deadline_timer m_history_timer;

	// the rate limit (bytes per second)
	int m_limit;

	// the sum of all recently handed out bandwidth blocks
	int m_current_quota;

	// these are the consumers that want bandwidth
	typedef std::deque<bw_queue_entry<PeerConnection> > queue_t;
	queue_t m_queue;

	// these are the consumers that have received bandwidth
	// that will expire
	typedef std::deque<history_entry<PeerConnection, Torrent> > history_t;
	history_t m_history;

	// this is the channel within the consumers
	// that bandwidth is assigned to (upload or download)
	int m_channel;

	// this is true while we're in the hand_out_bandwidth loop
	// to prevent recursive invocations to interfere
	bool m_in_hand_out_bandwidth;

	bool m_abort;
};

}

#endif

<commit_msg>fixed race condition in bandwidth limiter<commit_after>/*

Copyright (c) 2007, Arvid Norberg
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 its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS BE
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.

*/

#ifndef TORRENT_BANDWIDTH_MANAGER_HPP_INCLUDED
#define TORRENT_BANDWIDTH_MANAGER_HPP_INCLUDED

#include <boost/shared_ptr.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/integer_traits.hpp>
#include <boost/thread/mutex.hpp>
#include <deque>

#include "libtorrent/socket.hpp"
#include "libtorrent/invariant_check.hpp"
#include "libtorrent/assert.hpp"
#include "libtorrent/bandwidth_limit.hpp"
#include "libtorrent/bandwidth_queue_entry.hpp"

using boost::weak_ptr;
using boost::shared_ptr;
using boost::intrusive_ptr;
using boost::bind;

//#define TORRENT_VERBOSE_BANDWIDTH_LIMIT

namespace libtorrent {

// the maximum block of bandwidth quota to
// hand out is 33kB. The block size may
// be smaller on lower limits
enum
{
	max_bandwidth_block_size = 33000,
	min_bandwidth_block_size = 400
};

const time_duration bw_window_size = seconds(1);

template<class PeerConnection, class Torrent>
struct history_entry
{
	history_entry(intrusive_ptr<PeerConnection> p, weak_ptr<Torrent> t
		, int a, ptime exp)
		: expires_at(exp), amount(a), peer(p), tor(t) {}
	ptime expires_at;
	int amount;
	intrusive_ptr<PeerConnection> peer;
	weak_ptr<Torrent> tor;
};

template<class T>
T clamp(T val, T ceiling, T floor) throw()
{
	TORRENT_ASSERT(ceiling >= floor);
	if (val >= ceiling) return ceiling;
	else if (val <= floor) return floor;
	return val;
}

template<class PeerConnection, class Torrent>
struct bandwidth_manager
{
	bandwidth_manager(io_service& ios, int channel) throw()
		: m_ios(ios)
		, m_history_timer(m_ios)
		, m_limit(bandwidth_limit::inf)
		, m_current_quota(0)
		, m_channel(channel)
		, m_in_hand_out_bandwidth(false)
		, m_abort(false)
	{}

	void throttle(int limit) throw()
	{
		mutex_t::scoped_lock l(m_mutex);
		TORRENT_ASSERT(limit >= 0);
		m_limit = limit;
	}

	int throttle() const throw()
	{
		mutex_t::scoped_lock l(m_mutex);
		return m_limit;
	}

	void close()
	{
		m_abort = true;
		m_history_timer.cancel();
	}

#ifndef NDEBUG
	bool is_queued(PeerConnection const* peer) const
	{
		mutex_t::scoped_lock l(m_mutex);
		for (typename queue_t::const_iterator i = m_queue.begin()
			, end(m_queue.end()); i != end; ++i)
		{
			if (i->peer.get() == peer) return true;
		}
		return false;
	}

	bool is_in_history(PeerConnection const* peer) const
	{
		mutex_t::scoped_lock l(m_mutex);
		for (typename history_t::const_iterator i
			= m_history.begin(), end(m_history.end()); i != end; ++i)
		{
			if (i->peer.get() == peer) return true;
		}
		return false;
	}
#endif

	// non prioritized means that, if there's a line for bandwidth,
	// others will cut in front of the non-prioritized peers.
	// this is used by web seeds
	void request_bandwidth(intrusive_ptr<PeerConnection> const& peer
		, int blk
		, bool non_prioritized) throw()
	{
		INVARIANT_CHECK;
		TORRENT_ASSERT(blk > 0);
		TORRENT_ASSERT(!is_queued(peer.get()));

		mutex_t::scoped_lock l(m_mutex);
		TORRENT_ASSERT(!peer->ignore_bandwidth_limits());

		// make sure this peer isn't already in line
		// waiting for bandwidth
		TORRENT_ASSERT(peer->max_assignable_bandwidth(m_channel) > 0);

		boost::shared_ptr<Torrent> t = peer->associated_torrent().lock();
		m_queue.push_back(bw_queue_entry<PeerConnection>(peer, blk, non_prioritized));
		if (!non_prioritized)
		{
			typename queue_t::reverse_iterator i = m_queue.rbegin();
			typename queue_t::reverse_iterator j(i);
			for (++j; j != m_queue.rend(); ++j)
			{
				// if the peer's torrent is not the same one
				// continue looking for a peer from the same torrent
				if (j->peer->associated_torrent().lock() != t)
					continue;
				// if we found a peer from the same torrent that
				// is prioritized, there is no point looking
				// any further.
				if (!j->non_prioritized) break;

				using std::swap;
				swap(*i, *j);
				i = j;
			}
		}
#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
		std::cerr << " req_bandwidht. m_queue.size() = " << m_queue.size() << std::endl;
#endif
		if (!m_queue.empty()) hand_out_bandwidth(l);
	}

#ifndef NDEBUG
	void check_invariant() const
	{
		int current_quota = 0;
		for (typename history_t::const_iterator i
			= m_history.begin(), end(m_history.end()); i != end; ++i)
		{
			current_quota += i->amount;
		}

		TORRENT_ASSERT(current_quota == m_current_quota);
	}
#endif

private:

	void add_history_entry(history_entry<PeerConnection, Torrent> const& e) throw()
	{
		INVARIANT_CHECK;

		m_history.push_front(e);
		m_current_quota += e.amount;
		// in case the size > 1 there is already a timer
		// active that will be invoked, no need to set one up
		if (m_history.size() > 1) return;

		if (m_abort) return;

		asio::error_code ec;
		m_history_timer.expires_at(e.expires_at, ec);
		m_history_timer.async_wait(bind(&bandwidth_manager::on_history_expire, this, _1));
	}

	void on_history_expire(asio::error_code const& e) throw()
	{
		INVARIANT_CHECK;

		if (e) return;

		TORRENT_ASSERT(!m_history.empty());

		mutex_t::scoped_lock l(m_mutex);
		ptime now(time_now());
		while (!m_history.empty() && m_history.back().expires_at <= now)
		{
			history_entry<PeerConnection, Torrent> e = m_history.back();
			m_history.pop_back();
			m_current_quota -= e.amount;
			TORRENT_ASSERT(m_current_quota >= 0);
			intrusive_ptr<PeerConnection> c = e.peer;
			shared_ptr<Torrent> t = e.tor.lock();
			l.unlock();
			if (!c->is_disconnecting()) c->expire_bandwidth(m_channel, e.amount);
			if (t) t->expire_bandwidth(m_channel, e.amount);
			l.lock();
		}

		// now, wait for the next chunk to expire
		if (!m_history.empty() && !m_abort)
		{
			asio::error_code ec;
			m_history_timer.expires_at(m_history.back().expires_at, ec);
			m_history_timer.async_wait(bind(&bandwidth_manager::on_history_expire, this, _1));
		}

		// since some bandwidth just expired, it
		// means we can hand out more (in case there
		// are still consumers in line)
		if (!m_queue.empty()) hand_out_bandwidth(l);
	}

	void hand_out_bandwidth(boost::mutex::scoped_lock& l)
	{
		// if we're already handing out bandwidth, just return back
		// to the loop further down on the callstack
		if (m_in_hand_out_bandwidth) return;
		m_in_hand_out_bandwidth = true;

		INVARIANT_CHECK;

		ptime now(time_now());

		int limit = m_limit;

		// available bandwidth to hand out
		int amount = limit - m_current_quota;

#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
		std::cerr << " hand_out_bandwidht. m_queue.size() = " << m_queue.size()
			<< " amount = " << amount
			<< " limit = " << limit
			<< " m_current_quota = " << m_current_quota << std::endl;
#endif

		if (amount <= 0)
		{
			m_in_hand_out_bandwidth = false;
			return;
		}

		queue_t q;
		queue_t tmp;
		m_queue.swap(q);
		while (!q.empty() && amount > 0)
		{
			TORRENT_ASSERT(amount == limit - m_current_quota);
			bw_queue_entry<PeerConnection> qe = q.front();
			TORRENT_ASSERT(qe.max_block_size > 0);
			q.pop_front();

			shared_ptr<Torrent> t = qe.peer->associated_torrent().lock();
			if (!t) continue;
			if (qe.peer->is_disconnecting())
			{
				l.unlock();
				t->expire_bandwidth(m_channel, qe.max_block_size);
				l.lock();
				TORRENT_ASSERT(amount == limit - m_current_quota);
				continue;
			}

			// at this point, max_assignable may actually be zero. Since
			// the rate limit of the peer might have changed while it
			// was in the queue.
			int max_assignable = qe.peer->max_assignable_bandwidth(m_channel);
			if (max_assignable == 0)
			{
				TORRENT_ASSERT(is_in_history(qe.peer.get()));
				tmp.push_back(qe);
				continue;
			}

			// this is the limit of the block size. It depends on the throttle
			// so that it can be closer to optimal. Larger block sizes will give lower
			// granularity to the rate but will be more efficient. At high rates
			// the block sizes are bigger and at low rates, the granularity
			// is more important and block sizes are smaller

			// the minimum rate that can be given is the block size, so, the
			// block size must be smaller for lower rates. This is because
			// the history window is one second, and the block will be forgotten
			// after one second.
			int block_size = (std::min)(qe.peer->bandwidth_throttle(m_channel)
				, limit / 10);

			if (block_size < min_bandwidth_block_size)
			{
				block_size = (std::min)(int(min_bandwidth_block_size), limit);
			}
			else if (block_size > max_bandwidth_block_size)
			{
				if (limit == bandwidth_limit::inf)
				{
					block_size = max_bandwidth_block_size;
				}
				else
				{
					// try to make the block_size a divisor of
					// m_limit to make the distributions as fair
					// as possible
					// TODO: move this calculcation to where the limit
					// is changed
					block_size = limit
						/ (limit / max_bandwidth_block_size);
				}
			}
			if (block_size > qe.max_block_size) block_size = qe.max_block_size;

#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT
			std::cerr << " block_size = " << block_size << " amount = " << amount << std::endl;
#endif
			if (amount < block_size / 2)
			{
				tmp.push_back(qe);
				break;
			}

			// so, hand out max_assignable, but no more than
			// the available bandwidth (amount) and no more
			// than the max_bandwidth_block_size
			int hand_out_amount = (std::min)((std::min)(block_size, max_assignable)
				, amount);
			TORRENT_ASSERT(hand_out_amount > 0);
			TORRENT_ASSERT(amount == limit - m_current_quota);
			amount -= hand_out_amount;
			TORRENT_ASSERT(hand_out_amount <= qe.max_block_size);
			l.unlock();
			t->assign_bandwidth(m_channel, hand_out_amount, qe.max_block_size);
			qe.peer->assign_bandwidth(m_channel, hand_out_amount);
			l.lock();
			add_history_entry(history_entry<PeerConnection, Torrent>(
				qe.peer, t, hand_out_amount, now + bw_window_size));
			TORRENT_ASSERT(amount == limit - m_current_quota);
		}
		if (!q.empty()) m_queue.insert(m_queue.begin(), q.begin(), q.end());
		if (!tmp.empty()) m_queue.insert(m_queue.begin(), tmp.begin(), tmp.end());
		m_in_hand_out_bandwidth = false;
	}

	typedef boost::mutex mutex_t;
	mutable mutex_t m_mutex;

	// the io_service used for the timer
	io_service& m_ios;

	// the timer that is waiting for the entries
	// in the history queue to expire (slide out
	// of the history window)
	deadline_timer m_history_timer;

	// the rate limit (bytes per second)
	int m_limit;

	// the sum of all recently handed out bandwidth blocks
	int m_current_quota;

	// these are the consumers that want bandwidth
	typedef std::deque<bw_queue_entry<PeerConnection> > queue_t;
	queue_t m_queue;

	// these are the consumers that have received bandwidth
	// that will expire
	typedef std::deque<history_entry<PeerConnection, Torrent> > history_t;
	history_t m_history;

	// this is the channel within the consumers
	// that bandwidth is assigned to (upload or download)
	int m_channel;

	// this is true while we're in the hand_out_bandwidth loop
	// to prevent recursive invocations to interfere
	bool m_in_hand_out_bandwidth;

	bool m_abort;
};

}

#endif

<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2014 Artem Pavlenko
 *
 * 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
 *
 *****************************************************************************/

// mapnik
#include <mapnik/make_unique.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/global.hpp>
#include <mapnik/wkb.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/util/noncopyable.hpp>
#include <mapnik/geometry_adapters.hpp>
// boost.geometry
#include <boost/geometry/algorithms/correct.hpp>

namespace mapnik
{

struct wkb_reader : util::noncopyable
{
private:
    const char* wkb_;
    std::size_t size_;
    std::size_t pos_;
    wkbByteOrder byteOrder_;
    bool needSwap_;
    wkbFormat format_;

public:

    enum wkbGeometryType {
        wkbPoint=1,
        wkbLineString=2,
        wkbPolygon=3,
        wkbMultiPoint=4,
        wkbMultiLineString=5,
        wkbMultiPolygon=6,
        wkbGeometryCollection=7,
        // Z
        wkbPointZ=1001,
        wkbLineStringZ=1002,
        wkbPolygonZ=1003,
        wkbMultiPointZ=1004,
        wkbMultiLineStringZ=1005,
        wkbMultiPolygonZ=1006,
        wkbGeometryCollectionZ=1007,
        // M
        wkbPointM=2001,
        wkbLineStringM=2002,
        wkbPolygonM=2003,
        wkbMultiPointM=2004,
        wkbMultiLineStringM=2005,
        wkbMultiPolygonM=2006,
        wkbGeometryCollectionM=2007,
        // ZM
        wkbPointZM=3001,
        wkbLineStringZM=3002,
        wkbPolygonZM=3003,
        wkbMultiPointZM=3004,
        wkbMultiLineStringZM=3005,
        wkbMultiPolygonZM=3006,
        wkbGeometryCollectionZM=3007
    };

    wkb_reader(const char* wkb, std::size_t size, wkbFormat format)
        : wkb_(wkb),
          size_(size),
          pos_(0),
          format_(format)
    {
        // try to determine WKB format automatically
        if (format_ == wkbAuto)
        {
            if (size_ >= 44
                && static_cast<unsigned char>(wkb_[0]) == static_cast<unsigned char>(0x00)
                && static_cast<unsigned char>(wkb_[38]) == static_cast<unsigned char>(0x7C)
                && static_cast<unsigned char>(wkb_[size_ - 1]) == static_cast<unsigned char>(0xFE))
            {
                format_ = wkbSpatiaLite;
            }
            else
            {
                format_ = wkbGeneric;
            }
        }

        switch (format_)
        {
        case wkbSpatiaLite:
            byteOrder_ = (wkbByteOrder) wkb_[1];
            pos_ = 39;
            break;

        case wkbGeneric:
        default:
            byteOrder_ = (wkbByteOrder) wkb_[0];
            pos_ = 1;
            break;
        }

        needSwap_ = byteOrder_ ? wkbXDR : wkbNDR;
    }

    mapnik::geometry::geometry read()
    {
        int type = read_integer();
        switch (type)
        {
        case wkbPoint:
            return std::move(mapnik::geometry::geometry(read_point()));
        case wkbLineString:
            return std::move(mapnik::geometry::geometry(read_linestring()));
        case wkbPolygon:
            return std::move(mapnik::geometry::geometry(read_polygon()));
        case wkbMultiPoint:
            return std::move(mapnik::geometry::geometry(read_multipoint()));
        case wkbMultiLineString:
            return std::move(mapnik::geometry::geometry(read_multilinestring()));
        case wkbMultiPolygon:
            return std::move(mapnik::geometry::geometry(read_multipolygon()));
        case wkbGeometryCollection:
            return std::move(mapnik::geometry::geometry(read_collection()));
        case wkbPointZ:
        case wkbPointM:
            return std::move(mapnik::geometry::geometry(read_point<true>()));
        case wkbPointZM:
            return std::move(mapnik::geometry::geometry(read_point<true,true>()));
        case wkbLineStringZ:
        case wkbLineStringM:
            return std::move(mapnik::geometry::geometry(read_linestring<true>()));
        case wkbLineStringZM:
            return std::move(mapnik::geometry::geometry(read_linestring<true,true>()));
        case wkbPolygonZ:
        case wkbPolygonM:
            return std::move(mapnik::geometry::geometry(read_polygon<true>()));
        case wkbPolygonZM:
            return std::move(mapnik::geometry::geometry(read_polygon<true,true>()));
        case wkbMultiPointZ:
        case wkbMultiPointM:
            return std::move(mapnik::geometry::geometry(read_multipoint<true>()));
        case wkbMultiPointZM:
            return std::move(mapnik::geometry::geometry(read_multipoint<true,true>()));
        case wkbMultiLineStringZ:
        case wkbMultiLineStringM:
            return std::move(mapnik::geometry::geometry(read_multilinestring<true>()));
        case wkbMultiLineStringZM:
            return std::move(mapnik::geometry::geometry(read_multilinestring<true,true>()));
        case wkbMultiPolygonZ:
        case wkbMultiPolygonM:
            return std::move(mapnik::geometry::geometry(read_multipolygon<true>()));
        case wkbMultiPolygonZM:
            return std::move(mapnik::geometry::geometry(read_multipolygon<true,true>()));
        case wkbGeometryCollectionZ:
        case wkbGeometryCollectionM:
        case wkbGeometryCollectionZM:
            return std::move(mapnik::geometry::geometry(read_collection()));
        default:
            break;
        }
        throw std::runtime_error("Uknown WKB geometry type");
    }

private:

    int read_integer()
    {
        std::int32_t n;
        if (needSwap_)
        {
            read_int32_xdr(wkb_ + pos_, n);
        }
        else
        {
            read_int32_ndr(wkb_ + pos_, n);
        }
        pos_ += 4;

        return n;
    }

    double read_double()
    {
        double d;
        if (needSwap_)
        {
            read_double_xdr(wkb_ + pos_, d);
        }
        else
        {
            read_double_ndr(wkb_ + pos_, d);
        }
        pos_ += 8;

        return d;
    }

    template <typename Ring, bool Z = false, bool M = false>
    void read_coords(Ring & ring, std::size_t num_points)
    {
        double x,y;
        if (!needSwap_)
        {
            for (std::size_t i = 0; i < num_points; ++i)
            {
                read_double_ndr(wkb_ + pos_, x);
                read_double_ndr(wkb_ + pos_ + 8, y);
                ring.emplace_back(x,y);
                pos_ += 16; // skip XY
                if (Z) pos_ += 8;
                if (M) pos_ += 8;
            }
        }
        else
        {
            for (std::size_t i = 0; i < num_points; ++i)
            {
                read_double_xdr(wkb_ + pos_, x);
                read_double_xdr(wkb_ + pos_ + 8, y);
                ring.emplace_back(x,y);
                pos_ += 16; // skip XY
                if (Z) pos_ += 8;
                if (M) pos_ += 8;
            }
        }
    }

    template <bool Z = false, bool M = false>
    mapnik::geometry::point read_point()
    {
        double x = read_double();
        double y = read_double();
        if (Z) pos_ += 8;
        if (M) pos_ += 8;
        return mapnik::geometry::point(x, y);
    }

    template <bool Z = false, bool M = false>
    mapnik::geometry::multi_point read_multipoint()
    {
        mapnik::geometry::multi_point multi_point;
        int num_points = read_integer();
        multi_point.reserve(num_points);
        for (int i = 0; i < num_points; ++i)
        {
            pos_ += 5;
            multi_point.emplace_back(read_point<Z,M>());
        }
        return multi_point;
    }

    template <bool M = false, bool Z = false>
    mapnik::geometry::line_string read_linestring()
    {
        mapnik::geometry::line_string line;
        int num_points = read_integer();
        if (num_points > 0)
        {
            line.reserve(num_points);
            read_coords<mapnik::geometry::line_string, M, Z>(line, num_points);
        }
        return line;
    }

    template <bool M = false, bool Z = false>
    mapnik::geometry::multi_line_string read_multilinestring()
    {
        int num_lines = read_integer();
        mapnik::geometry::multi_line_string multi_line;
        multi_line.reserve(num_lines);
        for (int i = 0; i < num_lines; ++i)
        {
            pos_ += 5;
            multi_line.push_back(std::move(read_linestring<M, Z>()));
        }
        return multi_line;
    }

    template <bool M = false, bool Z = false>
    mapnik::geometry::polygon read_polygon()
    {
        int num_rings = read_integer();
        mapnik::geometry::polygon poly;
        if (num_rings > 1)
        {
            poly.interior_rings.reserve(num_rings - 1);
        }

        for (int i = 0; i < num_rings; ++i)
        {
            mapnik::geometry::linear_ring ring;
            int num_points = read_integer();
            if (num_points > 0)
            {
                ring.reserve(num_points);
                read_coords<mapnik::geometry::linear_ring, M, Z>(ring, num_points);
            }
            if ( i == 0) poly.set_exterior_ring(std::move(ring));
            else poly.add_hole(std::move(ring));
        }
        // correct orientations etc
        boost::geometry::correct(poly);
        return poly;
    }

    template <bool M = false, bool Z = false>
    mapnik::geometry::multi_polygon read_multipolygon()
    {
        int num_polys = read_integer();
        mapnik::geometry::multi_polygon multi_poly;
        for (int i = 0; i < num_polys; ++i)
        {
            pos_ += 5;
            multi_poly.push_back(std::move(read_polygon<M, Z>()));
        }
        return multi_poly;
    }

    mapnik::geometry::geometry_collection read_collection()
    {
        int num_geometries = read_integer();
        mapnik::geometry::geometry_collection collection;
        for (int i = 0; i < num_geometries; ++i)
        {
            pos_ += 1; // skip byte order
            collection.push_back(std::move(read()));
         }
        return collection;
     }

    std::string wkb_geometry_type_string(int type)
    {
        std::stringstream s;

        switch (type)
        {
        case wkbPoint:               s << "Point"; break;
        case wkbPointZ:              s << "PointZ"; break;
        case wkbPointM:              s << "PointM"; break;
        case wkbPointZM:             s << "PointZM"; break;
        case wkbMultiPoint:          s << "MultiPoint"; break;
        case wkbMultiPointZ:         s << "MultiPointZ"; break;
        case wkbMultiPointM:         s << "MultiPointM"; break;
        case wkbMultiPointZM:        s << "MultiPointZM"; break;
        case wkbLineString:          s << "LineString"; break;
        case wkbLineStringZ:         s << "LineStringZ"; break;
        case wkbLineStringM:         s << "LineStringM"; break;
        case wkbLineStringZM:        s << "LineStringZM"; break;
        case wkbMultiLineString:     s << "MultiLineString"; break;
        case wkbMultiLineStringZ:    s << "MultiLineStringZ"; break;
        case wkbMultiLineStringM:    s << "MultiLineStringM"; break;
        case wkbMultiLineStringZM:   s << "MultiLineStringZM"; break;
        case wkbPolygon:             s << "Polygon"; break;
        case wkbPolygonZ:            s << "PolygonZ"; break;
        case wkbPolygonM:            s << "PolygonM"; break;
        case wkbPolygonZM:           s << "PolygonZM"; break;
        case wkbMultiPolygon:        s << "MultiPolygon"; break;
        case wkbMultiPolygonZ:       s << "MultiPolygonZ"; break;
        case wkbMultiPolygonM:       s << "MultiPolygonM"; break;
        case wkbMultiPolygonZM:      s << "MultiPolygonZM"; break;
        case wkbGeometryCollection:  s << "GeometryCollection"; break;
        case wkbGeometryCollectionZ: s << "GeometryCollectionZ"; break;
        case wkbGeometryCollectionM: s << "GeometryCollectionM"; break;
        case wkbGeometryCollectionZM:s << "GeometryCollectionZM"; break;
        default:                     s << "wkbUnknown(" << type << ")"; break;
        }

        return s.str();
    }

};

mapnik::geometry::geometry geometry_utils::from_wkb(const char* wkb,
                                                        unsigned size,
                                                        wkbFormat format)
{
    wkb_reader reader(wkb, size, format);
    return mapnik::geometry::geometry(reader.read());
}

} // namespace mapnik
<commit_msg>wkb: use 'mapnik::geometry::correct'<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2014 Artem Pavlenko
 *
 * 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
 *
 *****************************************************************************/

// mapnik
#include <mapnik/make_unique.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/global.hpp>
#include <mapnik/wkb.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/util/noncopyable.hpp>
#include <mapnik/geometry_adapters.hpp>
#include <mapnik/geometry_correct.hpp>

namespace mapnik
{

struct wkb_reader : util::noncopyable
{
private:
    const char* wkb_;
    std::size_t size_;
    std::size_t pos_;
    wkbByteOrder byteOrder_;
    bool needSwap_;
    wkbFormat format_;

public:

    enum wkbGeometryType {
        wkbPoint=1,
        wkbLineString=2,
        wkbPolygon=3,
        wkbMultiPoint=4,
        wkbMultiLineString=5,
        wkbMultiPolygon=6,
        wkbGeometryCollection=7,
        // Z
        wkbPointZ=1001,
        wkbLineStringZ=1002,
        wkbPolygonZ=1003,
        wkbMultiPointZ=1004,
        wkbMultiLineStringZ=1005,
        wkbMultiPolygonZ=1006,
        wkbGeometryCollectionZ=1007,
        // M
        wkbPointM=2001,
        wkbLineStringM=2002,
        wkbPolygonM=2003,
        wkbMultiPointM=2004,
        wkbMultiLineStringM=2005,
        wkbMultiPolygonM=2006,
        wkbGeometryCollectionM=2007,
        // ZM
        wkbPointZM=3001,
        wkbLineStringZM=3002,
        wkbPolygonZM=3003,
        wkbMultiPointZM=3004,
        wkbMultiLineStringZM=3005,
        wkbMultiPolygonZM=3006,
        wkbGeometryCollectionZM=3007
    };

    wkb_reader(const char* wkb, std::size_t size, wkbFormat format)
        : wkb_(wkb),
          size_(size),
          pos_(0),
          format_(format)
    {
        // try to determine WKB format automatically
        if (format_ == wkbAuto)
        {
            if (size_ >= 44
                && static_cast<unsigned char>(wkb_[0]) == static_cast<unsigned char>(0x00)
                && static_cast<unsigned char>(wkb_[38]) == static_cast<unsigned char>(0x7C)
                && static_cast<unsigned char>(wkb_[size_ - 1]) == static_cast<unsigned char>(0xFE))
            {
                format_ = wkbSpatiaLite;
            }
            else
            {
                format_ = wkbGeneric;
            }
        }

        switch (format_)
        {
        case wkbSpatiaLite:
            byteOrder_ = (wkbByteOrder) wkb_[1];
            pos_ = 39;
            break;

        case wkbGeneric:
        default:
            byteOrder_ = (wkbByteOrder) wkb_[0];
            pos_ = 1;
            break;
        }

        needSwap_ = byteOrder_ ? wkbXDR : wkbNDR;
    }

    mapnik::geometry::geometry read()
    {
        int type = read_integer();
        switch (type)
        {
        case wkbPoint:
            return std::move(mapnik::geometry::geometry(read_point()));
        case wkbLineString:
            return std::move(mapnik::geometry::geometry(read_linestring()));
        case wkbPolygon:
            return std::move(mapnik::geometry::geometry(read_polygon()));
        case wkbMultiPoint:
            return std::move(mapnik::geometry::geometry(read_multipoint()));
        case wkbMultiLineString:
            return std::move(mapnik::geometry::geometry(read_multilinestring()));
        case wkbMultiPolygon:
            return std::move(mapnik::geometry::geometry(read_multipolygon()));
        case wkbGeometryCollection:
            return std::move(mapnik::geometry::geometry(read_collection()));
        case wkbPointZ:
        case wkbPointM:
            return std::move(mapnik::geometry::geometry(read_point<true>()));
        case wkbPointZM:
            return std::move(mapnik::geometry::geometry(read_point<true,true>()));
        case wkbLineStringZ:
        case wkbLineStringM:
            return std::move(mapnik::geometry::geometry(read_linestring<true>()));
        case wkbLineStringZM:
            return std::move(mapnik::geometry::geometry(read_linestring<true,true>()));
        case wkbPolygonZ:
        case wkbPolygonM:
            return std::move(mapnik::geometry::geometry(read_polygon<true>()));
        case wkbPolygonZM:
            return std::move(mapnik::geometry::geometry(read_polygon<true,true>()));
        case wkbMultiPointZ:
        case wkbMultiPointM:
            return std::move(mapnik::geometry::geometry(read_multipoint<true>()));
        case wkbMultiPointZM:
            return std::move(mapnik::geometry::geometry(read_multipoint<true,true>()));
        case wkbMultiLineStringZ:
        case wkbMultiLineStringM:
            return std::move(mapnik::geometry::geometry(read_multilinestring<true>()));
        case wkbMultiLineStringZM:
            return std::move(mapnik::geometry::geometry(read_multilinestring<true,true>()));
        case wkbMultiPolygonZ:
        case wkbMultiPolygonM:
            return std::move(mapnik::geometry::geometry(read_multipolygon<true>()));
        case wkbMultiPolygonZM:
            return std::move(mapnik::geometry::geometry(read_multipolygon<true,true>()));
        case wkbGeometryCollectionZ:
        case wkbGeometryCollectionM:
        case wkbGeometryCollectionZM:
            return std::move(mapnik::geometry::geometry(read_collection()));
        default:
            break;
        }
        throw std::runtime_error("Uknown WKB geometry type");
    }

private:

    int read_integer()
    {
        std::int32_t n;
        if (needSwap_)
        {
            read_int32_xdr(wkb_ + pos_, n);
        }
        else
        {
            read_int32_ndr(wkb_ + pos_, n);
        }
        pos_ += 4;

        return n;
    }

    double read_double()
    {
        double d;
        if (needSwap_)
        {
            read_double_xdr(wkb_ + pos_, d);
        }
        else
        {
            read_double_ndr(wkb_ + pos_, d);
        }
        pos_ += 8;

        return d;
    }

    template <typename Ring, bool Z = false, bool M = false>
    void read_coords(Ring & ring, std::size_t num_points)
    {
        double x,y;
        if (!needSwap_)
        {
            for (std::size_t i = 0; i < num_points; ++i)
            {
                read_double_ndr(wkb_ + pos_, x);
                read_double_ndr(wkb_ + pos_ + 8, y);
                ring.emplace_back(x,y);
                pos_ += 16; // skip XY
                if (Z) pos_ += 8;
                if (M) pos_ += 8;
            }
        }
        else
        {
            for (std::size_t i = 0; i < num_points; ++i)
            {
                read_double_xdr(wkb_ + pos_, x);
                read_double_xdr(wkb_ + pos_ + 8, y);
                ring.emplace_back(x,y);
                pos_ += 16; // skip XY
                if (Z) pos_ += 8;
                if (M) pos_ += 8;
            }
        }
    }

    template <bool Z = false, bool M = false>
    mapnik::geometry::point read_point()
    {
        double x = read_double();
        double y = read_double();
        if (Z) pos_ += 8;
        if (M) pos_ += 8;
        return mapnik::geometry::point(x, y);
    }

    template <bool Z = false, bool M = false>
    mapnik::geometry::multi_point read_multipoint()
    {
        mapnik::geometry::multi_point multi_point;
        int num_points = read_integer();
        multi_point.reserve(num_points);
        for (int i = 0; i < num_points; ++i)
        {
            pos_ += 5;
            multi_point.emplace_back(read_point<Z,M>());
        }
        return multi_point;
    }

    template <bool M = false, bool Z = false>
    mapnik::geometry::line_string read_linestring()
    {
        mapnik::geometry::line_string line;
        int num_points = read_integer();
        if (num_points > 0)
        {
            line.reserve(num_points);
            read_coords<mapnik::geometry::line_string, M, Z>(line, num_points);
        }
        return line;
    }

    template <bool M = false, bool Z = false>
    mapnik::geometry::multi_line_string read_multilinestring()
    {
        int num_lines = read_integer();
        mapnik::geometry::multi_line_string multi_line;
        multi_line.reserve(num_lines);
        for (int i = 0; i < num_lines; ++i)
        {
            pos_ += 5;
            multi_line.push_back(std::move(read_linestring<M, Z>()));
        }
        return multi_line;
    }

    template <bool M = false, bool Z = false>
    mapnik::geometry::polygon read_polygon()
    {
        int num_rings = read_integer();
        mapnik::geometry::polygon poly;
        if (num_rings > 1)
        {
            poly.interior_rings.reserve(num_rings - 1);
        }

        for (int i = 0; i < num_rings; ++i)
        {
            mapnik::geometry::linear_ring ring;
            int num_points = read_integer();
            if (num_points > 0)
            {
                ring.reserve(num_points);
                read_coords<mapnik::geometry::linear_ring, M, Z>(ring, num_points);
            }
            if ( i == 0) poly.set_exterior_ring(std::move(ring));
            else poly.add_hole(std::move(ring));
        }
        return poly;
    }

    template <bool M = false, bool Z = false>
    mapnik::geometry::multi_polygon read_multipolygon()
    {
        int num_polys = read_integer();
        mapnik::geometry::multi_polygon multi_poly;
        for (int i = 0; i < num_polys; ++i)
        {
            pos_ += 5;
            multi_poly.push_back(std::move(read_polygon<M, Z>()));
        }
        return multi_poly;
    }

    mapnik::geometry::geometry_collection read_collection()
    {
        int num_geometries = read_integer();
        mapnik::geometry::geometry_collection collection;
        for (int i = 0; i < num_geometries; ++i)
        {
            pos_ += 1; // skip byte order
            collection.push_back(std::move(read()));
         }
        return collection;
     }

    std::string wkb_geometry_type_string(int type)
    {
        std::stringstream s;

        switch (type)
        {
        case wkbPoint:               s << "Point"; break;
        case wkbPointZ:              s << "PointZ"; break;
        case wkbPointM:              s << "PointM"; break;
        case wkbPointZM:             s << "PointZM"; break;
        case wkbMultiPoint:          s << "MultiPoint"; break;
        case wkbMultiPointZ:         s << "MultiPointZ"; break;
        case wkbMultiPointM:         s << "MultiPointM"; break;
        case wkbMultiPointZM:        s << "MultiPointZM"; break;
        case wkbLineString:          s << "LineString"; break;
        case wkbLineStringZ:         s << "LineStringZ"; break;
        case wkbLineStringM:         s << "LineStringM"; break;
        case wkbLineStringZM:        s << "LineStringZM"; break;
        case wkbMultiLineString:     s << "MultiLineString"; break;
        case wkbMultiLineStringZ:    s << "MultiLineStringZ"; break;
        case wkbMultiLineStringM:    s << "MultiLineStringM"; break;
        case wkbMultiLineStringZM:   s << "MultiLineStringZM"; break;
        case wkbPolygon:             s << "Polygon"; break;
        case wkbPolygonZ:            s << "PolygonZ"; break;
        case wkbPolygonM:            s << "PolygonM"; break;
        case wkbPolygonZM:           s << "PolygonZM"; break;
        case wkbMultiPolygon:        s << "MultiPolygon"; break;
        case wkbMultiPolygonZ:       s << "MultiPolygonZ"; break;
        case wkbMultiPolygonM:       s << "MultiPolygonM"; break;
        case wkbMultiPolygonZM:      s << "MultiPolygonZM"; break;
        case wkbGeometryCollection:  s << "GeometryCollection"; break;
        case wkbGeometryCollectionZ: s << "GeometryCollectionZ"; break;
        case wkbGeometryCollectionM: s << "GeometryCollectionM"; break;
        case wkbGeometryCollectionZM:s << "GeometryCollectionZM"; break;
        default:                     s << "wkbUnknown(" << type << ")"; break;
        }

        return s.str();
    }

};

mapnik::geometry::geometry geometry_utils::from_wkb(const char* wkb,
                                                        unsigned size,
                                                        wkbFormat format)
{
    wkb_reader reader(wkb, size, format);
    mapnik::geometry::geometry geom(reader.read());
    mapnik::geometry::correct(geom);
    return geom;
}

} // namespace mapnik
<|endoftext|>


<commit_before>#include <iostream>
#include "integration_test_helper.h"
#include "global_include.h"
#include "dronecode_sdk.h"
#include "plugins/action/action.h"
#include "plugins/telemetry/telemetry.h"

using namespace dronecode_sdk;
using namespace std::placeholders; // for `_1`

void receive_result(ActionResult result);
void receive_health_all_ok(bool all_ok);
void receive_in_air(bool in_air);

static bool _all_ok = false;
static bool _in_air = false;

TEST_F(SitlTest, ActionHoverAsync)
{
    DronecodeSDK dc;

    ConnectionResult ret = dc.add_udp_connection();
    ASSERT_EQ(ret, ConnectionResult::SUCCESS);

    // Wait for system to connect via heartbeat.
    std::this_thread::sleep_for(std::chrono::seconds(2));

    // TODO: this test is pretty dumb, should be improved with more checks.
    System &system = dc.system();

    auto telemetry = std::make_shared<Telemetry>(system);
    telemetry->health_all_ok_async(std::bind(&receive_health_all_ok, _1));
    telemetry->in_air_async(std::bind(&receive_in_air, _1));

    auto action = std::make_shared<Action>(system);

    while (!_all_ok) {
        std::cout << "Waiting to be ready..." << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    action->arm_async(std::bind(&receive_result, _1));
    std::this_thread::sleep_for(std::chrono::seconds(2));

    action->set_takeoff_altitude(5.0f);

    action->takeoff_async(std::bind(&receive_result, _1));
    std::this_thread::sleep_for(std::chrono::seconds(5));

    action->land_async(std::bind(&receive_result, _1));

    while (_in_air) {
        std::cout << "Waiting to be landed..." << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    action->disarm_async(std::bind(&receive_result, _1));
    std::this_thread::sleep_for(std::chrono::seconds(2));
}

void receive_result(ActionResult result)
{
    LogDebug() << "got result: " << unsigned(result);
    EXPECT_EQ(result, ActionResult::SUCCESS);
}

void receive_health_all_ok(bool all_ok)
{
    if (all_ok && !_all_ok) {
        LogDebug() << "we're ready, let's go";
        _all_ok = true;
    }
}

void receive_in_air(bool in_air)
{
    _in_air = in_air;
}
<commit_msg>integration_tests: remove printf<commit_after>#include <iostream>
#include "integration_test_helper.h"
#include "global_include.h"
#include "dronecode_sdk.h"
#include "plugins/action/action.h"
#include "plugins/telemetry/telemetry.h"

using namespace dronecode_sdk;
using namespace std::placeholders; // for `_1`

void receive_result(ActionResult result);
void receive_health_all_ok(bool all_ok);
void receive_in_air(bool in_air);

static bool _all_ok = false;
static bool _in_air = false;

TEST_F(SitlTest, ActionHoverAsync)
{
    DronecodeSDK dc;

    ConnectionResult ret = dc.add_udp_connection();
    ASSERT_EQ(ret, ConnectionResult::SUCCESS);

    // Wait for system to connect via heartbeat.
    std::this_thread::sleep_for(std::chrono::seconds(2));

    // TODO: this test is pretty dumb, should be improved with more checks.
    System &system = dc.system();

    auto telemetry = std::make_shared<Telemetry>(system);
    telemetry->health_all_ok_async(std::bind(&receive_health_all_ok, _1));
    telemetry->in_air_async(std::bind(&receive_in_air, _1));

    auto action = std::make_shared<Action>(system);

    while (!_all_ok) {
        std::cout << "Waiting to be ready..." << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    action->arm_async(std::bind(&receive_result, _1));
    std::this_thread::sleep_for(std::chrono::seconds(2));

    action->set_takeoff_altitude(5.0f);

    action->takeoff_async(std::bind(&receive_result, _1));
    std::this_thread::sleep_for(std::chrono::seconds(5));

    action->land_async(std::bind(&receive_result, _1));

    while (_in_air) {
        std::cout << "Waiting to be landed..." << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    action->disarm_async(std::bind(&receive_result, _1));
    std::this_thread::sleep_for(std::chrono::seconds(2));
}

void receive_result(ActionResult result)
{
    EXPECT_EQ(result, ActionResult::SUCCESS);
}

void receive_health_all_ok(bool all_ok)
{
    if (all_ok && !_all_ok) {
        LogDebug() << "we're ready, let's go";
        _all_ok = true;
    }
}

void receive_in_air(bool in_air)
{
    _in_air = in_air;
}
<|endoftext|>