<commit_before>#include "graphics/3d_primitives.h"
#include <cmath>
#include <numeric>

namespace demoloop {

Mesh* cube(const float cx, const float cy, const float cz, const float radius) {
  static const GLfloat g_vertex_buffer_data[] = {
      -1.0f,-1.0f,-1.0f,
      -1.0f,-1.0f, 1.0f,
      -1.0f, 1.0f, 1.0f,

      1.0f, 1.0f,-1.0f,
      -1.0f,-1.0f,-1.0f,
      -1.0f, 1.0f,-1.0f,

      1.0f,-1.0f, 1.0f,
      -1.0f,-1.0f,-1.0f,
      1.0f,-1.0f,-1.0f,

      1.0f, 1.0f,-1.0f,
      1.0f,-1.0f,-1.0f,
      -1.0f,-1.0f,-1.0f,

      -1.0f,-1.0f,-1.0f,
      -1.0f, 1.0f, 1.0f,
      -1.0f, 1.0f,-1.0f,

      1.0f,-1.0f, 1.0f,
      -1.0f,-1.0f, 1.0f,
      -1.0f,-1.0f,-1.0f,

      -1.0f, 1.0f, 1.0f,
      -1.0f,-1.0f, 1.0f,
      1.0f,-1.0f, 1.0f,

      1.0f, 1.0f, 1.0f,
      1.0f,-1.0f,-1.0f,
      1.0f, 1.0f,-1.0f,

      1.0f,-1.0f,-1.0f,
      1.0f, 1.0f, 1.0f,
      1.0f,-1.0f, 1.0f,

      1.0f, 1.0f, 1.0f,
      1.0f, 1.0f,-1.0f,
      -1.0f, 1.0f,-1.0f,

      1.0f, 1.0f, 1.0f,
      -1.0f, 1.0f,-1.0f,
      -1.0f, 1.0f, 1.0f,

      1.0f, 1.0f, 1.0f,
      -1.0f, 1.0f, 1.0f,
      1.0f,-1.0f, 1.0f
  };

  std::vector<uint32_t> indices = {
    0, 1, 2, 3, 0, 5, 6, 0, 8, 3, 8,
    0, 0, 2, 5, 6, 1, 0, 2, 1, 6, 21,
    8, 3, 8, 21, 6, 21, 3, 5, 21, 5, 2, 21, 2, 6
  };

  std::vector<Vertex> vertices;
  vertices.reserve(36);
  for (int i = 0; i < 36; ++i)
  {
    Vertex v;
    v.x = g_vertex_buffer_data[i * 3 + 0] * radius + cx;
    v.y = g_vertex_buffer_data[i * 3 + 1] * radius + cy;
    v.z = g_vertex_buffer_data[i * 3 + 2] * radius + cz;
    vertices.push_back(v);
  }

  return new Mesh(vertices, indices);
}

Mesh* sphere(const float cx, const float cy, const float cz, const float radius) {
  const float t = (1.0 + sqrt(5.0)) / 2.0 * radius;

  Vertex points[12];
  points[0].x = -radius + cx;
  points[0].y =  t + cy;
  points[0].z =  0 + cz;

  points[1].x =  radius + cx;
  points[1].y =  t + cy;
  points[1].z =  0 + cz;

  points[2].x = -radius + cx;
  points[2].y = -t + cy;
  points[2].z =  0 + cz;

  points[3].x =  radius + cx;
  points[3].y = -t + cy;
  points[3].z =  0 + cz;

  points[4].x =  0 + cx;
  points[4].y = -radius + cy;
  points[4].z =  t + cz;

  points[5].x =  0 + cx;
  points[5].y =  radius + cy;
  points[5].z =  t + cz;

  points[6].x =  0 + cx;
  points[6].y = -radius + cy;
  points[6].z = -t + cz;

  points[7].x =  0 + cx;
  points[7].y =  radius + cy;
  points[7].z = -t + cz;

  points[8].x =  t + cx;
  points[8].y =  0 + cy;
  points[8].z = -radius + cz;

  points[9].x =  t + cx;
  points[9].y =  0 + cy;
  points[9].z =  radius + cz;

  points[10].x = -t + cx;
  points[10].y =  0 + cy;
  points[10].z = -radius + cz;

  points[11].x = -t + cx;
  points[11].y =  0 + cy;
  points[11].z =  radius + cz;

  std::vector<Vertex> vertices;
  vertices.reserve(60);

  vertices.push_back(Vertex(points[0]));
  vertices.push_back(Vertex(points[11]));
  vertices.push_back(Vertex(points[5]));

  vertices.push_back(Vertex(points[0]));
  vertices.push_back(Vertex(points[5]));
  vertices.push_back(Vertex(points[1]));

  vertices.push_back(Vertex(points[0]));
  vertices.push_back(Vertex(points[1]));
  vertices.push_back(Vertex(points[7]));

  vertices.push_back(Vertex(points[0]));
  vertices.push_back(Vertex(points[7]));
  vertices.push_back(Vertex(points[10]));

  vertices.push_back(Vertex(points[0]));
  vertices.push_back(Vertex(points[10]));
  vertices.push_back(Vertex(points[11]));

  vertices.push_back(Vertex(points[1]));
  vertices.push_back(Vertex(points[5]));
  vertices.push_back(Vertex(points[9]));

  vertices.push_back(Vertex(points[5]));
  vertices.push_back(Vertex(points[11]));
  vertices.push_back(Vertex(points[4]));

  vertices.push_back(Vertex(points[11]));
  vertices.push_back(Vertex(points[10]));
  vertices.push_back(Vertex(points[2]));

  vertices.push_back(Vertex(points[10]));
  vertices.push_back(Vertex(points[7]));
  vertices.push_back(Vertex(points[6]));

  vertices.push_back(Vertex(points[7]));
  vertices.push_back(Vertex(points[1]));
  vertices.push_back(Vertex(points[8]));

  vertices.push_back(Vertex(points[3]));
  vertices.push_back(Vertex(points[9]));
  vertices.push_back(Vertex(points[4]));

  vertices.push_back(Vertex(points[3]));
  vertices.push_back(Vertex(points[4]));
  vertices.push_back(Vertex(points[2]));

  vertices.push_back(Vertex(points[3]));
  vertices.push_back(Vertex(points[2]));
  vertices.push_back(Vertex(points[6]));

  vertices.push_back(Vertex(points[3]));
  vertices.push_back(Vertex(points[6]));
  vertices.push_back(Vertex(points[8]));

  vertices.push_back(Vertex(points[3]));
  vertices.push_back(Vertex(points[8]));
  vertices.push_back(Vertex(points[9]));

  vertices.push_back(Vertex(points[4]));
  vertices.push_back(Vertex(points[9]));
  vertices.push_back(Vertex(points[5]));

  vertices.push_back(Vertex(points[2]));
  vertices.push_back(Vertex(points[4]));
  vertices.push_back(Vertex(points[11]));

  vertices.push_back(Vertex(points[6]));
  vertices.push_back(Vertex(points[2]));
  vertices.push_back(Vertex(points[10]));

  vertices.push_back(Vertex(points[8]));
  vertices.push_back(Vertex(points[6]));
  vertices.push_back(Vertex(points[7]));

  vertices.push_back(Vertex(points[9]));
  vertices.push_back(Vertex(points[8]));
  vertices.push_back(Vertex(points[1]));

  std::vector<uint32_t> indices = {
    0, 1, 2, 0, 2, 5, 0, 5, 8, 0, 8, 11, 0,
    11, 1, 5, 2, 17, 2, 1, 20, 1, 11, 23, 11,
    8, 26, 8, 5, 29, 30, 17, 20, 30, 20, 23,
    30, 23, 26, 30, 26, 29, 30, 29, 17, 20,
    17, 2, 23, 20, 1, 26, 23, 11, 29, 26, 8,
    17, 29, 5
  };

  return new Mesh(vertices, indices);
}

void polygon(GL& gl, const float* xCoords, const float* yCoords, const float* zCoords, uint32_t count) {
  const float x = xCoords[0];
  const float y = yCoords[0];
  const float z = zCoords[0];

  Vertex *vertices = new Vertex[(count - 2) * 3];
  uint32_t vertexIndex = 0;
  for (uint32_t i = 1; i < count - 1; i++) {
    vertices[vertexIndex].x = x;
    vertices[vertexIndex].y = y;
    vertices[vertexIndex].z = z;
    vertexIndex++;

    vertices[vertexIndex].x = xCoords[i];
    vertices[vertexIndex].y = yCoords[i];
    vertices[vertexIndex].z = zCoords[i];
    vertexIndex++;

    vertices[vertexIndex].x = xCoords[i+1];
    vertices[vertexIndex].y = yCoords[i+1];
    vertices[vertexIndex].z = zCoords[i+1];
    vertexIndex++;
  }

  gl.triangles(vertices, vertexIndex);
  delete[] vertices;
}

void line(GL& gl, const float x1, const float y1, const float z1, const float x2, const float y2, const float z2) {
  Vertex vertices[2];
  vertices[0].x = x1;
  vertices[0].y = y1;
  vertices[0].z = z1;

  vertices[1].x = x2;
  vertices[1].y = y2;
  vertices[1].z = z2;
  gl.lines(vertices, 2);
}

}
<commit_msg>bind default texture for graphics primitives<commit_after>#include "graphics/3d_primitives.h"
#include <cmath>
#include <numeric>

