<commit_before>//=======================================================================
// Copyright (c) 2014-2015 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

/*!
 * \file
 * \brief Iterator implementation for ETL expressions.
 */

#pragma once

#include <iterator>

namespace etl {

/*!
 * \brief Configurable iterator for ETL expressions
 * \tparam Expr The type of expr for which the iterator is working
 * \tparam Ref Indicates if the iterator returns reference.
 * \tparam Const Indicates if the iterator returns const reference only.
 */
template <typename Expr, bool Ref = false, bool Const = true>
struct iterator : public std::iterator<std::random_access_iterator_tag, value_t<Expr>> {
private:
    Expr* expr;    ///< Pointer to the expression
    std::size_t i; ///< Current index

public:
    using base_iterator_t = std::iterator<std::random_access_iterator_tag, value_t<Expr>>; ///< The base iterator type
    using value_type      = value_t<Expr>;                                                 ///< The value type
    using reference_t     = std::conditional_t<Ref,
                                           std::conditional_t<Const, const value_type&, value_type&>,
                                           value_type>; ///< The type of reference
    using pointer_t    = std::conditional_t<Const, const value_type*, value_type*>; ///< The type of pointer
    using difference_t = typename base_iterator_t::difference_type;                 ///< The type used for subtracting two iterators

    /*!
     * \brief Construct a new iterator
     * \param expr The expr to iterate over.
     * \param i The starting position
     */
    iterator(Expr& expr, std::size_t i)
            : expr(&expr), i(i) {}

    /*!
     * \brief Dereference the iterator to get the current value
     * \return a reference to the current element
     */
    reference_t operator*() {
        return (*expr)[i];
    }

    /*!
     * \brief Dereferences the iterator at n forward position
     * \param n The number of forward position to advance
     * \return a reference to the element at the current position plus n
     */
    reference_t operator[](difference_t n) {
        return (*expr)[i + n];
    }

    /*!
     * \brief Dereference the iterator to get the current value
     * \return a pointer to the current element
     */
    pointer_t operator->() {
        return &(*expr)[i];
    }

    /*!
     * \brief Predecrement the iterator
     * \return a reference to the iterator
     */
    iterator& operator--() {
        --i;
        return *this;
    }

    /*!
     * \brief Postdecrement the iterator
     * \return an iterator the position prior to the decrement.
     */
    iterator operator--(int) {
        iterator prev(*this);
        --i;
        return prev;
    }

    /*!
     * \brief Preincrement the iterator
     * \return a reference to the iterator
     */
    iterator& operator++() {
        ++i;
        return *this;
    }

    /*!
     * \brief Postincrement the iterator
     * \return an iterator the position prior to the increment.
     */
    iterator operator++(int) {
        iterator prev(*this);
        ++i;
        return prev;
    }

    /*!
     * \brief Advances the iterator n positions
     * \param n The number of position to advance
     * \return a reference to the iterator
     */
    iterator& operator+=(difference_t n) {
        i += n;
        return *this;
    }

    /*!
     * \brief Back away the iterator n positions
     * \param n The number of position to back
     * \return a reference to the iterator
     */
    iterator& operator-=(difference_t n) {
        i -= n;
        return *this;
    }

    /*!
     * \brief Creates a new iterator poiting to the current position plus n
     * \param n The number of position to advance
     * \return the new interator
     */
    iterator operator+(difference_t n) {
        iterator it(*this);
        it += n;
        return it;
    }

    /*!
     * \brief Creates a new iterator poiting to the current position minus n
     * \param n The number of position to back away
     * \return the new interator
     */
    iterator operator-(difference_t n) {
        iterator it(*this);
        it -= n;
        return it;
    }

    /*!
     * \brief Computes the difference between two iterators
     * \param it the other iterator
     * \return the number of positions between the two iterators
     */
    difference_t operator-(const iterator& it) {
        return i - it.i;
    }

    /*!
     * \brief Compare two iterators
     * \param other The other iterator
     * \return true if this operator is equal to the other iterator
     */
    bool operator==(const iterator& other) const {
        return expr == other.expr && i == other.i;
    }

    /*!
     * \brief Compare two iterators
     * \param other The other iterator
     * \return true if this operator is not equal to the other iterator
     */
    bool operator!=(const iterator& other) const {
        return !(*this == other);
    }

    /*!
     * \brief Compare two iterators
     * \param other The other iterator
     * \return true if this operator is greater than the other iterator
     */
    bool operator>(const iterator& other) const {
        return i > other.i;
    }

    /*!
     * \brief Compare two iterators
     * \param other The other iterator
     * \return true if this operator is greater than or equal to the other iterator
     */
    bool operator>=(const iterator& other) const {
        return i >= other.i;
    }

    /*!
     * \brief Compare two iterators
     * \param other The other iterator
     * \return true if this operator is less than the other iterator
     */
    bool operator<(const iterator& other) const {
        return i < other.i;
    }

    /*!
     * \brief Compare two iterators
     * \param other The other iterator
     * \return true if this operator is less than or equal to the other iterator
     */
    bool operator<=(const iterator& other) const {
        return i <= other.i;
    }
};

} //end of namespace etl
<commit_msg>Cleanup<commit_after>//=======================================================================
// Copyright (c) 2014-2015 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

/*!
 * \file
 * \brief Iterator implementation for ETL expressions.
 */

#pragma once

#include <iterator>

namespace etl {

/*!
 * \brief Configurable iterator for ETL expressions
 * \tparam Expr The type of expr for which the iterator is working
 * \tparam Ref Indicates if the iterator returns reference.
 * \tparam Const Indicates if the iterator returns const reference only.
 */
template <typename Expr, bool Ref = false, bool Const = true>
struct iterator : public std::iterator<std::random_access_iterator_tag, value_t<Expr>> {
private:
    Expr* expr;    ///< Pointer to the expression
    std::size_t i; ///< Current index

public:
    using base_iterator_t = std::iterator<std::random_access_iterator_tag, value_t<Expr>>;                                  ///< The base iterator type
    using value_type      = value_t<Expr>;                                                                                ///< The value type
    using reference_t     = std::conditional_t<Ref, std::conditional_t<Const, const value_type&, value_type&>, value_type>; ///< The type of reference
    using pointer_t       = std::conditional_t<Const, const value_type*, value_type*>;                                      ///< The type of pointer
    using difference_t    = typename base_iterator_t::difference_type;                                                      ///< The type used for subtracting two iterators

    /*!
     * \brief Construct a new iterator
     * \param expr The expr to iterate over.
     * \param i The starting position
     */
    iterator(Expr& expr, std::size_t i)
            : expr(&expr), i(i) {}

    /*!
     * \brief Dereference the iterator to get the current value
     * \return a reference to the current element
     */
    reference_t operator*() {
        return (*expr)[i];
    }

    /*!
     * \brief Dereferences the iterator at n forward position
     * \param n The number of forward position to advance
     * \return a reference to the element at the current position plus n
     */
    reference_t operator[](difference_t n) {
        return (*expr)[i + n];
    }

    /*!
     * \brief Dereference the iterator to get the current value
     * \return a pointer to the current element
     */
    pointer_t operator->() {
        return &(*expr)[i];
    }

    /*!
     * \brief Predecrement the iterator
     * \return a reference to the iterator
     */
    iterator& operator--() {
        --i;
        return *this;
    }

    /*!
     * \brief Postdecrement the iterator
     * \return an iterator the position prior to the decrement.
     */
    iterator operator--(int) {
        iterator prev(*this);
        --i;
        return prev;
    }

    /*!
     * \brief Preincrement the iterator
     * \return a reference to the iterator
     */
    iterator& operator++() {
        ++i;
        return *this;
    }

    /*!
     * \brief Postincrement the iterator
     * \return an iterator the position prior to the increment.
     */
    iterator operator++(int) {
        iterator prev(*this);
        ++i;
        return prev;
    }

    /*!
     * \brief Advances the iterator n positions
     * \param n The number of position to advance
     * \return a reference to the iterator
     */
    iterator& operator+=(difference_t n) {
        i += n;
        return *this;
    }

    /*!
     * \brief Back away the iterator n positions
     * \param n The number of position to back
     * \return a reference to the iterator
     */
    iterator& operator-=(difference_t n) {
        i -= n;
        return *this;
    }

    /*!
     * \brief Creates a new iterator poiting to the current position plus n
     * \param n The number of position to advance
     * \return the new interator
     */
    iterator operator+(difference_t n) {
        iterator it(*this);
        it += n;
        return it;
    }

    /*!
     * \brief Creates a new iterator poiting to the current position minus n
     * \param n The number of position to back away
     * \return the new interator
     */
    iterator operator-(difference_t n) {
        iterator it(*this);
        it -= n;
        return it;
    }

    /*!
     * \brief Computes the difference between two iterators
     * \param it the other iterator
     * \return the number of positions between the two iterators
     */
    difference_t operator-(const iterator& it) {
        return i - it.i;
    }

    /*!
     * \brief Compare two iterators
     * \param other The other iterator
     * \return true if this operator is equal to the other iterator
     */
    bool operator==(const iterator& other) const {
        return expr == other.expr && i == other.i;
    }

    /*!
     * \brief Compare two iterators
     * \param other The other iterator
     * \return true if this operator is not equal to the other iterator
     */
    bool operator!=(const iterator& other) const {
        return !(*this == other);
    }

    /*!
     * \brief Compare two iterators
     * \param other The other iterator
     * \return true if this operator is greater than the other iterator
     */
    bool operator>(const iterator& other) const {
        return i > other.i;
    }

    /*!
     * \brief Compare two iterators
     * \param other The other iterator
     * \return true if this operator is greater than or equal to the other iterator
     */
    bool operator>=(const iterator& other) const {
        return i >= other.i;
    }

    /*!
     * \brief Compare two iterators
     * \param other The other iterator
     * \return true if this operator is less than the other iterator
     */
    bool operator<(const iterator& other) const {
        return i < other.i;
    }

    /*!
     * \brief Compare two iterators
     * \param other The other iterator
     * \return true if this operator is less than or equal to the other iterator
     */
    bool operator<=(const iterator& other) const {
        return i <= other.i;
    }
};

} //end of namespace etl
<|endoftext|>


<commit_before>
#include <ros/ros.h>

#include <gtest/gtest.h>

#include <control_toolbox/pid.h>

using namespace control_toolbox;

TEST(ParameterTest, zeroITermBadIBoundsTest)
{
  RecordProperty("description","This test checks robustness against poorly-defined integral term bounds.");

  Pid pid(1.0, 0.0, 0.0, -1.0, 0.0);

  double cmd = 0.0;
  double pe,ie,de;

  cmd = pid.updatePid(-1.0, ros::Duration(1.0));
  pid.getCurrentPIDErrors(&pe,&ie,&de);
  EXPECT_EQ(-1.0, ie);
  EXPECT_EQ(2.0, cmd);

  cmd = pid.updatePid(-1.0, ros::Duration(1.0));
  pid.getCurrentPIDErrors(&pe,&ie,&de);
  EXPECT_EQ(-2.0, ie);
  EXPECT_EQ(2.0, cmd);
}

