<commit_before>/*********************************************************************
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2013, Willow Garage, Inc.
 *  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 Willow Garage 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 <gtest/gtest.h>
#include <moveit/mesh_filter/mesh_filter.h>
#include <moveit/mesh_filter/stereo_camera_model.h>
#include <geometric_shapes/shapes.h>
#include <geometric_shapes/shape_operations.h>
#include <eigen3/Eigen/Eigen>
#include <vector>

using namespace mesh_filter;
using namespace Eigen;
using namespace std;
using namespace boost;

namespace mesh_filter_test
{

template<typename Type> inline const Type getRandomNumber (const Type& min, const Type& max)
{
  return Type(min + (max-min) * double(rand ()) / double(RAND_MAX));
}

template<typename Type>
class FilterTraits
{
  public:
    static const GLushort GL_TYPE = GL_ZERO;
};

template<>
class FilterTraits<unsigned short>
{
  public:
    static const GLushort GL_TYPE = GL_UNSIGNED_SHORT;
    static const double ToMetricScale = 0.001;
};

template<>
class FilterTraits<float>
{
  public:
    static const GLushort GL_TYPE = GL_FLOAT;
    static const double ToMetricScale = 1.0f;
};

template<typename Type>
class MeshFilterTest : public testing::TestWithParam <double>
{
  BOOST_STATIC_ASSERT_MSG (FilterTraits<Type>::GL_TYPE != GL_ZERO, "Only \"float\" and \"unsigned short int\" are allowed.");
  public:
    MeshFilterTest (unsigned width = 500, unsigned height = 500, double near = 0.5, double far = 5.0, double shadow = 0.1, double epsilon = 1e-7);
    void test ();
    void setMeshDistance (double distance) { distance_ = distance; }
  private:
    shapes::Mesh createMesh (double z) const;
    bool transform_callback (MeshHandle handle, Affine3d& transform) const;
    void getGroundTruth (unsigned int* labels, float* depth) const;
    const unsigned int width_;
    const unsigned int height_;
    const double near_;
    const double far_;
    const double shadow_;
    const double epsilon_;
    StereoCameraModel::Parameters sensor_parameters_;
    MeshFilter<StereoCameraModel> filter_;
    MeshHandle handle_;
    vector<Type> sensor_data_;
    double distance_;
};

template<typename Type>
MeshFilterTest<Type>::MeshFilterTest (unsigned width, unsigned height, double near, double far, double shadow, double epsilon)
: width_ (width)
, height_ (height)
, near_ (near)
, far_ (far)
, shadow_ (shadow)
, epsilon_ (epsilon)
, sensor_parameters_ (width, height, near_, far_, width >> 1, height >> 1, width >> 1, height >> 1, 0.1, 0.1)
, filter_ (boost::bind(&MeshFilterTest<Type>::transform_callback, this, _1, _2), sensor_parameters_)
, sensor_data_ (width_ * height_)
, distance_ (0.0)
{
  filter_.setShadowThreshold (shadow_);
  // no padding
  filter_.setPaddingOffset (0.0);
  filter_.setPaddingScale (0.0);

  // create a large plane that covers the whole visible area -> no boundaries

  shapes::Mesh mesh = createMesh (0);
  handle_ = filter_.addMesh (mesh);

  // make it random but reproducable
  srand (0);
  Type t_near = near_ / FilterTraits<Type>::ToMetricScale;
  Type t_far = far_ / FilterTraits<Type>::ToMetricScale;
  for (typename vector<Type>::iterator sIt = sensor_data_.begin (); sIt != sensor_data_.end (); ++sIt)
  {
    do {
      *sIt = getRandomNumber<Type> (0.0, 10.0 / FilterTraits<Type>::ToMetricScale);
    } while (*sIt == t_near || *sIt == t_far);
  }
}

template<typename Type>
shapes::Mesh MeshFilterTest<Type>::createMesh (double z) const
{
  shapes::Mesh mesh (4, 4);
  mesh.vertices [0]  = -5;
  mesh.vertices [1]  = -5;
  mesh.vertices [2]  =  z;

  mesh.vertices [3]  = -5;
  mesh.vertices [4]  =  5;
  mesh.vertices [5]  =  z;

  mesh.vertices [6]  =  5;
  mesh.vertices [7]  =  5;
  mesh.vertices [8]  =  z;

  mesh.vertices [9]  =  5;
  mesh.vertices [10] = -5;
  mesh.vertices [11] =  z;

  mesh.triangles [0] = 0;
  mesh.triangles [1] = 3;
  mesh.triangles [2] = 2;

  mesh.triangles [3] = 0;
  mesh.triangles [4] = 2;
  mesh.triangles [5] = 1;

  mesh.triangles [6] = 0;
  mesh.triangles [7] = 2;
  mesh.triangles [8] = 3;

  mesh.triangles [9] = 0;
  mesh.triangles [10] = 1;
  mesh.triangles [11] = 2;

  mesh.vertex_normals [0] = 0;
  mesh.vertex_normals [1] = 0;
  mesh.vertex_normals [2] = 1;

  mesh.vertex_normals [3] = 0;
  mesh.vertex_normals [4] = 0;
  mesh.vertex_normals [5] = 1;

  mesh.vertex_normals [6] = 0;
  mesh.vertex_normals [7] = 0;
  mesh.vertex_normals [8] = 1;

  mesh.vertex_normals [9] = 0;
  mesh.vertex_normals [10] = 0;
  mesh.vertex_normals [11] = 1;

  return mesh;
}

template<typename Type>
bool MeshFilterTest<Type>::transform_callback (MeshHandle handle, Affine3d& transform) const
{
  transform = Affine3d::Identity();
  if (handle == handle_)
    transform.translation () = Vector3d (0, 0, distance_);
  return true;
}

template<typename Type>
void MeshFilterTest<Type>::test ()
{
  shapes::Mesh mesh = createMesh (0);
  mesh_filter::MeshHandle handle = filter_.addMesh (mesh);
  filter_.filter (&sensor_data_[0], FilterTraits<Type>::GL_TYPE, false);

  vector<float> gt_depth (width_ * height_);
  vector<unsigned int> gt_labels (width_ * height_);
  getGroundTruth (&gt_labels [0], &gt_depth [0]);

  vector<float> filtered_depth (width_ * height_);
  vector<unsigned int> filtered_labels (width_ * height_);
  filter_.getFilteredDepth (& filtered_depth[0]);
  filter_.getFilteredLabels (& filtered_labels[0]);

  for (unsigned idx = 0; idx < width_ * height_; ++idx)
  {
    // Only test if we are not very close to boundaries of object meshes and shadow-boundaries.
    float sensor_depth = sensor_data_ [idx] * FilterTraits<Type>::ToMetricScale;
    if (fabs(sensor_depth - distance_ - shadow_) > epsilon_ && fabs(sensor_depth - distance_) > epsilon_)
    {
//      if (filtered_labels [idx] != gt_labels [idx])
//      {
//        cout << idx << " :: " <<  filtered_labels [idx] << " vs. " << gt_labels [idx] << " :: " <<  sensor_data_ [idx] << " = " << filtered_depth [idx] << " vs. " << gt_depth [idx] << endl;
//        exit (1);
//      }
      EXPECT_FLOAT_EQ (filtered_depth [idx], gt_depth [idx]);
      EXPECT_EQ (filtered_labels [idx], gt_labels [idx]);
    }
  }
  filter_.removeMesh (handle);
}

template<typename Type>
void MeshFilterTest<Type>::getGroundTruth (unsigned int *labels, float* depth) const
{
  const double scale = FilterTraits<Type>::ToMetricScale;
    if (distance_ <= near_ || distance_ >= far_)
  {
    // no filtering is done -> no shadow values or label values
    for (unsigned yIdx = 0, idx = 0; yIdx < height_; ++yIdx)
    {
      for (unsigned xIdx = 0; xIdx < width_; ++xIdx, ++idx)
      {
        depth[idx] = double(sensor_data_ [idx]) * scale;
        if (depth[idx] < near_)
          labels [idx] = MeshFilterBase::NearClip;
        else if (depth[idx] >= far_)
          labels [idx] = MeshFilterBase::FarClip;
        else
          labels [idx] = MeshFilterBase::Background;

        if (depth [idx] <= near_ || depth [idx] >= far_)
          depth [idx] = 0;
      }
    }
  }
  else
  {

    for (unsigned yIdx = 0, idx = 0; yIdx < height_; ++yIdx)
    {
      for (unsigned xIdx = 0; xIdx < width_; ++xIdx, ++idx)
      {
        depth[idx] =  double(sensor_data_ [idx]) * scale;

        if (depth [idx] < near_)
        {
          labels [idx] = MeshFilterBase::NearClip;
          depth [idx] = 0;
        }
        else
        {
          double diff = depth [idx] - distance_;
          if (diff < 0 && depth[idx] < far_)
            labels [idx] = MeshFilterBase::Background;
          else if (diff > shadow_)
            labels [idx] = MeshFilterBase::Shadow;
          else if (depth[idx] >= far_)
            labels [idx] = MeshFilterBase::FarClip;
          else
          {
            labels [idx] = MeshFilterBase::FirstLabel;
            depth [idx] = 0;
          }

          if (depth[idx] >= far_)
            depth[idx] = 0;
        }
      }
    }
  }
}

} // namespace mesh_filter_test

typedef mesh_filter_test::MeshFilterTest<float> MeshFilterTestFloat;
TEST_P (MeshFilterTestFloat, float)
{
  this->setMeshDistance (this->GetParam ());
  this->test ();
}
INSTANTIATE_TEST_CASE_P(float_test, MeshFilterTestFloat, ::testing::Range<double>(0.0f, 6.0f, 0.5f));

typedef mesh_filter_test::MeshFilterTest<unsigned short> MeshFilterTestUnsignedShort;
TEST_P (MeshFilterTestUnsignedShort, unsigned_short)
{
  this->setMeshDistance (this->GetParam ());
  this->test ();
}
INSTANTIATE_TEST_CASE_P(ushort_test, MeshFilterTestUnsignedShort, ::testing::Range<double>(0.0f, 6.0f, 0.5f));

int main(int argc, char **argv)
{
  testing::InitGoogleTest(&argc, argv);
  int arg;

  return RUN_ALL_TESTS();
}
<commit_msg>GL_TYPE() is a function in newer versions of OpenGL, this fixes tests on Ubuntu 14.04<commit_after>/*********************************************************************
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2013, Willow Garage, Inc.
 *  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 Willow Garage 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 <gtest/gtest.h>
#include <moveit/mesh_filter/mesh_filter.h>
#include <moveit/mesh_filter/stereo_camera_model.h>
#include <geometric_shapes/shapes.h>
#include <geometric_shapes/shape_operations.h>
#include <eigen3/Eigen/Eigen>
#include <vector>

using namespace mesh_filter;
using namespace Eigen;
using namespace std;
using namespace boost;

namespace mesh_filter_test
{

template<typename Type> inline const Type getRandomNumber (const Type& min, const Type& max)
{
  return Type(min + (max-min) * double(rand ()) / double(RAND_MAX));
}

template<typename Type>
class FilterTraits
{
  public:
    static const GLushort FILTER_GL_TYPE = GL_ZERO;
};

template<>
class FilterTraits<unsigned short>
{
  public:
    static const GLushort FILTER_GL_TYPE = GL_UNSIGNED_SHORT;
    static const double ToMetricScale = 0.001;
};

template<>
class FilterTraits<float>
{
  public:
    static const GLushort FILTER_GL_TYPE = GL_FLOAT;
    static const double ToMetricScale = 1.0f;
};

template<typename Type>
class MeshFilterTest : public testing::TestWithParam <double>
{
  BOOST_STATIC_ASSERT_MSG (FilterTraits<Type>::FILTER_GL_TYPE != GL_ZERO, "Only \"float\" and \"unsigned short int\" are allowed.");
  public:
    MeshFilterTest (unsigned width = 500, unsigned height = 500, double near = 0.5, double far = 5.0, double shadow = 0.1, double epsilon = 1e-7);
    void test ();
    void setMeshDistance (double distance) { distance_ = distance; }
  private:
    shapes::Mesh createMesh (double z) const;
    bool transform_callback (MeshHandle handle, Affine3d& transform) const;
    void getGroundTruth (unsigned int* labels, float* depth) const;
    const unsigned int width_;
    const unsigned int height_;
    const double near_;
    const double far_;
    const double shadow_;
    const double epsilon_;
    StereoCameraModel::Parameters sensor_parameters_;
    MeshFilter<StereoCameraModel> filter_;
    MeshHandle handle_;
    vector<Type> sensor_data_;
    double distance_;
};

template<typename Type>
MeshFilterTest<Type>::MeshFilterTest (unsigned width, unsigned height, double near, double far, double shadow, double epsilon)
: width_ (width)
, height_ (height)
, near_ (near)
, far_ (far)
, shadow_ (shadow)
, epsilon_ (epsilon)
, sensor_parameters_ (width, height, near_, far_, width >> 1, height >> 1, width >> 1, height >> 1, 0.1, 0.1)
, filter_ (boost::bind(&MeshFilterTest<Type>::transform_callback, this, _1, _2), sensor_parameters_)
, sensor_data_ (width_ * height_)
, distance_ (0.0)
{
  filter_.setShadowThreshold (shadow_);
  // no padding
  filter_.setPaddingOffset (0.0);
  filter_.setPaddingScale (0.0);

  // create a large plane that covers the whole visible area -> no boundaries

  shapes::Mesh mesh = createMesh (0);
  handle_ = filter_.addMesh (mesh);

  // make it random but reproducable
  srand (0);
  Type t_near = near_ / FilterTraits<Type>::ToMetricScale;
  Type t_far = far_ / FilterTraits<Type>::ToMetricScale;
  for (typename vector<Type>::iterator sIt = sensor_data_.begin (); sIt != sensor_data_.end (); ++sIt)
  {
    do {
      *sIt = getRandomNumber<Type> (0.0, 10.0 / FilterTraits<Type>::ToMetricScale);
    } while (*sIt == t_near || *sIt == t_far);
  }
}

template<typename Type>
shapes::Mesh MeshFilterTest<Type>::createMesh (double z) const
{
  shapes::Mesh mesh (4, 4);
  mesh.vertices [0]  = -5;
  mesh.vertices [1]  = -5;
  mesh.vertices [2]  =  z;

  mesh.vertices [3]  = -5;
  mesh.vertices [4]  =  5;
  mesh.vertices [5]  =  z;

  mesh.vertices [6]  =  5;
  mesh.vertices [7]  =  5;
  mesh.vertices [8]  =  z;

  mesh.vertices [9]  =  5;
  mesh.vertices [10] = -5;
  mesh.vertices [11] =  z;

  mesh.triangles [0] = 0;
  mesh.triangles [1] = 3;
  mesh.triangles [2] = 2;

  mesh.triangles [3] = 0;
  mesh.triangles [4] = 2;
  mesh.triangles [5] = 1;

  mesh.triangles [6] = 0;
  mesh.triangles [7] = 2;
  mesh.triangles [8] = 3;

  mesh.triangles [9] = 0;
  mesh.triangles [10] = 1;
  mesh.triangles [11] = 2;

  mesh.vertex_normals [0] = 0;
  mesh.vertex_normals [1] = 0;
  mesh.vertex_normals [2] = 1;

  mesh.vertex_normals [3] = 0;
  mesh.vertex_normals [4] = 0;
  mesh.vertex_normals [5] = 1;

  mesh.vertex_normals [6] = 0;
  mesh.vertex_normals [7] = 0;
  mesh.vertex_normals [8] = 1;

  mesh.vertex_normals [9] = 0;
  mesh.vertex_normals [10] = 0;
  mesh.vertex_normals [11] = 1;

  return mesh;
}

template<typename Type>
bool MeshFilterTest<Type>::transform_callback (MeshHandle handle, Affine3d& transform) const
{
  transform = Affine3d::Identity();
  if (handle == handle_)
    transform.translation () = Vector3d (0, 0, distance_);
  return true;
}

template<typename Type>
void MeshFilterTest<Type>::test ()
{
  shapes::Mesh mesh = createMesh (0);
  mesh_filter::MeshHandle handle = filter_.addMesh (mesh);
  filter_.filter (&sensor_data_[0], FilterTraits<Type>::FILTER_GL_TYPE, false);

  vector<float> gt_depth (width_ * height_);
  vector<unsigned int> gt_labels (width_ * height_);
  getGroundTruth (&gt_labels [0], &gt_depth [0]);

  vector<float> filtered_depth (width_ * height_);
  vector<unsigned int> filtered_labels (width_ * height_);
  filter_.getFilteredDepth (& filtered_depth[0]);
  filter_.getFilteredLabels (& filtered_labels[0]);

  for (unsigned idx = 0; idx < width_ * height_; ++idx)
  {
    // Only test if we are not very close to boundaries of object meshes and shadow-boundaries.
    float sensor_depth = sensor_data_ [idx] * FilterTraits<Type>::ToMetricScale;
    if (fabs(sensor_depth - distance_ - shadow_) > epsilon_ && fabs(sensor_depth - distance_) > epsilon_)
    {
//      if (filtered_labels [idx] != gt_labels [idx])
//      {
//        cout << idx << " :: " <<  filtered_labels [idx] << " vs. " << gt_labels [idx] << " :: " <<  sensor_data_ [idx] << " = " << filtered_depth [idx] << " vs. " << gt_depth [idx] << endl;
//        exit (1);
//      }
      EXPECT_FLOAT_EQ (filtered_depth [idx], gt_depth [idx]);
      EXPECT_EQ (filtered_labels [idx], gt_labels [idx]);
    }
  }
  filter_.removeMesh (handle);
}

template<typename Type>
void MeshFilterTest<Type>::getGroundTruth (unsigned int *labels, float* depth) const
{
  const double scale = FilterTraits<Type>::ToMetricScale;
    if (distance_ <= near_ || distance_ >= far_)
  {
    // no filtering is done -> no shadow values or label values
    for (unsigned yIdx = 0, idx = 0; yIdx < height_; ++yIdx)
    {
      for (unsigned xIdx = 0; xIdx < width_; ++xIdx, ++idx)
      {
        depth[idx] = double(sensor_data_ [idx]) * scale;
        if (depth[idx] < near_)
          labels [idx] = MeshFilterBase::NearClip;
        else if (depth[idx] >= far_)
          labels [idx] = MeshFilterBase::FarClip;
        else
          labels [idx] = MeshFilterBase::Background;

        if (depth [idx] <= near_ || depth [idx] >= far_)
          depth [idx] = 0;
      }
    }
  }
  else
  {

    for (unsigned yIdx = 0, idx = 0; yIdx < height_; ++yIdx)
    {
      for (unsigned xIdx = 0; xIdx < width_; ++xIdx, ++idx)
      {
        depth[idx] =  double(sensor_data_ [idx]) * scale;

        if (depth [idx] < near_)
        {
          labels [idx] = MeshFilterBase::NearClip;
          depth [idx] = 0;
        }
        else
        {
          double diff = depth [idx] - distance_;
          if (diff < 0 && depth[idx] < far_)
            labels [idx] = MeshFilterBase::Background;
          else if (diff > shadow_)
            labels [idx] = MeshFilterBase::Shadow;
          else if (depth[idx] >= far_)
            labels [idx] = MeshFilterBase::FarClip;
          else
          {
            labels [idx] = MeshFilterBase::FirstLabel;
            depth [idx] = 0;
          }

          if (depth[idx] >= far_)
            depth[idx] = 0;
        }
      }
    }
  }
}

} // namespace mesh_filter_test

typedef mesh_filter_test::MeshFilterTest<float> MeshFilterTestFloat;
TEST_P (MeshFilterTestFloat, float)
{
  this->setMeshDistance (this->GetParam ());
  this->test ();
}
INSTANTIATE_TEST_CASE_P(float_test, MeshFilterTestFloat, ::testing::Range<double>(0.0f, 6.0f, 0.5f));

typedef mesh_filter_test::MeshFilterTest<unsigned short> MeshFilterTestUnsignedShort;
TEST_P (MeshFilterTestUnsignedShort, unsigned_short)
{
  this->setMeshDistance (this->GetParam ());
  this->test ();
}
INSTANTIATE_TEST_CASE_P(ushort_test, MeshFilterTestUnsignedShort, ::testing::Range<double>(0.0f, 6.0f, 0.5f));

int main(int argc, char **argv)
{
  testing::InitGoogleTest(&argc, argv);
  int arg;

  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>/**
 * @file
 * FunctionBlockTest.cpp
 *
 * @date: Dec 18, 2013
 * @author: sblume
 */