namespace demoloop {

Mesh* cube(const float cx, const float cy, const float cz, const float radius) {
  static const GLfloat g_vertex_buffer_data[] = {
      -1.0f,-1.0f,-1.0f,
      -1.0f,-1.0f, 1.0f,
      -1.0f, 1.0f, 1.0f,

      1.0f, 1.0f,-1.0f,
      -1.0f,-1.0f,-1.0f,
      -1.0f, 1.0f,-1.0f,

      1.0f,-1.0f, 1.0f,
      -1.0f,-1.0f,-1.0f,
      1.0f,-1.0f,-1.0f,

      1.0f, 1.0f,-1.0f,
      1.0f,-1.0f,-1.0f,
      -1.0f,-1.0f,-1.0f,

      -1.0f,-1.0f,-1.0f,
      -1.0f, 1.0f, 1.0f,
      -1.0f, 1.0f,-1.0f,

      1.0f,-1.0f, 1.0f,
      -1.0f,-1.0f, 1.0f,
      -1.0f,-1.0f,-1.0f,

      -1.0f, 1.0f, 1.0f,
      -1.0f,-1.0f, 1.0f,
      1.0f,-1.0f, 1.0f,

      1.0f, 1.0f, 1.0f,
      1.0f,-1.0f,-1.0f,
      1.0f, 1.0f,-1.0f,

      1.0f,-1.0f,-1.0f,
      1.0f, 1.0f, 1.0f,
      1.0f,-1.0f, 1.0f,

      1.0f, 1.0f, 1.0f,
      1.0f, 1.0f,-1.0f,
      -1.0f, 1.0f,-1.0f,

      1.0f, 1.0f, 1.0f,
      -1.0f, 1.0f,-1.0f,
      -1.0f, 1.0f, 1.0f,

      1.0f, 1.0f, 1.0f,
      -1.0f, 1.0f, 1.0f,
      1.0f,-1.0f, 1.0f
  };

  std::vector<uint32_t> indices = {
    0, 1, 2, 3, 0, 5, 6, 0, 8, 3, 8,
    0, 0, 2, 5, 6, 1, 0, 2, 1, 6, 21,
    8, 3, 8, 21, 6, 21, 3, 5, 21, 5, 2, 21, 2, 6
  };

  std::vector<Vertex> vertices;
  vertices.reserve(36);
  for (int i = 0; i < 36; ++i)
  {
    Vertex v;
    v.x = g_vertex_buffer_data[i * 3 + 0] * radius + cx;
    v.y = g_vertex_buffer_data[i * 3 + 1] * radius + cy;
    v.z = g_vertex_buffer_data[i * 3 + 2] * radius + cz;
    vertices.push_back(v);
  }

  return new Mesh(vertices, indices);
}

Mesh* sphere(const float cx, const float cy, const float cz, const float radius) {
  const float t = (1.0 + sqrt(5.0)) / 2.0 * radius;

  Vertex points[12];
  points[0].x = -radius + cx;
  points[0].y =  t + cy;
  points[0].z =  0 + cz;

  points[1].x =  radius + cx;
  points[1].y =  t + cy;
  points[1].z =  0 + cz;

  points[2].x = -radius + cx;
  points[2].y = -t + cy;
  points[2].z =  0 + cz;

  points[3].x =  radius + cx;
  points[3].y = -t + cy;
  points[3].z =  0 + cz;

  points[4].x =  0 + cx;
  points[4].y = -radius + cy;
  points[4].z =  t + cz;

  points[5].x =  0 + cx;
  points[5].y =  radius + cy;
  points[5].z =  t + cz;

  points[6].x =  0 + cx;
  points[6].y = -radius + cy;
  points[6].z = -t + cz;

  points[7].x =  0 + cx;
  points[7].y =  radius + cy;
  points[7].z = -t + cz;

  points[8].x =  t + cx;
  points[8].y =  0 + cy;
  points[8].z = -radius + cz;

  points[9].x =  t + cx;
  points[9].y =  0 + cy;
  points[9].z =  radius + cz;

  points[10].x = -t + cx;
  points[10].y =  0 + cy;
  points[10].z = -radius + cz;

  points[11].x = -t + cx;
  points[11].y =  0 + cy;
  points[11].z =  radius + cz;

  std::vector<Vertex> vertices;
  vertices.reserve(60);

  vertices.push_back(Vertex(points[0]));
  vertices.push_back(Vertex(points[11]));
  vertices.push_back(Vertex(points[5]));

  vertices.push_back(Vertex(points[0]));
  vertices.push_back(Vertex(points[5]));
  vertices.push_back(Vertex(points[1]));

  vertices.push_back(Vertex(points[0]));
  vertices.push_back(Vertex(points[1]));
  vertices.push_back(Vertex(points[7]));

  vertices.push_back(Vertex(points[0]));
  vertices.push_back(Vertex(points[7]));
  vertices.push_back(Vertex(points[10]));

  vertices.push_back(Vertex(points[0]));
  vertices.push_back(Vertex(points[10]));
  vertices.push_back(Vertex(points[11]));

  vertices.push_back(Vertex(points[1]));
  vertices.push_back(Vertex(points[5]));
  vertices.push_back(Vertex(points[9]));

  vertices.push_back(Vertex(points[5]));
  vertices.push_back(Vertex(points[11]));
  vertices.push_back(Vertex(points[4]));

  vertices.push_back(Vertex(points[11]));
  vertices.push_back(Vertex(points[10]));
  vertices.push_back(Vertex(points[2]));

  vertices.push_back(Vertex(points[10]));
  vertices.push_back(Vertex(points[7]));
  vertices.push_back(Vertex(points[6]));

  vertices.push_back(Vertex(points[7]));
  vertices.push_back(Vertex(points[1]));
  vertices.push_back(Vertex(points[8]));

  vertices.push_back(Vertex(points[3]));
  vertices.push_back(Vertex(points[9]));
  vertices.push_back(Vertex(points[4]));

  vertices.push_back(Vertex(points[3]));
  vertices.push_back(Vertex(points[4]));
  vertices.push_back(Vertex(points[2]));

  vertices.push_back(Vertex(points[3]));
  vertices.push_back(Vertex(points[2]));
  vertices.push_back(Vertex(points[6]));

  vertices.push_back(Vertex(points[3]));
  vertices.push_back(Vertex(points[6]));
  vertices.push_back(Vertex(points[8]));

  vertices.push_back(Vertex(points[3]));
  vertices.push_back(Vertex(points[8]));
  vertices.push_back(Vertex(points[9]));

  vertices.push_back(Vertex(points[4]));
  vertices.push_back(Vertex(points[9]));
  vertices.push_back(Vertex(points[5]));

  vertices.push_back(Vertex(points[2]));
  vertices.push_back(Vertex(points[4]));
  vertices.push_back(Vertex(points[11]));

  vertices.push_back(Vertex(points[6]));
  vertices.push_back(Vertex(points[2]));
  vertices.push_back(Vertex(points[10]));

  vertices.push_back(Vertex(points[8]));
  vertices.push_back(Vertex(points[6]));
  vertices.push_back(Vertex(points[7]));

  vertices.push_back(Vertex(points[9]));
  vertices.push_back(Vertex(points[8]));
  vertices.push_back(Vertex(points[1]));

  std::vector<uint32_t> indices = {
    0, 1, 2, 0, 2, 5, 0, 5, 8, 0, 8, 11, 0,
    11, 1, 5, 2, 17, 2, 1, 20, 1, 11, 23, 11,
    8, 26, 8, 5, 29, 30, 17, 20, 30, 20, 23,
    30, 23, 26, 30, 26, 29, 30, 29, 17, 20,
    17, 2, 23, 20, 1, 26, 23, 11, 29, 26, 8,
    17, 29, 5
  };

  return new Mesh(vertices, indices);
}

void polygon(GL& gl, const float* xCoords, const float* yCoords, const float* zCoords, uint32_t count) {
  const float x = xCoords[0];
  const float y = yCoords[0];
  const float z = zCoords[0];

  Vertex *vertices = new Vertex[(count - 2) * 3];
  uint32_t vertexIndex = 0;
  for (uint32_t i = 1; i < count - 1; i++) {
    vertices[vertexIndex].x = x;
    vertices[vertexIndex].y = y;
    vertices[vertexIndex].z = z;
    vertexIndex++;

    vertices[vertexIndex].x = xCoords[i];
    vertices[vertexIndex].y = yCoords[i];
    vertices[vertexIndex].z = zCoords[i];
    vertexIndex++;

    vertices[vertexIndex].x = xCoords[i+1];
    vertices[vertexIndex].y = yCoords[i+1];
    vertices[vertexIndex].z = zCoords[i+1];
    vertexIndex++;
  }

  gl.bindTexture(gl.getDefaultTexture());
  gl.triangles(vertices, vertexIndex);
  delete[] vertices;
}

void line(GL& gl, const float x1, const float y1, const float z1, const float x2, const float y2, const float z2) {
  Vertex vertices[2];
  vertices[0].x = x1;
  vertices[0].y = y1;
  vertices[0].z = z1;

  vertices[1].x = x2;
  vertices[1].y = y2;
  vertices[1].z = z2;

  gl.bindTexture(gl.getDefaultTexture());
  gl.lines(vertices, 2);
}

}
<|endoftext|>


<commit_before>/*

PICCANTE
The hottest HDR imaging library!
http://vcg.isti.cnr.it/piccante

Copyright (C) 2014
Visual Computing Laboratory - ISTI CNR
http://vcg.isti.cnr.it
First author: Francesco Banterle

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/.

*/

#ifndef PIC_TONE_MAPPING_LISCHINSKI_TMO_HPP
#define PIC_TONE_MAPPING_LISCHINSKI_TMO_HPP

#include "../base.hpp"
#include "../util/math.hpp"
#include "../algorithms/lischinski_minimization.hpp"
#include "../tone_mapping/reinhard_tmo.hpp"
#include "../tone_mapping/tone_mapping_operator.hpp"

namespace pic {

class LischinskiTMO: public ToneMappingOperator
{
protected:
    FilterLuminance flt_lum;

    float alpha, whitePoint;