int main(int argc, char** argv) {
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<commit_msg>Specifying div-by-zero test, adding other integral term tests<commit_after>
#include <ros/ros.h>

#include <gtest/gtest.h>

#include <control_toolbox/pid.h>

#include <boost/math/special_functions/fpclassify.hpp>

using namespace control_toolbox;

TEST(ParameterTest, zeroITermBadIBoundsTest)
{
  RecordProperty("description","This test checks robustness against divide-by-zero errors when given integral term bounds which do not include 0.0.");

  Pid pid(1.0, 0.0, 0.0, -1.0, 0.0);

  double cmd = 0.0;
  double pe,ie,de;

  cmd = pid.updatePid(-1.0, ros::Duration(1.0));
  pid.getCurrentPIDErrors(&pe,&ie,&de);
  EXPECT_FALSE(boost::math::isinf(ie));
  EXPECT_FALSE(boost::math::isnan(cmd));

  cmd = pid.updatePid(-1.0, ros::Duration(1.0));
  pid.getCurrentPIDErrors(&pe,&ie,&de);
  EXPECT_FALSE(boost::math::isinf(ie));
  EXPECT_FALSE(boost::math::isnan(cmd));
}

TEST(ParameterTest, integrationWindupTest)
{
  RecordProperty("description","This test succeeds if the integral error is prevented from winding up when the integral gain is non-zero.");

  Pid pid(0.0, 1.0, 0.0, 1.0, -1.0);

  double cmd = 0.0;
  double pe,ie,de;

  cmd = pid.updatePid(-1.0, ros::Duration(1.0));
  pid.getCurrentPIDErrors(&pe,&ie,&de);
  EXPECT_EQ(-1.0, ie);
  EXPECT_EQ(1.0, cmd);

  cmd = pid.updatePid(-1.0, ros::Duration(1.0));
  pid.getCurrentPIDErrors(&pe,&ie,&de);
  EXPECT_EQ(-1.0, ie);
  EXPECT_EQ(1.0, cmd);
}

TEST(ParameterTest, integrationWindupZeroGainTest)
{
  RecordProperty("description","This test succeeds if the integral error is prevented from winding up when the integral gain is zero. If the integral error is allowed to wind up while it is disabled, it can cause sudden jumps to the minimum or maximum bound in control command when re-enabled.");

  double i_gain = 0.0;
  double i_min = -1.0;
  double i_max = 1.0;
  Pid pid(0.0, i_gain, 0.0, i_max, i_min);

  double cmd = 0.0;
  double pe,ie,de;

  cmd = pid.updatePid(-1.0, ros::Duration(1.0));
  pid.getCurrentPIDErrors(&pe,&ie,&de);
  EXPECT_LE(i_min, ie);
  EXPECT_LE(ie, i_max);
  EXPECT_EQ(0.0, cmd);

  cmd = pid.updatePid(-1.0, ros::Duration(1.0));
  pid.getCurrentPIDErrors(&pe,&ie,&de);
  EXPECT_LE(i_min, ie);
  EXPECT_LE(ie, i_max);
  EXPECT_EQ(0.0, cmd);
}

int main(int argc, char** argv) {
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>/*
Nazara Engine - Assimp Plugin

Copyright (C) 2015 Jérôme "Lynix" Leclercq (lynix680@gmail.com)

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <CustomStream.hpp>
#include <Nazara/Core/String.hpp>
#include <Nazara/Utility/Mesh.hpp>
#include <Nazara/Utility/IndexIterator.hpp>
#include <Nazara/Utility/IndexMapper.hpp>
#include <Nazara/Utility/StaticMesh.hpp>
#include <assimp/cfileio.h>
#include <assimp/cimport.h>
#include <assimp/config.h>
#include <assimp/mesh.h>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <set>

using namespace Nz;

void ProcessJoints(aiNode* node, Skeleton* skeleton, const std::set<Nz::String>& joints)
{
	Nz::String jointName(node->mName.data, node->mName.length);
	if (joints.count(jointName))
	{
		Joint* joint = skeleton->GetJoint(jointName);
		if (joint)
		{
			if (node->mParent)
				joint->SetParent(skeleton->GetJoint(node->mParent->mName.C_Str()));

			Matrix4f transformMatrix(node->mTransformation.a1, node->mTransformation.a2, node->mTransformation.a3, node->mTransformation.a4,
			                         node->mTransformation.b1, node->mTransformation.b2, node->mTransformation.b3, node->mTransformation.b4,
			                         node->mTransformation.c1, node->mTransformation.c2, node->mTransformation.c3, node->mTransformation.c4,
			                         node->mTransformation.d1, node->mTransformation.d2, node->mTransformation.d3, node->mTransformation.d4);

			transformMatrix.InverseAffine();

			joint->SetInverseBindMatrix(transformMatrix);
		}
	}

	for (unsigned int i = 0; i < node->mNumChildren; ++i)
		ProcessJoints(node->mChildren[i], skeleton, joints);
}

bool IsSupported(const String& extension)
{
	String dotExt = '.' + extension;
	return (aiIsExtensionSupported(dotExt.GetConstBuffer()) == AI_TRUE);
}

Ternary Check(Stream& stream, const MeshParams& parameters)
{
    bool skip;
    if (parameters.custom.GetBooleanParameter("SkipAssimpLoader", &skip) && skip)
        return Ternary_False;

	return Ternary_Unknown;
}

bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters)
{
	Nz::String streamPath = stream.GetPath();

	FileIOUserdata userdata;
	userdata.originalFilePath = (!streamPath.IsEmpty()) ? streamPath.GetConstBuffer() : StreamPath;
	userdata.originalStream = &stream;

	aiFileIO fileIO;
	fileIO.CloseProc = StreamCloser;
	fileIO.OpenProc = StreamOpener;
	fileIO.UserData = reinterpret_cast<char*>(&userdata);

	unsigned int postProcess = aiProcess_CalcTangentSpace     | aiProcess_JoinIdenticalVertices
	                         | aiProcess_MakeLeftHanded       | aiProcess_Triangulate
	                         | aiProcess_RemoveComponent      | aiProcess_GenSmoothNormals
	                         | aiProcess_SplitLargeMeshes     | aiProcess_LimitBoneWeights
	                         | aiProcess_ImproveCacheLocality | aiProcess_RemoveRedundantMaterials
	                         | aiProcess_FixInfacingNormals   | aiProcess_SortByPType
	                         | aiProcess_FindInvalidData      | aiProcess_GenUVCoords
	                         | aiProcess_TransformUVCoords    | aiProcess_OptimizeMeshes
	                         | aiProcess_OptimizeGraph        | aiProcess_FlipWindingOrder
	                         | aiProcess_Debone;

	if (!parameters.flipUVs)
		postProcess |= aiProcess_FlipUVs;

	if (parameters.optimizeIndexBuffers)
		postProcess |= aiProcess_ImproveCacheLocality;

	float smoothingAngle = 80.f;
	parameters.custom.GetFloatParameter("AssimpLoader_SmoothingAngle", &smoothingAngle);

	int triangleLimit = 1'000'000;
	parameters.custom.GetIntegerParameter("AssimpLoader_TriangleLimit", &triangleLimit);

	int vertexLimit   = 1'000'000;
	parameters.custom.GetIntegerParameter("AssimpLoader_VertexLimit", &vertexLimit);

	aiPropertyStore* properties = aiCreatePropertyStore();
	aiSetImportPropertyFloat(properties,   AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE, smoothingAngle);
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_LBW_MAX_WEIGHTS,         4);
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_SBP_REMOVE,              ~aiPrimitiveType_TRIANGLE); //< We only want triangles
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,      triangleLimit);
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_SLM_VERTEX_LIMIT,        vertexLimit);
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_RVC_FLAGS,               aiComponent_COLORS);

	const aiScene* scene = aiImportFileExWithProperties(userdata.originalFilePath, postProcess, &fileIO, properties);
	aiReleasePropertyStore(properties);

	std::set<Nz::String> joints;

	bool animatedMesh = false;
	if (parameters.animated)
	{
		for (unsigned int i = 0; i < scene->mNumMeshes; ++i)
		{
			aiMesh* mesh = scene->mMeshes[i];
			if (mesh->HasBones()) // Inline functions can be safely called
			{
				animatedMesh = true;
				for (unsigned int j = 0; j < mesh->mNumBones; ++j)
					joints.insert(mesh->mBones[j]->mName.C_Str());
			}
		}
	}

	if (animatedMesh)
	{
		mesh->CreateSkeletal(joints.size());

		Skeleton* skeleton = mesh->GetSkeleton();

		// First, assign names
		unsigned int jointIndex = 0;
		for (const Nz::String& jointName : joints)
			skeleton->GetJoint(jointIndex++)->SetName(jointName);

		ProcessJoints(scene->mRootNode, skeleton, joints);

		return false;
	}
	else
	{
		mesh->CreateStatic();

		for (unsigned int i = 0; i < scene->mNumMeshes; ++i)
		{
			aiMesh* iMesh = scene->mMeshes[i];
			if (!iMesh->HasBones()) // Don't process skeletal meshs
			{
				unsigned int indexCount = iMesh->mNumFaces * 3;
				unsigned int vertexCount = iMesh->mNumVertices;

				// Index buffer
				bool largeIndices = (vertexCount > std::numeric_limits<UInt16>::max());

				IndexBufferRef indexBuffer = IndexBuffer::New(largeIndices, indexCount, parameters.storage);

				IndexMapper indexMapper(indexBuffer, BufferAccess_DiscardAndWrite);
				IndexIterator index = indexMapper.begin();

				for (unsigned int j = 0; j < iMesh->mNumFaces; ++j)
				{
					aiFace& face = iMesh->mFaces[j];
					if (face.mNumIndices != 3)
						NazaraWarning("Assimp plugin: This face is not a triangle!");

					*index++ = face.mIndices[0];
					*index++ = face.mIndices[1];
					*index++ = face.mIndices[2];
				}
				indexMapper.Unmap();

				// Vertex buffer
				VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.storage);
				BufferMapper<VertexBuffer> vertexMapper(vertexBuffer, BufferAccess_WriteOnly);

				MeshVertex* vertex = static_cast<MeshVertex*>(vertexMapper.GetPointer());
				for (unsigned int j = 0; j < vertexCount; ++j)
				{
					aiVector3D position = iMesh->mVertices[j];
					aiVector3D normal = iMesh->mNormals[j];
					aiVector3D tangent = iMesh->mTangents[j];
					aiVector3D uv = iMesh->mTextureCoords[0][j];

					vertex->position = parameters.scale * Vector3f(position.x, position.y, position.z);
					vertex->normal.Set(normal.x, normal.y, normal.z);
					vertex->tangent.Set(tangent.x, tangent.y, tangent.z);
					vertex->uv.Set(uv.x, uv.y);
					vertex++;
				}

				vertexMapper.Unmap();

				// Submesh
				StaticMeshRef subMesh = StaticMesh::New(mesh);
				subMesh->Create(vertexBuffer);

				subMesh->SetIndexBuffer(indexBuffer);
				subMesh->GenerateAABB();
				subMesh->SetMaterialIndex(iMesh->mMaterialIndex);

				mesh->AddSubMesh(subMesh);
			}
		}

		if (parameters.center)
			mesh->Recenter();
	}

	aiReleaseImport(scene);

	return true;
}

extern "C"
{
	NAZARA_EXPORT int PluginLoad()
	{
		Nz::MeshLoader::RegisterLoader(IsSupported, Check, Load);
		return 1;
	}

	NAZARA_EXPORT void PluginUnload()
	{
		Nz::MeshLoader::UnregisterLoader(IsSupported, Check, Load);
	}
}
<commit_msg>Plugins/Assimp: Add material loading<commit_after>/*
Nazara Engine - Assimp Plugin

Copyright (C) 2015 Jérôme "Lynix" Leclercq (lynix680@gmail.com)

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <CustomStream.hpp>
#include <Nazara/Core/String.hpp>
#include <Nazara/Utility/Mesh.hpp>
#include <Nazara/Utility/IndexIterator.hpp>
#include <Nazara/Utility/IndexMapper.hpp>
#include <Nazara/Utility/MaterialData.hpp>
#include <Nazara/Utility/StaticMesh.hpp>
#include <assimp/cfileio.h>
#include <assimp/cimport.h>
#include <assimp/config.h>
#include <assimp/mesh.h>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <set>

using namespace Nz;

void ProcessJoints(aiNode* node, Skeleton* skeleton, const std::set<Nz::String>& joints)
{
	Nz::String jointName(node->mName.data, node->mName.length);
	if (joints.count(jointName))
	{
		Joint* joint = skeleton->GetJoint(jointName);
		if (joint)
		{
			if (node->mParent)
				joint->SetParent(skeleton->GetJoint(node->mParent->mName.C_Str()));

			Matrix4f transformMatrix(node->mTransformation.a1, node->mTransformation.a2, node->mTransformation.a3, node->mTransformation.a4,
			                         node->mTransformation.b1, node->mTransformation.b2, node->mTransformation.b3, node->mTransformation.b4,
			                         node->mTransformation.c1, node->mTransformation.c2, node->mTransformation.c3, node->mTransformation.c4,
			                         node->mTransformation.d1, node->mTransformation.d2, node->mTransformation.d3, node->mTransformation.d4);

			transformMatrix.InverseAffine();

			joint->SetInverseBindMatrix(transformMatrix);
		}
	}

	for (unsigned int i = 0; i < node->mNumChildren; ++i)
		ProcessJoints(node->mChildren[i], skeleton, joints);
}

bool IsSupported(const String& extension)
{
	String dotExt = '.' + extension;
	return (aiIsExtensionSupported(dotExt.GetConstBuffer()) == AI_TRUE);
}

Ternary Check(Stream& stream, const MeshParams& parameters)
{
    bool skip;
    if (parameters.custom.GetBooleanParameter("SkipAssimpLoader", &skip) && skip)
        return Ternary_False;

	return Ternary_Unknown;
}

bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters)
{
	Nz::String streamPath = stream.GetPath();

	FileIOUserdata userdata;
	userdata.originalFilePath = (!streamPath.IsEmpty()) ? streamPath.GetConstBuffer() : StreamPath;
	userdata.originalStream = &stream;

	aiFileIO fileIO;
	fileIO.CloseProc = StreamCloser;
	fileIO.OpenProc = StreamOpener;
	fileIO.UserData = reinterpret_cast<char*>(&userdata);

	unsigned int postProcess = aiProcess_CalcTangentSpace     | aiProcess_JoinIdenticalVertices
	                         | aiProcess_MakeLeftHanded       | aiProcess_Triangulate
	                         | aiProcess_RemoveComponent      | aiProcess_GenSmoothNormals
	                         | aiProcess_SplitLargeMeshes     | aiProcess_LimitBoneWeights
	                         | aiProcess_ImproveCacheLocality | aiProcess_RemoveRedundantMaterials
	                         | aiProcess_FixInfacingNormals   | aiProcess_SortByPType
	                         | aiProcess_FindInvalidData      | aiProcess_GenUVCoords
	                         | aiProcess_TransformUVCoords    | aiProcess_OptimizeMeshes
	                         | aiProcess_OptimizeGraph        | aiProcess_FlipWindingOrder
	                         | aiProcess_Debone;

	if (!parameters.flipUVs)
		postProcess |= aiProcess_FlipUVs;

	if (parameters.optimizeIndexBuffers)
		postProcess |= aiProcess_ImproveCacheLocality;

	float smoothingAngle = 80.f;
	parameters.custom.GetFloatParameter("AssimpLoader_SmoothingAngle", &smoothingAngle);

	int triangleLimit = 1'000'000;
	parameters.custom.GetIntegerParameter("AssimpLoader_TriangleLimit", &triangleLimit);

	int vertexLimit   = 1'000'000;
	parameters.custom.GetIntegerParameter("AssimpLoader_VertexLimit", &vertexLimit);

	aiPropertyStore* properties = aiCreatePropertyStore();
	aiSetImportPropertyFloat(properties,   AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE, smoothingAngle);
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_LBW_MAX_WEIGHTS,         4);
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_SBP_REMOVE,              ~aiPrimitiveType_TRIANGLE); //< We only want triangles
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,      triangleLimit);
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_SLM_VERTEX_LIMIT,        vertexLimit);
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_RVC_FLAGS,               aiComponent_COLORS);

	const aiScene* scene = aiImportFileExWithProperties(userdata.originalFilePath, postProcess, &fileIO, properties);
	aiReleasePropertyStore(properties);

	std::set<Nz::String> joints;

	bool animatedMesh = false;
	if (parameters.animated)
	{
		for (unsigned int i = 0; i < scene->mNumMeshes; ++i)
		{
			aiMesh* mesh = scene->mMeshes[i];
			if (mesh->HasBones()) // Inline functions can be safely called
			{
				animatedMesh = true;
				for (unsigned int j = 0; j < mesh->mNumBones; ++j)
					joints.insert(mesh->mBones[j]->mName.C_Str());
			}
		}
	}

	if (animatedMesh)
	{
		mesh->CreateSkeletal(joints.size());

		Skeleton* skeleton = mesh->GetSkeleton();

		// First, assign names
		unsigned int jointIndex = 0;
		for (const Nz::String& jointName : joints)
			skeleton->GetJoint(jointIndex++)->SetName(jointName);

		ProcessJoints(scene->mRootNode, skeleton, joints);

		return false;
	}
	else
	{
		mesh->CreateStatic();

		// aiMaterial index in scene => Material index and data in Mesh
		std::unordered_map<unsigned int, std::pair<unsigned int, ParameterList>> materials;

		for (unsigned int i = 0; i < scene->mNumMeshes; ++i)
		{
			aiMesh* iMesh = scene->mMeshes[i];
			if (!iMesh->HasBones()) // Don't process skeletal meshs
			{
				unsigned int indexCount = iMesh->mNumFaces * 3;
				unsigned int vertexCount = iMesh->mNumVertices;

				// Index buffer
				bool largeIndices = (vertexCount > std::numeric_limits<UInt16>::max());

				IndexBufferRef indexBuffer = IndexBuffer::New(largeIndices, indexCount, parameters.storage);

				IndexMapper indexMapper(indexBuffer, BufferAccess_DiscardAndWrite);
				IndexIterator index = indexMapper.begin();

				for (unsigned int j = 0; j < iMesh->mNumFaces; ++j)
				{
					aiFace& face = iMesh->mFaces[j];
					if (face.mNumIndices != 3)
						NazaraWarning("Assimp plugin: This face is not a triangle!");

					*index++ = face.mIndices[0];
					*index++ = face.mIndices[1];
					*index++ = face.mIndices[2];
				}
				indexMapper.Unmap();

				// Vertex buffer
				VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.storage);
				BufferMapper<VertexBuffer> vertexMapper(vertexBuffer, BufferAccess_WriteOnly);

				MeshVertex* vertex = static_cast<MeshVertex*>(vertexMapper.GetPointer());
				for (unsigned int j = 0; j < vertexCount; ++j)
				{
					aiVector3D position = iMesh->mVertices[j];
					aiVector3D normal = iMesh->mNormals[j];
					aiVector3D tangent = iMesh->mTangents[j];
					aiVector3D uv = iMesh->mTextureCoords[0][j];

					vertex->position = parameters.scale * Vector3f(position.x, position.y, position.z);
					vertex->normal.Set(normal.x, normal.y, normal.z);
					vertex->tangent.Set(tangent.x, tangent.y, tangent.z);
					vertex->uv.Set(uv.x, uv.y);
					vertex++;
				}

				vertexMapper.Unmap();

				// Submesh
				StaticMeshRef subMesh = StaticMesh::New(mesh);
				subMesh->Create(vertexBuffer);

				subMesh->SetIndexBuffer(indexBuffer);
				subMesh->GenerateAABB();
				subMesh->SetMaterialIndex(iMesh->mMaterialIndex);

				auto matIt = materials.find(iMesh->mMaterialIndex);
				if (matIt == materials.end())
				{
					ParameterList matData;
					aiMaterial* aiMat = scene->mMaterials[iMesh->mMaterialIndex];

					auto ConvertColor = [&] (const char* aiKey, unsigned int aiType, unsigned int aiIndex, const char* colorKey)
					{
						aiColor4D color;
						if (aiGetMaterialColor(aiMat, aiKey, aiType, aiIndex, &color) == aiReturn_SUCCESS)
						{
							matData.SetParameter(MaterialData::CustomDefined);

							matData.SetParameter(colorKey, Color(static_cast<UInt8>(color.r * 255), static_cast<UInt8>(color.g * 255), static_cast<UInt8>(color.b * 255), static_cast<UInt8>(color.a * 255)));
						}
					};

					auto ConvertTexture = [&] (aiTextureType aiType, const char* textureKey, const char* wrapKey = nullptr)
					{
						aiString path;
						aiTextureMapMode mapMode;
						if (aiGetMaterialTexture(aiMat, aiType, 0, &path, nullptr, nullptr, nullptr, nullptr, &mapMode, nullptr) == aiReturn_SUCCESS)
						{
							matData.SetParameter(MaterialData::CustomDefined);
							matData.SetParameter(textureKey, stream.GetDirectory() + String(path.data, path.length));

							if (wrapKey)
							{
								SamplerWrap wrap = SamplerWrap_Default;
								switch (mapMode)
								{
									case aiTextureMapMode_Clamp:
									case aiTextureMapMode_Decal:
										wrap = SamplerWrap_Clamp;
										break;

									case aiTextureMapMode_Mirror:
										wrap = SamplerWrap_MirroredRepeat;
										break;

									case aiTextureMapMode_Wrap:
										wrap = SamplerWrap_Repeat;
										break;

									default:
										NazaraWarning("Assimp texture map mode 0x" + String::Number(mapMode, 16) + " not handled");
										break;
								}

								matData.SetParameter(wrapKey, static_cast<int>(wrap));
							}
						}
					};

					ConvertColor(AI_MATKEY_COLOR_AMBIENT, MaterialData::AmbientColor);
					ConvertColor(AI_MATKEY_COLOR_DIFFUSE, MaterialData::DiffuseColor);
					ConvertColor(AI_MATKEY_COLOR_SPECULAR, MaterialData::SpecularColor);

					ConvertTexture(aiTextureType_DIFFUSE, MaterialData::DiffuseTexturePath, MaterialData::DiffuseWrap);
					ConvertTexture(aiTextureType_EMISSIVE, MaterialData::EmissiveTexturePath);
					ConvertTexture(aiTextureType_HEIGHT, MaterialData::HeightTexturePath);
					ConvertTexture(aiTextureType_NORMALS, MaterialData::NormalTexturePath);
					ConvertTexture(aiTextureType_OPACITY, MaterialData::AlphaTexturePath);
					ConvertTexture(aiTextureType_SPECULAR, MaterialData::SpecularTexturePath, MaterialData::SpecularWrap);

					int iValue;
					if (aiGetMaterialInteger(aiMat, AI_MATKEY_TWOSIDED, &iValue))
						matData.SetParameter(MaterialData::FaceCulling, !iValue);

					matIt = materials.insert(std::make_pair(iMesh->mMaterialIndex, std::make_pair(materials.size(), std::move(matData)))).first;
				}

				subMesh->SetMaterialIndex(matIt->first);

				mesh->AddSubMesh(subMesh);
			}

			mesh->SetMaterialCount(std::max<unsigned int>(materials.size(), 1));
			for (const auto& pair : materials)
				mesh->SetMaterialData(pair.second.first, pair.second.second);
		}

		if (parameters.center)
			mesh->Recenter();
	}

	aiReleaseImport(scene);

	return true;
}

extern "C"
{
	NAZARA_EXPORT int PluginLoad()
	{
		Nz::MeshLoader::RegisterLoader(IsSupported, Check, Load);
		return 1;
	}

	NAZARA_EXPORT void PluginUnload()
	{
		Nz::MeshLoader::UnregisterLoader(IsSupported, Check, Load);
	}
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2013-2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#ifndef UNIQUE_PTR_H
#define UNIQUE_PTR_H

#include <tuple.hpp>
#include <algorithms.hpp>

namespace std {

template<typename T>
struct default_delete {
    constexpr default_delete() = default;

    constexpr default_delete(const default_delete&) {}

    void operator()(T* ptr) const {
        static_assert(sizeof(T) > 0, "Type must be complete");
        delete ptr;
    }
};

//Partial specialization for arrays
template<typename T>
struct default_delete<T[]> {
    constexpr default_delete() = default;

    constexpr default_delete(const default_delete&) {}

    void operator()(T* ptr) const {
        static_assert(sizeof(T) > 0, "Type must be complete");
        delete[] ptr;
    }
};

template <typename T, typename D = default_delete<T>>
class unique_ptr {
public:
    typedef T* pointer_type;
    typedef T element_type;
    typedef D deleter_type;

private:
    typedef tuple<pointer_type, deleter_type> data_impl;

    data_impl _data;

public:
    unique_ptr() : _data() {}
    unique_ptr(decltype(nullptr)) : unique_ptr() {}

    explicit unique_ptr(pointer_type p) : _data(make_tuple(p, deleter_type())) {}

    unique_ptr(unique_ptr&& u) : _data(make_tuple(u.release(), u.get_deleter())) {}
    unique_ptr& operator=(unique_ptr&& u){
        reset(u.release());
        get_deleter() = std::forward<deleter_type>(u.get_deleter());
        return *this;
    }

    ~unique_ptr(){
        reset();
    }

    // Disable copy
    unique_ptr(const unique_ptr& rhs) = delete;
    unique_ptr& operator=(const unique_ptr& rhs) = delete;

    unique_ptr& operator=(decltype(nullptr)){
        reset();
        return *this;
    }

    //Access

    element_type& operator*() const {
        return *get();
    }

    pointer_type operator->() const {
        return get();
    }

    pointer_type get() const {
        return std::get<0>(_data);
    }

    deleter_type& get_deleter(){
        return std::get<1>(_data);
    }

    const deleter_type& get_deleter() const {
        return std::get<1>(_data);
    }

    explicit operator bool() const {
        return get() == pointer_type() ? false : true;
    }

    pointer_type release(){
        pointer_type p = get();
        std::get<0>(_data) = pointer_type();
        return p;
    }

    void reset(pointer_type p = pointer_type()){
        if(get() != pointer_type()){
            get_deleter()(get());
        }

        std::get<0>(_data) = p;
    }
};

//Partial specialization for array
template <typename T, typename D>
class unique_ptr<T[], D> {
public:
    typedef T* pointer_type;
    typedef T element_type;
    typedef D deleter_type;

private:
    typedef tuple<pointer_type, deleter_type> data_impl;

    data_impl _data;

public:
    unique_ptr() : _data() {}
    unique_ptr(decltype(nullptr)) : unique_ptr() {}

    explicit unique_ptr(pointer_type p) : _data(make_tuple(p, deleter_type())) {}

    unique_ptr(unique_ptr&& u) : _data(make_tuple(u.release(), u.get_deleter())) {}
    unique_ptr& operator=(unique_ptr&& u){
        reset(u.release());
        get_deleter() = std::forward<deleter_type>(u.get_deleter());
        return *this;
    }

    ~unique_ptr(){
        reset();
    }

    // Disable copy
    unique_ptr(const unique_ptr& rhs) = delete;
    unique_ptr& operator=(const unique_ptr& rhs) = delete;

    unique_ptr& operator=(decltype(nullptr)){
        reset();
        return *this;
    }

    pointer_type get() const {
        return std::get<0>(_data);
    }

    deleter_type& get_deleter(){
        return std::get<1>(_data);
    }

    const deleter_type& get_deleter() const {
        return std::get<1>(_data);
    }

    element_type& operator[](size_t i) const {
        return get()[i];
    }

    explicit operator bool() const {
        return get() == pointer_type() ? false : true;
    }

    pointer_type release(){
        pointer_type p = get();
        std::get<0>(_data) = pointer_type();
        return p;
    }

    void reset(){
        reset(pointer_type());
    }

    void reset(pointer_type p){
        if(get() != pointer_type()){
            get_deleter()(get());
        }

        std::get<0>(_data) = p;
    }
};

static_assert(sizeof(unique_ptr<long>) == sizeof(long), "unique_ptr must have zero overhead with default deleter");
static_assert(sizeof(unique_ptr<long[]>) == sizeof(long), "unique_ptr must have zero overhead with default deleter");

} //end of namespace std

#endif
<commit_msg>Cleanup<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2013-2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#ifndef UNIQUE_PTR_H
#define UNIQUE_PTR_H

#include <tuple.hpp>
#include <algorithms.hpp>

namespace std {

template<typename T>
struct default_delete {
    constexpr default_delete() = default;

    constexpr default_delete(const default_delete&) {}

    void operator()(T* ptr) const {
        static_assert(sizeof(T) > 0, "Type must be complete");
        delete ptr;
    }
};

//Partial specialization for arrays
template<typename T>
struct default_delete<T[]> {
    constexpr default_delete() = default;

    constexpr default_delete(const default_delete&) {}

    void operator()(T* ptr) const {
        static_assert(sizeof(T) > 0, "Type must be complete");
        delete[] ptr;
    }
};

template <typename T, typename D = default_delete<T>>
class unique_ptr {
public:
    typedef T* pointer_type;
    typedef T element_type;
    typedef D deleter_type;

private:
    typedef tuple<pointer_type, deleter_type> data_impl;

    data_impl _data;

public:
    unique_ptr() : _data() {}
    unique_ptr(decltype(nullptr)) : unique_ptr() {}

    explicit unique_ptr(pointer_type p) : _data(make_tuple(p, deleter_type())) {}

    unique_ptr(unique_ptr&& u) : _data(make_tuple(u.release(), u.get_deleter())) {}
    unique_ptr& operator=(unique_ptr&& u){
        reset(u.release());
        get_deleter() = std::forward<deleter_type>(u.get_deleter());
        return *this;
    }

    ~unique_ptr(){
        reset();
    }

    // Disable copy
    unique_ptr(const unique_ptr& rhs) = delete;
    unique_ptr& operator=(const unique_ptr& rhs) = delete;

    unique_ptr& operator=(decltype(nullptr)){
        reset();
        return *this;
    }

    //Access

    element_type& operator*() const {
        return *get();
    }

    pointer_type operator->() const {
        return get();
    }

    pointer_type get() const {
        return std::get<0>(_data);
    }

    deleter_type& get_deleter(){
        return std::get<1>(_data);
    }

    const deleter_type& get_deleter() const {
        return std::get<1>(_data);
    }

    explicit operator bool() const {
        return get() == pointer_type() ? false : true;
    }

    pointer_type release(){
        pointer_type p = get();
        std::get<0>(_data) = pointer_type();
        return p;
    }

    void reset(pointer_type p = pointer_type()){
        if(get() != pointer_type()){
            get_deleter()(get());
        }

        std::get<0>(_data) = p;
    }
};

//Partial specialization for array
template <typename T, typename D>
class unique_ptr<T[], D> {
public:
    typedef T* pointer_type;
    typedef T element_type;
    typedef D deleter_type;

private:
    typedef tuple<pointer_type, deleter_type> data_impl;

    data_impl _data;

public:
    unique_ptr() : _data() {}
    unique_ptr(decltype(nullptr)) : unique_ptr() {}

    explicit unique_ptr(pointer_type p) : _data(make_tuple(p, deleter_type())) {}

    unique_ptr(unique_ptr&& u) : _data(make_tuple(u.release(), u.get_deleter())) {}
    unique_ptr& operator=(unique_ptr&& u){
        reset(u.release());
        get_deleter() = std::forward<deleter_type>(u.get_deleter());
        return *this;
    }

    ~unique_ptr(){
        reset();
    }

    // Disable copy
    unique_ptr(const unique_ptr& rhs) = delete;
    unique_ptr& operator=(const unique_ptr& rhs) = delete;

    unique_ptr& operator=(decltype(nullptr)){
        reset();
        return *this;
    }

    pointer_type get() const {
        return std::get<0>(_data);
    }

    deleter_type& get_deleter(){
        return std::get<1>(_data);
    }

    const deleter_type& get_deleter() const {
        return std::get<1>(_data);
    }

    element_type& operator[](size_t i) const {
        return get()[i];
    }

    explicit operator bool() const {
        return get() == pointer_type() ? false : true;
    }

    pointer_type release(){
        pointer_type p = get();
        std::get<0>(_data) = pointer_type();
        return p;
    }

    void reset(){
        reset(pointer_type());
    }

    void reset(pointer_type p){
        auto tmp = get();
        std::get<0>(_data) = p;
        if(tmp){
            get_deleter()(tmp);
        }
    }
};

static_assert(sizeof(unique_ptr<long>) == sizeof(long), "unique_ptr must have zero overhead with default deleter");
static_assert(sizeof(unique_ptr<long[]>) == sizeof(long), "unique_ptr must have zero overhead with default deleter");

} //end of namespace std

#endif
<|endoftext|>


<commit_before>//===----------------------------------------------------------------------===//
//
//                         Peloton
//
// selectivity.cpp
//
// Identification: src/optimizer/stats/selectivity.cpp
//
// Copyright (c) 2015-16, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "optimizer/stats/selectivity.h"

#include "catalog/table_catalog.h"
#include "concurrency/transaction_manager_factory.h"
#include "function/string_functions.h"

namespace peloton {
namespace optimizer {

double Selectivity::ComputeSelectivity(const std::shared_ptr<TableStats> &stats,
                                       const ValueCondition &condition) {
  switch (condition.type) {
    case ExpressionType::COMPARE_LESSTHAN:
      return LessThan(stats, condition);
    case ExpressionType::COMPARE_GREATERTHAN:
      return GreaterThan(stats, condition);
    case ExpressionType::COMPARE_LESSTHANOREQUALTO:
      return LessThanOrEqualTo(stats, condition);
    case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
      return GreaterThanOrEqualTo(stats, condition);
    case ExpressionType::COMPARE_EQUAL:
      return Equal(stats, condition);
    case ExpressionType::COMPARE_NOTEQUAL:
      return NotEqual(stats, condition);
    case ExpressionType::COMPARE_LIKE:
      return Like(stats, condition);
    case ExpressionType::COMPARE_NOTLIKE:
      return NotLike(stats, condition);
    case ExpressionType::COMPARE_IN:
      return In(stats, condition);
    case ExpressionType::COMPARE_DISTINCT_FROM:
      return DistinctFrom(stats, condition);
    default:
      LOG_WARN("Expression type %s not supported for computing selectivity",
               ExpressionTypeToString(condition.type).c_str());
      return DEFAULT_SELECTIVITY;
  }
}

double Selectivity::LessThan(const std::shared_ptr<TableStats> &table_stats,
                             const ValueCondition &condition) {
  // Convert peloton value type to raw value (double)
  double v = StatsUtil::PelotonValueToNumericValue(condition.value);
  if (std::isnan(v)) {
    LOG_TRACE("Error computing less than for non-numeric type");
    return DEFAULT_SELECTIVITY;
  }
  // TODO: make sure condition uses column id. check if column name is not
  // empty.
  std::shared_ptr<ColumnStats> column_stats =
      table_stats->GetColumnStats(condition.column_name);
  // Return default selectivity if no column stats for given column_id
  if (column_stats == nullptr) {
    return DEFAULT_SELECTIVITY;
  }
  // Use histogram to estimate selectivity
  std::vector<double> histogram = column_stats->histogram_bounds;
  size_t n = histogram.size();
  PL_ASSERT(n > 0);
  // find correspond bin using binary search
  auto it = std::lower_bound(histogram.begin(), histogram.end(), v);
  double res = (it - histogram.begin()) * 1.0 / n;
  PL_ASSERT(res >= 0);
  PL_ASSERT(res <= 1);
  return res;
}

double Selectivity::Equal(const std::shared_ptr<TableStats> &table_stats,
                          const ValueCondition &condition) {
  double value = StatsUtil::PelotonValueToNumericValue(condition.value);
  auto column_stats = table_stats->GetColumnStats(condition.column_name);
  //  LOG_INFO("column name %s", condition.column_name);
  if (std::isnan(value) || column_stats == nullptr) {
    LOG_DEBUG("Calculate selectivity: return null");
    return DEFAULT_SELECTIVITY;
  }

  size_t numrows = column_stats->num_rows;
  // For now only double is supported in stats storage
  std::vector<double> most_common_vals = column_stats->most_common_vals;
  std::vector<double> most_common_freqs = column_stats->most_common_freqs;
  std::vector<double>::iterator first = most_common_vals.begin(),
                                last = most_common_vals.end();

  while (first != last) {
    // For now only double is supported in stats storage
    if (*first == value) {
      break;
    }
    ++first;
  }

  double res = DEFAULT_SELECTIVITY;
  if (first != last) {
    // the target value for equality comparison (param value) is
    // found in most common values
    size_t idx = first - most_common_vals.begin();

    res = most_common_freqs[idx] / (double)numrows;
  } else {
    // the target value for equality comparison (parm value) is
    // NOT found in most common values
    // (1 - sum(mvf))/(num_distinct - num_mcv)
    double sum_mvf = 0;
    std::vector<double>::iterator first = most_common_freqs.begin(),
                                  last = most_common_freqs.end();
    while (first != last) {
      sum_mvf += *first;
      ++first;
    }

    if (numrows == 0 || column_stats->cardinality == most_common_vals.size()) {
      LOG_TRACE("Equal selectivity division by 0.");
      return DEFAULT_SELECTIVITY;
    }

    res = (1 - sum_mvf / (double)numrows) /
          (column_stats->cardinality - most_common_vals.size());
  }
  PL_ASSERT(res >= 0);
  PL_ASSERT(res <= 1);
  return res;
}

// Selectivity for 'LIKE' operator. The column type must be VARCHAR.
// Complete implementation once we support LIKE operator.
double Selectivity::Like(const std::shared_ptr<TableStats> &table_stats,
                         const ValueCondition &condition) {
  // Check whether column type is VARCHAR.
  if ((condition.value).GetTypeId() != type::TypeId::VARCHAR) {
    return DEFAULT_SELECTIVITY;
  }

  const char *pattern = (condition.value).GetData();

  auto column_stats = table_stats->GetColumnStats(condition.column_name);
  if (column_stats == nullptr) {
    return DEFAULT_SELECTIVITY;
  }
  oid_t column_id = column_stats->column_id;

  size_t matched_count = 0;
  size_t total_count = 0;

  // Sample on the fly
  auto sampler = table_stats->GetSampler();
  PL_ASSERT(sampler != nullptr);
  if (sampler->GetSampledTuples().empty()) {
    sampler->AcquireSampleTuples(DEFAULT_SAMPLE_SIZE);
  }
  auto &sample_tuples = sampler->GetSampledTuples();
  for (size_t i = 0; i < sample_tuples.size(); i++) {
    auto value = sample_tuples[i]->GetValue(column_id);
    PL_ASSERT(value.GetTypeId() == type::TypeId::VARCHAR);
    if (function::StringFunctions::Like(value.GetData(), value.GetLength(),
                                        pattern,
                                        condition.value.GetLength())) {
      matched_count++;
    }
  }
  total_count = sample_tuples.size();
  LOG_TRACE("total sample size %lu matched tupe %lu", total_count, matched_count);

  return total_count == 0 ? DEFAULT_SELECTIVITY
                          : (double)matched_count / total_count;
}

}  // namespace optimizer
}  // namespace peloton
<commit_msg>Fix build error++<commit_after>//===----------------------------------------------------------------------===//
//
//                         Peloton
//
// selectivity.cpp
//
// Identification: src/optimizer/stats/selectivity.cpp
//
// Copyright (c) 2015-16, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "optimizer/stats/selectivity.h"

#include "catalog/table_catalog.h"
#include "concurrency/transaction_manager_factory.h"
#include "executor/executor_context.h"
#include "function/string_functions.h"

namespace peloton {
namespace optimizer {

double Selectivity::ComputeSelectivity(const std::shared_ptr<TableStats> &stats,
                                       const ValueCondition &condition) {
  switch (condition.type) {
    case ExpressionType::COMPARE_LESSTHAN:
      return LessThan(stats, condition);
    case ExpressionType::COMPARE_GREATERTHAN:
      return GreaterThan(stats, condition);
    case ExpressionType::COMPARE_LESSTHANOREQUALTO:
      return LessThanOrEqualTo(stats, condition);
    case ExpressionType::COMPARE_GREATERTHANOREQUALTO:
      return GreaterThanOrEqualTo(stats, condition);
    case ExpressionType::COMPARE_EQUAL:
      return Equal(stats, condition);
    case ExpressionType::COMPARE_NOTEQUAL:
      return NotEqual(stats, condition);
    case ExpressionType::COMPARE_LIKE:
      return Like(stats, condition);
    case ExpressionType::COMPARE_NOTLIKE:
      return NotLike(stats, condition);
    case ExpressionType::COMPARE_IN:
      return In(stats, condition);
    case ExpressionType::COMPARE_DISTINCT_FROM:
      return DistinctFrom(stats, condition);
    default:
      LOG_WARN("Expression type %s not supported for computing selectivity",
               ExpressionTypeToString(condition.type).c_str());
      return DEFAULT_SELECTIVITY;
  }
}

double Selectivity::LessThan(const std::shared_ptr<TableStats> &table_stats,
                             const ValueCondition &condition) {
  // Convert peloton value type to raw value (double)
  double v = StatsUtil::PelotonValueToNumericValue(condition.value);
  if (std::isnan(v)) {
    LOG_TRACE("Error computing less than for non-numeric type");
    return DEFAULT_SELECTIVITY;
  }
  // TODO: make sure condition uses column id. check if column name is not
  // empty.
  std::shared_ptr<ColumnStats> column_stats =
      table_stats->GetColumnStats(condition.column_name);
  // Return default selectivity if no column stats for given column_id
  if (column_stats == nullptr) {
    return DEFAULT_SELECTIVITY;
  }
  // Use histogram to estimate selectivity
  std::vector<double> histogram = column_stats->histogram_bounds;
  size_t n = histogram.size();
  PL_ASSERT(n > 0);
  // find correspond bin using binary search
  auto it = std::lower_bound(histogram.begin(), histogram.end(), v);
  double res = (it - histogram.begin()) * 1.0 / n;
  PL_ASSERT(res >= 0);
  PL_ASSERT(res <= 1);
  return res;
}

double Selectivity::Equal(const std::shared_ptr<TableStats> &table_stats,
                          const ValueCondition &condition) {
  double value = StatsUtil::PelotonValueToNumericValue(condition.value);
  auto column_stats = table_stats->GetColumnStats(condition.column_name);
  //  LOG_INFO("column name %s", condition.column_name);
  if (std::isnan(value) || column_stats == nullptr) {
    LOG_DEBUG("Calculate selectivity: return null");
    return DEFAULT_SELECTIVITY;
  }

  size_t numrows = column_stats->num_rows;
  // For now only double is supported in stats storage
  std::vector<double> most_common_vals = column_stats->most_common_vals;
  std::vector<double> most_common_freqs = column_stats->most_common_freqs;
  std::vector<double>::iterator first = most_common_vals.begin(),
                                last = most_common_vals.end();

  while (first != last) {
    // For now only double is supported in stats storage
    if (*first == value) {
      break;
    }
    ++first;
  }

  double res = DEFAULT_SELECTIVITY;
  if (first != last) {
    // the target value for equality comparison (param value) is
    // found in most common values
    size_t idx = first - most_common_vals.begin();

    res = most_common_freqs[idx] / (double)numrows;
  } else {
    // the target value for equality comparison (parm value) is
    // NOT found in most common values
    // (1 - sum(mvf))/(num_distinct - num_mcv)
    double sum_mvf = 0;
    std::vector<double>::iterator first = most_common_freqs.begin(),
                                  last = most_common_freqs.end();
    while (first != last) {
      sum_mvf += *first;
      ++first;
    }

    if (numrows == 0 || column_stats->cardinality == most_common_vals.size()) {
      LOG_TRACE("Equal selectivity division by 0.");
      return DEFAULT_SELECTIVITY;
    }

    res = (1 - sum_mvf / (double)numrows) /
          (column_stats->cardinality - most_common_vals.size());
  }
  PL_ASSERT(res >= 0);
  PL_ASSERT(res <= 1);
  return res;
}

// Selectivity for 'LIKE' operator. The column type must be VARCHAR.
// Complete implementation once we support LIKE operator.
double Selectivity::Like(const std::shared_ptr<TableStats> &table_stats,
                         const ValueCondition &condition) {
  // Check whether column type is VARCHAR.
  if ((condition.value).GetTypeId() != type::TypeId::VARCHAR) {
    return DEFAULT_SELECTIVITY;
  }

  const char *pattern = (condition.value).GetData();

  auto column_stats = table_stats->GetColumnStats(condition.column_name);
  if (column_stats == nullptr) {
    return DEFAULT_SELECTIVITY;
  }
  oid_t column_id = column_stats->column_id;

  size_t matched_count = 0;
  size_t total_count = 0;

  // Sample on the fly
  auto sampler = table_stats->GetSampler();
  PL_ASSERT(sampler != nullptr);
  if (sampler->GetSampledTuples().empty()) {
    sampler->AcquireSampleTuples(DEFAULT_SAMPLE_SIZE);
  }
  auto &sample_tuples = sampler->GetSampledTuples();
  for (size_t i = 0; i < sample_tuples.size(); i++) {
    auto value = sample_tuples[i]->GetValue(column_id);
    PL_ASSERT(value.GetTypeId() == type::TypeId::VARCHAR);
    executor::ExecutorContext dummy_context(nullptr);
    if (function::StringFunctions::Like(dummy_context, value.GetData(),
                                        value.GetLength(), pattern,
                                        condition.value.GetLength())) {
      matched_count++;
    }
  }
  total_count = sample_tuples.size();
  LOG_TRACE("total sample size %lu matched tupe %lu", total_count,
            matched_count);

  return total_count == 0 ? DEFAULT_SELECTIVITY
                          : (double)matched_count / total_count;
}

}  // namespace optimizer
}  // namespace peloton
<|endoftext|>


<commit_before>#include <getopt.h>
#include <locale.h>

#include <charconv>
#include <iostream>

#include "auracle/auracle.hh"
#include "auracle/sort.hh"
#include "auracle/terminal.hh"

constexpr char kAurBaseurl[] = "https://aur.archlinux.org";
constexpr char kPacmanConf[] = "/etc/pacman.conf";

struct Flags {
  std::string baseurl = kAurBaseurl;
  std::string pacman_config = kPacmanConf;
  int max_connections = 5;
  int connect_timeout = 10;
  terminal::WantColor color = terminal::WantColor::AUTO;

  auracle::Auracle::CommandOptions command_options;
};

__attribute__((noreturn)) void usage(void) {
  fputs(
      "auracle [options] command\n"
      "\n"
      "Query the AUR or download snapshots of packages.\n"
      "\n"
      "  -h, --help               Show this help\n"
      "      --version            Show software version\n"
      "\n"
      "  -q, --quiet              Output less, when possible\n"
      "  -r, --recurse            Recurse through dependencies on download\n"
      "      --literal            Disallow regex in searches\n"
      "      --searchby=BY        Change search-by dimension\n"
      "      --connect-timeout=N  Set connection timeout in seconds\n"
      "      --max-connections=N  Limit active connections\n"
      "      --color=WHEN         One of 'auto', 'never', or 'always'\n"
      "      --sort=KEY           Sort results in ascending order by KEY\n"
      "      --rsort=KEY          Sort results in descending order by KEY\n"
      "  -C DIR, --chdir=DIR      Change directory to DIR before downloading\n"
      "\n"
      "Commands:\n"
      "  buildorder               Show build order\n"
      "  clone                    Clone or update git repos for packages\n"
      "  download                 Download tarball snapshots\n"
      "  info                     Show detailed information\n"
      "  pkgbuild                 Show PKGBUILDs\n"
      "  search                   Search for packages\n"
      "  sync                     Check for updates for foreign packages\n"
      "  rawinfo                  Dump unformatted JSON for info query\n"
      "  rawsearch                Dump unformatted JSON for search query\n",
      stdout);
  exit(0);
}

__attribute__((noreturn)) void version(void) {
  std::cout << "auracle " << PACKAGE_VERSION << "\n";
  exit(0);
}

bool ParseFlags(int* argc, char*** argv, Flags* flags) {
  enum {
    ARG_COLOR = 1000,
    ARG_CONNECT_TIMEOUT,
    ARG_LITERAL,
    ARG_MAX_CONNECTIONS,
    ARG_SEARCHBY,
    ARG_VERSION,
    ARG_BASEURL,
    ARG_SORT,
    ARG_RSORT,
    ARG_PACMAN_CONFIG,
  };

  static constexpr struct option opts[] = {
      // clang-format off
      { "help",              no_argument,       0, 'h' },
      { "quiet",             no_argument,       0, 'q' },
      { "recurse",           no_argument,       0, 'r' },

      { "chdir",             required_argument, 0, 'C' },
      { "color",             required_argument, 0, ARG_COLOR },
      { "connect-timeout",   required_argument, 0, ARG_CONNECT_TIMEOUT },
      { "literal",           no_argument,       0, ARG_LITERAL },
      { "max-connections",   required_argument, 0, ARG_MAX_CONNECTIONS },
      { "rsort",             required_argument, 0, ARG_RSORT },
      { "searchby",          required_argument, 0, ARG_SEARCHBY },
      { "sort",              required_argument, 0, ARG_SORT },
      { "version",           no_argument,       0, ARG_VERSION },
      { "baseurl",           required_argument, 0, ARG_BASEURL },
      { "pacmanconfig",      required_argument, 0, ARG_PACMAN_CONFIG },
      {},
      // clang-format on
  };

  const auto stoi = [](std::string_view s, int* i) -> bool {
    return std::from_chars(s.data(), s.data() + s.size(), *i).ec == std::errc{};
  };

  int opt;
  while ((opt = getopt_long(*argc, *argv, "C:hqr", opts, nullptr)) != -1) {
    std::string_view sv_optarg(optarg);

    switch (opt) {
      case 'h':
        usage();
      case 'q':
        flags->command_options.quiet = true;
        break;
      case 'r':
        flags->command_options.recurse = true;
        break;
      case 'C':
        if (sv_optarg.empty()) {
          std::cerr << "error: meaningless option: -C ''\n";
          return false;
        }
        flags->command_options.directory = optarg;
        break;
      case ARG_LITERAL:
        flags->command_options.allow_regex = false;
        break;
      case ARG_SEARCHBY:
        using SearchBy = aur::SearchRequest::SearchBy;

        flags->command_options.search_by =
            aur::SearchRequest::ParseSearchBy(sv_optarg);
        if (flags->command_options.search_by == SearchBy::INVALID) {
          std::cerr << "error: invalid arg to --searchby: " << sv_optarg
                    << "\n";
          return false;
        }
        break;
      case ARG_CONNECT_TIMEOUT:
        if (!stoi(sv_optarg, &flags->connect_timeout) ||
            flags->max_connections < 0) {
          std::cerr << "error: invalid value to --connect-timeout: "
                    << sv_optarg << "\n";
          return false;
        }
        break;
      case ARG_MAX_CONNECTIONS:
        if (!stoi(sv_optarg, &flags->max_connections) ||
            flags->max_connections < 0) {
          std::cerr << "error: invalid value to --max-connections: "
                    << sv_optarg << "\n";
          return false;
        }
        break;
      case ARG_COLOR:
        if (sv_optarg == "auto") {
          flags->color = terminal::WantColor::AUTO;
        } else if (sv_optarg == "never") {
          flags->color = terminal::WantColor::NO;
        } else if (sv_optarg == "always") {
          flags->color = terminal::WantColor::YES;
        } else {
          std::cerr << "error: invalid arg to --color: " << sv_optarg << "\n";
          return false;
        }
        break;
      case ARG_SORT:
        flags->command_options.sorter =
            sort::MakePackageSorter(sv_optarg, sort::OrderBy::ORDER_ASC);
        if (flags->command_options.sorter == nullptr) {
          std::cerr << "error: invalid arg to --sort: " << sv_optarg << "\n";
        }
        break;
      case ARG_RSORT:
        flags->command_options.sorter =
            sort::MakePackageSorter(sv_optarg, sort::OrderBy::ORDER_DESC);
        if (flags->command_options.sorter == nullptr) {
          std::cerr << "error: invalid arg to --rsort: " << sv_optarg << "\n";
        }
        break;
      case ARG_BASEURL:
        flags->baseurl = std::string(sv_optarg);
        break;
      case ARG_PACMAN_CONFIG:
        flags->pacman_config = std::string(sv_optarg);
        break;
      case ARG_VERSION:
        version();
        break;
      default:
        return false;
    }
  }

  *argc -= optind - 1;
  *argv += optind - 1;

  return true;
}

int main(int argc, char** argv) {
  Flags flags;
  if (!ParseFlags(&argc, &argv, &flags)) {
    return 1;
  }

  if (argc < 2) {
    std::cerr << "error: no operation specified (use -h for help)\n";
    return 1;
  }

  setlocale(LC_ALL, "");
  terminal::Init(flags.color);

  const auto pacman = auracle::Pacman::NewFromConfig(flags.pacman_config);
  if (pacman == nullptr) {
    std::cerr << "error: failed to parse " << flags.pacman_config << "\n";
    return 1;
  }

  auracle::Auracle auracle(auracle::Auracle::Options()
                               .set_aur_baseurl(flags.baseurl)
                               .set_pacman(pacman.get())
                               .set_connection_timeout(flags.connect_timeout)
                               .set_max_connections(flags.max_connections));

  std::string_view action(argv[1]);
  std::vector<std::string> args(argv + 2, argv + argc);

  std::unordered_map<std::string_view,
                     int (auracle::Auracle::*)(
                         const std::vector<std::string>& args,
                         const auracle::Auracle::CommandOptions& options)>
      cmds{
          {"buildorder", &auracle::Auracle::BuildOrder},
          {"clone", &auracle::Auracle::Clone},
          {"download", &auracle::Auracle::Download},
          {"info", &auracle::Auracle::Info},
          {"pkgbuild", &auracle::Auracle::Pkgbuild},
          {"rawinfo", &auracle::Auracle::RawInfo},
          {"rawsearch", &auracle::Auracle::RawSearch},
          {"search", &auracle::Auracle::Search},
          {"sync", &auracle::Auracle::Sync},
      };

  if (auto iter = cmds.find(action); iter != cmds.end()) {
    return (auracle.*iter->second)(args, flags.command_options) < 0 ? 1 : 0;
  }

  std::cerr << "Unknown action " << action << "\n";
  return 1;
}

/* vim: set et ts=2 sw=2: */
<commit_msg>Add missing error return on bad value to --sort/--rsort<commit_after>#include <getopt.h>
#include <locale.h>

#include <charconv>
#include <iostream>

#include "auracle/auracle.hh"
#include "auracle/sort.hh"
#include "auracle/terminal.hh"

constexpr char kAurBaseurl[] = "https://aur.archlinux.org";
constexpr char kPacmanConf[] = "/etc/pacman.conf";

struct Flags {
  std::string baseurl = kAurBaseurl;
  std::string pacman_config = kPacmanConf;
  int max_connections = 5;
  int connect_timeout = 10;
  terminal::WantColor color = terminal::WantColor::AUTO;

  auracle::Auracle::CommandOptions command_options;
};

__attribute__((noreturn)) void usage(void) {
  fputs(
      "auracle [options] command\n"
      "\n"
      "Query the AUR or download snapshots of packages.\n"
      "\n"
      "  -h, --help               Show this help\n"
      "      --version            Show software version\n"
      "\n"
      "  -q, --quiet              Output less, when possible\n"
      "  -r, --recurse            Recurse through dependencies on download\n"
      "      --literal            Disallow regex in searches\n"
      "      --searchby=BY        Change search-by dimension\n"
      "      --connect-timeout=N  Set connection timeout in seconds\n"
      "      --max-connections=N  Limit active connections\n"
      "      --color=WHEN         One of 'auto', 'never', or 'always'\n"
      "      --sort=KEY           Sort results in ascending order by KEY\n"
      "      --rsort=KEY          Sort results in descending order by KEY\n"
      "  -C DIR, --chdir=DIR      Change directory to DIR before downloading\n"
      "\n"
      "Commands:\n"
      "  buildorder               Show build order\n"
      "  clone                    Clone or update git repos for packages\n"
      "  download                 Download tarball snapshots\n"
      "  info                     Show detailed information\n"
      "  pkgbuild                 Show PKGBUILDs\n"
      "  search                   Search for packages\n"
      "  sync                     Check for updates for foreign packages\n"
      "  rawinfo                  Dump unformatted JSON for info query\n"
      "  rawsearch                Dump unformatted JSON for search query\n",
      stdout);
  exit(0);
}

__attribute__((noreturn)) void version(void) {
  std::cout << "auracle " << PACKAGE_VERSION << "\n";
  exit(0);
}

bool ParseFlags(int* argc, char*** argv, Flags* flags) {
  enum {
    ARG_COLOR = 1000,
    ARG_CONNECT_TIMEOUT,
    ARG_LITERAL,
    ARG_MAX_CONNECTIONS,
    ARG_SEARCHBY,
    ARG_VERSION,
    ARG_BASEURL,
    ARG_SORT,
    ARG_RSORT,
    ARG_PACMAN_CONFIG,
  };

  static constexpr struct option opts[] = {
      // clang-format off
      { "help",              no_argument,       0, 'h' },
      { "quiet",             no_argument,       0, 'q' },
      { "recurse",           no_argument,       0, 'r' },

      { "chdir",             required_argument, 0, 'C' },
      { "color",             required_argument, 0, ARG_COLOR },
      { "connect-timeout",   required_argument, 0, ARG_CONNECT_TIMEOUT },
      { "literal",           no_argument,       0, ARG_LITERAL },
      { "max-connections",   required_argument, 0, ARG_MAX_CONNECTIONS },
      { "rsort",             required_argument, 0, ARG_RSORT },
      { "searchby",          required_argument, 0, ARG_SEARCHBY },
      { "sort",              required_argument, 0, ARG_SORT },
      { "version",           no_argument,       0, ARG_VERSION },
      { "baseurl",           required_argument, 0, ARG_BASEURL },
      { "pacmanconfig",      required_argument, 0, ARG_PACMAN_CONFIG },
      {},
      // clang-format on
  };

  const auto stoi = [](std::string_view s, int* i) -> bool {
    return std::from_chars(s.data(), s.data() + s.size(), *i).ec == std::errc{};
  };

  int opt;
  while ((opt = getopt_long(*argc, *argv, "C:hqr", opts, nullptr)) != -1) {
    std::string_view sv_optarg(optarg);

    switch (opt) {
      case 'h':
        usage();
      case 'q':
        flags->command_options.quiet = true;
        break;
      case 'r':
        flags->command_options.recurse = true;
        break;
      case 'C':
        if (sv_optarg.empty()) {
          std::cerr << "error: meaningless option: -C ''\n";
          return false;
        }
        flags->command_options.directory = optarg;
        break;
      case ARG_LITERAL:
        flags->command_options.allow_regex = false;
        break;
      case ARG_SEARCHBY:
        using SearchBy = aur::SearchRequest::SearchBy;

        flags->command_options.search_by =
            aur::SearchRequest::ParseSearchBy(sv_optarg);
        if (flags->command_options.search_by == SearchBy::INVALID) {
          std::cerr << "error: invalid arg to --searchby: " << sv_optarg
                    << "\n";
          return false;
        }
        break;
      case ARG_CONNECT_TIMEOUT:
        if (!stoi(sv_optarg, &flags->connect_timeout) ||
            flags->max_connections < 0) {
          std::cerr << "error: invalid value to --connect-timeout: "
                    << sv_optarg << "\n";
          return false;
        }
        break;
      case ARG_MAX_CONNECTIONS:
        if (!stoi(sv_optarg, &flags->max_connections) ||
            flags->max_connections < 0) {
          std::cerr << "error: invalid value to --max-connections: "
                    << sv_optarg << "\n";
          return false;
        }
        break;
      case ARG_COLOR:
        if (sv_optarg == "auto") {
          flags->color = terminal::WantColor::AUTO;
        } else if (sv_optarg == "never") {
          flags->color = terminal::WantColor::NO;
        } else if (sv_optarg == "always") {
          flags->color = terminal::WantColor::YES;
        } else {
          std::cerr << "error: invalid arg to --color: " << sv_optarg << "\n";
          return false;
        }
        break;
      case ARG_SORT:
        flags->command_options.sorter =
            sort::MakePackageSorter(sv_optarg, sort::OrderBy::ORDER_ASC);
        if (flags->command_options.sorter == nullptr) {
          std::cerr << "error: invalid arg to --sort: " << sv_optarg << "\n";
          return false;
        }
        break;
      case ARG_RSORT:
        flags->command_options.sorter =
            sort::MakePackageSorter(sv_optarg, sort::OrderBy::ORDER_DESC);
        if (flags->command_options.sorter == nullptr) {
          std::cerr << "error: invalid arg to --rsort: " << sv_optarg << "\n";
          return false;
        }
        break;
      case ARG_BASEURL:
        flags->baseurl = std::string(sv_optarg);
        break;
      case ARG_PACMAN_CONFIG:
        flags->pacman_config = std::string(sv_optarg);
        break;
      case ARG_VERSION:
        version();
        break;
      default:
        return false;
    }
  }

  *argc -= optind - 1;
  *argv += optind - 1;

  return true;
}

int main(int argc, char** argv) {
  Flags flags;
  if (!ParseFlags(&argc, &argv, &flags)) {
    return 1;
  }

  if (argc < 2) {
    std::cerr << "error: no operation specified (use -h for help)\n";
    return 1;
  }

  setlocale(LC_ALL, "");
  terminal::Init(flags.color);

  const auto pacman = auracle::Pacman::NewFromConfig(flags.pacman_config);
  if (pacman == nullptr) {
    std::cerr << "error: failed to parse " << flags.pacman_config << "\n";
    return 1;
  }

  auracle::Auracle auracle(auracle::Auracle::Options()
                               .set_aur_baseurl(flags.baseurl)
                               .set_pacman(pacman.get())
                               .set_connection_timeout(flags.connect_timeout)
                               .set_max_connections(flags.max_connections));

  std::string_view action(argv[1]);
  std::vector<std::string> args(argv + 2, argv + argc);

  std::unordered_map<std::string_view,
                     int (auracle::Auracle::*)(
                         const std::vector<std::string>& args,
                         const auracle::Auracle::CommandOptions& options)>
      cmds{
          {"buildorder", &auracle::Auracle::BuildOrder},
          {"clone", &auracle::Auracle::Clone},
          {"download", &auracle::Auracle::Download},
          {"info", &auracle::Auracle::Info},
          {"pkgbuild", &auracle::Auracle::Pkgbuild},
          {"rawinfo", &auracle::Auracle::RawInfo},
          {"rawsearch", &auracle::Auracle::RawSearch},
          {"search", &auracle::Auracle::Search},
          {"sync", &auracle::Auracle::Sync},
      };

  if (auto iter = cmds.find(action); iter != cmds.end()) {
    return (auracle.*iter->second)(args, flags.command_options) < 0 ? 1 : 0;
  }

  std::cerr << "Unknown action " << action << "\n";
  return 1;
}

/* vim: set et ts=2 sw=2: */
<|endoftext|>


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

#ifndef MAPNIK_TIMER_INCLUDED
#define MAPNIK_TIMER_INCLUDED

#include <cstdlib>
#include <string>
#include <sys/time.h>

namespace mapnik {

// Measure times in both wall clock time and CPU times. Results are returned in milliseconds.
class timer
{
public:
    timer()
    {
        restart();
    }

    void restart()
    {
        _stopped = false;
        gettimeofday(&_wall_clock_start, NULL);
        _cpu_start = clock();
    }

    virtual void stop() const
    {
        _stopped = true;
        _cpu_end = clock();
        gettimeofday(&_wall_clock_end, NULL);
    }

    double cpu_elapsed() const
    {
        // return elapsed CPU time in ms
        if (!_stopped)
            stop();

        return ((double) (_cpu_end - _cpu_start)) / CLOCKS_PER_SEC * 1000.0;
    }

    double wall_clock_elapsed() const
    {
        // return elapsed wall clock time in ms
        if (!_stopped)
            stop();

        long seconds  = _wall_clock_end.tv_sec  - _wall_clock_start.tv_sec;
        long useconds = _wall_clock_end.tv_usec - _wall_clock_start.tv_usec;

        return ((seconds) * 1000 + useconds / 1000.0) + 0.5;
    }
protected:
    mutable timeval _wall_clock_start, _wall_clock_end;
    mutable clock_t _cpu_start, _cpu_end;
    mutable bool _stopped;
};

//  A progress_timer behaves like a timer except that the destructor displays
//  an elapsed time message at an appropriate place in an appropriate form.
class progress_timer : public timer
{
public:
    progress_timer(std::ostream & os, std::string const& base_message):
      os_(os),
      base_message_(base_message)
      {}

    ~progress_timer()
    {
        if (!_stopped)
            stop();
    }

    void stop() const
    {
        timer::stop();
        try
        {
            std::ostringstream s;
            s.precision(2);
            s << std::fixed;
            s << wall_clock_elapsed() << "ms (cpu " << cpu_elapsed() << "ms)";
            s << std::setw(30 - (int)s.tellp()) << std::right << "| " << base_message_ << "\n";
            os_ << s.str();
        }
        catch (...) {} // eat any exceptions
    }

    void discard() {
        _stopped = true;
    }

private:
    std::ostream & os_;
    std::string base_message_;
};

};
#endif // MAPNIK_TIMER_INCLUDED<commit_msg>more includes for timer<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_TIMER_INCLUDED
#define MAPNIK_TIMER_INCLUDED

#include <cstdlib>
#include <string>
#include <sstream>
#include <iomanip>
#include <sys/time.h>

namespace mapnik {

// Measure times in both wall clock time and CPU times. Results are returned in milliseconds.
class timer
{
public:
    timer()
    {
        restart();
    }

    void restart()
    {
        _stopped = false;
        gettimeofday(&_wall_clock_start, NULL);
        _cpu_start = clock();
    }

    virtual void stop() const
    {
        _stopped = true;
        _cpu_end = clock();
        gettimeofday(&_wall_clock_end, NULL);
    }

    double cpu_elapsed() const
    {
        // return elapsed CPU time in ms
        if (!_stopped)
            stop();

        return ((double) (_cpu_end - _cpu_start)) / CLOCKS_PER_SEC * 1000.0;
    }

    double wall_clock_elapsed() const
    {
        // return elapsed wall clock time in ms
        if (!_stopped)
            stop();

        long seconds  = _wall_clock_end.tv_sec  - _wall_clock_start.tv_sec;
        long useconds = _wall_clock_end.tv_usec - _wall_clock_start.tv_usec;

        return ((seconds) * 1000 + useconds / 1000.0) + 0.5;
    }
protected:
    mutable timeval _wall_clock_start, _wall_clock_end;
    mutable clock_t _cpu_start, _cpu_end;
    mutable bool _stopped;
};

//  A progress_timer behaves like a timer except that the destructor displays
//  an elapsed time message at an appropriate place in an appropriate form.
class progress_timer : public timer
{
public:
    progress_timer(std::ostream & os, std::string const& base_message):
      os_(os),
      base_message_(base_message)
      {}

    ~progress_timer()
    {
        if (!_stopped)
            stop();
    }

    void stop() const
    {
        timer::stop();
        try
        {
            std::ostringstream s;
            s.precision(2);
            s << std::fixed;
            s << wall_clock_elapsed() << "ms (cpu " << cpu_elapsed() << "ms)";
            s << std::setw(30 - (int)s.tellp()) << std::right << "| " << base_message_ << "\n";
            os_ << s.str();
        }
        catch (...) {} // eat any exceptions
    }

    void discard() {
        _stopped = true;
    }

private:
    std::ostream & os_;
    std::string base_message_;
};

};
#endif // MAPNIK_TIMER_INCLUDED<|endoftext|>


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

#include <poll.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>

#include "gtest/gtest.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/test_util.h"

namespace gvisor {
namespace testing {

// Test fixture for tests that apply to pairs of connected sockets.
using ConnectStressTest = SocketPairTest;

TEST_P(ConnectStressTest, Reset65kTimes) {
  for (int i = 0; i < 1 << 16; ++i) {
    auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

    // Send some data to ensure that the connection gets reset and the port gets
    // released immediately. This avoids either end entering TIME-WAIT.
    char sent_data[100] = {};
    ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)),
                SyscallSucceedsWithValue(sizeof(sent_data)));
    // Poll the other FD to make sure that the data is in the receive buffer
    // before closing it to ensure a RST is triggered.
    const int kTimeout = 10000;
    struct pollfd pfd = {
        .fd = sockets->second_fd(),
        .events = POLL_IN,
    };
    ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
  }
}

INSTANTIATE_TEST_SUITE_P(
    AllConnectedSockets, ConnectStressTest,
    ::testing::Values(IPv6UDPBidirectionalBindSocketPair(0),
                      IPv4UDPBidirectionalBindSocketPair(0),
                      DualStackUDPBidirectionalBindSocketPair(0),

                      // Without REUSEADDR, we get port exhaustion on Linux.
                      SetSockOpt(SOL_SOCKET, SO_REUSEADDR,
                                 &kSockOptOn)(IPv6TCPAcceptBindSocketPair(0)),
                      SetSockOpt(SOL_SOCKET, SO_REUSEADDR,
                                 &kSockOptOn)(IPv4TCPAcceptBindSocketPair(0)),
                      SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
                          DualStackTCPAcceptBindSocketPair(0))));

