<commit_before>/** @file gsTensorBSpline.hpp

    @brief Provides implementation of a tensor-product B-spline patch
    of arbitrary dimension

    This file is part of the G+Smo library.

    This Source Code Form is subject to the terms of the Mozilla Public
    License, v. 2.0. If a copy of the MPL was not distributed with this
    file, You can obtain one at http://mozilla.org/MPL/2.0/.

    Author(s): A. Mantzaflaris
*/

#pragma once

#include <gsNurbs/gsBSplineBasis.h>
#include <gsNurbs/gsBoehm.h>

#include <gsUtils/gsMultiIndexIterators.h>

#include <gsIO/gsXml.h>
#include <gsIO/gsXmlGenericUtils.hpp>

#include <gsTensor/gsTensorTools.h>

#include <gsCore/gsConstantFunction.h>

namespace gismo
{

template<unsigned d, class T, class KnotVectorType>
gsTensorBSpline<d,T,KnotVectorType>::gsTensorBSpline(gsMatrix<T> const & corner, KnotVectorType const& KV1, KnotVectorType const & KV2)
{
  assert(d==2);

  gsBSplineBasis<T,KnotVectorType> * Bu= new gsBSplineBasis<T,KnotVectorType>(KV1);
  gsBSplineBasis<T,KnotVectorType> * Bv= new gsBSplineBasis<T,KnotVectorType>(KV2);
  Basis *tbasis = new Basis(Bu,Bv) ;//d==2

  int n1 = KV1.size() - KV1.degree() - 1;
  int n2 = KV2.size() - KV2.degree() - 1;

  GISMO_ASSERT( (corner.rows()==4) && (corner.cols()==3),
           "gsTensorBSpline: Please make sure that the size of *corner* is 4-by-3");

  gsMatrix<T> pcp (n1*n2, 3);
  // set up CPs on boundary first. The inner CPs on each boundary curve are
  // uniformly linear dependent on the two corner CPs
  int j=0; // boundary v=0
  for (int i=0; i<=n1-1; i++)
  {
      for (unsigned int xi=0; xi<=2; xi++) //specification of x or y or z
      {
          pcp(i+j*n1,xi)=corner(0,xi) + i/((T)(n1-1))*( corner(1,xi) - corner(0,xi) );
      }
  }
  j=n2-1; // boundary v=1
  for (int i=0; i<=n1-1; i++)
  {
      for (unsigned int xi=0; xi<=2; xi++) //specification of x or y or z
      {
          pcp(i+j*n1,xi)=corner(3,xi) + i/((T)(n1-1))*( corner(2,xi) - corner(3,xi) );
      }
  }
  int i=0; // boundary u=0;
  for (j=0; j<=n2-1; j++)
  {
      for (unsigned int xi=0; xi<=2; xi++) //specification of x or y or z
      {
          pcp(i+j*n1,xi)=corner(0,xi) + j/((T)(n2-1))*( corner(3,xi) - corner(0,xi) );
      }
  }
  i=n1-1; // boundary u=1;
  for (j=0; j<=n2-1; j++)
  {
      for (unsigned int xi=0; xi<=2; xi++) //specification of x or y or z
      {
          pcp(i+j*n1,xi)=corner(1,xi) + j/((T)(n2-1))*( corner(2,xi) - corner(1,xi) );
      }
  }
  // uniformly linear dependent in horizontal direction
  for (j=1; j<=n2-2; j++)
  {
    for (i=1; i<=n1-2; i++)
    {
      for (unsigned int xi=0; xi<=2; xi++) //specification of x or y or z
      {
          pcp(i+j*n1,xi)=pcp(0+j*n1,xi) + i/((T)(n1-1))*( pcp(n1-1+j*n1,xi)-pcp(0+j*n1,xi) );
      }
    }
  }

  this->m_basis = tbasis;
  this->m_coefs.swap( pcp );
}

// todo: move to hpp
template<unsigned d, class T, class KnotVectorType>
void gsTensorBSpline<d,T,KnotVectorType>::slice(index_t dir_fixed,T par,
                                                BoundaryGeometryType & result) const
{
    GISMO_ASSERT(d-1>=0,"d must be greater or equal than 1");
    GISMO_ASSERT(dir_fixed>=0 && static_cast<unsigned>(dir_fixed)<d,"cannot fix a dir greater than dim or smaller than 0");
    // construct the d-1 basis
    boxSide side(dir_fixed,0);
    BoundaryBasisType *tbasis = this->basis().boundaryBasis(side) ;

    if(d==1)
    {
        gsMatrix<T> val(1,1),point;
        val(0,0)=par;
        this->eval_into(val,point);
        result = BoundaryGeometryType(*tbasis, point );
    }
    else
    {
        const int mult   = this->basis().knots(dir_fixed).multiplicity(par);
        const int degree = this->basis().degree(dir_fixed);

        gsMatrix<T> coefs;
        if( mult>=degree )
        {
            // no knot insertion needed, just extract the right coefficients
            constructCoefsForSlice(dir_fixed,par,*this,coefs);
        }
        else
        {
            // clone the basis and inserting upto degree knots at par
            gsTensorBSpline<d,T,KnotVectorType>* clone = this->clone();

            gsVector<index_t,d> intStrides;
            this->basis().stride_cwise(intStrides);
            gsTensorBoehm<T,KnotVectorType,gsMatrix<T> >(
                        clone->basis().knots(dir_fixed),clone->coefs(),par,dir_fixed,
                        intStrides.template cast<unsigned>(), degree-mult,true);

            // extract right ceofficients
            constructCoefsForSlice(dir_fixed,par,*clone,coefs);
            delete clone;
        }

        // construct the object
        //result = gsTensorBSpline<d-1,T>(*tbasis, give(coefs) );
        //result = BoundaryGeometry(*tbasis, give(coefs) );
        result = BoundaryGeometryType(*tbasis, coefs );
    }
    delete tbasis;
}

template<unsigned d, class T, class KnotVectorType>
void gsTensorBSpline<d,T,KnotVectorType>::reverse(unsigned k)
{
    //todo: use flipTensor to generalize to any dimension
    GISMO_ASSERT(d==2, "only 2D for now");

    gsVector<int> str(d);
    gsVector<int> sz (d);
    gsTensorBSplineBasis<d,T,KnotVectorType> & tbsbasis = this->basis();

    sz[0]  = tbsbasis.component(k).size();
    sz[1]  = tbsbasis.component(!k).size();
    str[0] = tbsbasis.stride( k );
    str[1] = tbsbasis.stride(!k );

    for  ( int i=0; i< sz[0]; i++ )
        for  ( int j=0; j< sz[1]/2; j++ )
        {
            this->m_coefs.row(i*str[0] + j*str[1] ).swap(
                this->m_coefs.row(i*str[0] + (sz[1]-j-1)*str[1] )
                );
        }
    tbsbasis.component(k).reverse();
}

template<unsigned d, class T, class KnotVectorType>
bool gsTensorBSpline<d,T,KnotVectorType>::isPatchCorner(gsMatrix<T> const &v, T tol) const
{
    gsVector<index_t,d> str(d), vupp(d), curr = gsVector<index_t,d>::Zero(d);
    this->basis().stride_cwise(str);
    this->basis().size_cwise(vupp);
    vupp.array() -= 1;

    do // loop over all vertices
    {
        if ( (v - m_coefs.row(curr.dot(str))).squaredNorm() < tol )
            return true;
    }
    while ( nextCubeVertex(curr, vupp) );

    return false;
}

template<unsigned d, class T, class KnotVectorType>
void gsTensorBSpline<d,T,KnotVectorType>::findCorner(const gsMatrix<T> & v,
                                                     gsVector<index_t,d> & curr,
                                                     T tol)
{
    gsVector<index_t,d> str, // Tensor strides
                        sz; // Tensor sizes
    this->basis().stride_cwise(str);
    this->basis().size_cwise(sz);

    curr.setZero();
    do // loop over all vertices
    {
        if ( (v - m_coefs.row(curr.dot(str))).squaredNorm() < tol )
            return;
    }
    while ( nextLexicographic(curr, sz) );

    // Corner not found, Invalidate the result
    curr.swap(sz);
    gsWarn<<"Point "<< v <<" is not an corner of the patch. (Call isPatchCorner() first!).\n";
}

template<unsigned d, class T, class KnotVectorType>
void gsTensorBSpline<d,T,KnotVectorType>::setOriginCorner(gsMatrix<T> const &v)
{
    gsVector<index_t,d> curr;
    findCorner(v, curr);
    if ( curr[0] == this->basis().size(0) );
         return;
    for(unsigned k = 0; k!=d; ++k)
        if ( curr[k] != 0 )
            this->reverse(k);
}

template<unsigned d, class T, class KnotVectorType>
void gsTensorBSpline<d,T,KnotVectorType>::setFurthestCorner(gsMatrix<T> const &v)
{
    gsVector<index_t,d> curr;
    findCorner(v, curr);
    if ( curr[0] == this->basis().size(0) );
         return;
    for(unsigned k = 0; k!=d; ++k)
        if ( curr[k] == 0 )
            this->reverse(k);
}

template<unsigned d, class T, class KnotVectorType>
void gsTensorBSpline<d,T,KnotVectorType>::degreeElevate(int const i, int const dir)
{
    if (dir == -1)
    {
        for (unsigned j = 0; j < d; ++j)
            degreeElevate(i, j);
        return;
    }

    GISMO_ASSERT( dir >= 0 && static_cast<unsigned>(dir) < d,
                  "Invalid basis component "<< dir <<" requested for degree elevation" );

    const index_t n = this->m_coefs.cols();

    gsVector<index_t,d> sz;
    this->basis().size_cwise(sz);

    swapTensorDirection(0, dir, sz, this->m_coefs);
    this->m_coefs.resize( sz[0], n * sz.template tail<d-1>().prod() );

    bspline::degreeElevateBSpline(this->basis().component(dir), this->m_coefs, i);
    sz[0] = this->m_coefs.rows();

    this->m_coefs.resize( sz.prod(), n );
    swapTensorDirection(0, dir, sz, this->m_coefs);
}

template<unsigned d, class T, class KnotVectorType>
void gsTensorBSpline<d,T,KnotVectorType>::constructCoefsForSlice(unsigned dir_fixed,T par,const gsTensorBSpline<d,T,KnotVectorType>& geo,gsMatrix<T>& result) const
{
    const gsTensorBSplineBasis<d,T,KnotVectorType>& base = geo.basis();
    const gsMatrix<T>& fullCoefs=geo.coefs();
    // pick the right coefficients and store them in coefs
    const unsigned degree = base.degree(dir_fixed);
    const KnotVectorType& knots = base.knots(dir_fixed);
    const int index = knots.findspan(par)-degree;
    gsVector<index_t,d> sizes,lowerCorner,upperCorner;
    base.size_cwise( sizes );
    lowerCorner.setZero();
    lowerCorner(dir_fixed)=index;
    upperCorner=sizes;
    upperCorner(dir_fixed)=index+1;

    // to do: gsMatrix<index_t> ind = gsTensorBasis::coefSlice(dim_fixed, index) ?

    gsTensorGridIterator<index_t> gridIter(sizes);
    gsTensorGridIterator<index_t> * iter = gridIter.makeSubGridIterator(lowerCorner,upperCorner);
    index_t size=1;
    for(unsigned i = 0;i<d;++i)
        if(dir_fixed!=i)
            size*=sizes(i);
    result.resize(size,fullCoefs.cols());
    index_t i=0;
    for(iter->first();iter->good();iter->next())
    {
        result.row(i)=fullCoefs.row(iter->flatIndex());
        ++i;
    }

    delete iter;
}

template<unsigned d, class T, class KnotVectorType>
std::ostream & gsTensorBSpline<d,T,KnotVectorType>::print(std::ostream &os) const
{
    os << "Tensor BSpline geometry "<< "R^"<< d <<
        " --> R^"<< this->geoDim()
       << ", #control pnts= "<< this->coefsSize();
    if ( m_coefs.size() )
        os << ": "<< this->coef(0) <<" ... "<< this->coef(this->coefsSize()-1);
    if ( m_basis )
        os<<"\nBasis:\n" << this->basis() ;
    return os;
}

namespace internal
{

/// @brief Get a Tensor BSpline from XML data
///
/// \ingroup Nurbs
template<unsigned d, class T, class KnotVectorType>
class gsXml< gsTensorBSpline<d,T, KnotVectorType> >
{
private:
    gsXml() { }
public:
    GSXML_COMMON_FUNCTIONS(gsTensorBSpline<TMPLA3(d,T,KnotVectorType)>);
    static std::string tag ()  { return "Geometry"; }
    static std::string type () { return "TensorBSpline" +  to_string(d); }