    /**
     * @brief ProcessAux
     * @param imgIn
     * @param imgOut
     * @return
     */
    Image *ProcessAux(ImageVec imgIn, Image *imgOut)
    {
        if(imgIn[0]->channels != 3) {
            return imgOut;
        }

        updateImage(imgIn[0]);

        //extract luminance
        images[0] = flt_lum.Process(imgIn, images[0]);

        float minL, maxL, Lav;

        images[0]->getMinVal(NULL, &minL);
        images[0]->getMaxVal(NULL, &maxL);
        images[0]->getLogMeanVal(NULL, &Lav);

        float minL_log = log2fPlusEpsilon(minL);
        float maxL_log = log2fPlusEpsilon(maxL);

        int Z = int(ceilf(maxL_log - minL_log));

        if(Z <= 0) {
            return imgOut;
        }

        if(alpha <= 0.0f) {
            alpha = ReinhardTMO::estimateAlpha(minL, maxL, Lav);
        }

        if(whitePoint <= 0.0f) {
            whitePoint = ReinhardTMO::estimateWhitePoint(minL, maxL);
        }

        float whitePoint_sq = whitePoint * whitePoint;

        //choose the representative Rz for each zone
        std::vector<float> *zones = new std::vector<float>[Z];
        float *fstop = new float[Z];
        float *Rz = new float[Z];

        Array<float>::assign(0.0f, Rz, Z);
        Array<float>::assign(0.0f, fstop, Z);

        for(int i = 0; i < images[0]->size(); i++) {
            float L = images[0]->data[i];
            float L_log = log2fPlusEpsilon(L);

            int zone = CLAMP(int(ceilf(L_log - minL_log)), Z);
            zones[zone].push_back(L);
        }

        for(int i = 0; i < Z; i++) {
            int n = int(zones[i].size());
            if(n > 0) {
                std::sort(zones[i].begin(), zones[i].end());
                Rz[i] = zones[i][n / 2];
                if(Rz[i] > 0.0f) {
                    //photographic operator
                    float Rz_s = Rz[i] * alpha / Lav;
                    float f = (Rz_s * (1.0f + Rz_s / whitePoint_sq) ) / (1.0f + Rz_s);
                    fstop[i] = log2fPlusEpsilon(f / Rz[i]);
                }
            }
        }

        //create the fstop map
        images[0]->applyFunction(log2fPlusEpsilon);

        if(images[1] == NULL) {
            images[1] = images[0]->allocateSimilarOne();
        }

        for(int i = 0; i < images[0]->size(); i++) {
            float L_log = images[0]->data[i];
            int zone = CLAMP(int(ceilf(L_log - minL_log)), Z);
            images[1]->data[i] = fstop[zone];
        }

        //run Lischinski minimization
        images[2] = LischinskiMinimization(images[0], images[1], NULL, 0.007f, images[2]);

        images[2]->applyFunction(pow2f);

        *imgOut = *imgIn[0];
        *imgOut *= images[2];

        delete[] zones;
        delete[] Rz;
        delete[] fstop;

        return imgOut;
    }

public:

    /**
     * @brief LischinskiTMO
     * @param alpha
     * @param whitePoint
     */
    LischinskiTMO(float alpha = 0.15f, float whitePoint = 1e6f)
    {
        images.push_back(NULL);
        images.push_back(NULL);
        images.push_back(NULL);
        update(alpha, whitePoint);
    }

    /**
     * @brief update
     * @param alpha
     * @param whitePoint
     */
    void update(float alpha = 0.15f, float whitePoint = 1e6f)
    {
        this->alpha = alpha;
        this->whitePoint = whitePoint;
    }

    /**
     * @brief execute
     * @param imgIn
     * @param imgOut
     * @return
     */
    static Image *execute(Image *imgIn, Image *imgOut)
    {
        LischinskiTMO ltmo(0.05f, 1e6f);
        return ltmo.Process(Single(imgIn), imgOut);
    }
};

} // end namespace pic

#endif /* PIC_TONE_MAPPING_LISCHINSKI_TMO_HPP */

<commit_msg>Update lischinski_tmo.hpp<commit_after>/*

PICCANTE
The hottest HDR imaging library!
http://vcg.isti.cnr.it/piccante

Copyright (C) 2014
Visual Computing Laboratory - ISTI CNR
http://vcg.isti.cnr.it
First author: Francesco Banterle

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/.

*/

#ifndef PIC_TONE_MAPPING_LISCHINSKI_TMO_HPP
#define PIC_TONE_MAPPING_LISCHINSKI_TMO_HPP

#include "../base.hpp"
#include "../util/math.hpp"
#include "../algorithms/lischinski_minimization.hpp"
#include "../tone_mapping/reinhard_tmo.hpp"
#include "../tone_mapping/tone_mapping_operator.hpp"

namespace pic {

class LischinskiTMO: public ToneMappingOperator
{
protected:
    FilterLuminance flt_lum;

    float alpha, whitePoint;

    /**
     * @brief ProcessAux
     * @param imgIn
     * @param imgOut
     * @return
     */
    Image *ProcessAux(ImageVec imgIn, Image *imgOut)
    {
        updateImage(imgIn[0]);

        //extract luminance
        images[0] = flt_lum.Process(imgIn, images[0]);

        float minL, maxL, Lav;

        images[0]->getMinVal(NULL, &minL);
        images[0]->getMaxVal(NULL, &maxL);
        images[0]->getLogMeanVal(NULL, &Lav);

        float minL_log = log2fPlusEpsilon(minL);
        float maxL_log = log2fPlusEpsilon(maxL);

        int Z = int(ceilf(maxL_log - minL_log));

        if(Z <= 0) {
            return imgOut;
        }

        if(alpha <= 0.0f) {
            alpha = ReinhardTMO::estimateAlpha(minL, maxL, Lav);
        }

        if(whitePoint <= 0.0f) {
            whitePoint = ReinhardTMO::estimateWhitePoint(minL, maxL);
        }

        float whitePoint_sq = whitePoint * whitePoint;

        //choose the representative Rz for each zone
        std::vector<float> *zones = new std::vector<float>[Z];
        float *fstop = new float[Z];
        float *Rz = new float[Z];

        Array<float>::assign(0.0f, Rz, Z);
        Array<float>::assign(0.0f, fstop, Z);

        for(int i = 0; i < images[0]->size(); i++) {
            float L = images[0]->data[i];
            float L_log = log2fPlusEpsilon(L);

            int zone = CLAMP(int(ceilf(L_log - minL_log)), Z);
            zones[zone].push_back(L);
        }

        for(int i = 0; i < Z; i++) {
            if(!zones[i].empty()) {
                std::sort(zones[i].begin(), zones[i].end());
                Rz[i] = zones[i][zones[i].size() >> 1];

                if(Rz[i] > 0.0f) {
                    float Rz_s = Rz[i] * alpha / Lav; //photographic operator
                    float f = (Rz_s * (1.0f + Rz_s / whitePoint_sq) ) / (1.0f + Rz_s);
                    fstop[i] = log2fPlusEpsilon(f / Rz[i]);
                }
            }
        }

        //create the fstop map
        images[0]->applyFunction(log2fPlusEpsilon);

        if(images[1] == NULL) {
            images[1] = images[0]->allocateSimilarOne();
        }

        for(int i = 0; i < images[0]->size(); i++) {
            float L_log = images[0]->data[i];
            int zone = CLAMP(int(ceilf(L_log - minL_log)), Z);
            images[1]->data[i] = fstop[zone];
        }

        //run Lischinski minimization
        images[2] = LischinskiMinimization(images[0], images[1], NULL, 0.007f, images[2]);

        images[2]->applyFunction(pow2f);

        *imgOut = *imgIn[0];
        *imgOut *= images[2];

        delete[] zones;
        delete[] Rz;
        delete[] fstop;

        return imgOut;
    }

public:

    /**
     * @brief LischinskiTMO
     * @param alpha
     * @param whitePoint
     */
    LischinskiTMO(float alpha = 0.15f, float whitePoint = 1e6f)
    {
        images.push_back(NULL);
        images.push_back(NULL);
        images.push_back(NULL);
        update(alpha, whitePoint);
    }

    /**
     * @brief update
     * @param alpha
     * @param whitePoint
     */
    void update(float alpha = 0.15f, float whitePoint = 1e6f)
    {
        this->alpha = alpha;
        this->whitePoint = whitePoint;
    }

    /**
     * @brief execute
     * @param imgIn
     * @param imgOut
     * @return
     */
    static Image *execute(Image *imgIn, Image *imgOut)
    {
        LischinskiTMO ltmo(0.15f, 1e6f);
        return ltmo.Process(Single(imgIn), imgOut);
    }
};

} // end namespace pic

#endif /* PIC_TONE_MAPPING_LISCHINSKI_TMO_HPP */

<|endoftext|>


<commit_before>/*
 * This file is part of TelepathyQt4
 *
 * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
 * Copyright (C) 2009 Nokia Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <TelepathyQt4/OutgoingFileTransferChannel>

#include "TelepathyQt4/_gen/outgoing-file-transfer-channel.moc.hpp"

#include "TelepathyQt4/debug-internal.h"

#include <TelepathyQt4/Connection>
#include <TelepathyQt4/PendingFailure>
#include <TelepathyQt4/PendingVariant>
#include <TelepathyQt4/Types>

#include <QIODevice>
#include <QTcpSocket>

namespace Tp
{

struct OutgoingFileTransferChannel::Private
{
    Private(OutgoingFileTransferChannel *parent);
    ~Private();

    // Public object
    OutgoingFileTransferChannel *parent;

    // Introspection
    QIODevice *input;
    QTcpSocket *socket;
    SocketAddressIPv4 addr;

    qint64 pos;
};

OutgoingFileTransferChannel::Private::Private(OutgoingFileTransferChannel *parent)
    : parent(parent),
      input(0),
      socket(0),
      pos(0)
{
}

OutgoingFileTransferChannel::Private::~Private()
{
}

/**
 * \class OutgoingFileTransferChannel
 * \ingroup clientchannel
 * \headerfile <TelepathyQt4/outgoing-file-transfer-channel.h> <TelepathyQt4/OutgoingFileTransferChannel>
 *
 * \brief The OutgoingFileTransferChannel class provides an object representing
 * Telepathy file transfer
 * channel for outgoing file transfers.
 *
 * OutgoingFileTransferChannel is a high-level class representing a
 * Telepathy file transfer
 * channel for outgoing file transfers.
 */

const Feature OutgoingFileTransferChannel::FeatureCore = Feature(OutgoingFileTransferChannel::staticMetaObject.className(), 0);

OutgoingFileTransferChannelPtr OutgoingFileTransferChannel::create(const ConnectionPtr &connection,
        const QString &objectPath, const QVariantMap &immutableProperties)
{
    return OutgoingFileTransferChannelPtr(new OutgoingFileTransferChannel(connection, objectPath,
                immutableProperties));
}

/**
 * Construct a new outgoing file transfer channel associated with the given
 * \a objectPath on the same service as the given \a connection.
 *
 * \param connection Connection owning this channel, and specifying the service.
 * \param objectPath Path to the object on the service.
 * \param immutableProperties The immutable properties of the channel.
 */
OutgoingFileTransferChannel::OutgoingFileTransferChannel(
        const ConnectionPtr &connection,
        const QString &objectPath,
        const QVariantMap &immutableProperties)
    : FileTransferChannel(connection, objectPath, immutableProperties),
      mPriv(new Private(this))
{
}

/**
 * Class destructor.
 */