#include "FunctionBlockTest.h"
#include "brics_3d/core/Logger.h"
#include "brics_3d/worldModel/sceneGraph/DotGraphGenerator.h"

namespace unitTests {

// Registers the fixture into the 'registry'
CPPUNIT_TEST_SUITE_REGISTRATION( FunctionBlockTest );

void FunctionBlockTest::setUp() {

	wm = new brics_3d::WorldModel();

//	functionBlockFile = "cppdemo";
	functionBlockFile = "testblock";
	blockRepositoryPath = "/opt/src/sandbox/brics_3d_function_blocks/lib/";

	brics_3d::Logger::setMinLoglevel(brics_3d::Logger::LOGDEBUG);
}

void FunctionBlockTest::tearDown() {
	delete wm;
	brics_3d::Logger::setMinLoglevel(brics_3d::Logger::WARNING);
}

void FunctionBlockTest::testFunctionBlockLoader() {
	CPPUNIT_ASSERT(!wm->loadFunctionBlock("wrongFileName"));
	CPPUNIT_ASSERT(wm->loadFunctionBlock(functionBlockFile, blockRepositoryPath));
}

void FunctionBlockTest::testFunctionBlockExecution() {
	CPPUNIT_ASSERT(wm->loadFunctionBlock(functionBlockFile, blockRepositoryPath));

	vector<brics_3d::rsg::Id> input;
	input.push_back(wm->getRootNodeId()); // input hook
	input.push_back(wm->getRootNodeId()); // output hook
	vector<brics_3d::rsg::Id> output;
	CPPUNIT_ASSERT(!wm->executeFunctionBlock("wrongFileName", input, output));
	CPPUNIT_ASSERT(wm->executeFunctionBlock(functionBlockFile, input, output));
	CPPUNIT_ASSERT(wm->executeFunctionBlock(functionBlockFile, input, output));
	CPPUNIT_ASSERT(wm->executeFunctionBlock(functionBlockFile, input, output));
}

void FunctionBlockTest::testExternalFunctionBlockExecution() {
//	string blockName = "cppdemo";
//	string blockPath = "/home/sblume/sandbox/microblx/microblx/std_blocks/cppdemo/";
	string blockName = "roifilter";//"testblock";
	string blockPath = blockRepositoryPath;

	CPPUNIT_ASSERT(wm->loadFunctionBlock(blockName, blockPath));

	brics_3d::rsg::Id pc1Id = 1025;
	brics_3d::PointCloud3D::PointCloud3DPtr pointCloud1(new brics_3d::PointCloud3D());
	pointCloud1->addPoint(brics_3d::Point3D(1,2,3));
	brics_3d::rsg::PointCloud<brics_3d::PointCloud3D>::PointCloudPtr pc1Container(new brics_3d::rsg::PointCloud<brics_3d::PointCloud3D>());
	pc1Container->data = pointCloud1;
	std::vector<brics_3d::rsg::Attribute> tmpAttributes;
	tmpAttributes.clear();
	tmpAttributes.push_back(brics_3d::rsg::Attribute("name","point_cloud1"));
	wm->scene.addGeometricNode(wm->getRootNodeId(), pc1Id, tmpAttributes, pc1Container, brics_3d::rsg::TimeStamp(0.0)/*, true*/);

	/* Just print what the world model has to offer. */
	brics_3d::rsg::DotGraphGenerator* wmPrinter = new brics_3d::rsg::DotGraphGenerator();
	wmPrinter->reset();
	brics_3d::rsg::VisualizationConfiguration config;
	config.abbreviateIds = false;
	wmPrinter->setConfig(config);
	wm->scene.executeGraphTraverser(wmPrinter, wm->getRootNodeId());
	LOG(DEBUG) << "testExternalFunctionBlockExecution: Current state of the world model: " << std::endl << wmPrinter->getDotGraph();

	vector<brics_3d::rsg::Id> input;
	brics_3d::rsg::Id inputHook = pc1Id;//1042; //wm->getRootNodeId();
	brics_3d::rsg::Id outputHook = wm->getRootNodeId();
	input.push_back(outputHook); // output input hook
	input.push_back(inputHook); // input hook
	vector<brics_3d::rsg::Id> output;
	CPPUNIT_ASSERT(wm->executeFunctionBlock(blockName, input, output));

	wmPrinter->reset();
	wm->scene.executeGraphTraverser(wmPrinter, wm->getRootNodeId());
	LOG(DEBUG) << "testExternalFunctionBlockExecution: Current state of the world model: " << std::endl << wmPrinter->getDotGraph();

	/* modify mw */
	brics_3d::rsg::Id groupId = 0;
	std::vector<brics_3d::rsg::Attribute> attributes;
	attributes.push_back(brics_3d::rsg::Attribute("name","test_group1"));
	wm->scene.addGroup(wm->getRootNodeId(), groupId, attributes);

	wmPrinter->reset();
	wm->scene.executeGraphTraverser(wmPrinter, wm->getRootNodeId());
	LOG(DEBUG) << "testExternalFunctionBlockExecution: Current state of the world model: " << std::endl << wmPrinter->getDotGraph();

	output.clear();
	CPPUNIT_ASSERT_EQUAL(0u, static_cast<unsigned int>(output.size()));
	CPPUNIT_ASSERT(wm->executeFunctionBlock(blockName, input, output));
	CPPUNIT_ASSERT_EQUAL(2u, static_cast<unsigned int>(output.size()));
	CPPUNIT_ASSERT(output[0] == outputHook);
	LOG(DEBUG) << "testExternalFunctionBlockExecution: result ID: " << output[1];

	wmPrinter->reset();
	wm->scene.executeGraphTraverser(wmPrinter, wm->getRootNodeId());
	LOG(DEBUG) << "testExternalFunctionBlockExecution: Current state of the world model: " << std::endl << wmPrinter->getDotGraph();

//	CPPUNIT_ASSERT(wm->loadFunctionBlock(blockName, blockPath));  //not yet working
	CPPUNIT_ASSERT(wm->executeFunctionBlock(blockName, input, output));

	wmPrinter->reset();
	wm->scene.executeGraphTraverser(wmPrinter, wm->getRootNodeId());
	LOG(DEBUG) << "testExternalFunctionBlockExecution: Current state of the world model: " << std::endl << wmPrinter->getDotGraph();

}

}  // namespace unitTests