    static gsTensorBSpline<d,T,KnotVectorType> * get (gsXmlNode * node)
    {
        return getGeometryFromXml< gsTensorBSpline<d,T,KnotVectorType> >( node );
    }

    static gsXmlNode * put (const gsTensorBSpline<d,T,KnotVectorType> & obj,
                            gsXmlTree & data)
    {
        return putGeometryToXml(obj,data);
    }
};

}// namespace internal

} // namespace gismo
<commit_msg>fix typo<commit_after>/** @file gsTensorBSpline.hpp

    @brief Provides implementation of a tensor-product B-spline patch
    of arbitrary dimension

    This file is part of the G+Smo library.

    This Source Code Form is subject to the terms of the Mozilla Public
    License, v. 2.0. If a copy of the MPL was not distributed with this
    file, You can obtain one at http://mozilla.org/MPL/2.0/.

    Author(s): A. Mantzaflaris
*/

#pragma once

#include <gsNurbs/gsBSplineBasis.h>
#include <gsNurbs/gsBoehm.h>

#include <gsUtils/gsMultiIndexIterators.h>

#include <gsIO/gsXml.h>
#include <gsIO/gsXmlGenericUtils.hpp>

#include <gsTensor/gsTensorTools.h>

#include <gsCore/gsConstantFunction.h>

namespace gismo
{

template<unsigned d, class T, class KnotVectorType>
gsTensorBSpline<d,T,KnotVectorType>::gsTensorBSpline(gsMatrix<T> const & corner, KnotVectorType const& KV1, KnotVectorType const & KV2)
{
  assert(d==2);

  gsBSplineBasis<T,KnotVectorType> * Bu= new gsBSplineBasis<T,KnotVectorType>(KV1);
  gsBSplineBasis<T,KnotVectorType> * Bv= new gsBSplineBasis<T,KnotVectorType>(KV2);
  Basis *tbasis = new Basis(Bu,Bv) ;//d==2

  int n1 = KV1.size() - KV1.degree() - 1;
  int n2 = KV2.size() - KV2.degree() - 1;

  GISMO_ASSERT( (corner.rows()==4) && (corner.cols()==3),
           "gsTensorBSpline: Please make sure that the size of *corner* is 4-by-3");

  gsMatrix<T> pcp (n1*n2, 3);
  // set up CPs on boundary first. The inner CPs on each boundary curve are
  // uniformly linear dependent on the two corner CPs
  int j=0; // boundary v=0
  for (int i=0; i<=n1-1; i++)
  {
      for (unsigned int xi=0; xi<=2; xi++) //specification of x or y or z
      {
          pcp(i+j*n1,xi)=corner(0,xi) + i/((T)(n1-1))*( corner(1,xi) - corner(0,xi) );
      }
  }
  j=n2-1; // boundary v=1
  for (int i=0; i<=n1-1; i++)
  {
      for (unsigned int xi=0; xi<=2; xi++) //specification of x or y or z
      {
          pcp(i+j*n1,xi)=corner(3,xi) + i/((T)(n1-1))*( corner(2,xi) - corner(3,xi) );
      }
  }
  int i=0; // boundary u=0;
  for (j=0; j<=n2-1; j++)
  {
      for (unsigned int xi=0; xi<=2; xi++) //specification of x or y or z
      {
          pcp(i+j*n1,xi)=corner(0,xi) + j/((T)(n2-1))*( corner(3,xi) - corner(0,xi) );
      }
  }
  i=n1-1; // boundary u=1;
  for (j=0; j<=n2-1; j++)
  {
      for (unsigned int xi=0; xi<=2; xi++) //specification of x or y or z
      {
          pcp(i+j*n1,xi)=corner(1,xi) + j/((T)(n2-1))*( corner(2,xi) - corner(1,xi) );
      }
  }
  // uniformly linear dependent in horizontal direction
  for (j=1; j<=n2-2; j++)
  {
    for (i=1; i<=n1-2; i++)
    {
      for (unsigned int xi=0; xi<=2; xi++) //specification of x or y or z
      {
          pcp(i+j*n1,xi)=pcp(0+j*n1,xi) + i/((T)(n1-1))*( pcp(n1-1+j*n1,xi)-pcp(0+j*n1,xi) );
      }
    }
  }

  this->m_basis = tbasis;
  this->m_coefs.swap( pcp );
}

// todo: move to hpp
template<unsigned d, class T, class KnotVectorType>
void gsTensorBSpline<d,T,KnotVectorType>::slice(index_t dir_fixed,T par,
                                                BoundaryGeometryType & result) const
{
    GISMO_ASSERT(d-1>=0,"d must be greater or equal than 1");
    GISMO_ASSERT(dir_fixed>=0 && static_cast<unsigned>(dir_fixed)<d,"cannot fix a dir greater than dim or smaller than 0");
    // construct the d-1 basis
    boxSide side(dir_fixed,0);
    BoundaryBasisType *tbasis = this->basis().boundaryBasis(side) ;

    if(d==1)
    {
        gsMatrix<T> val(1,1),point;
        val(0,0)=par;
        this->eval_into(val,point);
        result = BoundaryGeometryType(*tbasis, point );
    }
    else
    {
        const int mult   = this->basis().knots(dir_fixed).multiplicity(par);
        const int degree = this->basis().degree(dir_fixed);

        gsMatrix<T> coefs;
        if( mult>=degree )
        {
            // no knot insertion needed, just extract the right coefficients
            constructCoefsForSlice(dir_fixed,par,*this,coefs);
        }
        else
        {
            // clone the basis and inserting upto degree knots at par
            gsTensorBSpline<d,T,KnotVectorType>* clone = this->clone();

            gsVector<index_t,d> intStrides;
            this->basis().stride_cwise(intStrides);
            gsTensorBoehm<T,KnotVectorType,gsMatrix<T> >(
                        clone->basis().knots(dir_fixed),clone->coefs(),par,dir_fixed,
                        intStrides.template cast<unsigned>(), degree-mult,true);

            // extract right ceofficients
            constructCoefsForSlice(dir_fixed,par,*clone,coefs);
            delete clone;
        }

        // construct the object
        //result = gsTensorBSpline<d-1,T>(*tbasis, give(coefs) );
        //result = BoundaryGeometry(*tbasis, give(coefs) );
        result = BoundaryGeometryType(*tbasis, coefs );
    }
    delete tbasis;
}

template<unsigned d, class T, class KnotVectorType>
void gsTensorBSpline<d,T,KnotVectorType>::reverse(unsigned k)
{
    //todo: use flipTensor to generalize to any dimension
    GISMO_ASSERT(d==2, "only 2D for now");

    gsVector<int> str(d);
    gsVector<int> sz (d);
    gsTensorBSplineBasis<d,T,KnotVectorType> & tbsbasis = this->basis();

    sz[0]  = tbsbasis.component(k).size();
    sz[1]  = tbsbasis.component(!k).size();
    str[0] = tbsbasis.stride( k );
    str[1] = tbsbasis.stride(!k );

    for  ( int i=0; i< sz[0]; i++ )
        for  ( int j=0; j< sz[1]/2; j++ )
        {
            this->m_coefs.row(i*str[0] + j*str[1] ).swap(
                this->m_coefs.row(i*str[0] + (sz[1]-j-1)*str[1] )
                );
        }
    tbsbasis.component(k).reverse();
}

template<unsigned d, class T, class KnotVectorType>
bool gsTensorBSpline<d,T,KnotVectorType>::isPatchCorner(gsMatrix<T> const &v, T tol) const
{
    gsVector<index_t,d> str(d), vupp(d), curr = gsVector<index_t,d>::Zero(d);
    this->basis().stride_cwise(str);
    this->basis().size_cwise(vupp);
    vupp.array() -= 1;

    do // loop over all vertices
    {
        if ( (v - m_coefs.row(curr.dot(str))).squaredNorm() < tol )
            return true;
    }
    while ( nextCubeVertex(curr, vupp) );

    return false;
}

template<unsigned d, class T, class KnotVectorType>
void gsTensorBSpline<d,T,KnotVectorType>::findCorner(const gsMatrix<T> & v,
                                                     gsVector<index_t,d> & curr,
                                                     T tol)
{
    gsVector<index_t,d> str, // Tensor strides
                        sz; // Tensor sizes
    this->basis().stride_cwise(str);
    this->basis().size_cwise(sz);

    curr.setZero();
    do // loop over all vertices
    {
        if ( (v - m_coefs.row(curr.dot(str))).squaredNorm() < tol )
            return;
    }
    while ( nextLexicographic(curr, sz) );

    // Corner not found, Invalidate the result
    curr.swap(sz);
    gsWarn<<"Point "<< v <<" is not an corner of the patch. (Call isPatchCorner() first!).\n";
}

template<unsigned d, class T, class KnotVectorType>
void gsTensorBSpline<d,T,KnotVectorType>::setOriginCorner(gsMatrix<T> const &v)
{
    gsVector<index_t,d> curr;
    findCorner(v, curr);
    if ( curr[0] == this->basis().size(0) )
        return;
    for(unsigned k = 0; k!=d; ++k)
        if ( curr[k] != 0 )
            this->reverse(k);
}

template<unsigned d, class T, class KnotVectorType>
void gsTensorBSpline<d,T,KnotVectorType>::setFurthestCorner(gsMatrix<T> const &v)
{
    gsVector<index_t,d> curr;
    findCorner(v, curr);
    if ( curr[0] == this->basis().size(0) )
        return;
    for(unsigned k = 0; k!=d; ++k)
        if ( curr[k] == 0 )
            this->reverse(k);
}

template<unsigned d, class T, class KnotVectorType>
void gsTensorBSpline<d,T,KnotVectorType>::degreeElevate(int const i, int const dir)
{
    if (dir == -1)
    {
        for (unsigned j = 0; j < d; ++j)
            degreeElevate(i, j);
        return;
    }

    GISMO_ASSERT( dir >= 0 && static_cast<unsigned>(dir) < d,
                  "Invalid basis component "<< dir <<" requested for degree elevation" );

    const index_t n = this->m_coefs.cols();

    gsVector<index_t,d> sz;
    this->basis().size_cwise(sz);

    swapTensorDirection(0, dir, sz, this->m_coefs);
    this->m_coefs.resize( sz[0], n * sz.template tail<d-1>().prod() );

    bspline::degreeElevateBSpline(this->basis().component(dir), this->m_coefs, i);
    sz[0] = this->m_coefs.rows();

    this->m_coefs.resize( sz.prod(), n );
    swapTensorDirection(0, dir, sz, this->m_coefs);
}

template<unsigned d, class T, class KnotVectorType>
void gsTensorBSpline<d,T,KnotVectorType>::constructCoefsForSlice(unsigned dir_fixed,T par,const gsTensorBSpline<d,T,KnotVectorType>& geo,gsMatrix<T>& result) const
{
    const gsTensorBSplineBasis<d,T,KnotVectorType>& base = geo.basis();
    const gsMatrix<T>& fullCoefs=geo.coefs();
    // pick the right coefficients and store them in coefs
    const unsigned degree = base.degree(dir_fixed);
    const KnotVectorType& knots = base.knots(dir_fixed);
    const int index = knots.findspan(par)-degree;
    gsVector<index_t,d> sizes,lowerCorner,upperCorner;
    base.size_cwise( sizes );
    lowerCorner.setZero();
    lowerCorner(dir_fixed)=index;
    upperCorner=sizes;
    upperCorner(dir_fixed)=index+1;

    // to do: gsMatrix<index_t> ind = gsTensorBasis::coefSlice(dim_fixed, index) ?

    gsTensorGridIterator<index_t> gridIter(sizes);
    gsTensorGridIterator<index_t> * iter = gridIter.makeSubGridIterator(lowerCorner,upperCorner);
    index_t size=1;
    for(unsigned i = 0;i<d;++i)
        if(dir_fixed!=i)
            size*=sizes(i);
    result.resize(size,fullCoefs.cols());
    index_t i=0;
    for(iter->first();iter->good();iter->next())
    {
        result.row(i)=fullCoefs.row(iter->flatIndex());
        ++i;
    }

    delete iter;
}

template<unsigned d, class T, class KnotVectorType>
std::ostream & gsTensorBSpline<d,T,KnotVectorType>::print(std::ostream &os) const
{
    os << "Tensor BSpline geometry "<< "R^"<< d <<
        " --> R^"<< this->geoDim()
       << ", #control pnts= "<< this->coefsSize();
    if ( m_coefs.size() )
        os << ": "<< this->coef(0) <<" ... "<< this->coef(this->coefsSize()-1);
    if ( m_basis )
        os<<"\nBasis:\n" << this->basis() ;
    return os;
}

namespace internal
{

/// @brief Get a Tensor BSpline from XML data
///
/// \ingroup Nurbs
template<unsigned d, class T, class KnotVectorType>
class gsXml< gsTensorBSpline<d,T, KnotVectorType> >
{
private:
    gsXml() { }
public:
    GSXML_COMMON_FUNCTIONS(gsTensorBSpline<TMPLA3(d,T,KnotVectorType)>);
    static std::string tag ()  { return "Geometry"; }
    static std::string type () { return "TensorBSpline" +  to_string(d); }