OutgoingFileTransferChannel::~OutgoingFileTransferChannel()
{
    delete mPriv;
}

/**
 * Provide the file for an outgoing file transfer which has been offered.
 * The state will change to %FileTransferStateOpen as soon as the transfer
 * starts.
 * The given input device should not be destroyed until the state()
 * changes to %FileTransferStateCompleted or %FileTransferStateCancelled.
 * If input is a sequential device QIODevice::isSequential(), it should be
 * closed when no more data is available, so we know when to stop reading.
 *
 * Only the primary handler of a file transfer channel may call this method.
 *
 * This method requires FileTransferChannel::FeatureCore to be enabled.
 *
 * \param input A QIODevice object where the data will be read from.
 * \return A PendingOperation object which will emit PendingOperation::finished
 *         when the call has finished.
 * \sa stateChanged(), state(), stateReason()
 */
PendingOperation *OutgoingFileTransferChannel::provideFile(QIODevice *input)
{
    if (!isReady(FileTransferChannel::FeatureCore)) {
        warning() << "FileTransferChannel::FeatureCore must be ready before "
            "calling provideFile";
        return new PendingFailure(this, TELEPATHY_ERROR_NOT_AVAILABLE,
                "Channel not ready");
    }

    // let's fail here direclty as we may only have one device to handle
    if (mPriv->input) {
        warning() << "File transfer can only be started once in the same "
            "channel";
        return new PendingFailure(this, TELEPATHY_ERROR_NOT_AVAILABLE,
                "File transfer can only be started once in the same channel");
    }

    if ((!input->isOpen() && !input->open(QIODevice::ReadOnly)) &&
        !input->isReadable()) {
        warning() << "Unable to open IO device for reading";
        return new PendingFailure(this, TELEPATHY_ERROR_PERMISSION_DENIED,
                "Unable to open IO device for reading");
    }

    mPriv->input = input;
    connect(input,
            SIGNAL(aboutToClose()),
            SLOT(onInputAboutToClose()));

    PendingVariant *pv = new PendingVariant(
            fileTransferInterface(BypassInterfaceCheck)->ProvideFile(
                SocketAddressTypeIPv4,
                SocketAccessControlLocalhost,
                QDBusVariant(QVariant(QString()))),
            this);
    connect(pv,
            SIGNAL(finished(Tp::PendingOperation*)),
            SLOT(onProvideFileFinished(Tp::PendingOperation*)));
    return pv;
}

void OutgoingFileTransferChannel::onProvideFileFinished(PendingOperation *op)
{
    if (op->isError()) {
        warning() << "Error providing file transfer " <<
            op->errorName() << ":" << op->errorMessage();
        return;
    }

    PendingVariant *pv = qobject_cast<PendingVariant *>(op);
    mPriv->addr = qdbus_cast<SocketAddressIPv4>(pv->result());
    debug().nospace() << "Got address " << mPriv->addr.address <<
        ":" << mPriv->addr.port;

    if (state() == FileTransferStateOpen) {
        connectToHost();
    }
}

void OutgoingFileTransferChannel::connectToHost()
{
    if (isConnected() || mPriv->addr.address.isNull()) {
        return;
    }

    mPriv->socket = new QTcpSocket(this);

    connect(mPriv->socket, SIGNAL(connected()),
            SLOT(onSocketConnected()));
    connect(mPriv->socket, SIGNAL(disconnected()),
            SLOT(onSocketDisconnected()));
    connect(mPriv->socket, SIGNAL(error(QAbstractSocket::SocketError)),
            SLOT(onSocketError(QAbstractSocket::SocketError)));
    connect(mPriv->socket, SIGNAL(bytesWritten(qint64)),
            SLOT(doTransfer()));

    debug().nospace() << "Connecting to host " <<
        mPriv->addr.address << ":" << mPriv->addr.port << "...";
    mPriv->socket->connectToHost(mPriv->addr.address, mPriv->addr.port);
}

void OutgoingFileTransferChannel::onSocketConnected()
{
    debug() << "Connected to host!";
    setConnected();

    connect(mPriv->input, SIGNAL(readyRead()),
            SLOT(doTransfer()));

    // for non sequential devices, let's seek to the initialOffset
    if (!mPriv->input->isSequential()) {
        if (mPriv->input->seek(initialOffset())) {
            mPriv->pos = initialOffset();
        }
    }

    debug() << "Starting transfer...";
    doTransfer();
}

void OutgoingFileTransferChannel::onSocketDisconnected()
{
    debug() << "Disconnected from host!";
    setFinished();
}

void OutgoingFileTransferChannel::onSocketError(QAbstractSocket::SocketError error)
{
    debug() << "Socket error" << error;
    setFinished();
}

void OutgoingFileTransferChannel::onInputAboutToClose()
{
    debug() << "Input closed!";

    // in case of sequential devices, we should read everything from it and
    // write to the socket. Let's not do this for non-sequential devices as we
    // don't want to read a whole file into memory.
    if (isConnected() && mPriv->input->isSequential()) {
        QByteArray data;
        data = mPriv->input->readAll();
        mPriv->socket->write(data); // never fails
    }

    setFinished();
}

void OutgoingFileTransferChannel::doTransfer()
{
    // read 16k each time, as input can be a QFile, we don't want to block
    // reading the whole file
    char buffer[16 * 1024];
    char *p = buffer;

    memset(buffer, 0, sizeof(buffer));
    qint64 len = mPriv->input->read(buffer, sizeof(buffer));

    bool scheduleTransfer = false;
    if (((qulonglong) mPriv->pos < initialOffset()) && (len > 0)) {
        qint64 skip = (qint64) qMin(initialOffset() - mPriv->pos,
                (qulonglong) len);

        debug() << "skipping" << skip << "bytes";
        if (skip == len) {
            // nothing to write, all data read was skipped
            // schedule a transfer, as readyRead may never be called and
            // bytesWriiten will not
            scheduleTransfer = true;
            goto end;
        }

        p += skip;
        len -= skip;
    }

    if (len > 0) {
        mPriv->socket->write(p, len); // never fails
    }

end:
    if (len == -1 || (!mPriv->input->isSequential() && mPriv->input->atEnd())) {
        // error or EOF
        setFinished();
        return;
    }

    mPriv->pos += len;

    if (scheduleTransfer) {
        QMetaObject::invokeMethod(this, SLOT(doTransfer()),
                Qt::QueuedConnection);
    }
}

void OutgoingFileTransferChannel::setFinished()
{
    if (isFinished()) {
        // it shouldn't happen but let's make sure
        return;
    }

    if (mPriv->socket) {
        disconnect(mPriv->socket, SIGNAL(connected()),
                   this, SLOT(onSocketConnected()));
        disconnect(mPriv->socket, SIGNAL(disconnected()),
                   this, SLOT(onSocketDisconnected()));
        disconnect(mPriv->socket, SIGNAL(error(QAbstractSocket::SocketError)),
                   this, SLOT(onSocketError(QAbstractSocket::SocketError)));
        disconnect(mPriv->socket, SIGNAL(bytesWritten(qint64)),
                   this, SLOT(doTransfer()));
        mPriv->socket->close();
    }

    if (mPriv->input) {
        disconnect(mPriv->input, SIGNAL(aboutToClose()),
                   this, SLOT(onInputAboutToClose()));
        disconnect(mPriv->input, SIGNAL(readyRead()),
                   this, SLOT(doTransfer()));
        mPriv->input->close();
    }

    FileTransferChannel::setFinished();
}

} // Tp
<commit_msg>OutgoingFileTransferChannel: Define FT_BLOCK_SIZE and use it as the read buffer size.<commit_after>/*
 * This file is part of TelepathyQt4
 *
 * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
 * Copyright (C) 2009 Nokia Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <TelepathyQt4/OutgoingFileTransferChannel>

#include "TelepathyQt4/_gen/outgoing-file-transfer-channel.moc.hpp"

#include "TelepathyQt4/debug-internal.h"

#include <TelepathyQt4/Connection>
#include <TelepathyQt4/PendingFailure>
#include <TelepathyQt4/PendingVariant>
#include <TelepathyQt4/Types>

#include <QIODevice>
#include <QTcpSocket>

namespace Tp
{

static const int FT_BLOCK_SIZE = 16 * 1024;

struct OutgoingFileTransferChannel::Private
{
    Private(OutgoingFileTransferChannel *parent);
    ~Private();

    // Public object
    OutgoingFileTransferChannel *parent;

    // Introspection
    QIODevice *input;
    QTcpSocket *socket;
    SocketAddressIPv4 addr;

    qint64 pos;
};

OutgoingFileTransferChannel::Private::Private(OutgoingFileTransferChannel *parent)
    : parent(parent),
      input(0),
      socket(0),
      pos(0)
{
}

OutgoingFileTransferChannel::Private::~Private()
{
}

/**
 * \class OutgoingFileTransferChannel
 * \ingroup clientchannel
 * \headerfile <TelepathyQt4/outgoing-file-transfer-channel.h> <TelepathyQt4/OutgoingFileTransferChannel>
 *
 * \brief The OutgoingFileTransferChannel class provides an object representing
 * Telepathy file transfer
 * channel for outgoing file transfers.
 *
 * OutgoingFileTransferChannel is a high-level class representing a
 * Telepathy file transfer
 * channel for outgoing file transfers.
 */

const Feature OutgoingFileTransferChannel::FeatureCore = Feature(OutgoingFileTransferChannel::staticMetaObject.className(), 0);

OutgoingFileTransferChannelPtr OutgoingFileTransferChannel::create(const ConnectionPtr &connection,
        const QString &objectPath, const QVariantMap &immutableProperties)
{
    return OutgoingFileTransferChannelPtr(new OutgoingFileTransferChannel(connection, objectPath,
                immutableProperties));
}

/**
 * Construct a new outgoing file transfer channel associated with the given
 * \a objectPath on the same service as the given \a connection.
 *
 * \param connection Connection owning this channel, and specifying the service.
 * \param objectPath Path to the object on the service.
 * \param immutableProperties The immutable properties of the channel.
 */
OutgoingFileTransferChannel::OutgoingFileTransferChannel(
        const ConnectionPtr &connection,
        const QString &objectPath,
        const QVariantMap &immutableProperties)
    : FileTransferChannel(connection, objectPath, immutableProperties),
      mPriv(new Private(this))
{
}

/**
 * Class destructor.
 */