/* EOF */
<commit_msg>Unit test for function blocks takes FBX_MODULES environment variable into account.<commit_after>/**
 * @file
 * FunctionBlockTest.cpp
 *
 * @date: Dec 18, 2013
 * @author: sblume
 */

#include "FunctionBlockTest.h"
#include "brics_3d/core/Logger.h"
#include "brics_3d/worldModel/sceneGraph/DotGraphGenerator.h"

namespace unitTests {

// Registers the fixture into the 'registry'
CPPUNIT_TEST_SUITE_REGISTRATION( FunctionBlockTest );

void FunctionBlockTest::setUp() {

	wm = new brics_3d::WorldModel();

//	functionBlockFile = "cppdemo";

	/*
	 * NOTE: This one is located in the brics_3d_function_blocks repository.
	 * If it is not installed the tests will fail.
	 */
	functionBlockFile = "testblock";

	if(getenv("FBX_MODULES") != 0) {
		string functionBlocksModulesPath(getenv("FBX_MODULES"));
		blockRepositoryPath = functionBlocksModulesPath + "/lib/";
	} else {
		blockRepositoryPath = "/opt/src/sandbox/brics_3d_function_blocks/lib/";
	}

	brics_3d::Logger::setMinLoglevel(brics_3d::Logger::LOGDEBUG);
}

void FunctionBlockTest::tearDown() {
	delete wm;
	brics_3d::Logger::setMinLoglevel(brics_3d::Logger::WARNING);
}

void FunctionBlockTest::testFunctionBlockLoader() {
	CPPUNIT_ASSERT(!wm->loadFunctionBlock("wrongFileName"));
	CPPUNIT_ASSERT(wm->loadFunctionBlock(functionBlockFile, blockRepositoryPath));
}

void FunctionBlockTest::testFunctionBlockExecution() {
	CPPUNIT_ASSERT(wm->loadFunctionBlock(functionBlockFile, blockRepositoryPath));

	vector<brics_3d::rsg::Id> input;
	input.push_back(wm->getRootNodeId()); // input hook
	input.push_back(wm->getRootNodeId()); // output hook
	vector<brics_3d::rsg::Id> output;
	CPPUNIT_ASSERT(!wm->executeFunctionBlock("wrongFileName", input, output));
	CPPUNIT_ASSERT(wm->executeFunctionBlock(functionBlockFile, input, output));
	CPPUNIT_ASSERT(wm->executeFunctionBlock(functionBlockFile, input, output));
	CPPUNIT_ASSERT(wm->executeFunctionBlock(functionBlockFile, input, output));
}

void FunctionBlockTest::testExternalFunctionBlockExecution() {
	string blockName = "roifilter";//"testblock";
	string blockPath = blockRepositoryPath;

	CPPUNIT_ASSERT(wm->loadFunctionBlock(blockName, blockPath));

	brics_3d::rsg::Id pc1Id = 1025;
	brics_3d::PointCloud3D::PointCloud3DPtr pointCloud1(new brics_3d::PointCloud3D());
	pointCloud1->addPoint(brics_3d::Point3D(1,2,3));
	brics_3d::rsg::PointCloud<brics_3d::PointCloud3D>::PointCloudPtr pc1Container(new brics_3d::rsg::PointCloud<brics_3d::PointCloud3D>());
	pc1Container->data = pointCloud1;
	std::vector<brics_3d::rsg::Attribute> tmpAttributes;
	tmpAttributes.clear();
	tmpAttributes.push_back(brics_3d::rsg::Attribute("name","point_cloud1"));
	wm->scene.addGeometricNode(wm->getRootNodeId(), pc1Id, tmpAttributes, pc1Container, brics_3d::rsg::TimeStamp(0.0)/*, true*/);

	/* Just print what the world model has to offer. */
	brics_3d::rsg::DotGraphGenerator* wmPrinter = new brics_3d::rsg::DotGraphGenerator();
	wmPrinter->reset();
	brics_3d::rsg::VisualizationConfiguration config;
	config.abbreviateIds = false;
	wmPrinter->setConfig(config);
	wm->scene.executeGraphTraverser(wmPrinter, wm->getRootNodeId());
	LOG(DEBUG) << "testExternalFunctionBlockExecution: Current state of the world model: " << std::endl << wmPrinter->getDotGraph();

	vector<brics_3d::rsg::Id> input;
	brics_3d::rsg::Id inputHook = pc1Id;//1042; //wm->getRootNodeId();
	brics_3d::rsg::Id outputHook = wm->getRootNodeId();
	input.push_back(outputHook); // output input hook
	input.push_back(inputHook); // input hook
	vector<brics_3d::rsg::Id> output;
	CPPUNIT_ASSERT(wm->executeFunctionBlock(blockName, input, output));

	wmPrinter->reset();
	wm->scene.executeGraphTraverser(wmPrinter, wm->getRootNodeId());
	LOG(DEBUG) << "testExternalFunctionBlockExecution: Current state of the world model: " << std::endl << wmPrinter->getDotGraph();

	/* modify mw */
	brics_3d::rsg::Id groupId = 0;
	std::vector<brics_3d::rsg::Attribute> attributes;
	attributes.push_back(brics_3d::rsg::Attribute("name","test_group1"));
	wm->scene.addGroup(wm->getRootNodeId(), groupId, attributes);

	wmPrinter->reset();
	wm->scene.executeGraphTraverser(wmPrinter, wm->getRootNodeId());
	LOG(DEBUG) << "testExternalFunctionBlockExecution: Current state of the world model: " << std::endl << wmPrinter->getDotGraph();

	output.clear();
	CPPUNIT_ASSERT_EQUAL(0u, static_cast<unsigned int>(output.size()));
	CPPUNIT_ASSERT(wm->executeFunctionBlock(blockName, input, output));
	CPPUNIT_ASSERT_EQUAL(2u, static_cast<unsigned int>(output.size()));
	CPPUNIT_ASSERT(output[0] == outputHook);
	LOG(DEBUG) << "testExternalFunctionBlockExecution: result ID: " << output[1];

	wmPrinter->reset();
	wm->scene.executeGraphTraverser(wmPrinter, wm->getRootNodeId());
	LOG(DEBUG) << "testExternalFunctionBlockExecution: Current state of the world model: " << std::endl << wmPrinter->getDotGraph();

//	CPPUNIT_ASSERT(wm->loadFunctionBlock(blockName, blockPath));  //not yet working
	CPPUNIT_ASSERT(wm->executeFunctionBlock(blockName, input, output));

	wmPrinter->reset();
	wm->scene.executeGraphTraverser(wmPrinter, wm->getRootNodeId());
	LOG(DEBUG) << "testExternalFunctionBlockExecution: Current state of the world model: " << std::endl << wmPrinter->getDotGraph();

}

}  // namespace unitTests

/* EOF */
<|endoftext|>


<commit_before>#ifndef TOUCH_HPP
#define TOUCH_HPP

#include <VBE/math.hpp>
#include <vector>

class InputImpl;

///
/// \brief The Touch class provides support to read multi-touch gestures
///
class Touch {
	public:
		///
		/// \brief The Finger class represents one of the user's fingers
		///
		class Finger {
			public:
				///
				/// \brief Returns the ID of this fingers
				///
				int getId() const;
				///
				/// \brief Returns whether this finger just made contact with the display
				///
				bool justPressed() const;
				///
				/// \brief Returns the current position of the finger in normalized (0..1) coordinates
				///
				vec2f position() const;
				///
				/// \brief Returns the current movement relative to the last frame
				///
				vec2f movement() const;

			private:
				int id;
				vec2f pos;
				vec2f oldPos;
				bool isNew;

				friend class Touch;
				friend class InputImpl;
		};
		///
		/// \class Finger Window.hpp <VBE/system/Touch.hpp>
		/// \ingroup System
		///
		/// Each user finger is kept track of since the moment of contact untill release, and it will
		/// maintain the same ID throughout the whole gesture.
		///
		/// \see Touch
		///