    static gsTensorBSpline<d,T,KnotVectorType> * get (gsXmlNode * node)
    {
        return getGeometryFromXml< gsTensorBSpline<d,T,KnotVectorType> >( node );
    }

    static gsXmlNode * put (const gsTensorBSpline<d,T,KnotVectorType> & obj,
                            gsXmlTree & data)
    {
        return putGeometryToXml(obj,data);
    }
};

}// namespace internal

} // namespace gismo
<|endoftext|>


<commit_before>#include "uuidxx.h"
#include <random>
#include <stdio.h>
#include <inttypes.h>

using namespace std;
using namespace uuidxx;

#ifdef _WIN32
#define sscanf sscanf_s
#endif

bool uuid::operator == (const uuid & guid2) const
{
	return memcmp(&guid2, this, sizeof(uuid)) == 0;
}

bool uuid::operator != (const uuid & guid2) const
{
	return !(*this == guid2);
}

bool uuid::operator < (const uuid &guid2) const
{
	return memcmp(this, &guid2, sizeof(uuid)) < 0;
}

bool uuid::operator > (const uuid &guid2) const
{
	return memcmp(this, &guid2, sizeof(uuid)) > 0;
}

uuid::uuid (const std::string &uuidString)
	: uuid(uuidString.c_str())
{
}