OutgoingFileTransferChannel::~OutgoingFileTransferChannel()
{
    delete mPriv;
}

/**
 * Provide the file for an outgoing file transfer which has been offered.
 * The state will change to %FileTransferStateOpen as soon as the transfer
 * starts.
 * The given input device should not be destroyed until the state()
 * changes to %FileTransferStateCompleted or %FileTransferStateCancelled.
 * If input is a sequential device QIODevice::isSequential(), it should be
 * closed when no more data is available, so we know when to stop reading.
 *
 * Only the primary handler of a file transfer channel may call this method.
 *
 * This method requires FileTransferChannel::FeatureCore to be enabled.
 *
 * \param input A QIODevice object where the data will be read from.
 * \return A PendingOperation object which will emit PendingOperation::finished
 *         when the call has finished.
 * \sa stateChanged(), state(), stateReason()
 */
PendingOperation *OutgoingFileTransferChannel::provideFile(QIODevice *input)
{
    if (!isReady(FileTransferChannel::FeatureCore)) {
        warning() << "FileTransferChannel::FeatureCore must be ready before "
            "calling provideFile";
        return new PendingFailure(this, TELEPATHY_ERROR_NOT_AVAILABLE,
                "Channel not ready");
    }

    // let's fail here direclty as we may only have one device to handle
    if (mPriv->input) {
        warning() << "File transfer can only be started once in the same "
            "channel";
        return new PendingFailure(this, TELEPATHY_ERROR_NOT_AVAILABLE,
                "File transfer can only be started once in the same channel");
    }

    if ((!input->isOpen() && !input->open(QIODevice::ReadOnly)) &&
        !input->isReadable()) {
        warning() << "Unable to open IO device for reading";
        return new PendingFailure(this, TELEPATHY_ERROR_PERMISSION_DENIED,
                "Unable to open IO device for reading");
    }

    mPriv->input = input;
    connect(input,
            SIGNAL(aboutToClose()),
            SLOT(onInputAboutToClose()));

    PendingVariant *pv = new PendingVariant(
            fileTransferInterface(BypassInterfaceCheck)->ProvideFile(
                SocketAddressTypeIPv4,
                SocketAccessControlLocalhost,
                QDBusVariant(QVariant(QString()))),
            this);
    connect(pv,
            SIGNAL(finished(Tp::PendingOperation*)),
            SLOT(onProvideFileFinished(Tp::PendingOperation*)));
    return pv;
}

void OutgoingFileTransferChannel::onProvideFileFinished(PendingOperation *op)
{
    if (op->isError()) {
        warning() << "Error providing file transfer " <<
            op->errorName() << ":" << op->errorMessage();
        return;
    }

    PendingVariant *pv = qobject_cast<PendingVariant *>(op);
    mPriv->addr = qdbus_cast<SocketAddressIPv4>(pv->result());
    debug().nospace() << "Got address " << mPriv->addr.address <<
        ":" << mPriv->addr.port;

    if (state() == FileTransferStateOpen) {
        connectToHost();
    }
}

void OutgoingFileTransferChannel::connectToHost()
{
    if (isConnected() || mPriv->addr.address.isNull()) {
        return;
    }

    mPriv->socket = new QTcpSocket(this);

    connect(mPriv->socket, SIGNAL(connected()),
            SLOT(onSocketConnected()));
    connect(mPriv->socket, SIGNAL(disconnected()),
            SLOT(onSocketDisconnected()));
    connect(mPriv->socket, SIGNAL(error(QAbstractSocket::SocketError)),
            SLOT(onSocketError(QAbstractSocket::SocketError)));
    connect(mPriv->socket, SIGNAL(bytesWritten(qint64)),
            SLOT(doTransfer()));

    debug().nospace() << "Connecting to host " <<
        mPriv->addr.address << ":" << mPriv->addr.port << "...";
    mPriv->socket->connectToHost(mPriv->addr.address, mPriv->addr.port);
}

void OutgoingFileTransferChannel::onSocketConnected()
{
    debug() << "Connected to host!";
    setConnected();

    connect(mPriv->input, SIGNAL(readyRead()),
            SLOT(doTransfer()));

    // for non sequential devices, let's seek to the initialOffset
    if (!mPriv->input->isSequential()) {
        if (mPriv->input->seek(initialOffset())) {
            mPriv->pos = initialOffset();
        }
    }

    debug() << "Starting transfer...";
    doTransfer();
}

void OutgoingFileTransferChannel::onSocketDisconnected()
{
    debug() << "Disconnected from host!";
    setFinished();
}

void OutgoingFileTransferChannel::onSocketError(QAbstractSocket::SocketError error)
{
    debug() << "Socket error" << error;
    setFinished();
}

void OutgoingFileTransferChannel::onInputAboutToClose()
{
    debug() << "Input closed!";

    // in case of sequential devices, we should read everything from it and
    // write to the socket. Let's not do this for non-sequential devices as we
    // don't want to read a whole file into memory.
    if (isConnected() && mPriv->input->isSequential()) {
        QByteArray data;
        data = mPriv->input->readAll();
        mPriv->socket->write(data); // never fails
    }

    setFinished();
}

void OutgoingFileTransferChannel::doTransfer()
{
    // read FT_BLOCK_SIZE each time, as input can be a QFile, we don't want to
    // block reading the whole file
    char buffer[FT_BLOCK_SIZE];
    char *p = buffer;

    memset(buffer, 0, sizeof(buffer));
    qint64 len = mPriv->input->read(buffer, sizeof(buffer));

    bool scheduleTransfer = false;
    if (((qulonglong) mPriv->pos < initialOffset()) && (len > 0)) {
        qint64 skip = (qint64) qMin(initialOffset() - mPriv->pos,
                (qulonglong) len);

        debug() << "skipping" << skip << "bytes";
        if (skip == len) {
            // nothing to write, all data read was skipped
            // schedule a transfer, as readyRead may never be called and
            // bytesWriiten will not
            scheduleTransfer = true;
            goto end;
        }

        p += skip;
        len -= skip;
    }

    if (len > 0) {
        mPriv->socket->write(p, len); // never fails
    }

end:
    if (len == -1 || (!mPriv->input->isSequential() && mPriv->input->atEnd())) {
        // error or EOF
        setFinished();
        return;
    }

    mPriv->pos += len;

    if (scheduleTransfer) {
        QMetaObject::invokeMethod(this, SLOT(doTransfer()),
                Qt::QueuedConnection);
    }
}

void OutgoingFileTransferChannel::setFinished()
{
    if (isFinished()) {
        // it shouldn't happen but let's make sure
        return;
    }

    if (mPriv->socket) {
        disconnect(mPriv->socket, SIGNAL(connected()),
                   this, SLOT(onSocketConnected()));
        disconnect(mPriv->socket, SIGNAL(disconnected()),
                   this, SLOT(onSocketDisconnected()));
        disconnect(mPriv->socket, SIGNAL(error(QAbstractSocket::SocketError)),
                   this, SLOT(onSocketError(QAbstractSocket::SocketError)));
        disconnect(mPriv->socket, SIGNAL(bytesWritten(qint64)),
                   this, SLOT(doTransfer()));
        mPriv->socket->close();
    }

    if (mPriv->input) {
        disconnect(mPriv->input, SIGNAL(aboutToClose()),
                   this, SLOT(onInputAboutToClose()));
        disconnect(mPriv->input, SIGNAL(readyRead()),
                   this, SLOT(doTransfer()));
        mPriv->input->close();
    }

    FileTransferChannel::setFinished();
}

} // Tp
<|endoftext|>


<commit_before>// Copyright (c) 2014-2019 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/

#ifndef TAO_PEGTL_POSITION_HPP
#define TAO_PEGTL_POSITION_HPP

#include <cstdlib>
#include <ostream>
#include <sstream>
#include <string>
#include <utility>

#include "config.hpp"

#include "internal/iterator.hpp"

namespace TAO_PEGTL_NAMESPACE
{
   struct position
   {
      position() noexcept {}

      position( position&& ) noexcept = default;
      position( const position& ) = default;

      position& operator=( position&& ) noexcept = default;
      position& operator=( const position& ) = default;

      template< typename T >
      position( const internal::iterator& in_iter, T&& in_source )
         : byte( in_iter.byte ),
           line( in_iter.line ),
           byte_in_line( in_iter.byte_in_line ),
           source( std::forward< T >( in_source ) )
      {
      }

      std::size_t byte;
      std::size_t line;
      std::size_t byte_in_line;
      std::string source;
   };

   inline std::ostream& operator<<( std::ostream& o, const position& p )
   {
      return o << p.source << ':' << p.line << ':' << p.byte_in_line << '(' << p.byte << ')';
   }

   [[nodiscard]] inline std::string to_string( const position& p )
   {
      std::ostringstream o;
      o << p;
      return o.str();
   }

}  // namespace TAO_PEGTL_NAMESPACE

#endif
<commit_msg>Avoid uninitialized member warnings<commit_after>// Copyright (c) 2014-2019 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/

#ifndef TAO_PEGTL_POSITION_HPP
#define TAO_PEGTL_POSITION_HPP

#include <cstdlib>
#include <ostream>
#include <sstream>
#include <string>
#include <utility>

#include "config.hpp"

#include "internal/iterator.hpp"

namespace TAO_PEGTL_NAMESPACE
{
   struct position
   {
      position() noexcept {}

      position( position&& ) noexcept = default;
      position( const position& ) = default;

      position& operator=( position&& ) noexcept = default;
      position& operator=( const position& ) = default;

      template< typename T >
      position( const internal::iterator& in_iter, T&& in_source )
         : byte( in_iter.byte ),
           line( in_iter.line ),
           byte_in_line( in_iter.byte_in_line ),
           source( std::forward< T >( in_source ) )
      {
      }

      std::size_t byte = 0;
      std::size_t line = 0;
      std::size_t byte_in_line = 0;
      std::string source;
   };

   inline std::ostream& operator<<( std::ostream& o, const position& p )
   {
      return o << p.source << ':' << p.line << ':' << p.byte_in_line << '(' << p.byte << ')';
   }

   [[nodiscard]] inline std::string to_string( const position& p )
   {
      std::ostringstream o;
      o << p;
      return o.str();
   }

}  // namespace TAO_PEGTL_NAMESPACE

#endif
<|endoftext|>


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

/*! \file jacobi.inl
 *  \brief Inline file for jacobi.h
 */