		///
		/// \brief Returns a vector reference that contains all the currently tracked fingers
		///
		static const std::vector<Finger>& getFingers();
};
/// \class Touch Touch.hpp <VBE/system/Touch.hpp>
/// \ingroup System
///
///  The total number of fingers is only limited by the actual device.
///

#endif
<commit_msg>Update Touch.hpp<commit_after>#ifndef TOUCH_HPP
#define TOUCH_HPP

#include <VBE/math.hpp>
#include <vector>

class InputImpl;

///
/// \brief The Touch class provides support to read touch and multi-touch screens
///
class Touch {
	public:
		///
		/// \brief The Finger class represents one of the user's fingers
		///
		class Finger {
			public:
				///
				/// \brief Returns the ID of this finger
				///
				int getId() const;
				///
				/// \brief Returns whether this finger just made contact with the display in this frame
				///
				bool justPressed() const;
				///
				/// \brief Returns the current position of the finger in normalized (0..1) coordinates
				///
				vec2f position() const;
				///
				/// \brief Returns the movement relative to the last frame
				///
				vec2f movement() const;

			private:
				int id;
				vec2f pos;
				vec2f oldPos;
				bool isNew;

				friend class Touch;
				friend class InputImpl;
		};
		///
		/// \class Finger Window.hpp <VBE/system/Touch.hpp>
		/// \ingroup System
		///
		/// Each user finger is tracked from the moment of contact until release, and it will
		/// maintain the same ID throughout the whole gesture.
		///
		/// \see Touch
		///

		///
		/// \brief Returns all the tracked fingers
		///
		static const std::vector<Finger>& getFingers();
};
/// \class Touch Touch.hpp <VBE/system/Touch.hpp>
/// \ingroup System
///
///  The total number of fingers is only limited by the actual device.
///

#endif
<|endoftext|>


<commit_before>#include <core/stdafx.h>
#include <core/mapi/cache/namedPropCache.h>
#include <core/mapi/cache/namedProps.h>
#include <core/interpret/guid.h>
#include <core/mapi/mapiMemory.h>
#include <core/mapi/mapiFunctions.h>
#include <core/utility/registry.h>
#include <core/utility/strings.h>
#include <core/utility/output.h>
#include <core/addin/mfcmapi.h>
#include <core/addin/addin.h>
#include <core/utility/error.h>

namespace cache
{
	namespace directMapi
	{
		// Returns a vector of NamedPropCacheEntry for the input tags
		// Sourced directly from MAPI
		_Check_return_ std::vector<std::shared_ptr<namedPropCacheEntry>>
		GetNamesFromIDs(_In_ LPMAPIPROP lpMAPIProp, _In_ LPSPropTagArray* lppPropTags, ULONG ulFlags)
		{
			if (!lpMAPIProp) return {};

			LPMAPINAMEID* lppPropNames = nullptr;
			auto ulPropNames = ULONG{};

			WC_H_GETPROPS_S(lpMAPIProp->GetNamesFromIDs(lppPropTags, nullptr, ulFlags, &ulPropNames, &lppPropNames));

			auto ids = std::vector<std::shared_ptr<namedPropCacheEntry>>{};

			if (ulPropNames && lppPropNames)
			{
				for (ULONG i = 0; i < ulPropNames; i++)
				{
					auto ulPropID = ULONG{};
					if (lppPropTags && *lppPropTags) ulPropID = PROP_ID(mapi::getTag(*lppPropTags, i));
					ids.emplace_back(namedPropCacheEntry::make(lppPropNames[i], ulPropID));
				}
			}

			MAPIFreeBuffer(lppPropNames);
			return ids;
		}

		// Returns a vector of NamedPropCacheEntry for the input tags
		// Sourced directly from MAPI
		_Check_return_ std::vector<std::shared_ptr<namedPropCacheEntry>>
		GetNamesFromIDs(_In_ LPMAPIPROP lpMAPIProp, _In_ const std::vector<ULONG> tags, ULONG ulFlags)
		{
			if (!lpMAPIProp) return {};

			auto ids = std::vector<std::shared_ptr<namedPropCacheEntry>>{};
			auto ulPropTags = mapi::allocate<LPSPropTagArray>(CbNewSPropTagArray(tags.size()));
			if (ulPropTags)
			{
				ulPropTags->cValues = tags.size();
				ULONG i = 0;
				for (const auto& tag : tags)
				{
					mapi::setTag(ulPropTags, i++) = tag;
				}

				ids = GetNamesFromIDs(lpMAPIProp, &ulPropTags, ulFlags);
			}

			MAPIFreeBuffer(ulPropTags);

			return ids;
		}

		// Returns a vector of tags for the input names
		// Sourced directly from MAPI
		_Check_return_ LPSPropTagArray
		GetIDsFromNames(_In_ LPMAPIPROP lpMAPIProp, std::vector<MAPINAMEID> nameIDs, ULONG ulFlags)
		{
			if (!lpMAPIProp) return {};

			LPSPropTagArray lpTags = nullptr;

			if (nameIDs.empty())
			{
				WC_H_GETPROPS_S(lpMAPIProp->GetIDsFromNames(0, nullptr, ulFlags, &lpTags));
			}
			else
			{
				std::vector<const MAPINAMEID*> lpNameIDs = {};
				for (const auto& nameID : nameIDs)
				{
					lpNameIDs.emplace_back(&nameID);
				}

				auto names = const_cast<MAPINAMEID**>(lpNameIDs.data());
				WC_H_GETPROPS_S(lpMAPIProp->GetIDsFromNames(lpNameIDs.size(), names, ulFlags, &lpTags));
			}

			return lpTags;
		}
	} // namespace directMapi

	std::list<std::shared_ptr<namedPropCacheEntry>>& namedPropCache::getCache() noexcept
	{
		// We keep a list of named prop cache entries
		static std::list<std::shared_ptr<namedPropCacheEntry>> cache;
		return cache;
	}

	_Check_return_ std::shared_ptr<namedPropCacheEntry>
	namedPropCache::find(const std::function<bool(const std::shared_ptr<namedPropCacheEntry>&)>& compare)
	{
		const auto& cache = getCache();
		const auto entry =
			find_if(cache.begin(), cache.end(), [compare](const auto& _entry) { return compare(_entry); });

		return entry != cache.end() ? *entry : namedPropCacheEntry::empty();
	}

	_Check_return_ std::shared_ptr<namedPropCacheEntry> namedPropCache::find(
		const std::shared_ptr<cache::namedPropCacheEntry>& entry,
		bool bMatchSig,
		bool bMatchID,
		bool bMatchName)
	{
		return find([&](const auto& _entry) { return _entry->match(entry, bMatchSig, bMatchID, bMatchName); });
	}

	_Check_return_ std::shared_ptr<namedPropCacheEntry>
	namedPropCache::find(_In_ const std::vector<BYTE>& _sig, _In_ const MAPINAMEID& _mapiNameId)
	{
		return find([&](const auto& _entry) { return _entry->match(_sig, _mapiNameId); });
	}

	_Check_return_ std::shared_ptr<namedPropCacheEntry>
	namedPropCache::find(_In_ const std::vector<BYTE>& _sig, ULONG _ulPropID)
	{
		return find([&](const auto& _entry) { return _entry->match(_sig, _ulPropID); });
	}

	_Check_return_ std::shared_ptr<namedPropCacheEntry>
	namedPropCache::find(ULONG _ulPropID, _In_ const MAPINAMEID& _mapiNameId)
	{
		return find([&](const auto& _entry) { return _entry->match(_ulPropID, _mapiNameId); });
	}

	// Add a mapping to the cache if it doesn't already exist
	// If given a signature, we include it in our search.
	// If not, we search without it
	void namedPropCache::add(std::vector<std::shared_ptr<namedPropCacheEntry>>& entries, const std::vector<BYTE>& sig)
	{
		auto& cache = getCache();
		for (auto& entry : entries)
		{
			auto match = std::shared_ptr<namedPropCacheEntry>{};
			if (sig.empty())
			{
				match = find(entry, false, true, true);
			}
			else
			{
				entry->setSig(sig);
				match = find(entry, true, true, true);
			}

			if (!match || !match->valid())
			{
				if (fIsSet(output::dbgLevel::NamedPropCacheMisses))
				{
					const auto mni = entry->getMapiNameId();
					const auto names = NameIDToPropNames(mni);
					if (names.empty())
					{
						output::DebugPrint(
							output::dbgLevel::NamedPropCacheMisses,
							L"add: Caching unknown property 0x%08X %ws\n",
							mni->Kind.lID,
							guid::GUIDToStringAndName(mni->lpguid).c_str());
					}
					else
					{
						output::DebugPrint(
							output::dbgLevel::NamedPropCacheMisses,
							L"add: Caching property 0x%08X %ws = %ws\n",
							mni->Kind.lID,
							guid::GUIDToStringAndName(mni->lpguid).c_str(),
							names[0].c_str());
					}
				}

				cache.emplace_back(entry);
			}
		}
	}

	// If signature is empty then do not use a signature
	_Check_return_ std::vector<std::shared_ptr<namedPropCacheEntry>> namedPropCache::GetNamesFromIDs(
		_In_ LPMAPIPROP lpMAPIProp,
		const std::vector<BYTE>& sig,
		_In_ LPSPropTagArray* lppPropTags)
	{
		if (!lpMAPIProp || !lppPropTags || !*lppPropTags) return {};

		// We're going to walk the cache, looking for the values we need. As soon as we have all the values we need, we're done
		// If we reach the end of the cache and don't have everything, we set up to make a GetNamesFromIDs call.

		auto results = std::vector<std::shared_ptr<namedPropCacheEntry>>{};
		const SPropTagArray* lpPropTags = *lppPropTags;

		auto misses = std::vector<ULONG>{};

		// First pass, find any misses we might have
		for (ULONG ulTarget = 0; ulTarget < lpPropTags->cValues; ulTarget++)
		{
			const auto ulPropTag = mapi::getTag(lpPropTags, ulTarget);
			const auto ulPropId = PROP_ID(ulPropTag);
			// ...check the cache
			const auto lpEntry = find([&](const auto& entry) noexcept { return entry->match(sig, ulPropId); });

			if (!lpEntry)
			{
				misses.emplace_back(ulPropTag);
			}
		}

		// Go to MAPI with whatever's left. We set up for a single call to GetNamesFromIDs.
		if (!misses.empty())
		{
			auto missed = directMapi::GetNamesFromIDs(lpMAPIProp, misses, NULL);
			// Cache the results
			add(missed, sig);
		}

		// Second pass, do our lookup with a populated cache
		for (ULONG ulTarget = 0; ulTarget < lpPropTags->cValues; ulTarget++)
		{
			const auto ulPropId = PROP_ID(mapi::getTag(lpPropTags, ulTarget));
			// ...check the cache
			const auto lpEntry = find([&](const auto& entry) noexcept { return entry->match(sig, ulPropId); });

			if (lpEntry)
			{
				results.emplace_back(lpEntry);
			}
			else
			{
				results.emplace_back(namedPropCacheEntry::make(nullptr, ulPropId, sig));
			}
		}

		return results;
	}