// Test fixture for tests that apply to pairs of connected sockets created with
// a persistent listener (if applicable).
using PersistentListenerConnectStressTest = SocketPairTest;

TEST_P(PersistentListenerConnectStressTest, 65kTimesShutdownCloseFirst) {
  for (int i = 0; i < 1 << 16; ++i) {
    auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
    ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds());
    if (GetParam().type == SOCK_STREAM) {
      // Poll the other FD to make sure that we see the FIN from the other
      // side before closing the second_fd. This ensures that the first_fd
      // enters TIME-WAIT and not second_fd.
      const int kTimeout = 10000;
      struct pollfd pfd = {
          .fd = sockets->second_fd(),
          .events = POLL_IN,
      };
      ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
    }
    ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds());
  }
}

TEST_P(PersistentListenerConnectStressTest, 65kTimesShutdownCloseSecond) {
  for (int i = 0; i < 1 << 16; ++i) {
    auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
    ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds());
    if (GetParam().type == SOCK_STREAM) {
      // Poll the other FD to make sure that we see the FIN from the other
      // side before closing the first_fd. This ensures that the second_fd
      // enters TIME-WAIT and not first_fd.
      const int kTimeout = 10000;
      struct pollfd pfd = {
          .fd = sockets->first_fd(),
          .events = POLL_IN,
      };
      ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
    }
    ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds());
  }
}