#include <cusp/multiply.h>
#include <cusp/detail/format_utils.h>

#include <thrust/functional.h>
#include <thrust/transform.h>
#include <thrust/iterator/zip_iterator.h>

namespace cusp
{
namespace relaxation
{
namespace detail
{

template <typename ValueType>
struct jacobi_functor
{
    ValueType omega;

    jacobi_functor(ValueType omega) : omega(omega) {}

    template <typename Tuple>
    __host__ __device__
    ValueType operator()(const Tuple& t)
    {
        const ValueType x = thrust::get<0>(t);
        const ValueType d = thrust::get<1>(t);
        const ValueType b = thrust::get<2>(t);
        const ValueType y = thrust::get<3>(t);

        return x + omega * (b - y) / d;
    }
};

} // end namespace detail

// constructor
template <typename ValueType, typename MemorySpace>
    jacobi<ValueType,MemorySpace>
    ::jacobi() : default_omega(0.0)
    {
    }

template <typename ValueType, typename MemorySpace>
template<typename MatrixType>
    jacobi<ValueType,MemorySpace>
    ::jacobi(const MatrixType& A, ValueType omega) : default_omega(omega)
    {
        // extract the main diagonal
        cusp::detail::extract_diagonal(A, diagonal);
    }

// linear_operator
template <typename ValueType, typename MemorySpace>
template<typename MatrixType, typename VectorType1, typename VectorType2>
    void jacobi<ValueType,MemorySpace>
    ::operator()(const MatrixType& A, const VectorType1& b, VectorType2& x) const
    {
        jacobi<ValueType,MemorySpace>::operator()(A,b,x,default_omega);
    }

// override default omega
template <typename ValueType, typename MemorySpace>
template<typename MatrixType, typename VectorType1, typename VectorType2>
    void jacobi<ValueType,MemorySpace>
    ::operator()(const MatrixType& A, const VectorType1& b, VectorType2& x, ValueType omega) const
    {
        // TODO see if preallocating y is noticably faster
        cusp::array1d<ValueType,MemorySpace> y(x.size());

        // y <- A*x
        cusp::multiply(A, x, y);

        // x <- D^-1 (b - y)
        thrust::transform(thrust::make_zip_iterator(thrust::make_tuple(x.begin(), diagonal.begin(), b.begin(), y.begin())),
                          thrust::make_zip_iterator(thrust::make_tuple(x.end(),   diagonal.end(),   b.end(),   y.end())),
                          x.begin(),
                          detail::jacobi_functor<ValueType>(omega));
    }

} // end namespace relaxation
} // end namespace cusp

<commit_msg>fixed error in jacobi.inl comment<commit_after>/*
 *  Copyright 2008-2009 NVIDIA Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/*! \file jacobi.inl
 *  \brief Inline file for jacobi.h
 */

#include <cusp/multiply.h>
#include <cusp/detail/format_utils.h>

#include <thrust/functional.h>
#include <thrust/transform.h>
#include <thrust/iterator/zip_iterator.h>

namespace cusp
{
namespace relaxation
{
namespace detail
{

template <typename ValueType>
struct jacobi_functor
{
    ValueType omega;

    jacobi_functor(ValueType omega) : omega(omega) {}

    template <typename Tuple>
    __host__ __device__
    ValueType operator()(const Tuple& t)
    {
        const ValueType x = thrust::get<0>(t);
        const ValueType d = thrust::get<1>(t);
        const ValueType b = thrust::get<2>(t);
        const ValueType y = thrust::get<3>(t);

        return x + omega * (b - y) / d;
    }
};

} // end namespace detail

// constructor
template <typename ValueType, typename MemorySpace>
    jacobi<ValueType,MemorySpace>
    ::jacobi() : default_omega(0.0)
    {
    }

template <typename ValueType, typename MemorySpace>
template<typename MatrixType>
    jacobi<ValueType,MemorySpace>
    ::jacobi(const MatrixType& A, ValueType omega) : default_omega(omega)
    {
        // extract the main diagonal
        cusp::detail::extract_diagonal(A, diagonal);
    }

// linear_operator
template <typename ValueType, typename MemorySpace>
template<typename MatrixType, typename VectorType1, typename VectorType2>
    void jacobi<ValueType,MemorySpace>
    ::operator()(const MatrixType& A, const VectorType1& b, VectorType2& x) const
    {
        jacobi<ValueType,MemorySpace>::operator()(A,b,x,default_omega);
    }

// override default omega
template <typename ValueType, typename MemorySpace>
template<typename MatrixType, typename VectorType1, typename VectorType2>
    void jacobi<ValueType,MemorySpace>
    ::operator()(const MatrixType& A, const VectorType1& b, VectorType2& x, ValueType omega) const
    {
        // TODO see if preallocating y is noticably faster
        cusp::array1d<ValueType,MemorySpace> y(x.size());

        // y <- A*x
        cusp::multiply(A, x, y);

        // x <- x + D^-1 (b - y)
        thrust::transform(thrust::make_zip_iterator(thrust::make_tuple(x.begin(), diagonal.begin(), b.begin(), y.begin())),
                          thrust::make_zip_iterator(thrust::make_tuple(x.end(),   diagonal.end(),   b.end(),   y.end())),
                          x.begin(),
                          detail::jacobi_functor<ValueType>(omega));
    }

} // end namespace relaxation
} // end namespace cusp

<|endoftext|>


<commit_before>#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <utility>
#include <signal.h>

#include <boost/program_options.hpp>

#include <boost/shared_ptr.hpp>
#include <boost/filesystem.hpp>

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/foreach.hpp>

#include <log4cxx/logger.h>
#include <log4cxx/basicconfigurator.h>
#include <log4cxx/propertyconfigurator.h>
#include <log4cxx/helpers/exception.h>
#include <log4cxx/xml/domconfigurator.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "ControlUtility.h"

using namespace log4cxx;
using namespace log4cxx::helpers;

using namespace FrameSimulatorTest;

namespace po = boost::program_options;

#include "logging.h"
#include "DebugLevelLogger.h"

/** Check that str contains suffix
 * /param[in] str - string to test
 * /param[in] str - test suffix
 * /return true if suffix found; else false
 */
static bool has_suffix(const std::string &str, const std::string &suffix) {
  return str.size() >= suffix.size() &&
         str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}

/** Parse command line arguments
 * /param[in] argc - argument count; number of arguments to parse
 * /param[in] argv - one-dimensional string array of command line arguments
 * /param[in] vm - boost program_options variables_map
 * /param[in] logger - pointer to logging instance
 * /param[in] ptree - property tree instance
 */
int parse_arguments(int argc,
                    char **argv,
                    po::variables_map &vm,
                    LoggerPtr &logger,
                    boost::property_tree::ptree &ptree) {

  int rc = 0;

  try {

    po::options_description generic("Generic options");

    generic.add_options()
            ("json", po::value<std::string>(), "Configuration file")
            ("help", "Print help message");

    po::options_description cmdline_options;
    cmdline_options.add(generic);

    po::parsed_options parsed = po::command_line_parser(argc, argv).
            options(generic).
            allow_unregistered().
            run();

    po::store(parsed, vm);

    // If the command-line help option was given, print help and exit

    if (vm.count("help")) {
      std::cout << "usage: FrameTest [options]" << std::endl << std::endl;
      std::cout << cmdline_options << std::endl;
      exit(1);
    }

    // Setup logging

    if (vm.count("logconfig")) {
      std::string logconf_fname = vm["logconfig"].as<std::string>();
      if (has_suffix(logconf_fname, ".xml")) {
        log4cxx::xml::DOMConfigurator::configure(logconf_fname);
      } else {
        PropertyConfigurator::configure(logconf_fname);
      }
      LOG4CXX_DEBUG(logger, "log4cxx config file is set to " << logconf_fname);
    } else {
      BasicConfigurator::configure();
    }

    // Read configuration file; store as boost::property_tree::ptree

    if (vm.count("json")) {
      std::string config_file = vm["json"].as<std::string>();
      LOG4CXX_DEBUG(logger, "Reading config file " << config_file);
      boost::property_tree::json_parser::read_json(config_file, ptree);
    } else {
      LOG4CXX_ERROR(logger, "No configuration file specified. Exiting.");
      exit(1);
    }

  }

  catch (...) {
    std::cout << "Exception parsing arguments.";
    rc = 1;
  }

  return rc;

}

int main(int argc, char *argv[]) {

  LoggerPtr logger(Logger::getLogger("Test.App"));

  try {

    // Process IDs
    std::vector<pid_t> process_pids;

    // Read command arguments into pt
    boost::property_tree::ptree pt;
    po::variables_map vm;
    parse_arguments(argc, argv, vm, logger, pt);

    // Setup ControlUtility instances
    std::vector<ControlUtility*> utilities;
    BOOST_FOREACH(boost::property_tree::ptree::value_type &vc, pt.get_child("Main")) {
      pid_t pid;
      std::string process = vc.first;
      LOG4CXX_INFO(logger, "Process to launch: " + process);
      std::string command_entry = "Main." + process + "." + "command";
      std::string pos_args = pt.get<std::string>("Main." + process + "." + "pos-args");
      int sleeptime = pt.get<int>("Main." + process + "." + "sleep");
      ControlUtility* control = new ControlUtility(pt, pos_args, command_entry, process, pid, logger);
      // Run
      if (pt.get<bool>("Main." + process +"." + "process")) {
        process_pids.push_back(pid);
        control->run_process();
      }
      else
        control->run_command();
      sleep(sleeptime);
    }

    sleep(10);
    for(int i=0; i<process_pids.size(); i++)
      kill(process_pids[i], SIGINT);

    int s;
    wait(&s);

  } catch (const std::exception &e) {
    LOG4CXX_ERROR(logger, "Caught unhandled exception in FrameTestApp, application will terminate: " << e.what());
    throw;
  }

  return 0;

}
<commit_msg>switch kill signal; remove wait<commit_after>#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <utility>
#include <signal.h>

#include <boost/program_options.hpp>

#include <boost/shared_ptr.hpp>
#include <boost/filesystem.hpp>

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/foreach.hpp>

#include <log4cxx/logger.h>
#include <log4cxx/basicconfigurator.h>
#include <log4cxx/propertyconfigurator.h>
#include <log4cxx/helpers/exception.h>
#include <log4cxx/xml/domconfigurator.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "ControlUtility.h"