	// If signature is empty then do not use a signature
	_Check_return_ LPSPropTagArray namedPropCache::GetIDsFromNames(
		_In_ LPMAPIPROP lpMAPIProp,
		_In_ const std::vector<BYTE>& sig,
		_In_ std::vector<MAPINAMEID> nameIDs,
		ULONG ulFlags)
	{
		if (!lpMAPIProp || !nameIDs.size()) return {};

		// We're going to walk the cache, looking for the values we need. As soon as we have all the values we need, we're done
		// If we reach the end of the cache and don't have everything, we set up to make a GetIDsFromNames call.

		auto misses = std::vector<MAPINAMEID>{};

		// First pass, find the tags we don't have cached
		for (const auto& nameID : nameIDs)
		{
			const auto lpEntry = find([&](const auto& entry) noexcept { return entry->match(sig, nameID); });

			if (!lpEntry)
			{
				misses.emplace_back(nameID);
			}
		}

		// Go to MAPI with whatever's left.
		if (!misses.empty())
		{
			auto missed = directMapi::GetIDsFromNames(lpMAPIProp, misses, ulFlags);
			if (missed && missed->cValues == misses.size())
			{
				// Cache the results
				auto toCache = std::vector<std::shared_ptr<namedPropCacheEntry>>{};
				for (ULONG i = 0; i < misses.size(); i++)
				{
					toCache.emplace_back(namedPropCacheEntry::make(&misses[i], mapi::getTag(missed, i), sig));
				}

				add(toCache, sig);
			}

			MAPIFreeBuffer(missed);
		}

		// Second pass, do our lookup with a populated cache
		auto results = mapi::allocate<LPSPropTagArray>(CbNewSPropTagArray(nameIDs.size()));
		results->cValues = nameIDs.size();
		ULONG i = 0;
		for (const auto nameID : nameIDs)
		{
			const auto lpEntry = find([&](const auto& entry) noexcept { return entry->match(sig, nameID); });

			mapi::setTag(results, i++) = lpEntry ? lpEntry->getPropID() : 0;
		}

		return results;
	}
} // namespace cache<commit_msg>add some validation checks<commit_after>#include <core/stdafx.h>
#include <core/mapi/cache/namedPropCache.h>
#include <core/mapi/cache/namedProps.h>
#include <core/interpret/guid.h>
#include <core/mapi/mapiMemory.h>
#include <core/mapi/mapiFunctions.h>
#include <core/utility/registry.h>
#include <core/utility/strings.h>
#include <core/utility/output.h>
#include <core/addin/mfcmapi.h>
#include <core/addin/addin.h>
#include <core/utility/error.h>

namespace cache
{
	namespace directMapi
	{
		// Returns a vector of NamedPropCacheEntry for the input tags
		// Sourced directly from MAPI
		_Check_return_ std::vector<std::shared_ptr<namedPropCacheEntry>>
		GetNamesFromIDs(_In_ LPMAPIPROP lpMAPIProp, _In_ LPSPropTagArray* lppPropTags, ULONG ulFlags)
		{
			if (!lpMAPIProp) return {};

			LPMAPINAMEID* lppPropNames = nullptr;
			auto ulPropNames = ULONG{};

			WC_H_GETPROPS_S(lpMAPIProp->GetNamesFromIDs(lppPropTags, nullptr, ulFlags, &ulPropNames, &lppPropNames));

			auto ids = std::vector<std::shared_ptr<namedPropCacheEntry>>{};

			if (ulPropNames && lppPropNames)
			{
				for (ULONG i = 0; i < ulPropNames; i++)
				{
					auto ulPropID = ULONG{};
					if (lppPropTags && *lppPropTags) ulPropID = PROP_ID(mapi::getTag(*lppPropTags, i));
					ids.emplace_back(namedPropCacheEntry::make(lppPropNames[i], ulPropID));
				}
			}

			MAPIFreeBuffer(lppPropNames);
			return ids;
		}

		// Returns a vector of NamedPropCacheEntry for the input tags
		// Sourced directly from MAPI
		_Check_return_ std::vector<std::shared_ptr<namedPropCacheEntry>>
		GetNamesFromIDs(_In_ LPMAPIPROP lpMAPIProp, _In_ const std::vector<ULONG> tags, ULONG ulFlags)
		{
			if (!lpMAPIProp) return {};

			auto ids = std::vector<std::shared_ptr<namedPropCacheEntry>>{};
			auto ulPropTags = mapi::allocate<LPSPropTagArray>(CbNewSPropTagArray(tags.size()));
			if (ulPropTags)
			{
				ulPropTags->cValues = tags.size();
				ULONG i = 0;
				for (const auto& tag : tags)
				{
					mapi::setTag(ulPropTags, i++) = tag;
				}

				ids = GetNamesFromIDs(lpMAPIProp, &ulPropTags, ulFlags);
			}

			MAPIFreeBuffer(ulPropTags);

			return ids;
		}

		// Returns a vector of tags for the input names
		// Sourced directly from MAPI
		_Check_return_ LPSPropTagArray
		GetIDsFromNames(_In_ LPMAPIPROP lpMAPIProp, std::vector<MAPINAMEID> nameIDs, ULONG ulFlags)
		{
			if (!lpMAPIProp) return {};

			LPSPropTagArray lpTags = nullptr;

			if (nameIDs.empty())
			{
				WC_H_GETPROPS_S(lpMAPIProp->GetIDsFromNames(0, nullptr, ulFlags, &lpTags));
			}
			else
			{
				std::vector<const MAPINAMEID*> lpNameIDs = {};
				for (const auto& nameID : nameIDs)
				{
					lpNameIDs.emplace_back(&nameID);
				}

				auto names = const_cast<MAPINAMEID**>(lpNameIDs.data());
				WC_H_GETPROPS_S(lpMAPIProp->GetIDsFromNames(lpNameIDs.size(), names, ulFlags, &lpTags));
			}

			return lpTags;
		}
	} // namespace directMapi

	std::list<std::shared_ptr<namedPropCacheEntry>>& namedPropCache::getCache() noexcept
	{
		// We keep a list of named prop cache entries
		static std::list<std::shared_ptr<namedPropCacheEntry>> cache;
		return cache;
	}

	_Check_return_ std::shared_ptr<namedPropCacheEntry>
	namedPropCache::find(const std::function<bool(const std::shared_ptr<namedPropCacheEntry>&)>& compare)
	{
		const auto& cache = getCache();
		const auto entry =
			find_if(cache.begin(), cache.end(), [compare](const auto& _entry) { return compare(_entry); });

		return entry != cache.end() ? *entry : namedPropCacheEntry::empty();
	}

	_Check_return_ std::shared_ptr<namedPropCacheEntry> namedPropCache::find(
		const std::shared_ptr<cache::namedPropCacheEntry>& entry,
		bool bMatchSig,
		bool bMatchID,
		bool bMatchName)
	{
		return find([&](const auto& _entry) { return _entry->match(entry, bMatchSig, bMatchID, bMatchName); });
	}

	_Check_return_ std::shared_ptr<namedPropCacheEntry>
	namedPropCache::find(_In_ const std::vector<BYTE>& _sig, _In_ const MAPINAMEID& _mapiNameId)
	{
		return find([&](const auto& _entry) { return _entry->match(_sig, _mapiNameId); });
	}

	_Check_return_ std::shared_ptr<namedPropCacheEntry>
	namedPropCache::find(_In_ const std::vector<BYTE>& _sig, ULONG _ulPropID)
	{
		return find([&](const auto& _entry) { return _entry->match(_sig, _ulPropID); });
	}

	_Check_return_ std::shared_ptr<namedPropCacheEntry>
	namedPropCache::find(ULONG _ulPropID, _In_ const MAPINAMEID& _mapiNameId)
	{
		return find([&](const auto& _entry) { return _entry->match(_ulPropID, _mapiNameId); });
	}

	// Add a mapping to the cache if it doesn't already exist
	// If given a signature, we include it in our search.
	// If not, we search without it
	void namedPropCache::add(std::vector<std::shared_ptr<namedPropCacheEntry>>& entries, const std::vector<BYTE>& sig)
	{
		auto& cache = getCache();
		for (auto& entry : entries)
		{
			auto match = std::shared_ptr<namedPropCacheEntry>{};
			if (sig.empty())
			{
				match = find(entry, false, true, true);
			}
			else
			{
				entry->setSig(sig);
				match = find(entry, true, true, true);
			}

			if (!match || !match->valid())
			{
				if (fIsSet(output::dbgLevel::NamedPropCacheMisses))
				{
					const auto mni = entry->getMapiNameId();
					const auto names = NameIDToPropNames(mni);
					if (names.empty())
					{
						output::DebugPrint(
							output::dbgLevel::NamedPropCacheMisses,
							L"add: Caching unknown property 0x%08X %ws\n",
							mni->Kind.lID,
							guid::GUIDToStringAndName(mni->lpguid).c_str());
					}
					else
					{
						output::DebugPrint(
							output::dbgLevel::NamedPropCacheMisses,
							L"add: Caching property 0x%08X %ws = %ws\n",
							mni->Kind.lID,
							guid::GUIDToStringAndName(mni->lpguid).c_str(),
							names[0].c_str());
					}
				}

				cache.emplace_back(entry);
			}
		}
	}

	// If signature is empty then do not use a signature
	_Check_return_ std::vector<std::shared_ptr<namedPropCacheEntry>> namedPropCache::GetNamesFromIDs(
		_In_ LPMAPIPROP lpMAPIProp,
		const std::vector<BYTE>& sig,
		_In_ LPSPropTagArray* lppPropTags)
	{
		if (!lpMAPIProp || !lppPropTags || !*lppPropTags) return {};

		// We're going to walk the cache, looking for the values we need. As soon as we have all the values we need, we're done
		// If we reach the end of the cache and don't have everything, we set up to make a GetNamesFromIDs call.

		auto results = std::vector<std::shared_ptr<namedPropCacheEntry>>{};
		const SPropTagArray* lpPropTags = *lppPropTags;

		auto misses = std::vector<ULONG>{};

		// First pass, find any misses we might have
		for (ULONG ulTarget = 0; ulTarget < lpPropTags->cValues; ulTarget++)
		{
			const auto ulPropTag = mapi::getTag(lpPropTags, ulTarget);
			const auto ulPropId = PROP_ID(ulPropTag);
			// ...check the cache
			const auto lpEntry = find([&](const auto& entry) noexcept { return entry->match(sig, ulPropId); });

			if (!lpEntry || !lpEntry->valid())
			{
				misses.emplace_back(ulPropTag);
			}
		}

		// Go to MAPI with whatever's left. We set up for a single call to GetNamesFromIDs.
		if (!misses.empty())
		{
			auto missed = directMapi::GetNamesFromIDs(lpMAPIProp, misses, NULL);
			// Cache the results
			add(missed, sig);
		}

		// Second pass, do our lookup with a populated cache
		for (ULONG ulTarget = 0; ulTarget < lpPropTags->cValues; ulTarget++)
		{
			const auto ulPropId = PROP_ID(mapi::getTag(lpPropTags, ulTarget));
			// ...check the cache
			const auto lpEntry = find([&](const auto& entry) noexcept { return entry->match(sig, ulPropId); });

			if (lpEntry)
			{
				results.emplace_back(lpEntry);
			}
			else
			{
				results.emplace_back(namedPropCacheEntry::make(nullptr, ulPropId, sig));
			}
		}

		return results;
	}