TEST_P(PersistentListenerConnectStressTest, 65kTimesClose) {
  for (int i = 0; i < 1 << 16; ++i) {
    auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  }
}

INSTANTIATE_TEST_SUITE_P(
    AllConnectedSockets, PersistentListenerConnectStressTest,
    ::testing::Values(
        IPv6UDPBidirectionalBindSocketPair(0),
        IPv4UDPBidirectionalBindSocketPair(0),
        DualStackUDPBidirectionalBindSocketPair(0),

        // Without REUSEADDR, we get port exhaustion on Linux.
        SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
            IPv6TCPAcceptBindPersistentListenerSocketPair(0)),
        SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
            IPv4TCPAcceptBindPersistentListenerSocketPair(0)),
        SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
            DualStackTCPAcceptBindPersistentListenerSocketPair(0))));

}  // namespace testing
}  // namespace gvisor
<commit_msg>Skip socket stress tests on KVM platform.<commit_after>// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <poll.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>

#include "gtest/gtest.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/test_util.h"

namespace gvisor {
namespace testing {

// Test fixture for tests that apply to pairs of connected sockets.
using ConnectStressTest = SocketPairTest;

TEST_P(ConnectStressTest, Reset65kTimes) {
  // TODO(b/165912341): These are too slow on KVM platform with nested virt.
  SKIP_IF(GvisorPlatform() == Platform::kKVM);

  for (int i = 0; i < 1 << 16; ++i) {
    auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());

    // Send some data to ensure that the connection gets reset and the port gets
    // released immediately. This avoids either end entering TIME-WAIT.
    char sent_data[100] = {};
    ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)),
                SyscallSucceedsWithValue(sizeof(sent_data)));
    // Poll the other FD to make sure that the data is in the receive buffer
    // before closing it to ensure a RST is triggered.
    const int kTimeout = 10000;
    struct pollfd pfd = {
        .fd = sockets->second_fd(),
        .events = POLL_IN,
    };
    ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
  }
}