uuid::uuid (const char *uuidString)
{
	if (uuidString == nullptr)
	{
		return;
	}

	if (uuidString[0] == '{')
	{
		sscanf(uuidString, "{%08" SCNx32 "-%04" SCNx16 "-%04" SCNx16 "-%02" SCNx8 "%02" SCNx8 "-%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "}", &Uuid.Data1, &Uuid.Data2, &Uuid.Data3, &Uuid.Data4[0], &Uuid.Data4[1], &Uuid.Data4[2], &Uuid.Data4[3], &Uuid.Data4[4], &Uuid.Data4[5], &Uuid.Data4[6], &Uuid.Data4[7]);
	}
	else
	{
		sscanf(uuidString, "%08" SCNx32 "-%04" SCNx16 "-%04" SCNx16 "-%02" SCNx8 "%02" SCNx8 "-%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "", &Uuid.Data1, &Uuid.Data2, &Uuid.Data3, &Uuid.Data4[0], &Uuid.Data4[1], &Uuid.Data4[2], &Uuid.Data4[3], &Uuid.Data4[4], &Uuid.Data4[5], &Uuid.Data4[6], &Uuid.Data4[7]);
	}
}

string uuid::ToString(bool withBraces) const
{
	string buffer;
	buffer.reserve(38);
#ifndef _WIN32
	sprintf(const_cast<char *>(buffer.data()), "%s%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X%s", withBraces ? "{" : "", Uuid.Data1, Uuid.Data2, Uuid.Data3, Uuid.Data4[0], Uuid.Data4[1], Uuid.Data4[2], Uuid.Data4[3], Uuid.Data4[4], Uuid.Data4[5], Uuid.Data4[6], Uuid.Data4[7], withBraces ? "}" : "");
#else
	sprintf_s(const_cast<char *>(buffer.data()), 38+1, "%s%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X%s", withBraces ? "{" : "", Uuid.Data1, Uuid.Data2, Uuid.Data3, Uuid.Data4[0], Uuid.Data4[1], Uuid.Data4[2], Uuid.Data4[3], Uuid.Data4[4], Uuid.Data4[5], Uuid.Data4[6], Uuid.Data4[7], withBraces ? "}" : "");
#endif
	return buffer;
}

uuid uuid::FromString(const char *uuidString)
{
	uuid temp(uuidString);
	return temp;
}

uuid uuid::FromString(const std::string &uuidString)
{
	uuid temp(uuidString.c_str());
	return temp;
}

uuid uuid::Generate()
{
#if !defined(__APPLE__) //mach-o does not support TLS
	thread_local std::random_device rd;
	thread_local auto gen = std::mt19937_64(rd());
#else
	std::random_device rd;
	std::mt19937_64 gen(rd());
#endif
	std::uniform_int_distribution<uint64_t> dis64;

	uuid newGuid;
	newGuid.WideIntegers[0] = dis64(gen);
	newGuid.WideIntegers[1] = dis64(gen);

	return newGuid;
}
<commit_msg>thread_local also has issues on clang/freebsd; disabled there too<commit_after>#include "uuidxx.h"
#include <random>
#include <stdio.h>
#include <inttypes.h>

using namespace std;
using namespace uuidxx;

#ifdef _WIN32
#define sscanf sscanf_s
#endif

bool uuid::operator == (const uuid & guid2) const
{
	return memcmp(&guid2, this, sizeof(uuid)) == 0;
}

bool uuid::operator != (const uuid & guid2) const
{
	return !(*this == guid2);
}

bool uuid::operator < (const uuid &guid2) const
{
	return memcmp(this, &guid2, sizeof(uuid)) < 0;
}

bool uuid::operator > (const uuid &guid2) const
{
	return memcmp(this, &guid2, sizeof(uuid)) > 0;
}

uuid::uuid (const std::string &uuidString)
	: uuid(uuidString.c_str())
{
}

uuid::uuid (const char *uuidString)
{
	if (uuidString == nullptr)
	{
		return;
	}

	if (uuidString[0] == '{')
	{
		sscanf(uuidString, "{%08" SCNx32 "-%04" SCNx16 "-%04" SCNx16 "-%02" SCNx8 "%02" SCNx8 "-%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "}", &Uuid.Data1, &Uuid.Data2, &Uuid.Data3, &Uuid.Data4[0], &Uuid.Data4[1], &Uuid.Data4[2], &Uuid.Data4[3], &Uuid.Data4[4], &Uuid.Data4[5], &Uuid.Data4[6], &Uuid.Data4[7]);
	}
	else
	{
		sscanf(uuidString, "%08" SCNx32 "-%04" SCNx16 "-%04" SCNx16 "-%02" SCNx8 "%02" SCNx8 "-%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "", &Uuid.Data1, &Uuid.Data2, &Uuid.Data3, &Uuid.Data4[0], &Uuid.Data4[1], &Uuid.Data4[2], &Uuid.Data4[3], &Uuid.Data4[4], &Uuid.Data4[5], &Uuid.Data4[6], &Uuid.Data4[7]);
	}
}

string uuid::ToString(bool withBraces) const
{
	string buffer;
	buffer.reserve(38);
#ifndef _WIN32
	sprintf(const_cast<char *>(buffer.data()), "%s%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X%s", withBraces ? "{" : "", Uuid.Data1, Uuid.Data2, Uuid.Data3, Uuid.Data4[0], Uuid.Data4[1], Uuid.Data4[2], Uuid.Data4[3], Uuid.Data4[4], Uuid.Data4[5], Uuid.Data4[6], Uuid.Data4[7], withBraces ? "}" : "");
#else
	sprintf_s(const_cast<char *>(buffer.data()), 38+1, "%s%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X%s", withBraces ? "{" : "", Uuid.Data1, Uuid.Data2, Uuid.Data3, Uuid.Data4[0], Uuid.Data4[1], Uuid.Data4[2], Uuid.Data4[3], Uuid.Data4[4], Uuid.Data4[5], Uuid.Data4[6], Uuid.Data4[7], withBraces ? "}" : "");
#endif
	return buffer;
}

uuid uuid::FromString(const char *uuidString)
{
	uuid temp(uuidString);
	return temp;
}

uuid uuid::FromString(const std::string &uuidString)
{
	uuid temp(uuidString.c_str());
	return temp;
}

uuid uuid::Generate()
{
//mach-o does not support TLS and clang still has issues with thread_local
#if !defined(__APPLE__) && !defined(__clang__)
	thread_local std::random_device rd;
	thread_local auto gen = std::mt19937_64(rd());
#else
	std::random_device rd;
	std::mt19937_64 gen(rd());
#endif
	std::uniform_int_distribution<uint64_t> dis64;

	uuid newGuid;
	newGuid.WideIntegers[0] = dis64(gen);
	newGuid.WideIntegers[1] = dis64(gen);

	return newGuid;
}
<|endoftext|>


<commit_before>/*
 * MRustC - Rust Compiler
 * - By John Hodge (Mutabah/thePowersGang)
 *
 * hir_expand/erased_types.cpp
 * - HIR Expansion - Replace `impl Trait` with the real type
 */
#include <hir/visitor.hpp>
#include <hir/expr.hpp>
#include <hir_typeck/static.hpp>
#include <algorithm>
#include "main_bindings.hpp"

const ::HIR::Function& HIR_Expand_ErasedType_GetFunction(const Span& sp, const StaticTraitResolve& resolve, const ::HIR::Path& origin_path, MonomorphStatePtr& monomorph_cb, ::HIR::PathParams& impl_params)
{
    const ::HIR::Function*  fcn_ptr = nullptr;
    switch(origin_path.m_data.tag())
    {
    case ::HIR::Path::Data::TAGDEAD:
        BUG(Span(), "DEAD in ErasedType - " << origin_path);
    case ::HIR::Path::Data::TAG_UfcsUnknown:
        BUG(Span(), "UfcsUnknown in ErasedType - " << origin_path);
    case ::HIR::Path::Data::TAG_Generic: {
        const auto& pe = origin_path.m_data.as_Generic();
        monomorph_cb = MonomorphStatePtr(nullptr, nullptr, &pe.m_params);
        fcn_ptr = &resolve.m_crate.get_function_by_path(sp, pe.m_path);
        } break;
    case ::HIR::Path::Data::TAG_UfcsKnown:
        // NOTE: This isn't possible yet (will it be? or will it expand to an associated type?)
        TODO(sp, "Replace ErasedType - " << origin_path << " with source (UfcsKnown)");
        break;
    case ::HIR::Path::Data::TAG_UfcsInherent: {
        const auto& pe = origin_path.m_data.as_UfcsInherent();
        // 1. Find correct impl block for the path
        const ::HIR::TypeImpl* impl_ptr = nullptr;
        resolve.m_crate.find_type_impls(pe.type, [&](const auto& ty)->const auto& { return ty; },
            [&](const auto& impl) {
                DEBUG("- impl" << impl.m_params.fmt_args() << " " << impl.m_type);
                auto it = impl.m_methods.find(pe.item);
                if( it == impl.m_methods.end() )
                    return false;
                fcn_ptr = &it->second.data;
                impl_ptr = &impl;
                return true;
            });
        ASSERT_BUG(sp, fcn_ptr, "Failed to locate function " << origin_path);
        assert(impl_ptr);

        // 2. Obtain monomorph_cb (including impl params)
        impl_params.m_types.resize(impl_ptr->m_params.m_types.size());
        class Matcher: public ::HIR::MatchGenerics
        {
            ::HIR::PathParams& impl_params;
        public:
            Matcher(::HIR::PathParams& impl_params): impl_params(impl_params) {}

            ::HIR::Compare match_ty(const ::HIR::GenericRef& g, const ::HIR::TypeRef& ty, ::HIR::t_cb_resolve_type _resolve_cb) override {
                assert( g.binding < impl_params.m_types.size() );
                impl_params.m_types[g.binding] = ty.clone();
                return ::HIR::Compare::Equal;
            }
            ::HIR::Compare match_val(const ::HIR::GenericRef& g, const ::HIR::Literal& sz) override {
                TODO(Span(), "HIR_Expand_ErasedType_GetFunction::Matcher::match_val " << g << " with " << sz);
            }
        } matcher(impl_params);
        impl_ptr->m_type .match_test_generics(sp, pe.type, [](const auto& x)->const auto&{return x;}, matcher);
        for(const auto& t : impl_params.m_types)
        {
            if( t == ::HIR::TypeRef() )
            {
                TODO(sp, "Handle ErasedType where an impl parameter comes from a bound - " << origin_path);
            }
        }

        monomorph_cb = MonomorphStatePtr(&pe.type, &impl_params, &pe.params);
        } break;
    }
    assert(fcn_ptr);
    return *fcn_ptr;
}

namespace {

    class ExprVisitor_Extract:
        public ::HIR::ExprVisitorDef
    {
        const StaticTraitResolve& m_resolve;

    public:
        ExprVisitor_Extract(const StaticTraitResolve& resolve):
            m_resolve(resolve)
        {
        }

        void visit_root(::HIR::ExprPtr& root)
        {
            root->visit(*this);
            visit_type(root->m_res_type);
            for(auto& ty : root.m_bindings)
                visit_type(ty);
            for(auto& ty : root.m_erased_types)
                visit_type(ty);
        }

        void visit_node_ptr(::std::unique_ptr< ::HIR::ExprNode>& node_ptr) override {
            assert(node_ptr);
            node_ptr->visit(*this);
            visit_type(node_ptr->m_res_type);
        }

        void visit_type(::HIR::TypeRef& ty) override
        {
            static Span sp;

            if( ty.data().is_ErasedType() )
            {
                TRACE_FUNCTION_FR(ty, ty);

                const auto& e = ty.data().as_ErasedType();

                ::HIR::PathParams   impl_params;    // cache.
                MonomorphStatePtr    monomorph_cb;
                const auto& fcn = HIR_Expand_ErasedType_GetFunction(sp, m_resolve, e.m_origin, monomorph_cb, impl_params);
                const auto& erased_types = fcn.m_code.m_erased_types;

                ASSERT_BUG(sp, e.m_index < erased_types.size(), "Erased type index out of range for " << e.m_origin << " - " << e.m_index << " >= " << erased_types.size());
                const auto& tpl = erased_types[e.m_index];

                auto new_ty = monomorph_cb.monomorph_type(sp, tpl);
                DEBUG("> " << ty << " => " << new_ty);
                ty = mv$(new_ty);
                // Recurse (TODO: Cleanly prevent infinite recursion - TRACE_FUNCTION does crude prevention)
                visit_type(ty);
            }
            else
            {
                ::HIR::ExprVisitorDef::visit_type(ty);
            }
        }
    };

    class OuterVisitor:
        public ::HIR::Visitor
    {
        StaticTraitResolve  m_resolve;
        const ::HIR::ItemPath* m_fcn_path = nullptr;
    public:
        OuterVisitor(const ::HIR::Crate& crate):
            m_resolve(crate)
        {}

        void visit_expr(::HIR::ExprPtr& exp) override
        {
            if( exp )
            {
                ExprVisitor_Extract    ev(m_resolve);
                ev.visit_root( exp );
            }
        }

        void visit_function(::HIR::ItemPath p, ::HIR::Function& fcn) override
        {
            m_fcn_path = &p;
            ::HIR::Visitor::visit_function(p, fcn);
            m_fcn_path = nullptr;
        }
    };
    class OuterVisitor_Fixup:
        public ::HIR::Visitor
    {
        StaticTraitResolve  m_resolve;
    public:
        OuterVisitor_Fixup(const ::HIR::Crate& crate):
            m_resolve(crate)
        {}

        void visit_type(::HIR::TypeRef& ty) override
        {
            static const Span   sp;
            if( ty.data().is_ErasedType() )
            {
                const auto& e = ty.data().as_ErasedType();

                TRACE_FUNCTION_FR(ty, ty);

                ::HIR::PathParams   impl_params;
                MonomorphStatePtr    monomorph_cb;
                const auto& fcn = HIR_Expand_ErasedType_GetFunction(sp, m_resolve, e.m_origin, monomorph_cb, impl_params);
                const auto& erased_types = fcn.m_code.m_erased_types;

                ASSERT_BUG(sp, e.m_index < erased_types.size(), "Erased type index out of range for " << e.m_origin << " - " << e.m_index << " >= " << erased_types.size());
                const auto& tpl = erased_types[e.m_index];

                auto new_ty = monomorph_cb.monomorph_type(sp, tpl);
                DEBUG("> " << ty << " => " << new_ty);
                ty = mv$(new_ty);
                // Recurse (TODO: Cleanly prevent infinite recursion - TRACE_FUNCTION does crude prevention)
                visit_type(ty);
            }
            else
            {
                ::HIR::Visitor::visit_type(ty);
            }
        }
    };
}

void HIR_Expand_ErasedType(::HIR::Crate& crate)
{
    OuterVisitor    ov(crate);
    ov.visit_crate( crate );

    OuterVisitor_Fixup  ov_fix(crate);
    ov_fix.visit_crate(crate);
}

<commit_msg>Expand Erased Types - Run EAT on result<commit_after>/*
 * MRustC - Rust Compiler
 * - By John Hodge (Mutabah/thePowersGang)
 *
 * hir_expand/erased_types.cpp
 * - HIR Expansion - Replace `impl Trait` with the real type
 */
#include <hir/visitor.hpp>
#include <hir/expr.hpp>
#include <hir_typeck/static.hpp>
#include <algorithm>
#include "main_bindings.hpp"

const ::HIR::Function& HIR_Expand_ErasedType_GetFunction(const Span& sp, const StaticTraitResolve& resolve, const ::HIR::Path& origin_path, MonomorphStatePtr& monomorph_cb, ::HIR::PathParams& impl_params)
{
    const ::HIR::Function*  fcn_ptr = nullptr;
    switch(origin_path.m_data.tag())
    {
    case ::HIR::Path::Data::TAGDEAD:
        BUG(Span(), "DEAD in ErasedType - " << origin_path);
    case ::HIR::Path::Data::TAG_UfcsUnknown:
        BUG(Span(), "UfcsUnknown in ErasedType - " << origin_path);
    case ::HIR::Path::Data::TAG_Generic: {
        const auto& pe = origin_path.m_data.as_Generic();
        monomorph_cb = MonomorphStatePtr(nullptr, nullptr, &pe.m_params);
        fcn_ptr = &resolve.m_crate.get_function_by_path(sp, pe.m_path);
        } break;
    case ::HIR::Path::Data::TAG_UfcsKnown:
        // NOTE: This isn't possible yet (will it be? or will it expand to an associated type?)
        TODO(sp, "Replace ErasedType - " << origin_path << " with source (UfcsKnown)");
        break;
    case ::HIR::Path::Data::TAG_UfcsInherent: {
        const auto& pe = origin_path.m_data.as_UfcsInherent();
        // 1. Find correct impl block for the path
        const ::HIR::TypeImpl* impl_ptr = nullptr;
        resolve.m_crate.find_type_impls(pe.type, [&](const auto& ty)->const auto& { return ty; },
            [&](const auto& impl) {
                DEBUG("- impl" << impl.m_params.fmt_args() << " " << impl.m_type);
                auto it = impl.m_methods.find(pe.item);
                if( it == impl.m_methods.end() )
                    return false;
                fcn_ptr = &it->second.data;
                impl_ptr = &impl;
                return true;
            });
        ASSERT_BUG(sp, fcn_ptr, "Failed to locate function " << origin_path);
        assert(impl_ptr);

        // 2. Obtain monomorph_cb (including impl params)
        impl_params.m_types.resize(impl_ptr->m_params.m_types.size());
        class Matcher: public ::HIR::MatchGenerics
        {
            ::HIR::PathParams& impl_params;
        public:
            Matcher(::HIR::PathParams& impl_params): impl_params(impl_params) {}

            ::HIR::Compare match_ty(const ::HIR::GenericRef& g, const ::HIR::TypeRef& ty, ::HIR::t_cb_resolve_type _resolve_cb) override {
                assert( g.binding < impl_params.m_types.size() );
                impl_params.m_types[g.binding] = ty.clone();
                return ::HIR::Compare::Equal;
            }
            ::HIR::Compare match_val(const ::HIR::GenericRef& g, const ::HIR::Literal& sz) override {
                TODO(Span(), "HIR_Expand_ErasedType_GetFunction::Matcher::match_val " << g << " with " << sz);
            }
        } matcher(impl_params);
        impl_ptr->m_type .match_test_generics(sp, pe.type, [](const auto& x)->const auto&{return x;}, matcher);
        for(const auto& t : impl_params.m_types)
        {
            if( t == ::HIR::TypeRef() )
            {
                TODO(sp, "Handle ErasedType where an impl parameter comes from a bound - " << origin_path);
            }
        }

        monomorph_cb = MonomorphStatePtr(&pe.type, &impl_params, &pe.params);
        } break;
    }
    assert(fcn_ptr);
    return *fcn_ptr;
}

namespace {

    class ExprVisitor_Extract:
        public ::HIR::ExprVisitorDef
    {
        const StaticTraitResolve& m_resolve;

    public:
        ExprVisitor_Extract(const StaticTraitResolve& resolve):
            m_resolve(resolve)
        {
        }

        void visit_root(::HIR::ExprPtr& root)
        {
            root->visit(*this);
            visit_type(root->m_res_type);
            for(auto& ty : root.m_bindings)
                visit_type(ty);
            for(auto& ty : root.m_erased_types)
                visit_type(ty);
        }

        void visit_node_ptr(::std::unique_ptr< ::HIR::ExprNode>& node_ptr) override {
            assert(node_ptr);
            node_ptr->visit(*this);
            visit_type(node_ptr->m_res_type);
        }

        void visit_type(::HIR::TypeRef& ty) override
        {
            static Span sp;

            if( ty.data().is_ErasedType() )
            {
                TRACE_FUNCTION_FR(ty, ty);

                const auto& e = ty.data().as_ErasedType();

                ::HIR::PathParams   impl_params;    // cache.
                MonomorphStatePtr    monomorph_cb;
                const auto& fcn = HIR_Expand_ErasedType_GetFunction(sp, m_resolve, e.m_origin, monomorph_cb, impl_params);
                const auto& erased_types = fcn.m_code.m_erased_types;

                ASSERT_BUG(sp, e.m_index < erased_types.size(), "Erased type index out of range for " << e.m_origin << " - " << e.m_index << " >= " << erased_types.size());
                const auto& tpl = erased_types[e.m_index];

                auto new_ty = monomorph_cb.monomorph_type(sp, tpl);
                m_resolve.expand_associated_types(sp, new_ty);
                DEBUG("> " << ty << " => " << new_ty);
                ty = mv$(new_ty);
                // Recurse (TODO: Cleanly prevent infinite recursion - TRACE_FUNCTION does crude prevention)
                visit_type(ty);
            }
            else
            {
                ::HIR::ExprVisitorDef::visit_type(ty);
            }
        }
    };

    class OuterVisitor:
        public ::HIR::Visitor
    {
        StaticTraitResolve  m_resolve;
        const ::HIR::ItemPath* m_fcn_path = nullptr;
    public:
        OuterVisitor(const ::HIR::Crate& crate):
            m_resolve(crate)
        {}

        void visit_expr(::HIR::ExprPtr& exp) override
        {
            if( exp )
            {
                ExprVisitor_Extract    ev(m_resolve);
                ev.visit_root( exp );
            }
        }

        void visit_function(::HIR::ItemPath p, ::HIR::Function& fcn) override
        {
            m_fcn_path = &p;
            ::HIR::Visitor::visit_function(p, fcn);
            m_fcn_path = nullptr;
        }
    };
    class OuterVisitor_Fixup:
        public ::HIR::Visitor
    {
        StaticTraitResolve  m_resolve;
    public:
        OuterVisitor_Fixup(const ::HIR::Crate& crate):
            m_resolve(crate)
        {}

        void visit_type(::HIR::TypeRef& ty) override
        {
            static const Span   sp;
            if( ty.data().is_ErasedType() )
            {
                const auto& e = ty.data().as_ErasedType();

                TRACE_FUNCTION_FR(ty, ty);

                ::HIR::PathParams   impl_params;
                MonomorphStatePtr    monomorph_cb;
                const auto& fcn = HIR_Expand_ErasedType_GetFunction(sp, m_resolve, e.m_origin, monomorph_cb, impl_params);
                const auto& erased_types = fcn.m_code.m_erased_types;

                ASSERT_BUG(sp, e.m_index < erased_types.size(), "Erased type index out of range for " << e.m_origin << " - " << e.m_index << " >= " << erased_types.size());
                const auto& tpl = erased_types[e.m_index];

                auto new_ty = monomorph_cb.monomorph_type(sp, tpl);
                DEBUG("> " << ty << " => " << new_ty);
                ty = mv$(new_ty);
                // Recurse (TODO: Cleanly prevent infinite recursion - TRACE_FUNCTION does crude prevention)
                visit_type(ty);
            }
            else
            {
                ::HIR::Visitor::visit_type(ty);
            }
        }
    };
}

void HIR_Expand_ErasedType(::HIR::Crate& crate)
{
    OuterVisitor    ov(crate);
    ov.visit_crate( crate );

    OuterVisitor_Fixup  ov_fix(crate);
    ov_fix.visit_crate(crate);
}

<|endoftext|>


<commit_before>#include "SDL/SDL.h"
#include "SDL/SDL_ttf.h"
#include "SDL/SDL_gfxPrimitives.h"
#include <vector>
#include <iostream>
using namespace std;

// ----> GUI FRAME
class GuiFrame {
  protected:
    Uint8 bpp;
    SDL_Surface *frame;
    SDL_Rect frameRect;
    SDL_Rect innerFrameRect;
    bool hasFrame;
    bool hasBorder;
    Uint8 borderWidth;
    struct stGuiFrameBorderColor {
      Uint8 r;
      Uint8 g;
      Uint8 b;
    } borderColor;
  public:
    GuiFrame(Uint8);
    void set(Uint16, Uint16, Uint16, Uint16);
    void setBorder(Uint8, Uint8, Uint8, Uint8);
    SDL_Rect *getRect();
    SDL_Rect *getInnerRect();
    SDL_Surface *getSurface();
    void bgFill(Uint8, Uint8, Uint8);
    void drawBorder();
    void unset();
    ~GuiFrame();
};
GuiFrame::GuiFrame(Uint8 _bpp) {
  bpp = _bpp;
  hasFrame = false;
  hasBorder = false;
}
void GuiFrame::set(Uint16 _x, Uint16 _y, Uint16 _w, Uint16 _h) {
  unset();
  frameRect.x = _x;
  frameRect.y = _y;
  frameRect.w = _w;
  frameRect.h = _h;
  innerFrameRect = frameRect;
  frame = SDL_CreateRGBSurface(
    0, frameRect.w, frameRect.h, bpp, 0, 0, 0, 0
  );
  hasFrame = true;
}
void GuiFrame::setBorder(Uint8 _width, Uint8 _r, Uint8 _g, Uint8 _b) {
  borderWidth = _width;
  borderColor.r = _r;
  borderColor.g = _g;
  borderColor.b = _b;
  hasBorder = true;
  innerFrameRect.x += _width;
  innerFrameRect.y += _width;
  innerFrameRect.w = frameRect.w - 1 - 2 * _width;
  innerFrameRect.h = frameRect.h - 1 - 2 * _width;
}
SDL_Rect *GuiFrame::getRect() {
  return &frameRect;
}
SDL_Rect *GuiFrame::getInnerRect() {
  return &innerFrameRect;
}
SDL_Surface *GuiFrame::getSurface() {
  return frame;
}
void GuiFrame::bgFill(Uint8 _r, Uint8 _g, Uint8 _b) {
  SDL_FillRect(
    frame, &frame->clip_rect, SDL_MapRGB(frame->format, _r, _g, _b)
  );
}
void GuiFrame::drawBorder() {
  if (hasBorder == true) {
    boxRGBA(
      frame, 0, 0, frame->clip_rect.w - 1, borderWidth,
      borderColor.r, borderColor.g, borderColor.b, 255
    ); // top
    boxRGBA(
      frame,
      0, 0,
      borderWidth, frame->clip_rect.h - 1,
      borderColor.r, borderColor.g, borderColor.b, 255
    ); // left
    boxRGBA(
      frame,
      0, frame->clip_rect.h - 1 - borderWidth,
      frame->clip_rect.w - 1, frame->clip_rect.h - 1,
      borderColor.r, borderColor.g, borderColor.b, 255
    ); // bottom
    boxRGBA(
      frame,
      frame->clip_rect.w - 1 - borderWidth, 0,
      frame->clip_rect.w - 1, frame->clip_rect.h - 1,
      borderColor.r, borderColor.g, borderColor.b, 255
    ); // bottom
  }
}
void GuiFrame::unset() {
  if (hasFrame == true) {
    SDL_FreeSurface(frame);
    hasFrame = false;
  }
}
GuiFrame::~GuiFrame() {
  unset();
}

// ----> GUI SCREEN
class GuiScreen {
  protected:
    SDL_Surface *screen;
    vector<GuiFrame *> frames;
  public:
    GuiScreen(SDL_Surface *);
    GuiFrame *addFrame(Uint16, Uint16, Uint16, Uint16);
    void bgFill(Uint8, Uint8, Uint8);
    virtual void update();
    virtual ~GuiScreen();
};
GuiScreen::GuiScreen(SDL_Surface *_screen) {
  screen = _screen;
}
GuiFrame *GuiScreen::addFrame(Uint16 _x, Uint16 _y, Uint16 _w, Uint16 _h) {
  GuiFrame *tempFrame = new GuiFrame(screen->format->BytesPerPixel * 8);
  tempFrame->set(_x, _y, _w, _h);
  frames.push_back(tempFrame);
  return tempFrame;
}
void GuiScreen::bgFill(Uint8 _r, Uint8 _g, Uint8 _b) {
  SDL_FillRect(
    screen, &screen->clip_rect, SDL_MapRGB(screen->format, _r, _g, _b)
  );
}
void GuiScreen::update() {
  if (frames.size() > 0) {
    vector<GuiFrame *>::iterator it;
    for (it = frames.begin(); it != frames.end(); it++) {
      (*it)->drawBorder();
      SDL_BlitSurface((*it)->getSurface(), NULL, screen, (*it)->getRect());
    }
  }
  SDL_Flip(screen);
}
GuiScreen::~GuiScreen() {
  vector<GuiFrame *>::iterator it;
  for (it = frames.begin(); it != frames.end(); it++) {
    delete (*it);
  }
  frames.clear();
}

// ----> GUI WINDOW
class GuiWindow: public GuiScreen {
  protected:
    int windowFrameIdx;
    Uint8 windowBorderWidth;
    int titleFrameIdx;
    SDL_Surface *titleText;
    bool hasTitleText;
    SDL_Rect *innerRect;
  public:
    GuiWindow(SDL_Surface *);
    void setTitle(string, Uint8, string, Uint8, Uint8, Uint8);
    void addWindowFrame(Uint16, Uint16, Uint16, Uint16, Uint8, Uint8, Uint8);
    void setWindowBorder(Uint8, Uint8, Uint8, Uint8);
    void addTitleFrame(Uint8, Uint8, Uint8);
    void unsetTitleText();
    virtual void update();
    virtual ~GuiWindow();
};
GuiWindow::GuiWindow(SDL_Surface *_screen) : GuiScreen(_screen) {
  hasTitleText = false;
  windowFrameIdx = -1;
  titleFrameIdx = -1;
}
void GuiWindow::setTitle(string _title, Uint8 _fontSize, string _fontFile,
                         Uint8 _r, Uint8 _g, Uint8 _b) {
  unsetTitleText();
  TTF_Font *font = TTF_OpenFont(_fontFile.c_str(), _fontSize);
  SDL_Color tmpFontColor = { _r, _g, _b };
  titleText = TTF_RenderText_Solid(font, _title.c_str(), tmpFontColor);
  TTF_CloseFont(font);
  hasTitleText = true;
}
void GuiWindow::addWindowFrame(Uint16 _x, Uint16 _y, Uint16 _w, Uint16 _h,
                               Uint8 _r, Uint8 _g, Uint8 _b) {
  addFrame(_x, _y, _w, _h);
  windowFrameIdx = frames.size() - 1;
  frames[windowFrameIdx]->bgFill(_r, _g, _b);
  innerRect = frames[windowFrameIdx]->getInnerRect();
}
void GuiWindow::setWindowBorder(Uint8 _width, Uint8 _r, Uint8 _g, Uint8 _b) {
  if (windowFrameIdx > -1) {
    windowBorderWidth = _width;
    frames[windowFrameIdx]->setBorder(_width, _r, _g, _b);
    innerRect = frames[windowFrameIdx]->getInnerRect();
  }
}
void GuiWindow::addTitleFrame(Uint8 _r, Uint8 _g, Uint8 _b) {
  if (hasTitleText == true && windowFrameIdx > -1) {
    addFrame(innerRect->x, innerRect->y, innerRect->w, titleText->h + windowBorderWidth);
    titleFrameIdx = frames.size() - 1;
    frames[titleFrameIdx]->bgFill(_r, _g, _b);
    innerRect->y += titleText->h + windowBorderWidth;
  }
}
void GuiWindow::update() {
  if (hasTitleText == true) {
    SDL_BlitSurface(titleText, NULL, frames[titleFrameIdx]->getSurface(), NULL);
  }
  GuiScreen::update();
}
void GuiWindow::unsetTitleText() {
  if (hasTitleText == true) {
    SDL_FreeSurface(titleText);
    hasTitleText = false;
  }
}
GuiWindow::~GuiWindow() {
  unsetTitleText();
}

// ----> GUI TEXT
class GuiTextWindow: public GuiWindow {
  protected:
    SDL_Surface *text;
    bool hasText;
  public:
    GuiTextWindow(SDL_Surface *);
    vector<string> wrapText(const string &, int);
    void setText(string, Uint8, string, Uint8, Uint8, Uint8);
    void unsetText();
    virtual void update();
    virtual ~GuiTextWindow();
};
GuiTextWindow::GuiTextWindow(SDL_Surface *_screen) : GuiWindow(_screen) {
  hasText = false;
}
vector<string> GuiTextWindow::wrapText(const string &text, int line_length) {

  vector<string> split_text;
  string::size_type start_point = 0, split_point = 0;

  while (start_point + line_length < text.size()) {
    split_point = text.rfind(' ', start_point+line_length);

    if (split_point < start_point || split_point == string::npos) {
      split_text.push_back( text.substr(start_point, line_length) );
      start_point += line_length;
    } else {
      split_text.push_back( text.substr(start_point, split_point-start_point) );
      start_point = split_point + 1;
    }
  }

  split_text.push_back(text.substr(start_point, line_length));
  return split_text;
}
void GuiTextWindow::setText(string _text, Uint8 _fontSize, string _fontFile,
                            Uint8 _r, Uint8 _g, Uint8 _b) {
  unsetText();
  TTF_Font *font = TTF_OpenFont(_fontFile.c_str(), _fontSize);
  SDL_Color tmpFontColor = { _r, _g, _b };

  vector<string> lines = wrapText(_text, 20);
  text = TTF_RenderText_Solid(font, lines[0].c_str(), tmpFontColor);
  /*for (Uint8 i = 0; i < lines.size(); i++) {
    // todo multiline surface
  };*/
  TTF_CloseFont(font);
  hasText = true;
}
void GuiTextWindow::unsetText() {
  if (hasText == true) {
    SDL_FreeSurface(text);
    hasText = false;
  }
}
void GuiTextWindow::update() {
  GuiWindow::update();
  if (hasText == true) {
    SDL_BlitSurface(text, NULL, screen, innerRect);
  }
  SDL_Flip(screen);
}
GuiTextWindow::~GuiTextWindow() {
  unsetText();
}

// ----> MAIN
int main (int argc, char *argv[]) {
  SDL_Init(SDL_INIT_VIDEO);
  TTF_Init();

  SDL_WM_SetCaption("SDL GUI", NULL);
  SDL_Surface* screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);

  GuiTextWindow *gui = new GuiTextWindow(screen);
  gui->setTitle("TEST WINDOW", 24, "mkds.ttf", 0, 0, 0);
  gui->addWindowFrame(screen->w / 2 - 150, screen->h / 2 - 150, 300, 300, 0, 0, 0);
  gui->setWindowBorder(5, 255, 255, 255);
  gui->addTitleFrame(255, 255, 255);
  gui->setText(
    "Lorem ipsum dolor sit amet, nam et detracto contentiones. Eirmod accusamus has in, fastidii liberavisse sit ad. Postea constituto qui te. In laudem semper dolores usu, sed id meis expetenda salutatus, sea nisl modus iudicabit ea. No eum cibo dicunt.",
    24, "mkds.ttf", 255, 255, 255
  );
  gui->bgFill(120, 120, 120);
  gui->update();

  SDL_Event event;
  bool quit = false;
  while (quit == false) {
    if (SDL_PollEvent(&event)) {
      switch (event.type) {
        case SDL_QUIT:
          quit = 1;
          break;
        case SDL_KEYDOWN:
          switch (event.key.keysym.sym) {
            case SDLK_ESCAPE:
            case SDLK_q:
              quit = true;
              break;
            default:
              break;
          }
          break;
      }
    }
  }

  delete gui;
  TTF_Quit();
  SDL_Quit();
  return 0;
}
<commit_msg>multi line window text<commit_after>#include "SDL/SDL.h"
#include "SDL/SDL_ttf.h"
#include "SDL/SDL_gfxPrimitives.h"
#include <vector>
#include <iostream>
using namespace std;

// ----> GUI FRAME
class GuiFrame {
  protected:
    Uint8 bpp;
    SDL_Surface *frame;
    SDL_Rect frameRect;
    SDL_Rect innerFrameRect;
    bool hasFrame;
    bool hasBorder;
    Uint8 borderWidth;
    struct stGuiFrameBorderColor {
      Uint8 r;
      Uint8 g;
      Uint8 b;
    } borderColor;
  public:
    GuiFrame(Uint8);
    void set(Uint16, Uint16, Uint16, Uint16);
    void setBorder(Uint8, Uint8, Uint8, Uint8);
    SDL_Rect *getRect();
    SDL_Rect *getInnerRect();
    SDL_Surface *getSurface();
    void bgFill(Uint8, Uint8, Uint8);
    void drawBorder();
    void unset();
    ~GuiFrame();
};
GuiFrame::GuiFrame(Uint8 _bpp) {
  bpp = _bpp;
  hasFrame = false;
  hasBorder = false;
}
void GuiFrame::set(Uint16 _x, Uint16 _y, Uint16 _w, Uint16 _h) {
  unset();
  frameRect.x = _x;
  frameRect.y = _y;
  frameRect.w = _w;
  frameRect.h = _h;
  innerFrameRect = frameRect;
  frame = SDL_CreateRGBSurface(
    0, frameRect.w, frameRect.h, bpp, 0, 0, 0, 0
  );
  hasFrame = true;
}
void GuiFrame::setBorder(Uint8 _width, Uint8 _r, Uint8 _g, Uint8 _b) {
  borderWidth = _width;
  borderColor.r = _r;
  borderColor.g = _g;
  borderColor.b = _b;
  hasBorder = true;
  innerFrameRect.x += _width;
  innerFrameRect.y += _width;
  innerFrameRect.w = frameRect.w - 1 - 2 * _width;
  innerFrameRect.h = frameRect.h - 1 - 2 * _width;
}
SDL_Rect *GuiFrame::getRect() {
  return &frameRect;
}
SDL_Rect *GuiFrame::getInnerRect() {
  return &innerFrameRect;
}
SDL_Surface *GuiFrame::getSurface() {
  return frame;
}
void GuiFrame::bgFill(Uint8 _r, Uint8 _g, Uint8 _b) {
  SDL_FillRect(
    frame, &frame->clip_rect, SDL_MapRGB(frame->format, _r, _g, _b)
  );
}
void GuiFrame::drawBorder() {
  if (hasBorder == true) {
    boxRGBA(
      frame, 0, 0, frame->clip_rect.w - 1, borderWidth,
      borderColor.r, borderColor.g, borderColor.b, 255
    ); // top
    boxRGBA(
      frame,
      0, 0,
      borderWidth, frame->clip_rect.h - 1,
      borderColor.r, borderColor.g, borderColor.b, 255
    ); // left
    boxRGBA(
      frame,
      0, frame->clip_rect.h - 1 - borderWidth,
      frame->clip_rect.w - 1, frame->clip_rect.h - 1,
      borderColor.r, borderColor.g, borderColor.b, 255
    ); // bottom
    boxRGBA(
      frame,
      frame->clip_rect.w - 1 - borderWidth, 0,
      frame->clip_rect.w - 1, frame->clip_rect.h - 1,
      borderColor.r, borderColor.g, borderColor.b, 255
    ); // bottom
  }
}
void GuiFrame::unset() {
  if (hasFrame == true) {
    SDL_FreeSurface(frame);
    hasFrame = false;
  }
}
GuiFrame::~GuiFrame() {
  unset();
}

// ----> GUI SCREEN
class GuiScreen {
  protected:
    SDL_Surface *screen;
    vector<GuiFrame *> frames;
  public:
    GuiScreen(SDL_Surface *);
    GuiFrame *addFrame(Uint16, Uint16, Uint16, Uint16);
    void bgFill(Uint8, Uint8, Uint8);
    virtual void update();
    virtual ~GuiScreen();
};
GuiScreen::GuiScreen(SDL_Surface *_screen) {
  screen = _screen;
}
GuiFrame *GuiScreen::addFrame(Uint16 _x, Uint16 _y, Uint16 _w, Uint16 _h) {
  GuiFrame *tempFrame = new GuiFrame(screen->format->BytesPerPixel * 8);
  tempFrame->set(_x, _y, _w, _h);
  frames.push_back(tempFrame);
  return tempFrame;
}
void GuiScreen::bgFill(Uint8 _r, Uint8 _g, Uint8 _b) {
  SDL_FillRect(
    screen, &screen->clip_rect, SDL_MapRGB(screen->format, _r, _g, _b)
  );
}
void GuiScreen::update() {
  if (frames.size() > 0) {
    vector<GuiFrame *>::iterator it;
    for (it = frames.begin(); it != frames.end(); it++) {
      (*it)->drawBorder();
      SDL_BlitSurface((*it)->getSurface(), NULL, screen, (*it)->getRect());
    }
  }
  SDL_Flip(screen);
}
GuiScreen::~GuiScreen() {
  vector<GuiFrame *>::iterator it;
  for (it = frames.begin(); it != frames.end(); it++) {
    delete (*it);
  }
  frames.clear();
}

// ----> GUI WINDOW
class GuiWindow: public GuiScreen {
  protected:
    int windowFrameIdx;
    Uint8 windowBorderWidth;
    int titleFrameIdx;
    SDL_Surface *titleText;
    bool hasTitleText;
    SDL_Rect *innerRect;
  public:
    GuiWindow(SDL_Surface *);
    void setTitle(string, Uint8, string, Uint8, Uint8, Uint8);
    void addWindowFrame(Uint16, Uint16, Uint16, Uint16, Uint8, Uint8, Uint8);
    void setWindowBorder(Uint8, Uint8, Uint8, Uint8);
    void addTitleFrame(Uint8, Uint8, Uint8);
    void unsetTitleText();
    virtual void update();
    virtual ~GuiWindow();
};
GuiWindow::GuiWindow(SDL_Surface *_screen) : GuiScreen(_screen) {
  hasTitleText = false;
  windowFrameIdx = -1;
  titleFrameIdx = -1;
}
void GuiWindow::setTitle(string _title, Uint8 _fontSize, string _fontFile,
                         Uint8 _r, Uint8 _g, Uint8 _b) {
  unsetTitleText();
  TTF_Font *font = TTF_OpenFont(_fontFile.c_str(), _fontSize);
  SDL_Color tmpFontColor = { _r, _g, _b };
  titleText = TTF_RenderText_Solid(font, _title.c_str(), tmpFontColor);
  TTF_CloseFont(font);
  hasTitleText = true;
}
void GuiWindow::addWindowFrame(Uint16 _x, Uint16 _y, Uint16 _w, Uint16 _h,
                               Uint8 _r, Uint8 _g, Uint8 _b) {
  addFrame(_x, _y, _w, _h);
  windowFrameIdx = frames.size() - 1;
  frames[windowFrameIdx]->bgFill(_r, _g, _b);
  innerRect = frames[windowFrameIdx]->getInnerRect();
}
void GuiWindow::setWindowBorder(Uint8 _width, Uint8 _r, Uint8 _g, Uint8 _b) {
  if (windowFrameIdx > -1) {
    windowBorderWidth = _width;
    frames[windowFrameIdx]->setBorder(_width, _r, _g, _b);
    innerRect = frames[windowFrameIdx]->getInnerRect();
  }
}
void GuiWindow::addTitleFrame(Uint8 _r, Uint8 _g, Uint8 _b) {
  if (hasTitleText == true && windowFrameIdx > -1) {
    addFrame(innerRect->x, innerRect->y, innerRect->w, titleText->h + windowBorderWidth);
    titleFrameIdx = frames.size() - 1;
    frames[titleFrameIdx]->bgFill(_r, _g, _b);
    innerRect->y += titleText->h + windowBorderWidth;
  }
}
void GuiWindow::update() {
  if (hasTitleText == true) {
    SDL_BlitSurface(titleText, NULL, frames[titleFrameIdx]->getSurface(), NULL);
  }
  GuiScreen::update();
}
void GuiWindow::unsetTitleText() {
  if (hasTitleText == true) {
    SDL_FreeSurface(titleText);
    hasTitleText = false;
  }
}
GuiWindow::~GuiWindow() {
  unsetTitleText();
}

// ----> GUI TEXT
class GuiTextWindow: public GuiWindow {
  protected:
    SDL_Surface *text;
    bool hasText;
  public:
    GuiTextWindow(SDL_Surface *);
    vector<string> wrapText(const string &, int);
    void setText(string, Uint8, string, Uint8, Uint8, Uint8);
    void unsetText();
    virtual void update();
    virtual ~GuiTextWindow();
};
GuiTextWindow::GuiTextWindow(SDL_Surface *_screen) : GuiWindow(_screen) {
  hasText = false;
}
vector<string> GuiTextWindow::wrapText(const string &text, int line_length) {
  vector<string> split_text;
  string::size_type start_point = 0, split_point = 0;
  while (start_point + line_length < text.size()) {
    split_point = text.rfind(' ', start_point+line_length);
    if (split_point < start_point || split_point == string::npos) {
      split_text.push_back(text.substr(start_point, line_length));
      start_point += line_length;
    } else {
      split_text.push_back(text.substr(start_point, split_point-start_point));
      start_point = split_point + 1;
    }
  }
  split_text.push_back(text.substr(start_point, line_length));
  return split_text;
}
void GuiTextWindow::setText(string _text, Uint8 _fontSize, string _fontFile,
                            Uint8 _r, Uint8 _g, Uint8 _b) {
  unsetText();
  TTF_Font *font = TTF_OpenFont(_fontFile.c_str(), _fontSize);
  SDL_Color tmpFontColor = { _r, _g, _b };
  SDL_Surface *tempText = SDL_CreateRGBSurface(
    0, innerRect->w, innerRect->h - titleText->h - windowBorderWidth,
    screen->format->BytesPerPixel * 8, 0, 0, 0, 0
  );
  SDL_Rect tempTextRect;
  tempTextRect.x = 0;
  tempTextRect.y = 0;
  vector<string> lines = wrapText(_text, 20);
  for (Uint8 i = 0; i < lines.size(); i++) {
    if (i > 0)
      SDL_FreeSurface(text);
    text = TTF_RenderText_Solid(font, lines[i].c_str(), tmpFontColor);
    SDL_BlitSurface(text, NULL, tempText, &tempTextRect);
    tempTextRect.y += text->h;
  };
  SDL_FreeSurface(text);
  text = tempText;
  TTF_CloseFont(font);
  hasText = true;
}
void GuiTextWindow::unsetText() {
  if (hasText == true) {
    SDL_FreeSurface(text);
    hasText = false;
  }
}
void GuiTextWindow::update() {
  GuiWindow::update();
  if (hasText == true) {
    SDL_BlitSurface(text, NULL, screen, innerRect);
  }
  SDL_Flip(screen);
}
GuiTextWindow::~GuiTextWindow() {
  unsetText();
}

// ----> MAIN
int main (int argc, char *argv[]) {
  SDL_Init(SDL_INIT_VIDEO);
  TTF_Init();

  SDL_WM_SetCaption("SDL GUI", NULL);
  SDL_Surface* screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);

  GuiTextWindow *gui = new GuiTextWindow(screen);
  gui->setTitle("TEST WINDOW", 24, "mkds.ttf", 0, 0, 0);
  gui->addWindowFrame(screen->w / 2 - 150, screen->h / 2 - 150, 300, 300, 0, 0, 0);
  gui->setWindowBorder(5, 255, 255, 255);
  gui->addTitleFrame(255, 255, 255);
  gui->setText(
    "Lorem ipsum dolor sit amet, nam et detracto contentiones.",
    24, "mkds.ttf", 255, 255, 255
  );
  gui->bgFill(120, 120, 120);
  gui->update();

  SDL_Event event;
  bool quit = false;
  while (quit == false) {
    if (SDL_PollEvent(&event)) {
      switch (event.type) {
        case SDL_QUIT:
          quit = 1;
          break;
        case SDL_KEYDOWN:
          switch (event.key.keysym.sym) {
            case SDLK_ESCAPE:
            case SDLK_q:
              quit = true;
              break;
            default:
              break;
          }
          break;
      }
    }
  }

  delete gui;
  TTF_Quit();
  SDL_Quit();
  return 0;
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2003, 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.

*/

#include "libtorrent/pch.hpp"

#include <vector>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>
#include <algorithm>

#include "libtorrent/config.hpp"
#include "libtorrent/gzip.hpp"

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#include <boost/bind.hpp>

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/http_connection.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/io.hpp"
#include "libtorrent/socket.hpp"

using namespace libtorrent;
using boost::bind;

namespace libtorrent
{

	http_tracker_connection::http_tracker_connection(
		io_service& ios
		, connection_queue& cc
		, tracker_manager& man
		, tracker_request const& req
		, address bind_infc
		, boost::weak_ptr<request_callback> c
		, session_settings const& stn
		, proxy_settings const& ps
		, std::string const& auth)
		: tracker_connection(man, req, ios, bind_infc, c)
		, m_man(man)
	{
		// TODO: authentication
		std::string url = req.url;

		if (req.kind == tracker_request::scrape_request)
		{
			// find and replace "announce" with "scrape"
			// in request

			std::size_t pos = url.find("announce");
			if (pos == std::string::npos)
			{
				fail(-1, ("scrape is not available on url: '"
					+ req.url +"'").c_str());
				return;
			}
			url.replace(pos, 8, "scrape");
		}

		// if request-string already contains
		// some parameters, append an ampersand instead
		// of a question mark
		size_t arguments_start = url.find('?');
		if (arguments_start != std::string::npos)
			url += "&";
		else
			url += "?";

		url += "info_hash=";
		url += escape_string(
			reinterpret_cast<const char*>(req.info_hash.begin()), 20);

		if (req.kind == tracker_request::announce_request)
		{
			url += "&peer_id=";
			url += escape_string(
				reinterpret_cast<const char*>(req.pid.begin()), 20);

			url += "&port=";
			url += boost::lexical_cast<std::string>(req.listen_port);

			url += "&uploaded=";
			url += boost::lexical_cast<std::string>(req.uploaded);

			url += "&downloaded=";
			url += boost::lexical_cast<std::string>(req.downloaded);

			url += "&left=";
			url += boost::lexical_cast<std::string>(req.left);

			if (req.event != tracker_request::none)
			{
				const char* event_string[] = {"completed", "started", "stopped"};
				url += "&event=";
				url += event_string[req.event - 1];
			}

			url += "&key=";
			std::stringstream key_string;
			key_string << std::hex << req.key;
			url += key_string.str();

			url += "&compact=1";

			url += "&numwant=";
			url += boost::lexical_cast<std::string>(
				(std::min)(req.num_want, 999));

			if (stn.announce_ip != address())
			{
				url += "&ip=";
				url += stn.announce_ip.to_string();
			}

#ifndef TORRENT_DISABLE_ENCRYPTION
			url += "&supportcrypto=1";
#endif
			url += "&ipv6=";
			url += req.ipv6;

			// extension that tells the tracker that
			// we don't need any peer_id's in the response
			url += "&no_peer_id=1";
		}

		m_tracker_connection.reset(new http_connection(ios, cc
			, boost::bind(&http_tracker_connection::on_response, self(), _1, _2, _3, _4)));

		int timeout = req.event==tracker_request::stopped
			?stn.stop_tracker_timeout
			:stn.tracker_completion_timeout;

		m_tracker_connection->get(url, seconds(timeout)
			, 1, &ps, 5, stn.user_agent, bind_infc);

#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)

		boost::shared_ptr<request_callback> cb = requester();
		if (cb)
		{
			cb->debug_log("==> TRACKER_REQUEST [ url: " + url + " ]");
		}
#endif
	}

	void http_tracker_connection::close()
	{
		if (m_tracker_connection)
		{
			m_tracker_connection->close();
			m_tracker_connection.reset();
		}
		tracker_connection::close();
	}

	void http_tracker_connection::on_response(error_code const& ec
		, http_parser const& parser, char const* data, int size)
	{
		// keep this alive
		boost::intrusive_ptr<http_tracker_connection> me(this);

		if (ec && ec != asio::error::eof)
		{
			fail(-1, ec.message().c_str());
			return;
		}

		if (!parser.header_finished())
		{
			fail(-1, "premature end of file");
			return;
		}

		if (parser.status_code() != 200)
		{
			fail(parser.status_code(), parser.message().c_str());
			return;
		}

		if (ec && ec != asio::error::eof)
		{
			fail(parser.status_code(), ec.message().c_str());
			return;
		}

		// handle tracker response
		entry e = bdecode(data, data + size);

		if (e.type() != entry::undefined_t)
		{
			parse(parser.status_code(), e);
		}
		else
		{
			std::string error_str("invalid bencoding of tracker response: \"");
			for (char const* i = data, *end(data + size); i != end; ++i)
			{
				if (std::isprint(*i)) error_str += *i;
				else error_str += "0x" + boost::lexical_cast<std::string>((unsigned int)*i) + " ";
			}
			error_str += "\"";
			fail(parser.status_code(), error_str.c_str());
		}
		close();
	}

	bool http_tracker_connection::extract_peer_info(const entry& info, peer_entry& ret)
	{
		// extract peer id (if any)
		if (info.type() != entry::dictionary_t)
		{
			fail(-1, "invalid response from tracker (invalid peer entry)");
			return false;
		}
		entry const* i = info.find_key("peer id");
		if (i != 0)
		{
			if (i->type() != entry::string_t || i->string().length() != 20)
			{
				fail(-1, "invalid response from tracker (invalid peer id)");
				return false;
			}
			std::copy(i->string().begin(), i->string().end(), ret.pid.begin());
		}
		else
		{
			// if there's no peer_id, just initialize it to a bunch of zeroes
			std::fill_n(ret.pid.begin(), 20, 0);
		}

		// extract ip
		i = info.find_key("ip");
		if (i == 0 || i->type() != entry::string_t)
		{
			fail(-1, "invalid response from tracker");
			return false;
		}
		ret.ip = i->string();

		// extract port
		i = info.find_key("port");
		if (i == 0 || i->type() != entry::int_t)
		{
			fail(-1, "invalid response from tracker");
			return false;
		}
		ret.port = (unsigned short)i->integer();

		return true;
	}

	void http_tracker_connection::parse(int status_code, entry const& e)
	{
		boost::shared_ptr<request_callback> cb = requester();
		if (!cb) return;

		// parse the response
		entry const* failure = e.find_key("failure reason");
		if (failure && failure->type() == entry::string_t)
		{
			fail(status_code, failure->string().c_str());
			return;
		}

		entry const* warning = e.find_key("warning message");
		if (warning && warning->type() == entry::string_t)
		{
			cb->tracker_warning(tracker_req(), warning->string());
		}

		std::vector<peer_entry> peer_list;

		if (tracker_req().kind == tracker_request::scrape_request)
		{
			std::string ih = tracker_req().info_hash.to_string();

			entry const* files = e.find_key("files");
			if (files == 0 || files->type() != entry::dictionary_t)
			{
				fail(-1, "invalid or missing 'files' entry in scrape response");
				return;
			}

			entry const* scrape_data = files->find_key(ih);
			if (scrape_data == 0 || scrape_data->type() != entry::dictionary_t)
			{
				fail(-1, "missing or invalid info-hash entry in scrape response");
				return;
			}
			entry const* complete = scrape_data->find_key("complete");
			entry const* incomplete = scrape_data->find_key("incomplete");
			entry const* downloaded = scrape_data->find_key("downloaded");
			if (complete == 0 || incomplete == 0 || downloaded == 0
				|| complete->type() != entry::int_t
				|| incomplete->type() != entry::int_t
				|| downloaded->type() != entry::int_t)
			{
				fail(-1, "missing 'complete' or 'incomplete' entries in scrape response");
				return;
			}
			cb->tracker_scrape_response(tracker_req(), int(complete->integer())
				, int(incomplete->integer()), int(downloaded->integer()));
			return;
		}

		entry const* interval = e.find_key("interval");
		if (interval == 0 || interval->type() != entry::int_t)
		{
			fail(-1, "missing or invalid 'interval' entry in tracker response");
			return;
		}

		entry const* peers_ent = e.find_key("peers");
		if (peers_ent == 0)
		{
			fail(-1, "missing 'peers' entry in tracker response");
			return;
		}

		if (peers_ent->type() == entry::string_t)
		{
			std::string const& peers = peers_ent->string();
			for (std::string::const_iterator i = peers.begin();
				i != peers.end();)
			{
				if (std::distance(i, peers.end()) < 6) break;

				peer_entry p;
				p.pid.clear();
				p.ip = detail::read_v4_address(i).to_string();
				p.port = detail::read_uint16(i);
				peer_list.push_back(p);
			}
		}
		else if (peers_ent->type() == entry::list_t)
		{
			entry::list_type const& l = peers_ent->list();
			for(entry::list_type::const_iterator i = l.begin(); i != l.end(); ++i)
			{
				peer_entry p;
				if (!extract_peer_info(*i, p)) return;
				peer_list.push_back(p);
			}
		}
		else
		{
			fail(-1, "invalid 'peers' entry in tracker response");
			return;
		}

		entry const* ipv6_peers = e.find_key("peers6");
		if (ipv6_peers && ipv6_peers->type() == entry::string_t)
		{
			std::string const& peers = ipv6_peers->string();
			for (std::string::const_iterator i = peers.begin();
				i != peers.end();)
			{
				if (std::distance(i, peers.end()) < 18) break;

				peer_entry p;
				p.pid.clear();
				p.ip = detail::read_v6_address(i).to_string();
				p.port = detail::read_uint16(i);
				peer_list.push_back(p);
			}
		}

		// look for optional scrape info
		int complete = -1;
		int incomplete = -1;
		address external_ip;

		entry const* ip_ent = e.find_key("external ip");
		if (ip_ent && ip_ent->type() == entry::string_t)
		{
			std::string const& ip = ip_ent->string();
			char const* p = &ip[0];
			if (ip.size() == address_v4::bytes_type::static_size)
				external_ip = detail::read_v4_address(p);
			else if (ip.size() == address_v6::bytes_type::static_size)
				external_ip = detail::read_v6_address(p);
		}

		entry const* complete_ent = e.find_key("complete");
		if (complete_ent && complete_ent->type() == entry::int_t)
			complete = int(complete_ent->integer());

		entry const* incomplete_ent = e.find_key("incomplete");
		if (incomplete_ent && incomplete_ent->type() == entry::int_t)
			incomplete = int(incomplete_ent->integer());

		cb->tracker_response(tracker_req(), peer_list, interval->integer(), complete
			, incomplete, external_ip);
	}

}

<commit_msg>fixed exception issue when tracker sends invalid response<commit_after>/*

Copyright (c) 2003, 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.

*/

#include "libtorrent/pch.hpp"

#include <vector>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>
#include <algorithm>

#include "libtorrent/config.hpp"
#include "libtorrent/gzip.hpp"

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#include <boost/bind.hpp>

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/http_connection.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/io.hpp"
#include "libtorrent/socket.hpp"

using namespace libtorrent;
using boost::bind;

namespace libtorrent
{

	http_tracker_connection::http_tracker_connection(
		io_service& ios
		, connection_queue& cc
		, tracker_manager& man
		, tracker_request const& req
		, address bind_infc
		, boost::weak_ptr<request_callback> c
		, session_settings const& stn
		, proxy_settings const& ps
		, std::string const& auth)
		: tracker_connection(man, req, ios, bind_infc, c)
		, m_man(man)
	{
		// TODO: authentication
		std::string url = req.url;

		if (req.kind == tracker_request::scrape_request)
		{
			// find and replace "announce" with "scrape"
			// in request

			std::size_t pos = url.find("announce");
			if (pos == std::string::npos)
			{
				fail(-1, ("scrape is not available on url: '"
					+ req.url +"'").c_str());
				return;
			}
			url.replace(pos, 8, "scrape");
		}

		// if request-string already contains
		// some parameters, append an ampersand instead
		// of a question mark
		size_t arguments_start = url.find('?');
		if (arguments_start != std::string::npos)
			url += "&";
		else
			url += "?";

		url += "info_hash=";
		url += escape_string(
			reinterpret_cast<const char*>(req.info_hash.begin()), 20);

		if (req.kind == tracker_request::announce_request)
		{
			url += "&peer_id=";
			url += escape_string(
				reinterpret_cast<const char*>(req.pid.begin()), 20);

			url += "&port=";
			url += boost::lexical_cast<std::string>(req.listen_port);

			url += "&uploaded=";
			url += boost::lexical_cast<std::string>(req.uploaded);

			url += "&downloaded=";
			url += boost::lexical_cast<std::string>(req.downloaded);

			url += "&left=";
			url += boost::lexical_cast<std::string>(req.left);

			if (req.event != tracker_request::none)
			{
				const char* event_string[] = {"completed", "started", "stopped"};
				url += "&event=";
				url += event_string[req.event - 1];
			}

			url += "&key=";
			std::stringstream key_string;
			key_string << std::hex << req.key;
			url += key_string.str();

			url += "&compact=1";

			url += "&numwant=";
			url += boost::lexical_cast<std::string>(
				(std::min)(req.num_want, 999));

			if (stn.announce_ip != address())
			{
				url += "&ip=";
				url += stn.announce_ip.to_string();
			}

#ifndef TORRENT_DISABLE_ENCRYPTION
			url += "&supportcrypto=1";
#endif
			url += "&ipv6=";
			url += req.ipv6;

			// extension that tells the tracker that
			// we don't need any peer_id's in the response
			url += "&no_peer_id=1";
		}

		m_tracker_connection.reset(new http_connection(ios, cc
			, boost::bind(&http_tracker_connection::on_response, self(), _1, _2, _3, _4)));

		int timeout = req.event==tracker_request::stopped
			?stn.stop_tracker_timeout
			:stn.tracker_completion_timeout;

		m_tracker_connection->get(url, seconds(timeout)
			, 1, &ps, 5, stn.user_agent, bind_infc);

#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)

		boost::shared_ptr<request_callback> cb = requester();
		if (cb)
		{
			cb->debug_log("==> TRACKER_REQUEST [ url: " + url + " ]");
		}
#endif
	}

	void http_tracker_connection::close()
	{
		if (m_tracker_connection)
		{
			m_tracker_connection->close();
			m_tracker_connection.reset();
		}
		tracker_connection::close();
	}

	void http_tracker_connection::on_response(error_code const& ec
		, http_parser const& parser, char const* data, int size)
	{
		// keep this alive
		boost::intrusive_ptr<http_tracker_connection> me(this);

		if (ec && ec != asio::error::eof)
		{
			fail(-1, ec.message().c_str());
			return;
		}

		if (!parser.header_finished())
		{
			fail(-1, "premature end of file");
			return;
		}

		if (parser.status_code() != 200)
		{
			fail(parser.status_code(), parser.message().c_str());
			return;
		}

		if (ec && ec != asio::error::eof)
		{
			fail(parser.status_code(), ec.message().c_str());
			return;
		}

		// handle tracker response
		entry e;
#ifndef BOOST_NO_EXCEPTIONS
		try {
#endif
		e = bdecode(data, data + size);
#ifndef BOOST_NO_EXCEPTIONS
		} catch (std::exception&) {}
#endif

		if (e.type() != entry::undefined_t)
		{
			parse(parser.status_code(), e);
		}
		else
		{
			std::string error_str("invalid bencoding of tracker response: \"");
			for (char const* i = data, *end(data + size); i != end; ++i)
			{
				if (std::isprint(*i)) error_str += *i;
				else error_str += "0x" + boost::lexical_cast<std::string>((unsigned int)*i) + " ";
			}
			error_str += "\"";
			fail(parser.status_code(), error_str.c_str());
		}
		close();
	}

	bool http_tracker_connection::extract_peer_info(const entry& info, peer_entry& ret)
	{
		// extract peer id (if any)
		if (info.type() != entry::dictionary_t)
		{
			fail(-1, "invalid response from tracker (invalid peer entry)");
			return false;
		}
		entry const* i = info.find_key("peer id");
		if (i != 0)
		{
			if (i->type() != entry::string_t || i->string().length() != 20)
			{
				fail(-1, "invalid response from tracker (invalid peer id)");
				return false;
			}
			std::copy(i->string().begin(), i->string().end(), ret.pid.begin());
		}
		else
		{
			// if there's no peer_id, just initialize it to a bunch of zeroes
			std::fill_n(ret.pid.begin(), 20, 0);
		}

		// extract ip
		i = info.find_key("ip");
		if (i == 0 || i->type() != entry::string_t)
		{
			fail(-1, "invalid response from tracker");
			return false;
		}
		ret.ip = i->string();

		// extract port
		i = info.find_key("port");
		if (i == 0 || i->type() != entry::int_t)
		{
			fail(-1, "invalid response from tracker");
			return false;
		}
		ret.port = (unsigned short)i->integer();

		return true;
	}

	void http_tracker_connection::parse(int status_code, entry const& e)
	{
		boost::shared_ptr<request_callback> cb = requester();
		if (!cb) return;

		// parse the response
		entry const* failure = e.find_key("failure reason");
		if (failure && failure->type() == entry::string_t)
		{
			fail(status_code, failure->string().c_str());
			return;
		}

		entry const* warning = e.find_key("warning message");
		if (warning && warning->type() == entry::string_t)
		{
			cb->tracker_warning(tracker_req(), warning->string());
		}

		std::vector<peer_entry> peer_list;

		if (tracker_req().kind == tracker_request::scrape_request)
		{
			std::string ih = tracker_req().info_hash.to_string();

			entry const* files = e.find_key("files");
			if (files == 0 || files->type() != entry::dictionary_t)
			{
				fail(-1, "invalid or missing 'files' entry in scrape response");
				return;
			}

			entry const* scrape_data = files->find_key(ih);
			if (scrape_data == 0 || scrape_data->type() != entry::dictionary_t)
			{
				fail(-1, "missing or invalid info-hash entry in scrape response");
				return;
			}
			entry const* complete = scrape_data->find_key("complete");
			entry const* incomplete = scrape_data->find_key("incomplete");
			entry const* downloaded = scrape_data->find_key("downloaded");
			if (complete == 0 || incomplete == 0 || downloaded == 0
				|| complete->type() != entry::int_t
				|| incomplete->type() != entry::int_t
				|| downloaded->type() != entry::int_t)
			{
				fail(-1, "missing 'complete' or 'incomplete' entries in scrape response");
				return;
			}
			cb->tracker_scrape_response(tracker_req(), int(complete->integer())
				, int(incomplete->integer()), int(downloaded->integer()));
			return;
		}

		entry const* interval = e.find_key("interval");
		if (interval == 0 || interval->type() != entry::int_t)
		{
			fail(-1, "missing or invalid 'interval' entry in tracker response");
			return;
		}

		entry const* peers_ent = e.find_key("peers");
		if (peers_ent == 0)
		{
			fail(-1, "missing 'peers' entry in tracker response");
			return;
		}

		if (peers_ent->type() == entry::string_t)
		{
			std::string const& peers = peers_ent->string();
			for (std::string::const_iterator i = peers.begin();
				i != peers.end();)
			{
				if (std::distance(i, peers.end()) < 6) break;

				peer_entry p;
				p.pid.clear();
				p.ip = detail::read_v4_address(i).to_string();
				p.port = detail::read_uint16(i);
				peer_list.push_back(p);
			}
		}
		else if (peers_ent->type() == entry::list_t)
		{
			entry::list_type const& l = peers_ent->list();
			for(entry::list_type::const_iterator i = l.begin(); i != l.end(); ++i)
			{
				peer_entry p;
				if (!extract_peer_info(*i, p)) return;
				peer_list.push_back(p);
			}
		}
		else
		{
			fail(-1, "invalid 'peers' entry in tracker response");
			return;
		}

		entry const* ipv6_peers = e.find_key("peers6");
		if (ipv6_peers && ipv6_peers->type() == entry::string_t)
		{
			std::string const& peers = ipv6_peers->string();
			for (std::string::const_iterator i = peers.begin();
				i != peers.end();)
			{
				if (std::distance(i, peers.end()) < 18) break;

				peer_entry p;
				p.pid.clear();
				p.ip = detail::read_v6_address(i).to_string();
				p.port = detail::read_uint16(i);
				peer_list.push_back(p);
			}
		}

		// look for optional scrape info
		int complete = -1;
		int incomplete = -1;
		address external_ip;

		entry const* ip_ent = e.find_key("external ip");
		if (ip_ent && ip_ent->type() == entry::string_t)
		{
			std::string const& ip = ip_ent->string();
			char const* p = &ip[0];
			if (ip.size() == address_v4::bytes_type::static_size)
				external_ip = detail::read_v4_address(p);
			else if (ip.size() == address_v6::bytes_type::static_size)
				external_ip = detail::read_v6_address(p);
		}

		entry const* complete_ent = e.find_key("complete");
		if (complete_ent && complete_ent->type() == entry::int_t)
			complete = int(complete_ent->integer());

		entry const* incomplete_ent = e.find_key("incomplete");
		if (incomplete_ent && incomplete_ent->type() == entry::int_t)
			incomplete = int(incomplete_ent->integer());

		cb->tracker_response(tracker_req(), peer_list, interval->integer(), complete
			, incomplete, external_ip);
	}

}

<|endoftext|>