	// If signature is empty then do not use a signature
	_Check_return_ LPSPropTagArray namedPropCache::GetIDsFromNames(
		_In_ LPMAPIPROP lpMAPIProp,
		_In_ const std::vector<BYTE>& sig,
		_In_ std::vector<MAPINAMEID> nameIDs,
		ULONG ulFlags)
	{
		if (!lpMAPIProp || !nameIDs.size()) return {};

		// We're going to walk the cache, looking for the values we need. As soon as we have all the values we need, we're done
		// If we reach the end of the cache and don't have everything, we set up to make a GetIDsFromNames call.

		auto misses = std::vector<MAPINAMEID>{};

		// First pass, find the tags we don't have cached
		for (const auto& nameID : nameIDs)
		{
			const auto lpEntry = find([&](const auto& entry) noexcept { return entry->match(sig, nameID); });

			if (!lpEntry || !lpEntry->valid())
			{
				misses.emplace_back(nameID);
			}
		}

		// Go to MAPI with whatever's left.
		if (!misses.empty())
		{
			auto missed = directMapi::GetIDsFromNames(lpMAPIProp, misses, ulFlags);
			if (missed && missed->cValues == misses.size())
			{
				// Cache the results
				auto toCache = std::vector<std::shared_ptr<namedPropCacheEntry>>{};
				for (ULONG i = 0; i < misses.size(); i++)
				{
					toCache.emplace_back(namedPropCacheEntry::make(&misses[i], mapi::getTag(missed, i), sig));
				}

				add(toCache, sig);
			}

			MAPIFreeBuffer(missed);
		}

		// Second pass, do our lookup with a populated cache
		auto results = mapi::allocate<LPSPropTagArray>(CbNewSPropTagArray(nameIDs.size()));
		results->cValues = nameIDs.size();
		ULONG i = 0;
		for (const auto nameID : nameIDs)
		{
			const auto lpEntry = find([&](const auto& entry) noexcept { return entry->match(sig, nameID); });

			mapi::setTag(results, i++) = lpEntry ? lpEntry->getPropID() : 0;
		}

		return results;
	}
} // namespace cache<|endoftext|>


<commit_before>/*!
 * \file dependency_list.hpp
 * \brief file dependency_list.hpp
 *
 * Copyright 2016 by Intel.
 *
 * Contact: kevin.rogovin@intel.com
 *
 * 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 Kevin Rogovin <kevin.rogovin@intel.com>
 *
 */

#pragma once

#include <string>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <vector>
#include <map>
#include <set>
#include <fastuidraw/util/reference_counted.hpp>
#include <fastuidraw/util/vecN.hpp>
#include <fastuidraw/util/string_array.hpp>
#include <fastuidraw/glsl/painter_item_shader_glsl.hpp>

namespace fastuidraw { namespace glsl { namespace detail {

template<typename T>
class DependencyListPrivateT
{
public:
  void
  add_element(c_string name, const reference_counted_ptr<const T> &shader,
              const varying_list *varyings);

  varying_list
  compute_varyings(const varying_list &combine_with) const;

  string_array
  compute_name_list(void) const;

  std::vector<reference_counted_ptr<const T> >
  compute_shader_list(void) const;

private:
  class PerShader
  {
  public:
    reference_counted_ptr<const T> m_shader;
    varying_list m_varyings;
  };

  class EqClass:public reference_counted<EqClass>::non_concurrent
  {
  public:
    EqClass(void):
      m_type(varying_list::interpolator_number_types),
      m_added(false)
    {}

    std::string
    extract_double_colon_value(void);

    std::set<std::string> m_names;
    enum varying_list::interpolator_type_t m_type;
    bool m_added;
  };

  class VaryingTracker
  {
  public:
    void
    add_to_tracker(c_string prefix, const varying_list &src);

    void
    add_varyings_from_tracker(varying_list *dst);

  private:
    typedef reference_counted_ptr<EqClass> EqClassRef;

    static
    std::string
    make_name(c_string prefix, c_string name);

    void
    add_varying(const std::string &name,
                enum varying_list::interpolator_type_t q);

    void
    add_alias(const std::string &name, const std::string &src_name);

    std::map<std::string, EqClassRef> m_data;
  };

  static
  void
  add_varyings(c_string prefix, const varying_list &src, varying_list *dst);

  std::map<std::string, PerShader> m_shaders;
};

//////////////////////////////////////////////
// DependencyListPrivateT<T>::EqClass methods
template<typename T>
std::string
DependencyListPrivateT<T>::EqClass::
extract_double_colon_value(void)
{
  std::string return_value;
  std::set<std::string>::iterator iter;

  for (iter = m_names.begin(); iter != m_names.end(); ++iter)
    {
      if (iter->find("::") != std::string::npos)
        {
          return_value = *iter;
          m_names.erase(iter);
          return return_value;
        }
    }

  iter = m_names.begin();
  return_value = *iter;
  m_names.erase(iter);

  return return_value;
}

/////////////////////////////////////////////////////
// DependencyListPrivateT<T>::VaryingTracker methods
template<typename T>
std::string
DependencyListPrivateT<T>::VaryingTracker::
make_name(c_string prefix, c_string name)
{
  FASTUIDRAWassert(name);
  if (!prefix)
    {
      return name;
    }

  std::ostringstream tmp;
  tmp << prefix << "::" << name;
  return tmp.str();
}

template<typename T>
void
DependencyListPrivateT<T>::VaryingTracker::
add_varying(const std::string &name,
            enum varying_list::interpolator_type_t q)
{
  EqClassRef &ref(m_data[name]);
  if (!ref)
    {
      ref = FASTUIDRAWnew EqClass();
    }

  ref->m_names.insert(name);
  FASTUIDRAWmessaged_assert(ref->m_type == varying_list::interpolator_number_types
                            || ref->m_type == q,
                            "Shader aliases merge across different varying types");
  ref->m_type = q;
}

template<typename T>
void
DependencyListPrivateT<T>::VaryingTracker::
add_alias(const std::string &name, const std::string &src_name)
{
  EqClassRef &ref1(m_data[name]);
  EqClassRef &ref2(m_data[src_name]);

  if (!ref1)
    {
      ref1 = FASTUIDRAWnew EqClass();
    }

  if (!ref2)
    {
      ref2 = FASTUIDRAWnew EqClass();
    }

  ref1->m_names.insert(name);
  ref2->m_names.insert(src_name);
  if (&ref1 != &ref2)
    {
      for (const std::string &nm : ref2->m_names)
        {
          ref1->m_names.insert(nm);
        }

      FASTUIDRAWmessaged_assert(ref1->m_type == varying_list::interpolator_number_types
                                || ref1->m_type == ref2->m_type,
                                "Shader aliases merge across different varying types");
      if (ref1->m_type == varying_list::interpolator_number_types)
        {
          ref1->m_type = ref2->m_type;
        }

      ref2 = ref1;
    }
}

template<typename T>
void
DependencyListPrivateT<T>::VaryingTracker::
add_to_tracker(c_string prefix, const varying_list &src)
{
  for (unsigned int i = 0; i < varying_list::interpolator_number_types; ++i)
    {
      enum varying_list::interpolator_type_t q;
      q = static_cast<enum varying_list::interpolator_type_t>(i);

      for (c_string v : src.varyings(q))
        {
          add_varying(make_name(prefix, v), q);
        }
    }

  c_array<const c_string> names(src.alias_varying_names());
  c_array<const c_string> src_names(src.alias_varying_source_names());
  FASTUIDRAWassert(names.size() == src_names.size());
  for (unsigned int i = 0; i < names.size(); ++i)
    {
      add_alias(make_name(prefix, names[i]).c_str(),
                make_name(prefix, src_names[i]).c_str());
    }
}

template<typename T>
void
DependencyListPrivateT<T>::VaryingTracker::
add_varyings_from_tracker(varying_list *dst)
{
  for (const auto &e : m_data)
    {
      EqClassRef ref(e.second);

      FASTUIDRAWassert(ref);
      if (!ref->m_added)
        {
          FASTUIDRAWassert(!ref->m_names.empty());

          ref->m_added = true;
          FASTUIDRAWmessaged_assert(ref->m_type != varying_list::interpolator_number_types,
                                    "Shader alias chain lacks alias to actual varying");

          /* find an entry that does not have :: in it */
          std::string vname(ref->extract_double_colon_value());

          dst->add_varying(vname.c_str(), ref->m_type);
          for (const std::string &nm : ref->m_names)
            {
              dst->add_varying_alias(nm.c_str(), vname.c_str());
            }
        }
    }
}

/////////////////////////////////////////////
// DependencyListPrivateT<T> methods
template<typename T>
void
DependencyListPrivateT<T>::
add_element(c_string pname,
            const reference_counted_ptr<const T> &shader,
            const varying_list *varyings)
{
  FASTUIDRAWassert(pname && shader);

  std::string name(pname);
  FASTUIDRAWassert(m_shaders.find(name) == m_shaders.end());

  PerShader &sh(m_shaders[name]);
  sh.m_shader = shader;
  if (varyings)
    {
      sh.m_varyings = *varyings;
    }
}

template<typename T>
string_array
DependencyListPrivateT<T>::
compute_name_list(void) const
{
  string_array return_value;
  for (const auto &v : m_shaders)
    {
      return_value.push_back(v.first.c_str());
    }
  return return_value;
}

template<typename T>
std::vector<reference_counted_ptr<const T> >
DependencyListPrivateT<T>::
compute_shader_list(void) const
{
  std::vector<reference_counted_ptr<const T> > return_value;
  for (const auto &v : m_shaders)
    {
      return_value.push_back(v.second.m_shader);
    }
  return return_value;
}

template<typename T>
varying_list
DependencyListPrivateT<T>::
compute_varyings(const varying_list &combine_with) const
{
  VaryingTracker tracker;
  varying_list return_value;

  for (const auto &v : m_shaders)
    {
      tracker.add_to_tracker(v.first.c_str(), v.second.m_varyings);
    }
  tracker.add_to_tracker(nullptr, combine_with);
  tracker.add_varyings_from_tracker(&return_value);

  return return_value;
}

}}}
<commit_msg>fastuidraw/glsl/dependency_list: bug fix for merging aliases together<commit_after>/*!
 * \file dependency_list.hpp
 * \brief file dependency_list.hpp
 *
 * Copyright 2016 by Intel.
 *
 * Contact: kevin.rogovin@intel.com
 *
 * 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 Kevin Rogovin <kevin.rogovin@intel.com>
 *
 */

#pragma once

#include <string>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <vector>
#include <map>
#include <set>
#include <fastuidraw/util/reference_counted.hpp>
#include <fastuidraw/util/vecN.hpp>
#include <fastuidraw/util/string_array.hpp>
#include <fastuidraw/glsl/painter_item_shader_glsl.hpp>

namespace fastuidraw { namespace glsl { namespace detail {

template<typename T>
class DependencyListPrivateT
{
public:
  void
  add_element(c_string name, const reference_counted_ptr<const T> &shader,
              const varying_list *varyings);

  varying_list
  compute_varyings(const varying_list &combine_with) const;

  string_array
  compute_name_list(void) const;

  std::vector<reference_counted_ptr<const T> >
  compute_shader_list(void) const;

private:
  class PerShader
  {
  public:
    reference_counted_ptr<const T> m_shader;
    varying_list m_varyings;
  };

  class EqClass:public reference_counted<EqClass>::non_concurrent
  {
  public:
    EqClass(void):
      m_type(varying_list::interpolator_number_types),
      m_added(false)
    {}

    std::string
    extract_double_colon_value(void);

    std::set<std::string> m_names;
    enum varying_list::interpolator_type_t m_type;
    bool m_added;
  };

  class VaryingTracker
  {
  public:
    void
    add_to_tracker(c_string prefix, const varying_list &src);

    void
    add_varyings_from_tracker(varying_list *dst);

  private:
    typedef reference_counted_ptr<EqClass> EqClassRef;

    static
    std::string
    make_name(c_string prefix, c_string name);

    void
    add_varying(const std::string &name,
                enum varying_list::interpolator_type_t q);

    void
    add_alias(const std::string &name, const std::string &src_name);