INSTANTIATE_TEST_SUITE_P(
    AllConnectedSockets, ConnectStressTest,
    ::testing::Values(IPv6UDPBidirectionalBindSocketPair(0),
                      IPv4UDPBidirectionalBindSocketPair(0),
                      DualStackUDPBidirectionalBindSocketPair(0),

                      // Without REUSEADDR, we get port exhaustion on Linux.
                      SetSockOpt(SOL_SOCKET, SO_REUSEADDR,
                                 &kSockOptOn)(IPv6TCPAcceptBindSocketPair(0)),
                      SetSockOpt(SOL_SOCKET, SO_REUSEADDR,
                                 &kSockOptOn)(IPv4TCPAcceptBindSocketPair(0)),
                      SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
                          DualStackTCPAcceptBindSocketPair(0))));

// Test fixture for tests that apply to pairs of connected sockets created with
// a persistent listener (if applicable).
using PersistentListenerConnectStressTest = SocketPairTest;

TEST_P(PersistentListenerConnectStressTest, 65kTimesShutdownCloseFirst) {
  // TODO(b/165912341): These are too slow on KVM platform with nested virt.
  SKIP_IF(GvisorPlatform() == Platform::kKVM);

  for (int i = 0; i < 1 << 16; ++i) {
    auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
    ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds());
    if (GetParam().type == SOCK_STREAM) {
      // Poll the other FD to make sure that we see the FIN from the other
      // side before closing the second_fd. This ensures that the first_fd
      // enters TIME-WAIT and not second_fd.
      const int kTimeout = 10000;
      struct pollfd pfd = {
          .fd = sockets->second_fd(),
          .events = POLL_IN,
      };
      ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
    }
    ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds());
  }
}