using namespace log4cxx;
using namespace log4cxx::helpers;

using namespace FrameSimulatorTest;

namespace po = boost::program_options;

#include "logging.h"
#include "DebugLevelLogger.h"

/** Check that str contains suffix
 * /param[in] str - string to test
 * /param[in] str - test suffix
 * /return true if suffix found; else false
 */
static bool has_suffix(const std::string &str, const std::string &suffix) {
  return str.size() >= suffix.size() &&
         str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}

/** Parse command line arguments
 * /param[in] argc - argument count; number of arguments to parse
 * /param[in] argv - one-dimensional string array of command line arguments
 * /param[in] vm - boost program_options variables_map
 * /param[in] logger - pointer to logging instance
 * /param[in] ptree - property tree instance
 */
int parse_arguments(int argc,
                    char **argv,
                    po::variables_map &vm,
                    LoggerPtr &logger,
                    boost::property_tree::ptree &ptree) {

  int rc = 0;

  try {

    po::options_description generic("Generic options");

    generic.add_options()
            ("json", po::value<std::string>(), "Configuration file")
            ("help", "Print help message");

    po::options_description cmdline_options;
    cmdline_options.add(generic);

    po::parsed_options parsed = po::command_line_parser(argc, argv).
            options(generic).
            allow_unregistered().
            run();

    po::store(parsed, vm);

    // If the command-line help option was given, print help and exit

    if (vm.count("help")) {
      std::cout << "usage: FrameTest [options]" << std::endl << std::endl;
      std::cout << cmdline_options << std::endl;
      exit(1);
    }

    // Setup logging

    if (vm.count("logconfig")) {
      std::string logconf_fname = vm["logconfig"].as<std::string>();
      if (has_suffix(logconf_fname, ".xml")) {
        log4cxx::xml::DOMConfigurator::configure(logconf_fname);
      } else {
        PropertyConfigurator::configure(logconf_fname);
      }
      LOG4CXX_DEBUG(logger, "log4cxx config file is set to " << logconf_fname);
    } else {
      BasicConfigurator::configure();
    }

    // Read configuration file; store as boost::property_tree::ptree

    if (vm.count("json")) {
      std::string config_file = vm["json"].as<std::string>();
      LOG4CXX_DEBUG(logger, "Reading config file " << config_file);
      boost::property_tree::json_parser::read_json(config_file, ptree);
    } else {
      LOG4CXX_ERROR(logger, "No configuration file specified. Exiting.");
      exit(1);
    }

  }

  catch (...) {
    std::cout << "Exception parsing arguments.";
    rc = 1;
  }

  return rc;

}

int main(int argc, char *argv[]) {

  LoggerPtr logger(Logger::getLogger("Test.App"));

  try {

    // Process IDs
    std::vector<pid_t> process_pids;

    // Read command arguments into pt
    boost::property_tree::ptree pt;
    po::variables_map vm;
    parse_arguments(argc, argv, vm, logger, pt);

    // Setup ControlUtility instances
    std::vector<ControlUtility*> utilities;
    BOOST_FOREACH(boost::property_tree::ptree::value_type &vc, pt.get_child("Main")) {
      pid_t pid;
      std::string process = vc.first;
      LOG4CXX_INFO(logger, "Process to launch: " + process);
      std::string command_entry = "Main." + process + "." + "command";
      std::string pos_args = pt.get<std::string>("Main." + process + "." + "pos-args");
      int sleeptime = pt.get<int>("Main." + process + "." + "sleep");
      ControlUtility* control = new ControlUtility(pt, pos_args, command_entry, process, pid, logger);
      // Run
      if (pt.get<bool>("Main." + process +"." + "process")) {
        process_pids.push_back(pid);
        control->run_process();
      }
      else
        control->run_command();
      sleep(sleeptime);
    }

    sleep(10);
    for(int i=0; i<process_pids.size(); i++)
      kill(process_pids[i], SIGTERM);

    //int s;
    //wait(&s);

  } catch (const std::exception &e) {
    LOG4CXX_ERROR(logger, "Caught unhandled exception in FrameTestApp, application will terminate: " << e.what());
    throw;
  }

  return 0;

}
<|endoftext|>


<commit_before>//===- CallGraphSort.cpp --------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// Implementation of Call-Chain Clustering from: Optimizing Function Placement
/// for Large-Scale Data-Center Applications
/// https://research.fb.com/wp-content/uploads/2017/01/cgo2017-hfsort-final1.pdf
///
/// The goal of this algorithm is to improve runtime performance of the final
/// executable by arranging code sections such that page table and i-cache
/// misses are minimized.
///
/// Definitions:
/// * Cluster
///   * An ordered list of input sections which are layed out as a unit. At the
///     beginning of the algorithm each input section has its own cluster and
///     the weight of the cluster is the sum of the weight of all incomming
///     edges.
/// * Call-Chain Clustering (C³) Heuristic
///   * Defines when and how clusters are combined. Pick the highest weighted
///     input section then add it to its most likely predecessor if it wouldn't
///     penalize it too much.
/// * Density
///   * The weight of the cluster divided by the size of the cluster. This is a
///     proxy for the ammount of execution time spent per byte of the cluster.
///
/// It does so given a call graph profile by the following:
/// * Build a weighted call graph from the call graph profile
/// * Sort input sections by weight
/// * For each input section starting with the highest weight
///   * Find its most likely predecessor cluster
///   * Check if the combined cluster would be too large, or would have too low
///     a density.
///   * If not, then combine the clusters.
/// * Sort non-empty clusters by density
///
//===----------------------------------------------------------------------===//

#include "CallGraphSort.h"
#include "OutputSections.h"
#include "SymbolTable.h"
#include "Symbols.h"

using namespace llvm;
using namespace lld;
using namespace lld::elf;

namespace {
struct Edge {
  int from;
  uint64_t weight;
};

struct Cluster {
  Cluster(int sec, size_t s) : sections{sec}, size(s) {}

  double getDensity() const {
    if (size == 0)
      return 0;
    return double(weight) / double(size);
  }

  std::vector<int> sections;
  size_t size = 0;
  uint64_t weight = 0;
  uint64_t initialWeight = 0;
  Edge bestPred = {-1, 0};
};

class CallGraphSort {
public:
  CallGraphSort();

  DenseMap<const InputSectionBase *, int> run();

private:
  std::vector<Cluster> clusters;
  std::vector<const InputSectionBase *> sections;

  void groupClusters();
};

// Maximum ammount the combined cluster density can be worse than the original
// cluster to consider merging.
constexpr int MAX_DENSITY_DEGRADATION = 8;

// Maximum cluster size in bytes.
constexpr uint64_t MAX_CLUSTER_SIZE = 1024 * 1024;
} // end anonymous namespace

using SectionPair =
    std::pair<const InputSectionBase *, const InputSectionBase *>;

// Take the edge list in Config->CallGraphProfile, resolve symbol names to
// Symbols, and generate a graph between InputSections with the provided
// weights.
CallGraphSort::CallGraphSort() {
  MapVector<SectionPair, uint64_t> &profile = config->callGraphProfile;
  DenseMap<const InputSectionBase *, int> secToCluster;

  auto getOrCreateNode = [&](const InputSectionBase *isec) -> int {
    auto res = secToCluster.insert(std::make_pair(isec, clusters.size()));
    if (res.second) {
      sections.push_back(isec);
      clusters.emplace_back(clusters.size(), isec->getSize());
    }
    return res.first->second;
  };

  // Create the graph.
  for (std::pair<SectionPair, uint64_t> &c : profile) {
    const auto *fromSB = cast<InputSectionBase>(c.first.first->repl);
    const auto *toSB = cast<InputSectionBase>(c.first.second->repl);
    uint64_t weight = c.second;

    // Ignore edges between input sections belonging to different output
    // sections.  This is done because otherwise we would end up with clusters
    // containing input sections that can't actually be placed adjacently in the
    // output.  This messes with the cluster size and density calculations.  We
    // would also end up moving input sections in other output sections without
    // moving them closer to what calls them.
    if (fromSB->getOutputSection() != toSB->getOutputSection())
      continue;

    int from = getOrCreateNode(fromSB);
    int to = getOrCreateNode(toSB);

    clusters[to].weight += weight;

    if (from == to)
      continue;

    // Remember the best edge.
    Cluster &toC = clusters[to];
    if (toC.bestPred.from == -1 || toC.bestPred.weight < weight) {
      toC.bestPred.from = from;
      toC.bestPred.weight = weight;
    }
  }
  for (Cluster &c : clusters)
    c.initialWeight = c.weight;
}

// It's bad to merge clusters which would degrade the density too much.
static bool isNewDensityBad(Cluster &a, Cluster &b) {
  double newDensity = double(a.weight + b.weight) / double(a.size + b.size);
  return newDensity < a.getDensity() / MAX_DENSITY_DEGRADATION;
}

static void mergeClusters(Cluster &into, Cluster &from) {
  into.sections.insert(into.sections.end(), from.sections.begin(),
                       from.sections.end());
  into.size += from.size;
  into.weight += from.weight;
  from.sections.clear();
  from.size = 0;
  from.weight = 0;
}

// Group InputSections into clusters using the Call-Chain Clustering heuristic
// then sort the clusters by density.
void CallGraphSort::groupClusters() {
  std::vector<int> sortedSecs(clusters.size());
  std::vector<Cluster *> secToCluster(clusters.size());

  for (size_t i = 0; i < clusters.size(); ++i) {
    sortedSecs[i] = i;
    secToCluster[i] = &clusters[i];
  }

  llvm::stable_sort(sortedSecs, [&](int a, int b) {
    return clusters[a].getDensity() > clusters[b].getDensity();
  });

  for (int si : sortedSecs) {
    // clusters[si] is the same as secToClusters[si] here because it has not
    // been merged into another cluster yet.
    Cluster &c = clusters[si];

    // Don't consider merging if the edge is unlikely.
    if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight)
      continue;

    Cluster *predC = secToCluster[c.bestPred.from];
    if (predC == &c)
      continue;

    if (c.size + predC->size > MAX_CLUSTER_SIZE)
      continue;

    if (isNewDensityBad(*predC, c))
      continue;

    // NOTE: Consider using a disjoint-set to track section -> cluster mapping
    // if this is ever slow.
    for (int si : c.sections)
      secToCluster[si] = predC;

    mergeClusters(*predC, c);
  }

  // Remove empty or dead nodes. Invalidates all cluster indices.
  llvm::erase_if(clusters, [](const Cluster &c) {
    return c.size == 0 || c.sections.empty();
  });

  // Sort by density.
  llvm::stable_sort(clusters, [](const Cluster &a, const Cluster &b) {
    return a.getDensity() > b.getDensity();
  });
}

DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
  groupClusters();

  // Generate order.
  DenseMap<const InputSectionBase *, int> orderMap;
  ssize_t curOrder = 1;

  for (const Cluster &c : clusters)
    for (int secIndex : c.sections)
      orderMap[sections[secIndex]] = curOrder++;

  if (!config->printSymbolOrder.empty()) {
    std::error_code ec;
    raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::OF_None);
    if (ec) {
      error("cannot open " + config->printSymbolOrder + ": " + ec.message());
      return orderMap;
    }

    // Print the symbols ordered by C3, in the order of increasing curOrder
    // Instead of sorting all the orderMap, just repeat the loops above.
    for (const Cluster &c : clusters)
      for (int secIndex : c.sections)
        // Search all the symbols in the file of the section
        // and find out a Defined symbol with name that is within the section.
        for (Symbol *sym: sections[secIndex]->file->getSymbols())
          if (!sym->isSection()) // Filter out section-type symbols here.
            if (auto *d = dyn_cast<Defined>(sym))
              if (sections[secIndex] == d->section)
                os << sym->getName() << "\n";
  }

  return orderMap;
}

// Sort sections by the profile data provided by -callgraph-profile-file
//
// This first builds a call graph based on the profile data then merges sections
// according to the C³ huristic. All clusters are then sorted by a density
// metric to further improve locality.
DenseMap<const InputSectionBase *, int> elf::computeCallGraphProfileOrder() {
  return CallGraphSort().run();
}
<commit_msg>[ELF] Use union-find set and doubly linked list in Call-Chain Clustering (C³) heuristic<commit_after>//===- CallGraphSort.cpp --------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// Implementation of Call-Chain Clustering from: Optimizing Function Placement
/// for Large-Scale Data-Center Applications
/// https://research.fb.com/wp-content/uploads/2017/01/cgo2017-hfsort-final1.pdf
///
/// The goal of this algorithm is to improve runtime performance of the final
/// executable by arranging code sections such that page table and i-cache
/// misses are minimized.
///
/// Definitions:
/// * Cluster
///   * An ordered list of input sections which are layed out as a unit. At the
///     beginning of the algorithm each input section has its own cluster and
///     the weight of the cluster is the sum of the weight of all incomming
///     edges.
/// * Call-Chain Clustering (C³) Heuristic
///   * Defines when and how clusters are combined. Pick the highest weighted
///     input section then add it to its most likely predecessor if it wouldn't
///     penalize it too much.
/// * Density
///   * The weight of the cluster divided by the size of the cluster. This is a
///     proxy for the ammount of execution time spent per byte of the cluster.
///
/// It does so given a call graph profile by the following:
/// * Build a weighted call graph from the call graph profile
/// * Sort input sections by weight
/// * For each input section starting with the highest weight
///   * Find its most likely predecessor cluster
///   * Check if the combined cluster would be too large, or would have too low
///     a density.
///   * If not, then combine the clusters.
/// * Sort non-empty clusters by density
///
//===----------------------------------------------------------------------===//

#include "CallGraphSort.h"
#include "OutputSections.h"
#include "SymbolTable.h"
#include "Symbols.h"

#include <numeric>

using namespace llvm;
using namespace lld;
using namespace lld::elf;

namespace {
struct Edge {
  int from;
  uint64_t weight;
};

struct Cluster {
  Cluster(int sec, size_t s) : next(sec), prev(sec), size(s) {}

  double getDensity() const {
    if (size == 0)
      return 0;
    return double(weight) / double(size);
  }

  int next;
  int prev;
  size_t size = 0;
  uint64_t weight = 0;
  uint64_t initialWeight = 0;
  Edge bestPred = {-1, 0};
};

class CallGraphSort {
public:
  CallGraphSort();

  DenseMap<const InputSectionBase *, int> run();

private:
  std::vector<Cluster> clusters;
  std::vector<const InputSectionBase *> sections;
};

// Maximum ammount the combined cluster density can be worse than the original
// cluster to consider merging.
constexpr int MAX_DENSITY_DEGRADATION = 8;

// Maximum cluster size in bytes.
constexpr uint64_t MAX_CLUSTER_SIZE = 1024 * 1024;
} // end anonymous namespace

using SectionPair =
    std::pair<const InputSectionBase *, const InputSectionBase *>;

// Take the edge list in Config->CallGraphProfile, resolve symbol names to
// Symbols, and generate a graph between InputSections with the provided
// weights.
CallGraphSort::CallGraphSort() {
  MapVector<SectionPair, uint64_t> &profile = config->callGraphProfile;
  DenseMap<const InputSectionBase *, int> secToCluster;

  auto getOrCreateNode = [&](const InputSectionBase *isec) -> int {
    auto res = secToCluster.try_emplace(isec, clusters.size());
    if (res.second) {
      sections.push_back(isec);
      clusters.emplace_back(clusters.size(), isec->getSize());
    }
    return res.first->second;
  };

  // Create the graph.
  for (std::pair<SectionPair, uint64_t> &c : profile) {
    const auto *fromSB = cast<InputSectionBase>(c.first.first->repl);
    const auto *toSB = cast<InputSectionBase>(c.first.second->repl);
    uint64_t weight = c.second;

    // Ignore edges between input sections belonging to different output
    // sections.  This is done because otherwise we would end up with clusters
    // containing input sections that can't actually be placed adjacently in the
    // output.  This messes with the cluster size and density calculations.  We
    // would also end up moving input sections in other output sections without
    // moving them closer to what calls them.
    if (fromSB->getOutputSection() != toSB->getOutputSection())
      continue;

    int from = getOrCreateNode(fromSB);
    int to = getOrCreateNode(toSB);

    clusters[to].weight += weight;

    if (from == to)
      continue;

    // Remember the best edge.
    Cluster &toC = clusters[to];
    if (toC.bestPred.from == -1 || toC.bestPred.weight < weight) {
      toC.bestPred.from = from;
      toC.bestPred.weight = weight;
    }
  }
  for (Cluster &c : clusters)
    c.initialWeight = c.weight;
}

// It's bad to merge clusters which would degrade the density too much.
static bool isNewDensityBad(Cluster &a, Cluster &b) {
  double newDensity = double(a.weight + b.weight) / double(a.size + b.size);
  return newDensity < a.getDensity() / MAX_DENSITY_DEGRADATION;
}

// Find the leader of V's belonged cluster (represented as an equivalence
// class). We apply union-find path-halving technique (simple to implement) in
// the meantime as it decreases depths and the time complexity.
static int getLeader(std::vector<int> &leaders, int v) {
  while (leaders[v] != v) {
    leaders[v] = leaders[leaders[v]];
    v = leaders[v];
  }
  return v;
}

static void mergeClusters(std::vector<Cluster> &cs, Cluster &into, int intoIdx,
                          Cluster &from, int fromIdx) {
  int tail1 = into.prev, tail2 = from.prev;
  into.prev = tail2;
  cs[tail2].next = intoIdx;
  from.prev = tail1;
  cs[tail1].next = fromIdx;
  into.size += from.size;
  into.weight += from.weight;
  from.size = 0;
  from.weight = 0;
}

// Group InputSections into clusters using the Call-Chain Clustering heuristic
// then sort the clusters by density.
DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
  std::vector<int> sorted(clusters.size());
  std::vector<int> leaders(clusters.size());

  std::iota(leaders.begin(), leaders.end(), 0);
  std::iota(sorted.begin(), sorted.end(), 0);
  llvm::stable_sort(sorted, [&](int a, int b) {
    return clusters[a].getDensity() > clusters[b].getDensity();
  });

  for (int l : sorted) {
    // The cluster index is the same as the index of its leader here because
    // clusters[L] has not been merged into another cluster yet.
    Cluster &c = clusters[l];

    // Don't consider merging if the edge is unlikely.
    if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight)
      continue;

    int predL = getLeader(leaders, c.bestPred.from);
    if (l == predL)
      continue;

    Cluster *predC = &clusters[predL];
    if (c.size + predC->size > MAX_CLUSTER_SIZE)
      continue;

    if (isNewDensityBad(*predC, c))
      continue;

    leaders[l] = predL;
    mergeClusters(clusters, *predC, predL, c, l);
  }

  // Sort remaining non-empty clusters by density.
  sorted.clear();
  for (int i = 0, e = (int)clusters.size(); i != e; ++i)
    if (clusters[i].size > 0)
      sorted.push_back(i);
  llvm::stable_sort(sorted, [&](int a, int b) {
    return clusters[a].getDensity() > clusters[b].getDensity();
  });

  DenseMap<const InputSectionBase *, int> orderMap;
  int curOrder = 1;
  for (int leader : sorted)
    for (int i = leader;;) {
      orderMap[sections[i]] = curOrder++;
      i = clusters[i].next;
      if (i == leader)
        break;
    }

  if (!config->printSymbolOrder.empty()) {
    std::error_code ec;
    raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::OF_None);
    if (ec) {
      error("cannot open " + config->printSymbolOrder + ": " + ec.message());
      return orderMap;
    }

    // Print the symbols ordered by C3, in the order of increasing curOrder
    // Instead of sorting all the orderMap, just repeat the loops above.
    for (int leader : sorted)
      for (int i = leader;;) {
        // Search all the symbols in the file of the section
        // and find out a Defined symbol with name that is within the section.
        for (Symbol *sym : sections[i]->file->getSymbols())
          if (!sym->isSection()) // Filter out section-type symbols here.
            if (auto *d = dyn_cast<Defined>(sym))
              if (sections[i] == d->section)
                os << sym->getName() << "\n";
        i = clusters[i].next;
        if (i == leader)
          break;
      }
  }

  return orderMap;
}

// Sort sections by the profile data provided by -callgraph-profile-file
//
// This first builds a call graph based on the profile data then merges sections
// according to the C³ huristic. All clusters are then sorted by a density
// metric to further improve locality.
DenseMap<const InputSectionBase *, int> elf::computeCallGraphProfileOrder() {
  return CallGraphSort().run();
}
<|endoftext|>