    std::map<std::string, EqClassRef> m_data;
  };

  static
  void
  add_varyings(c_string prefix, const varying_list &src, varying_list *dst);

  std::map<std::string, PerShader> m_shaders;
};

//////////////////////////////////////////////
// DependencyListPrivateT<T>::EqClass methods
template<typename T>
std::string
DependencyListPrivateT<T>::EqClass::
extract_double_colon_value(void)
{
  std::string return_value;
  std::set<std::string>::iterator iter;

  for (iter = m_names.begin(); iter != m_names.end(); ++iter)
    {
      if (iter->find("::") != std::string::npos)
        {
          return_value = *iter;
          m_names.erase(iter);
          return return_value;
        }
    }

  iter = m_names.begin();
  return_value = *iter;
  m_names.erase(iter);

  return return_value;
}

/////////////////////////////////////////////////////
// DependencyListPrivateT<T>::VaryingTracker methods
template<typename T>
std::string
DependencyListPrivateT<T>::VaryingTracker::
make_name(c_string prefix, c_string name)
{
  FASTUIDRAWassert(name);
  if (!prefix)
    {
      return name;
    }

  std::ostringstream tmp;
  tmp << prefix << "::" << name;
  return tmp.str();
}

template<typename T>
void
DependencyListPrivateT<T>::VaryingTracker::
add_varying(const std::string &name,
            enum varying_list::interpolator_type_t q)
{
  EqClassRef &ref(m_data[name]);
  if (!ref)
    {
      ref = FASTUIDRAWnew EqClass();
    }

  ref->m_names.insert(name);
  FASTUIDRAWmessaged_assert(ref->m_type == varying_list::interpolator_number_types
                            || ref->m_type == q,
                            "Shader aliases merge across different varying types");
  ref->m_type = q;
}

template<typename T>
void
DependencyListPrivateT<T>::VaryingTracker::
add_alias(const std::string &name, const std::string &src_name)
{
  EqClassRef &pref1(m_data[name]);
  EqClassRef &pref2(m_data[src_name]);

  if (!pref1)
    {
      pref1 = FASTUIDRAWnew EqClass();
    }

  if (!pref2)
    {
      pref2 = FASTUIDRAWnew EqClass();
    }

  EqClassRef r1(pref1);
  EqClassRef r2(pref2);

  r1->m_names.insert(name);
  r2->m_names.insert(src_name);
  if (r1 != r2)
    {
      for (const std::string &nm : r2->m_names)
        {
          r1->m_names.insert(nm);

          EqClassRef &ref(m_data[nm]);
          if (ref != r1)
            {
              ref = r1;
            }
        }

      FASTUIDRAWmessaged_assert(r1->m_type == varying_list::interpolator_number_types
                                || r1->m_type == r2->m_type,
                                "Shader aliases merge across different varying types");
      if (r1->m_type == varying_list::interpolator_number_types)
        {
          r1->m_type = r2->m_type;
        }
    }
}

template<typename T>
void
DependencyListPrivateT<T>::VaryingTracker::
add_to_tracker(c_string prefix, const varying_list &src)
{
  for (unsigned int i = 0; i < varying_list::interpolator_number_types; ++i)
    {
      enum varying_list::interpolator_type_t q;
      q = static_cast<enum varying_list::interpolator_type_t>(i);

      for (c_string v : src.varyings(q))
        {
          add_varying(make_name(prefix, v), q);
        }
    }

  c_array<const c_string> names(src.alias_varying_names());
  c_array<const c_string> src_names(src.alias_varying_source_names());
  FASTUIDRAWassert(names.size() == src_names.size());
  for (unsigned int i = 0; i < names.size(); ++i)
    {
      add_alias(make_name(prefix, names[i]).c_str(),
                make_name(prefix, src_names[i]).c_str());
    }
}

template<typename T>
void
DependencyListPrivateT<T>::VaryingTracker::
add_varyings_from_tracker(varying_list *dst)
{
  for (const auto &e : m_data)
    {
      EqClassRef ref(e.second);

      FASTUIDRAWassert(ref);
      if (!ref->m_added)
        {
          FASTUIDRAWassert(!ref->m_names.empty());

          ref->m_added = true;
          FASTUIDRAWmessaged_assert(ref->m_type != varying_list::interpolator_number_types,
                                    "Shader alias chain lacks alias to actual varying");

          /* find an entry that does not have :: in it */
          std::string vname(ref->extract_double_colon_value());

          dst->add_varying(vname.c_str(), ref->m_type);
          for (const std::string &nm : ref->m_names)
            {
              dst->add_varying_alias(nm.c_str(), vname.c_str());
            }
        }
    }
}

/////////////////////////////////////////////
// DependencyListPrivateT<T> methods
template<typename T>
void
DependencyListPrivateT<T>::
add_element(c_string pname,
            const reference_counted_ptr<const T> &shader,
            const varying_list *varyings)
{
  FASTUIDRAWassert(pname && shader);

  std::string name(pname);
  FASTUIDRAWassert(m_shaders.find(name) == m_shaders.end());

  PerShader &sh(m_shaders[name]);
  sh.m_shader = shader;
  if (varyings)
    {
      sh.m_varyings = *varyings;
    }
}

template<typename T>
string_array
DependencyListPrivateT<T>::
compute_name_list(void) const
{
  string_array return_value;
  for (const auto &v : m_shaders)
    {
      return_value.push_back(v.first.c_str());
    }
  return return_value;
}

template<typename T>
std::vector<reference_counted_ptr<const T> >
DependencyListPrivateT<T>::
compute_shader_list(void) const
{
  std::vector<reference_counted_ptr<const T> > return_value;
  for (const auto &v : m_shaders)
    {
      return_value.push_back(v.second.m_shader);
    }
  return return_value;
}

template<typename T>
varying_list
DependencyListPrivateT<T>::
compute_varyings(const varying_list &combine_with) const
{
  VaryingTracker tracker;
  varying_list return_value;

  for (const auto &v : m_shaders)
    {
      tracker.add_to_tracker(v.first.c_str(), v.second.m_varyings);
    }
  tracker.add_to_tracker(nullptr, combine_with);
  tracker.add_varyings_from_tracker(&return_value);

  return return_value;
}

}}}
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome_frame/chrome_launcher.h"

#include <windows.h>
#include <shellapi.h>
#include <shlwapi.h>

#include "policy/policy_constants.h"

// Herein lies stuff selectively stolen from Chrome. We don't pull it in
// directly because all of it results in many things we don't want being
// included as well.
namespace {

// These are the switches we will allow (along with their values) in the
// safe-for-Low-Integrity version of the Chrome command line.
// Including the chrome switch files pulls in a bunch of dependencies sadly, so
// we redefine things here:
const wchar_t* kAllowedSwitches[] = {
  L"automation-channel",
  L"chrome-frame",
  L"chrome-version",
  L"disable-background-mode",
  L"disable-popup-blocking",
  L"disable-print-preview",
  L"disable-renderer-accessibility",
  L"enable-experimental-extension-apis",
  L"force-renderer-accessibility",
  L"full-memory-crash-report",
  L"lang",
  L"no-default-browser-check",
  L"no-first-run",
  L"noerrdialogs",
  L"user-data-dir",
};

const wchar_t kWhitespaceChars[] = {
  0x0009, /* <control-0009> to <control-000D> */
  0x000A,
  0x000B,
  0x000C,
  0x000D,
  0x0020, /* Space */
  0x0085, /* <control-0085> */
  0x00A0, /* No-Break Space */
  0x1680, /* Ogham Space Mark */
  0x180E, /* Mongolian Vowel Separator */
  0x2000, /* En Quad to Hair Space */
  0x2001,
  0x2002,
  0x2003,
  0x2004,
  0x2005,
  0x2006,
  0x2007,
  0x2008,
  0x2009,
  0x200A,
  0x200C, /* Zero Width Non-Joiner */
  0x2028, /* Line Separator */
  0x2029, /* Paragraph Separator */
  0x202F, /* Narrow No-Break Space */
  0x205F, /* Medium Mathematical Space */
  0x3000, /* Ideographic Space */
  0
};

const wchar_t kLauncherExeBaseName[] = L"chrome_launcher.exe";
const wchar_t kBrowserProcessExecutableName[] = L"chrome.exe";

}  // end namespace

namespace chrome_launcher {

std::wstring TrimWhiteSpace(const wchar_t* input_str) {
  std::wstring output;
  if (input_str != NULL) {
    std::wstring str(input_str);

    const std::wstring::size_type first_good_char =
        str.find_first_not_of(kWhitespaceChars);
    const std::wstring::size_type last_good_char =
        str.find_last_not_of(kWhitespaceChars);

    if (first_good_char != std::wstring::npos &&
        last_good_char != std::wstring::npos &&
        last_good_char >= first_good_char) {
      // + 1 because find_last_not_of returns the index, and we want the count
      output = str.substr(first_good_char,
                          last_good_char - first_good_char + 1);
    }
  }

  return output;
}

bool IsValidArgument(const std::wstring& arg) {
  if (arg.length() < 2) {
    return false;
  }

  for (int i = 0; i < arraysize(kAllowedSwitches); ++i) {
    size_t arg_length = lstrlenW(kAllowedSwitches[i]);
    if (arg.find(kAllowedSwitches[i], 2) == 2) {
      // The argument starts off right, now it must either end here, or be
      // followed by an equals sign.
      if (arg.length() == (arg_length + 2) ||
          (arg.length() > (arg_length + 2) && arg[arg_length+2] == L'=')) {
        return true;
      }
    }
  }

  return false;
}

bool IsValidCommandLine(const wchar_t* command_line) {
  if (command_line == NULL) {
    return false;
  }

  int num_args = 0;
  wchar_t** args = NULL;
  args = CommandLineToArgvW(command_line, &num_args);

  bool success = true;
  // Note that we skip args[0] since that is just our executable name and
  // doesn't get passed through to Chrome.
  for (int i = 1; i < num_args; ++i) {
    std::wstring trimmed_arg = TrimWhiteSpace(args[i]);
    if (!IsValidArgument(trimmed_arg.c_str())) {
      success = false;
      break;
    }
  }

  return success;
}

// Looks up optionally configured launch parameters for Chrome that may have
// been set via group policy.
void AppendAdditionalLaunchParameters(std::wstring* command_line) {
  static const HKEY kRootKeys[] = {
    HKEY_LOCAL_MACHINE,
    HKEY_CURRENT_USER
  };

  std::wstring launch_params_value_name(
      &policy::key::kAdditionalLaunchParameters[0],
      &policy::key::kAdditionalLaunchParameters[
          lstrlenA(policy::key::kAdditionalLaunchParameters)]);

  // Used for basic checks since CreateProcess doesn't support command lines
  // longer than 0x8000 characters. If we surpass that length, we do not add the
  // additional parameters.  Because we need to add a space before the
  // extra parameters, we use 0x7fff and not 0x8000.
  const size_t kMaxChars = 0x7FFF - command_line->size();
  HKEY key;
  LONG result;
  bool found = false;
  for (int i = 0; !found && i < arraysize(kRootKeys); ++i) {
    result = ::RegOpenKeyExW(kRootKeys[i], policy::kRegistryChromePolicyKey, 0,
                             KEY_QUERY_VALUE, &key);
    if (result == ERROR_SUCCESS) {
      DWORD size = 0;
      DWORD type = 0;
      result = RegQueryValueExW(key, launch_params_value_name.c_str(),
                                0, &type, NULL, &size);
      if (result == ERROR_SUCCESS && type == REG_SZ && size > 0 &&
          (size / sizeof(wchar_t)) < kMaxChars) {
        // This size includes any terminating null character or characters
        // unless the data was stored without them, so for safety we allocate
        // one extra char and zero out the buffer.
        wchar_t* value = new wchar_t[(size / sizeof(wchar_t)) + 1];
        memset(value, 0, size + sizeof(wchar_t));
        result = RegQueryValueExW(key, launch_params_value_name.c_str(), 0,
                                  &type, reinterpret_cast<BYTE*>(&value[0]),
                                  &size);
        if (result == ERROR_SUCCESS) {
          *command_line += L' ';
          *command_line += value;
          found = true;
        }
        delete [] value;
      }
      ::RegCloseKey(key);
    }
  }
}

bool SanitizeAndLaunchChrome(const wchar_t* command_line) {
  bool success = false;
  if (IsValidCommandLine(command_line)) {
    std::wstring chrome_path;
    if (GetChromeExecutablePath(&chrome_path)) {
      const wchar_t* args = PathGetArgs(command_line);

      // Build the command line string with the quoted path to chrome.exe.
      std::wstring command_line;
      command_line.reserve(chrome_path.size() + 2);
      command_line.append(1, L'\"').append(chrome_path).append(1, L'\"');

      if (args != NULL) {
        command_line += L' ';
        command_line += args;
      }

      // Append parameters that might be set by group policy.
      AppendAdditionalLaunchParameters(&command_line);

      STARTUPINFO startup_info = {0};
      startup_info.cb = sizeof(startup_info);
      startup_info.dwFlags = STARTF_USESHOWWINDOW;
      startup_info.wShowWindow = SW_SHOW;
      PROCESS_INFORMATION process_info = {0};
      if (CreateProcess(&chrome_path[0], &command_line[0],
                        NULL, NULL, FALSE, 0, NULL, NULL,
                        &startup_info, &process_info)) {
        // Close handles.
        CloseHandle(process_info.hThread);
        CloseHandle(process_info.hProcess);
        success = true;
      } else {
        _ASSERT(FALSE);
      }
    }
  }

  return success;
}

bool GetChromeExecutablePath(std::wstring* chrome_path) {
  _ASSERT(chrome_path);

  wchar_t cur_path[MAX_PATH * 4] = {0};
  // Assume that we are always built into an exe.
  GetModuleFileName(NULL, cur_path, arraysize(cur_path) / 2);

  PathRemoveFileSpec(cur_path);

  bool success = false;
  if (PathAppend(cur_path, kBrowserProcessExecutableName)) {
    if (!PathFileExists(cur_path)) {
      // The installation model for Chrome places the DLLs in a versioned
      // sub-folder one down from the Chrome executable. If we fail to find
      // chrome.exe in the current path, try looking one up and launching that
      // instead. In practice, that means we back up two and append the
      // executable name again.
      PathRemoveFileSpec(cur_path);
      PathRemoveFileSpec(cur_path);
      PathAppend(cur_path, kBrowserProcessExecutableName);
    }

    if (PathFileExists(cur_path)) {
      *chrome_path = cur_path;
      success = true;
    }
  }

  return success;
}

}  // namespace chrome_launcher
<commit_msg>Remove useless string copy.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome_frame/chrome_launcher.h"