TEST_P(PersistentListenerConnectStressTest, 65kTimesShutdownCloseSecond) {
  // TODO(b/165912341): These are too slow on KVM platform with nested virt.
  SKIP_IF(GvisorPlatform() == Platform::kKVM);

  for (int i = 0; i < 1 << 16; ++i) {
    auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
    ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds());
    if (GetParam().type == SOCK_STREAM) {
      // Poll the other FD to make sure that we see the FIN from the other
      // side before closing the first_fd. This ensures that the second_fd
      // enters TIME-WAIT and not first_fd.
      const int kTimeout = 10000;
      struct pollfd pfd = {
          .fd = sockets->first_fd(),
          .events = POLL_IN,
      };
      ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
    }
    ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds());
  }
}

TEST_P(PersistentListenerConnectStressTest, 65kTimesClose) {
  // TODO(b/165912341): These are too slow on KVM platform with nested virt.
  SKIP_IF(GvisorPlatform() == Platform::kKVM);

  for (int i = 0; i < 1 << 16; ++i) {
    auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
  }
}

INSTANTIATE_TEST_SUITE_P(
    AllConnectedSockets, PersistentListenerConnectStressTest,
    ::testing::Values(
        IPv6UDPBidirectionalBindSocketPair(0),
        IPv4UDPBidirectionalBindSocketPair(0),
        DualStackUDPBidirectionalBindSocketPair(0),

        // Without REUSEADDR, we get port exhaustion on Linux.
        SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
            IPv6TCPAcceptBindPersistentListenerSocketPair(0)),
        SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
            IPv4TCPAcceptBindPersistentListenerSocketPair(0)),
        SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
            DualStackTCPAcceptBindPersistentListenerSocketPair(0))));

}  // namespace testing
}  // namespace gvisor
<|endoftext|>