#include <windows.h>
#include <shellapi.h>
#include <shlwapi.h>

#include "policy/policy_constants.h"

// Herein lies stuff selectively stolen from Chrome. We don't pull it in
// directly because all of it results in many things we don't want being
// included as well.
namespace {

// These are the switches we will allow (along with their values) in the
// safe-for-Low-Integrity version of the Chrome command line.
// Including the chrome switch files pulls in a bunch of dependencies sadly, so
// we redefine things here:
const wchar_t* kAllowedSwitches[] = {
  L"automation-channel",
  L"chrome-frame",
  L"chrome-version",
  L"disable-background-mode",
  L"disable-popup-blocking",
  L"disable-print-preview",
  L"disable-renderer-accessibility",
  L"enable-experimental-extension-apis",
  L"force-renderer-accessibility",
  L"full-memory-crash-report",
  L"lang",
  L"no-default-browser-check",
  L"no-first-run",
  L"noerrdialogs",
  L"user-data-dir",
};

const wchar_t kWhitespaceChars[] = {
  0x0009, /* <control-0009> to <control-000D> */
  0x000A,
  0x000B,
  0x000C,
  0x000D,
  0x0020, /* Space */
  0x0085, /* <control-0085> */
  0x00A0, /* No-Break Space */
  0x1680, /* Ogham Space Mark */
  0x180E, /* Mongolian Vowel Separator */
  0x2000, /* En Quad to Hair Space */
  0x2001,
  0x2002,
  0x2003,
  0x2004,
  0x2005,
  0x2006,
  0x2007,
  0x2008,
  0x2009,
  0x200A,
  0x200C, /* Zero Width Non-Joiner */
  0x2028, /* Line Separator */
  0x2029, /* Paragraph Separator */
  0x202F, /* Narrow No-Break Space */
  0x205F, /* Medium Mathematical Space */
  0x3000, /* Ideographic Space */
  0
};

const wchar_t kLauncherExeBaseName[] = L"chrome_launcher.exe";
const wchar_t kBrowserProcessExecutableName[] = L"chrome.exe";

}  // end namespace

namespace chrome_launcher {

std::wstring TrimWhiteSpace(const wchar_t* input_str) {
  std::wstring output;
  if (input_str != NULL) {
    std::wstring str(input_str);

    const std::wstring::size_type first_good_char =
        str.find_first_not_of(kWhitespaceChars);
    const std::wstring::size_type last_good_char =
        str.find_last_not_of(kWhitespaceChars);

    if (first_good_char != std::wstring::npos &&
        last_good_char != std::wstring::npos &&
        last_good_char >= first_good_char) {
      // + 1 because find_last_not_of returns the index, and we want the count
      output = str.substr(first_good_char,
                          last_good_char - first_good_char + 1);
    }
  }

  return output;
}

bool IsValidArgument(const std::wstring& arg) {
  if (arg.length() < 2) {
    return false;
  }

  for (int i = 0; i < arraysize(kAllowedSwitches); ++i) {
    size_t arg_length = lstrlenW(kAllowedSwitches[i]);
    if (arg.find(kAllowedSwitches[i], 2) == 2) {
      // The argument starts off right, now it must either end here, or be
      // followed by an equals sign.
      if (arg.length() == (arg_length + 2) ||
          (arg.length() > (arg_length + 2) && arg[arg_length+2] == L'=')) {
        return true;
      }
    }
  }

  return false;
}

bool IsValidCommandLine(const wchar_t* command_line) {
  if (command_line == NULL) {
    return false;
  }

  int num_args = 0;
  wchar_t** args = NULL;
  args = CommandLineToArgvW(command_line, &num_args);

  bool success = true;
  // Note that we skip args[0] since that is just our executable name and
  // doesn't get passed through to Chrome.
  for (int i = 1; i < num_args; ++i) {
    std::wstring trimmed_arg = TrimWhiteSpace(args[i]);
    if (!IsValidArgument(trimmed_arg)) {
      success = false;
      break;
    }
  }

  return success;
}

// Looks up optionally configured launch parameters for Chrome that may have
// been set via group policy.
void AppendAdditionalLaunchParameters(std::wstring* command_line) {
  static const HKEY kRootKeys[] = {
    HKEY_LOCAL_MACHINE,
    HKEY_CURRENT_USER
  };

  std::wstring launch_params_value_name(
      &policy::key::kAdditionalLaunchParameters[0],
      &policy::key::kAdditionalLaunchParameters[
          lstrlenA(policy::key::kAdditionalLaunchParameters)]);

  // Used for basic checks since CreateProcess doesn't support command lines
  // longer than 0x8000 characters. If we surpass that length, we do not add the
  // additional parameters.  Because we need to add a space before the
  // extra parameters, we use 0x7fff and not 0x8000.
  const size_t kMaxChars = 0x7FFF - command_line->size();
  HKEY key;
  LONG result;
  bool found = false;
  for (int i = 0; !found && i < arraysize(kRootKeys); ++i) {
    result = ::RegOpenKeyExW(kRootKeys[i], policy::kRegistryChromePolicyKey, 0,
                             KEY_QUERY_VALUE, &key);
    if (result == ERROR_SUCCESS) {
      DWORD size = 0;
      DWORD type = 0;
      result = RegQueryValueExW(key, launch_params_value_name.c_str(),
                                0, &type, NULL, &size);
      if (result == ERROR_SUCCESS && type == REG_SZ && size > 0 &&
          (size / sizeof(wchar_t)) < kMaxChars) {
        // This size includes any terminating null character or characters
        // unless the data was stored without them, so for safety we allocate
        // one extra char and zero out the buffer.
        wchar_t* value = new wchar_t[(size / sizeof(wchar_t)) + 1];
        memset(value, 0, size + sizeof(wchar_t));
        result = RegQueryValueExW(key, launch_params_value_name.c_str(), 0,
                                  &type, reinterpret_cast<BYTE*>(&value[0]),
                                  &size);
        if (result == ERROR_SUCCESS) {
          *command_line += L' ';
          *command_line += value;
          found = true;
        }
        delete [] value;
      }
      ::RegCloseKey(key);
    }
  }
}

bool SanitizeAndLaunchChrome(const wchar_t* command_line) {
  bool success = false;
  if (IsValidCommandLine(command_line)) {
    std::wstring chrome_path;
    if (GetChromeExecutablePath(&chrome_path)) {
      const wchar_t* args = PathGetArgs(command_line);

      // Build the command line string with the quoted path to chrome.exe.
      std::wstring command_line;
      command_line.reserve(chrome_path.size() + 2);
      command_line.append(1, L'\"').append(chrome_path).append(1, L'\"');

      if (args != NULL) {
        command_line += L' ';
        command_line += args;
      }

      // Append parameters that might be set by group policy.
      AppendAdditionalLaunchParameters(&command_line);

      STARTUPINFO startup_info = {0};
      startup_info.cb = sizeof(startup_info);
      startup_info.dwFlags = STARTF_USESHOWWINDOW;
      startup_info.wShowWindow = SW_SHOW;
      PROCESS_INFORMATION process_info = {0};
      if (CreateProcess(&chrome_path[0], &command_line[0],
                        NULL, NULL, FALSE, 0, NULL, NULL,
                        &startup_info, &process_info)) {
        // Close handles.
        CloseHandle(process_info.hThread);
        CloseHandle(process_info.hProcess);
        success = true;
      } else {
        _ASSERT(FALSE);
      }
    }
  }

  return success;
}

bool GetChromeExecutablePath(std::wstring* chrome_path) {
  _ASSERT(chrome_path);

  wchar_t cur_path[MAX_PATH * 4] = {0};
  // Assume that we are always built into an exe.
  GetModuleFileName(NULL, cur_path, arraysize(cur_path) / 2);

  PathRemoveFileSpec(cur_path);

  bool success = false;
  if (PathAppend(cur_path, kBrowserProcessExecutableName)) {
    if (!PathFileExists(cur_path)) {
      // The installation model for Chrome places the DLLs in a versioned
      // sub-folder one down from the Chrome executable. If we fail to find
      // chrome.exe in the current path, try looking one up and launching that
      // instead. In practice, that means we back up two and append the
      // executable name again.
      PathRemoveFileSpec(cur_path);
      PathRemoveFileSpec(cur_path);
      PathAppend(cur_path, kBrowserProcessExecutableName);
    }

    if (PathFileExists(cur_path)) {
      *chrome_path = cur_path;
      success = true;
    }
  }

  return success;
}

}  // namespace chrome_launcher
<|endoftext|>