<commit_before>/**
*  Copyright (C) 2016 3D Repo Ltd
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU Affero General Public License as
*  published by the Free Software Foundation, either version 3 of the
*  License, or (at your option) any later version.
*
*  This program 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 Affero General Public License for more details.
*
*  You should have received a copy of the GNU Affero General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "repo_maker_selection_tree.h"
#include "../../core/model/bson/repo_node_reference.h"
#include "../modeloptimizer/repo_optimizer_ifc.h"

using namespace repo::manipulator::modelutility;

const static std::string REPO_LABEL_VISIBILITY_STATE = "toggleState";
const static std::string REPO_VISIBILITY_STATE_SHOW = "visible";
const static std::string REPO_VISIBILITY_STATE_HIDDEN = "invisible";
const static std::string REPO_VISIBILITY_STATE_HALF_HIDDEN = "parentOfInvisible";

SelectionTreeMaker::SelectionTreeMaker(
	const repo::core::model::RepoScene *scene)
	: scene(scene)
{
}

repo::lib::PropertyTree SelectionTreeMaker::generatePTree(
	const repo::core::model::RepoNode            *currentNode,
	std::unordered_map < std::string,
	std::pair < std::string, std::string >> &idMaps,
	const std::string                            &currentPath,
	bool                                         &hiddenOnDefault,
	std::vector<std::string>                     &hiddenNode) const
{
	repo::lib::PropertyTree tree;
	if (currentNode)
	{
		std::string idString = currentNode->getUniqueID().toString();
		repo::lib::RepoUUID sharedID = currentNode->getSharedID();
		std::string childPath = currentPath.empty() ? idString : currentPath + "__" + idString;

		auto children = scene->getChildrenAsNodes(repo::core::model::RepoScene::GraphType::DEFAULT, sharedID);
		std::vector<repo::lib::PropertyTree> childrenTrees;

		std::vector<repo::core::model::RepoNode*> childrenTypes[2];
		std::vector<repo::lib::RepoUUID> metaIDs;
		for (const auto &child : children)
		{
			//Ensure IFC Space (if any) are put into the tree first.
			if (child->getName().find(IFC_TYPE_SPACE_LABEL) != std::string::npos)
				childrenTypes[0].push_back(child);
			else if (child->getTypeAsEnum() == repo::core::model::NodeType::METADATA)
			{
				metaIDs.push_back(child->getUniqueID());
			}
			else
				childrenTypes[1].push_back(child);
		}

		bool hasHiddenChildren = false;
		for (const auto childrenSet : childrenTypes)
		{
			for (const auto &child : childrenSet)
			{
				if (child)
				{
					switch (child->getTypeAsEnum())
					{
					case repo::core::model::NodeType::MESH:
					case repo::core::model::NodeType::TRANSFORMATION:
					case repo::core::model::NodeType::CAMERA:
					case repo::core::model::NodeType::REFERENCE:
					{
						bool hiddenChild = false;
						childrenTrees.push_back(generatePTree(child, idMaps, childPath, hiddenChild, hiddenNode));
						hasHiddenChildren = hasHiddenChildren || hiddenChild;
					}
					}
				}
				else
				{
					repoDebug << "Null pointer for child node at generatePTree, current path : " << currentPath;
					repoError << "Unexpected error at selection tree generation, the tree may not be complete.";
				}
			}
		}
		std::string name = currentNode->getName();
		if (repo::core::model::NodeType::REFERENCE == currentNode->getTypeAsEnum())
		{
			if (auto refNode = dynamic_cast<const repo::core::model::ReferenceNode*>(currentNode))
			{
				auto refDb = refNode->getDatabaseName();
				name = (scene->getDatabaseName() == refDb ? "" : (refDb + "/")) + refNode->getProjectName();
			}
		}

		tree.addToTree("account", scene->getDatabaseName());
		tree.addToTree("project", scene->getProjectName());
		if (!name.empty())
			tree.addToTree("name", name);
		tree.addToTree("path", childPath);
		tree.addToTree("_id", idString);
		tree.addToTree("shared_id", sharedID.toString());
		tree.addToTree("children", childrenTrees);
		tree.addToTree("meta", metaIDs);

		if (name.find(IFC_TYPE_SPACE_LABEL) != std::string::npos
			&& currentNode->getTypeAsEnum() == repo::core::model::NodeType::MESH)
		{
			tree.addToTree(REPO_LABEL_VISIBILITY_STATE, REPO_VISIBILITY_STATE_HIDDEN);
			hiddenOnDefault = true;
			hiddenNode.push_back(idString);
		}
		else if (hiddenOnDefault = hiddenOnDefault || hasHiddenChildren)
		{
			tree.addToTree(REPO_LABEL_VISIBILITY_STATE, REPO_VISIBILITY_STATE_HALF_HIDDEN);
		}
		else
		{
			tree.addToTree(REPO_LABEL_VISIBILITY_STATE, REPO_VISIBILITY_STATE_SHOW);
		}

		idMaps[idString] = { name, childPath };
	}
	else
	{
		repoDebug << "Null pointer at generatePTree, current path : " << currentPath;
		repoError << "Unexpected error at selection tree generation, the tree may not be complete.";
	}

	return tree;
}

std::map<std::string, std::vector<uint8_t>> SelectionTreeMaker::getSelectionTreeAsBuffer() const
{
	auto trees = getSelectionTreeAsPropertyTree();
	std::map<std::string, std::vector<uint8_t>> buffer;
	for (const auto &tree : trees)
	{
		std::stringstream ss;
		tree.second.write_json(ss);
		std::string jsonString = ss.str();
		if (!jsonString.empty())
		{
			size_t byteLength = jsonString.size() * sizeof(*jsonString.data());
			buffer[tree.first] = std::vector<uint8_t>();
			buffer[tree.first].resize(byteLength);
			memcpy(buffer[tree.first].data(), jsonString.data(), byteLength);
		}
		else
		{
			repoError << "Failed to write selection tree into the buffer: JSON string is empty.";
		}
	}

	return buffer;
}

std::map<std::string, repo::lib::PropertyTree>  SelectionTreeMaker::getSelectionTreeAsPropertyTree() const
{
	std::map<std::string, repo::lib::PropertyTree> trees;

	repo::core::model::RepoNode *root;
	if (scene && (root = scene->getRoot(repo::core::model::RepoScene::GraphType::DEFAULT)))
	{
		std::unordered_map< std::string, std::pair<std::string, std::string>> map;
		std::vector<std::string> hiddenNodes;
		bool dummy;
		repo::lib::PropertyTree tree, settingsTree;
		tree.mergeSubTree("nodes", generatePTree(root, map, "", dummy, hiddenNodes));
		for (const auto pair : map)
		{
			//if there's an entry in maps it must have an entry in paths
			tree.addToTree("idToName." + pair.first, pair.second.first);
			tree.addToTree("idToPath." + pair.first, pair.second.second);
		}

		trees["fulltree.json"] = tree;

		if (hiddenNodes.size())
		{
			settingsTree.addToTree("hiddenNodes", hiddenNodes);
			trees["modelProperties.json"] = settingsTree;
		}
	}
	else
	{
		repoError << "Failed to generate selection tree: scene is empty or default scene is not loaded";
	}

	return trees;
}

SelectionTreeMaker::~SelectionTreeMaker()
{
}<commit_msg>#165 do not create a meta field if there are no metadata IDs to print<commit_after>/**
*  Copyright (C) 2016 3D Repo Ltd
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU Affero General Public License as
*  published by the Free Software Foundation, either version 3 of the
*  License, or (at your option) any later version.
*
*  This program 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 Affero General Public License for more details.
*
*  You should have received a copy of the GNU Affero General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "repo_maker_selection_tree.h"
#include "../../core/model/bson/repo_node_reference.h"
#include "../modeloptimizer/repo_optimizer_ifc.h"

using namespace repo::manipulator::modelutility;

const static std::string REPO_LABEL_VISIBILITY_STATE = "toggleState";
const static std::string REPO_VISIBILITY_STATE_SHOW = "visible";
const static std::string REPO_VISIBILITY_STATE_HIDDEN = "invisible";
const static std::string REPO_VISIBILITY_STATE_HALF_HIDDEN = "parentOfInvisible";

SelectionTreeMaker::SelectionTreeMaker(
	const repo::core::model::RepoScene *scene)
	: scene(scene)
{
}

repo::lib::PropertyTree SelectionTreeMaker::generatePTree(
	const repo::core::model::RepoNode            *currentNode,
	std::unordered_map < std::string,
	std::pair < std::string, std::string >> &idMaps,
	const std::string                            &currentPath,
	bool                                         &hiddenOnDefault,
	std::vector<std::string>                     &hiddenNode) const
{
	repo::lib::PropertyTree tree;
	if (currentNode)
	{
		std::string idString = currentNode->getUniqueID().toString();
		repo::lib::RepoUUID sharedID = currentNode->getSharedID();
		std::string childPath = currentPath.empty() ? idString : currentPath + "__" + idString;

		auto children = scene->getChildrenAsNodes(repo::core::model::RepoScene::GraphType::DEFAULT, sharedID);
		std::vector<repo::lib::PropertyTree> childrenTrees;

		std::vector<repo::core::model::RepoNode*> childrenTypes[2];
		std::vector<repo::lib::RepoUUID> metaIDs;
		for (const auto &child : children)
		{
			//Ensure IFC Space (if any) are put into the tree first.
			if (child->getName().find(IFC_TYPE_SPACE_LABEL) != std::string::npos)
				childrenTypes[0].push_back(child);
			else if (child->getTypeAsEnum() == repo::core::model::NodeType::METADATA)
			{
				metaIDs.push_back(child->getUniqueID());
			}
			else
				childrenTypes[1].push_back(child);
		}

		bool hasHiddenChildren = false;
		for (const auto childrenSet : childrenTypes)
		{
			for (const auto &child : childrenSet)
			{
				if (child)
				{
					switch (child->getTypeAsEnum())
					{
					case repo::core::model::NodeType::MESH:
					case repo::core::model::NodeType::TRANSFORMATION:
					case repo::core::model::NodeType::CAMERA:
					case repo::core::model::NodeType::REFERENCE:
					{
						bool hiddenChild = false;
						childrenTrees.push_back(generatePTree(child, idMaps, childPath, hiddenChild, hiddenNode));
						hasHiddenChildren = hasHiddenChildren || hiddenChild;
					}
					}
				}
				else
				{
					repoDebug << "Null pointer for child node at generatePTree, current path : " << currentPath;
					repoError << "Unexpected error at selection tree generation, the tree may not be complete.";
				}
			}
		}
		std::string name = currentNode->getName();
		if (repo::core::model::NodeType::REFERENCE == currentNode->getTypeAsEnum())
		{
			if (auto refNode = dynamic_cast<const repo::core::model::ReferenceNode*>(currentNode))
			{
				auto refDb = refNode->getDatabaseName();
				name = (scene->getDatabaseName() == refDb ? "" : (refDb + "/")) + refNode->getProjectName();
			}
		}

		tree.addToTree("account", scene->getDatabaseName());
		tree.addToTree("project", scene->getProjectName());
		if (!name.empty())
			tree.addToTree("name", name);
		tree.addToTree("path", childPath);
		tree.addToTree("_id", idString);
		tree.addToTree("shared_id", sharedID.toString());
		tree.addToTree("children", childrenTrees);
		if (metaIDs.size())
			tree.addToTree("meta", metaIDs);

		if (name.find(IFC_TYPE_SPACE_LABEL) != std::string::npos
			&& currentNode->getTypeAsEnum() == repo::core::model::NodeType::MESH)
		{
			tree.addToTree(REPO_LABEL_VISIBILITY_STATE, REPO_VISIBILITY_STATE_HIDDEN);
			hiddenOnDefault = true;
			hiddenNode.push_back(idString);
		}
		else if (hiddenOnDefault = hiddenOnDefault || hasHiddenChildren)
		{
			tree.addToTree(REPO_LABEL_VISIBILITY_STATE, REPO_VISIBILITY_STATE_HALF_HIDDEN);
		}
		else
		{
			tree.addToTree(REPO_LABEL_VISIBILITY_STATE, REPO_VISIBILITY_STATE_SHOW);
		}

		idMaps[idString] = { name, childPath };
	}
	else
	{
		repoDebug << "Null pointer at generatePTree, current path : " << currentPath;
		repoError << "Unexpected error at selection tree generation, the tree may not be complete.";
	}

	return tree;
}

std::map<std::string, std::vector<uint8_t>> SelectionTreeMaker::getSelectionTreeAsBuffer() const
{
	auto trees = getSelectionTreeAsPropertyTree();
	std::map<std::string, std::vector<uint8_t>> buffer;
	for (const auto &tree : trees)
	{
		std::stringstream ss;
		tree.second.write_json(ss);
		std::string jsonString = ss.str();
		if (!jsonString.empty())
		{
			size_t byteLength = jsonString.size() * sizeof(*jsonString.data());
			buffer[tree.first] = std::vector<uint8_t>();
			buffer[tree.first].resize(byteLength);
			memcpy(buffer[tree.first].data(), jsonString.data(), byteLength);
		}
		else
		{
			repoError << "Failed to write selection tree into the buffer: JSON string is empty.";
		}
	}

	return buffer;
}

std::map<std::string, repo::lib::PropertyTree>  SelectionTreeMaker::getSelectionTreeAsPropertyTree() const
{
	std::map<std::string, repo::lib::PropertyTree> trees;

	repo::core::model::RepoNode *root;
	if (scene && (root = scene->getRoot(repo::core::model::RepoScene::GraphType::DEFAULT)))
	{
		std::unordered_map< std::string, std::pair<std::string, std::string>> map;
		std::vector<std::string> hiddenNodes;
		bool dummy;
		repo::lib::PropertyTree tree, settingsTree;
		tree.mergeSubTree("nodes", generatePTree(root, map, "", dummy, hiddenNodes));
		for (const auto pair : map)
		{
			//if there's an entry in maps it must have an entry in paths
			tree.addToTree("idToName." + pair.first, pair.second.first);
			tree.addToTree("idToPath." + pair.first, pair.second.second);
		}

		trees["fulltree.json"] = tree;

		if (hiddenNodes.size())
		{
			settingsTree.addToTree("hiddenNodes", hiddenNodes);
			trees["modelProperties.json"] = settingsTree;
		}
	}
	else
	{
		repoError << "Failed to generate selection tree: scene is empty or default scene is not loaded";
	}

	return trees;
}

SelectionTreeMaker::~SelectionTreeMaker()
{
}<|endoftext|>


<commit_before>#ifndef GNR_INVOKE_HPP
# define GNR_INVOKE_HPP
# pragma once

#include <functional>

#include <tuple>

namespace gnr
{

namespace detail::invoke
{

template <typename>
struct is_tuple : std::false_type {};

template <typename ...T>
struct is_tuple<std::tuple<T...>> : std::true_type {};

template <typename T>
static constexpr bool is_tuple_v(is_tuple<std::remove_cvref_t<T>>{});

template <std::size_t N>
constexpr auto split(auto&& t) noexcept requires(bool(N))
{
  constexpr auto n(std::tuple_size_v<std::remove_cvref_t<decltype(t)>>);
  static_assert(n && !(n % N));
  return [&]<auto ...I>(std::index_sequence<I...>) noexcept
  {
    return std::make_tuple(
      [&]<auto ...J>(std::index_sequence<J...>) noexcept
      {
        constexpr auto K(N * I);
        return std::forward_as_tuple(
          std::get<K + J>(std::forward<decltype(t)>(t))...);
      }(std::make_index_sequence<N + I - I>())...
    );
  }(std::make_index_sequence<n / N>());
}

template <typename F, typename T>
constexpr bool is_noexcept_invocable() noexcept
{
  auto const f(static_cast<std::remove_reference_t<F>*>(nullptr));
  auto const t(static_cast<std::remove_reference_t<T>*>(nullptr));

  return noexcept(
    [&]<auto ...I>(std::index_sequence<I...>)
    {
      return (std::invoke(F(*f), std::get<I>(T(*t))...));
    }(std::make_index_sequence<
        std::tuple_size_v<std::remove_cvref_t<T>>
      >()
    )
  );
}

}

constexpr decltype(auto) apply(auto&& f, auto&& t)
  noexcept(
    detail::invoke::is_noexcept_invocable<decltype(f), decltype(t)>()
  )
{
  return [&]<auto ...I>(std::index_sequence<I...>)
    noexcept(noexcept(
      std::invoke(
        std::forward<decltype(f)>(f),
        std::get<I>(std::forward<decltype(t)>(t))...
      )
    )
  )
  {
    return std::invoke(
      std::forward<decltype(f)>(f),
      std::get<I>(std::forward<decltype(t)>(t))...
    );
  }(std::make_index_sequence<
      std::tuple_size_v<std::remove_cvref_t<decltype(t)>>
    >()
  );
}

constexpr auto invoke_all(auto f, auto&& ...a)
  noexcept(noexcept((f(std::forward<decltype(a)>(a)), ...)))
{
  return (f(std::forward<decltype(a)>(a)), ...);
}

constexpr auto invoke_cond(auto f, auto&& ...a)
  noexcept(noexcept((f(std::forward<decltype(a)>(a)), ...)))
{
  return (f(std::forward<decltype(a)>(a)) || ...);
}

namespace detail::invoke
{

template <std::size_t N, typename F, typename ...A>
constexpr bool is_noexcept_split_invocable() noexcept
{
  auto const f(static_cast<std::remove_reference_t<F>*>(nullptr));

  return noexcept(
    ::gnr::apply([&](auto&& ...t)
      noexcept(noexcept(
        (::gnr::apply(F(*f), std::forward<decltype(t)>(t)), ...)))
      {
        (::gnr::apply(F(*f), std::forward<decltype(t)>(t)), ...);
      },
      detail::invoke::split<N>(
        std::forward_as_tuple(
          A(*static_cast<std::remove_reference_t<A>*>(nullptr))...
        )
      )
    )
  );
}

}

template <std::size_t N>
constexpr void invoke_split(auto&& f, auto&& ...a)
  noexcept(
    detail::invoke::is_noexcept_split_invocable<N, decltype(f),
      decltype(a)...>()
  )
{
  ::gnr::apply(
    [&](auto&& ...t)
      noexcept(noexcept(
        (::gnr::apply(f, std::forward<decltype(t)>(t)), ...))
      )
    {
      (::gnr::apply(f, std::forward<decltype(t)>(t)), ...);
    },
    detail::invoke::split<N>(std::forward_as_tuple(a...))
  );
}

template <std::size_t N>
constexpr void invoke_split_cond(auto&& f, auto&& ...a)
  noexcept(
    detail::invoke::is_noexcept_split_invocable<
      N, decltype(f), decltype(a)...
    >()
  )
{
  ::gnr::apply(
    [&](auto&& ...t)
      noexcept(noexcept(
          (::gnr::apply(f, std::forward<decltype(t)>(t)), ...)
        )
      )
    {
      (::gnr::apply(f, std::forward<decltype(t)>(t)) || ...);
    },
    detail::invoke::split<N>(std::forward_as_tuple(a...))
  );
}

constexpr auto chain_apply(auto&& t, auto&& f, auto&& ...fs)
  noexcept(noexcept(
      ::gnr::apply(
        std::forward<decltype(f)>(f),
        std::forward<decltype(t)>(t)
      )
    )
  )
  requires(detail::invoke::is_tuple_v<decltype(t)>)
{
  if constexpr(sizeof...(fs))
  {
    using R = decltype(
      ::gnr::apply(
        std::forward<decltype(f)>(f),
        std::forward<decltype(t)>(t)
      )
    );

    if constexpr(gnr::detail::invoke::is_tuple_v<R>)
    {
      return chain_apply(
        ::gnr::apply(
          std::forward<decltype(f)>(f),
          std::forward<decltype(t)>(t)
        ),
        std::forward<decltype(fs)>(fs)...
      );
    }
    else if constexpr(std::is_void_v<R>)
    {
      ::gnr::apply(
        std::forward<decltype(f)>(f),
        std::forward<decltype(t)>(t)
      );

      return chain_apply(
        std::tuple(),
        std::forward<decltype(fs)>(fs)...
      );
    }
    else
    {
      return chain_apply(
        std::forward_as_tuple(
          ::gnr::apply(
            std::forward<decltype(f)>(f),
            std::forward<decltype(t)>(t)
          )
        ),
        std::forward<decltype(fs)>(fs)...
      );
    }
  }
  else
  {
    return ::gnr::apply(
      std::forward<decltype(f)>(f),
      std::forward<decltype(t)>(t)
    );
  }
}

constexpr auto chain_apply(auto&& a, auto&& ...f)
  noexcept(noexcept(
      chain_apply(
        std::forward_as_tuple(a),
        std::forward<decltype(f)>(f)...
      )
    )
  )
  requires(!detail::invoke::is_tuple_v<decltype(a)>)
{
  return chain_apply(
    std::forward_as_tuple(a),
    std::forward<decltype(f)>(f)...
  );
}

}

#endif // GNR_INVOKE_HPP
<commit_msg>some fixes<commit_after>#ifndef GNR_INVOKE_HPP
# define GNR_INVOKE_HPP
# pragma once

#include <functional>

#include <tuple>

namespace gnr
{

namespace detail::invoke
{

template <typename>
struct is_tuple : std::false_type {};

template <typename ...T>
struct is_tuple<std::tuple<T...>> : std::true_type {};

template <typename T>
static constexpr bool is_tuple_v(is_tuple<std::remove_cvref_t<T>>{});

template <std::size_t N>
constexpr auto split(auto&& t) noexcept requires(bool(N))
{
  constexpr auto n(std::tuple_size_v<std::remove_cvref_t<decltype(t)>>);
  static_assert(n && !(n % N));
  return [&]<auto ...I>(std::index_sequence<I...>) noexcept
  {
    return std::make_tuple(
      [&]<auto ...J>(std::index_sequence<J...>) noexcept
      {
        constexpr auto K(N * I);
        return std::forward_as_tuple(
          std::get<K + J>(std::forward<decltype(t)>(t))...);
      }(std::make_index_sequence<N + I - I>())...
    );
  }(std::make_index_sequence<n / N>());
}

template <typename F, typename T>
constexpr bool is_noexcept_invocable() noexcept
{
  auto const f(static_cast<std::remove_reference_t<F>*>(nullptr));
  auto const t(static_cast<std::remove_reference_t<T>*>(nullptr));

  return noexcept(
    [&]<auto ...I>(std::index_sequence<I...>)
    {
      return (std::invoke(F(*f), std::get<I>(T(*t))...));
    }(std::make_index_sequence<
        std::tuple_size_v<std::remove_cvref_t<T>>
      >()
    )
  );
}

}

constexpr decltype(auto) apply(auto&& f, auto&& t)
  noexcept(
    detail::invoke::is_noexcept_invocable<decltype(f), decltype(t)>()
  )
{
  return [&]<auto ...I>(std::index_sequence<I...>)
    noexcept(noexcept(
      std::invoke(
        std::forward<decltype(f)>(f),
        std::get<I>(std::forward<decltype(t)>(t))...
      )
    )
  )
  {
    return std::invoke(
      std::forward<decltype(f)>(f),
      std::get<I>(std::forward<decltype(t)>(t))...
    );
  }(std::make_index_sequence<
      std::tuple_size_v<std::remove_cvref_t<decltype(t)>>
    >()
  );
}

constexpr auto invoke_all(auto f, auto&& ...a)
  noexcept(noexcept((f(std::forward<decltype(a)>(a)), ...)))
{
  return (f(std::forward<decltype(a)>(a)), ...);
}

constexpr auto invoke_cond(auto f, auto&& ...a)
  noexcept(noexcept((f(std::forward<decltype(a)>(a)), ...)))
{
  return (f(std::forward<decltype(a)>(a)) || ...);
}

namespace detail::invoke
{

template <std::size_t N, typename F, typename ...A>
constexpr bool is_noexcept_split_invocable() noexcept
{
  auto const f(static_cast<std::remove_reference_t<F>*>(nullptr));

  return noexcept(
    ::gnr::apply([&](auto&& ...t)
      noexcept(noexcept(
        (::gnr::apply(F(*f), std::forward<decltype(t)>(t)), ...)))
      {
        (::gnr::apply(F(*f), std::forward<decltype(t)>(t)), ...);
      },
      detail::invoke::split<N>(
        std::forward_as_tuple(
          A(*static_cast<std::remove_reference_t<A>*>(nullptr))...
        )
      )
    )
  );
}

}

template <std::size_t N>
constexpr void invoke_split(auto&& f, auto&& ...a)
  noexcept(
    detail::invoke::is_noexcept_split_invocable<N, decltype(f),
      decltype(a)...>()
  )
{
  ::gnr::apply(
    [&](auto&& ...t)
      noexcept(noexcept(
        (::gnr::apply(f, std::forward<decltype(t)>(t)), ...))
      )
    {
      (::gnr::apply(f, std::forward<decltype(t)>(t)), ...);
    },
    detail::invoke::split<N>(std::forward_as_tuple(a...))
  );
}

template <std::size_t N>
constexpr bool invoke_split_cond(auto&& f, auto&& ...a)
  noexcept(
    detail::invoke::is_noexcept_split_invocable<
      N, decltype(f), decltype(a)...
    >()
  )
{
  return ::gnr::apply(
    [&](auto&& ...t)
      noexcept(noexcept(
          (::gnr::apply(f, std::forward<decltype(t)>(t)), ...)
        )
      )
    {
      return (::gnr::apply(f, std::forward<decltype(t)>(t)) || ...);
    },
    detail::invoke::split<N>(std::forward_as_tuple(a...))
  );
}

constexpr auto chain_apply(auto&& t, auto&& f, auto&& ...fs)
  noexcept(noexcept(
      ::gnr::apply(
        std::forward<decltype(f)>(f),
        std::forward<decltype(t)>(t)
      )
    )
  )
  requires(detail::invoke::is_tuple_v<decltype(t)>)
{
  if constexpr(sizeof...(fs))
  {
    using R = decltype(
      ::gnr::apply(
        std::forward<decltype(f)>(f),
        std::forward<decltype(t)>(t)
      )
    );

    if constexpr(gnr::detail::invoke::is_tuple_v<R>)
    {
      return chain_apply(
        ::gnr::apply(
          std::forward<decltype(f)>(f),
          std::forward<decltype(t)>(t)
        ),
        std::forward<decltype(fs)>(fs)...
      );
    }
    else if constexpr(std::is_void_v<R>)
    {
      ::gnr::apply(
        std::forward<decltype(f)>(f),
        std::forward<decltype(t)>(t)
      );

      return chain_apply(
        std::tuple(),
        std::forward<decltype(fs)>(fs)...
      );
    }
    else
    {
      return chain_apply(
        std::forward_as_tuple(
          ::gnr::apply(
            std::forward<decltype(f)>(f),
            std::forward<decltype(t)>(t)
          )
        ),
        std::forward<decltype(fs)>(fs)...
      );
    }
  }
  else
  {
    return ::gnr::apply(
      std::forward<decltype(f)>(f),
      std::forward<decltype(t)>(t)
    );
  }
}

constexpr auto chain_apply(auto&& a, auto&& ...f)
  noexcept(noexcept(
      chain_apply(
        std::forward_as_tuple(a),
        std::forward<decltype(f)>(f)...
      )
    )
  )
  requires(!detail::invoke::is_tuple_v<decltype(a)>)
{
  return chain_apply(
    std::forward_as_tuple(a),
    std::forward<decltype(f)>(f)...
  );
}

}

#endif // GNR_INVOKE_HPP
<|endoftext|>


<commit_before>/**
 * \file
 * \brief threadTestCases object definition
 *
 * \author Copyright (C) 2014 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2014-08-24
 */

#include "threadTestCases.hpp"

#include "ThreadPriorityTestCase.hpp"

namespace distortos
{

namespace test
{

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local objects
+---------------------------------------------------------------------------------------------------------------------*/

/// ThreadPriorityTestCase instance
const ThreadPriorityTestCase priorityTestCase;

/// array with references to TestCase objects related to threads
const TestCaseRange::value_type threadTestCases_[]
{
		priorityTestCase,
};

}	// namespace

/*---------------------------------------------------------------------------------------------------------------------+
| global objects
+---------------------------------------------------------------------------------------------------------------------*/

const TestCaseRange threadTestCases {threadTestCases_};

}	// namespace test

}	// namespace distortos
<commit_msg>test: add ThreadFunctionTypesTestCase to list of executed testcases<commit_after>/**
 * \file
 * \brief threadTestCases object definition
 *
 * \author Copyright (C) 2014 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2014-08-28
 */

#include "threadTestCases.hpp"

#include "ThreadPriorityTestCase.hpp"
#include "ThreadFunctionTypesTestCase.hpp"

namespace distortos
{

namespace test
{

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local objects
+---------------------------------------------------------------------------------------------------------------------*/

/// ThreadPriorityTestCase instance
const ThreadPriorityTestCase priorityTestCase;

/// ThreadFunctionTypesTestCase instance
const ThreadFunctionTypesTestCase functionTypesTestCase;

/// array with references to TestCase objects related to threads
const TestCaseRange::value_type threadTestCases_[]
{
		priorityTestCase,
		functionTypesTestCase,
};

}	// namespace

/*---------------------------------------------------------------------------------------------------------------------+
| global objects
+---------------------------------------------------------------------------------------------------------------------*/

const TestCaseRange threadTestCases {threadTestCases_};

}	// namespace test

}	// namespace distortos
<|endoftext|>


<commit_before>/* Carloop mileage reminder app
 * Copyright 2016 1000 Tools, Inc.
 *
 * Accumulates miles in a counter stored in EEPROM.
 * When the limit is reached, set a flag in EEPROM that an event should
 * be published to the Particle Cloud.
 * In the cloud, set the event to send an SMS to the driver.
 */
#include "application.h"
#include "carloop/carloop.h"
#include "storage.h"

// Function declarations
void publishEvent();
void setupCloud();
int resetIntervalCounter(String);
int changeIntervalLimit(String arg);
void updateSpeed();
void requestVehicleSpeed();
void waitForVehicleSpeedResponse();

// Don't block the main program while connecting to WiFi/cellular.
// This way the main program runs on the Carloop even outside of WiFi range.
SYSTEM_THREAD(ENABLED);

// Tell the program which revision of Carloop you are using.
Carloop<CarloopRevision2> carloop;

// The data that is stored and loaded in permanent storage (EEPROM)
// including the current interval count and limit
Data data;
// The class that will read and write the data to EEPROM
Storage storage;

// OBD constants for CAN

// CAN IDs for OBD messages
const auto OBD_CAN_REQUEST_ID      = 0x7E0;
const auto OBD_CAN_REPLY_ID_MIN    = 0x7E8;

// Modes (aka services) for OBD
const auto OBD_MODE_CURRENT_DATA   = 0x01;

// OBD signals (aka PID) that can be requested
const auto OBD_PID_VEHICLE_SPEED   = 0x0d;

// Time to wait for a reply for an OBD request
const auto OBD_TIMEOUT_MS = 10;

uint8_t vehicleSpeedKmh = 0;

void setup()
{
  setupCloud();
  storage.load(data);

  // Configure the CAN bus speed for 500 kbps, the standard speed for the OBD-II port.
  // Other common speeds are 250 kbps and 1 Mbps.
  carloop.setCANSpeed(500000);

  // Connect to the CAN bus
  carloop.begin();
}

void setupCloud()
{
  Particle.function("reset", resetIntervalCounter);
  Particle.function("limit", changeIntervalLimit);
  Particle.variable("count", data.intervalCounter);
}

int resetIntervalCounter(String)
{
  data.intervalCounter = 0;
  data.intervalReached = 0;
  storage.store(data);
  return 0;
}

int changeIntervalLimit(String arg)
{
  long newLimit = arg.toInt();
  if (newLimit <= 0)
  {
    return -1;
  }

  data.intervalLimit = newLimit;
  while (data.intervalCounter >= data.intervalLimit)
  {
    data.intervalCounter -= data.intervalLimit;
    data.intervalReached = 1;
  }
  storage.store(data);
  return 0;
}

void loop()
{
  updateSpeed();
  publishEvent();
}

void updateSpeed()
{
  requestVehicleSpeed();
  waitForVehicleSpeedResponse();
}

void requestVehicleSpeed()
{
  CANMessage message;

  // A CAN message to request the vehicle speed
  message.id = OBD_CAN_REQUEST_ID;
  message.len = 8;

  // Data is an OBD request: get current value of the vehicle speed PID
  message.data[0] = 2; // 2 byte request
  message.data[1] = OBD_MODE_CURRENT_DATA;
  message.data[2] = OBD_PID_VEHICLE_SPEED;

  // Send the message on the bus!
  carloop.can().transmit(message);
}

void waitForVehicleSpeedResponse()
{
  uint32_t start = millis();
  bool received = false;
  while (millis() - start < OBD_TIMEOUT_MS)
  {

  }
}

// If we reached the limit and we're connected to the cloud through WiFi
// or cellular, publish the event
void publishEvent()
{
  if (data.intervalReached && Particle.connected())
  {
    // Publish the event interval with the value set to the mileage that was reached
    Particle.publish("interval", String::format("%.0f", data.intervalLimit), PRIVATE);
    data.intervalReached = 0;
    storage.store(data);
  }
}
<commit_msg>First compiling version<commit_after>/* Carloop mileage reminder app
 * Copyright 2016 1000 Tools, Inc.
 *
 * Accumulates miles in a counter stored in EEPROM.
 * When the limit is reached, set a flag in EEPROM that an event should
 * be published to the Particle Cloud.
 * In the cloud, set the event to send an SMS to the driver.
 */
#include "application.h"
#include "carloop/carloop.h"
#include "storage.h"

// Function declarations
void publishEvent();
void setupCloud();
int resetIntervalCounter(String);
int changeIntervalLimit(String arg);
void updateSpeed();
void requestVehicleSpeed();
void waitForVehicleSpeedResponse();
void updateMileage(uint8_t newVehicleSpeedKhm);
double computeDeltaMileage(uint8_t newVehicleSpeedKhm);
void checkIntervalLimit();
void storeMileage();

// Don't block the main program while connecting to WiFi/cellular.
// This way the main program runs on the Carloop even outside of WiFi range.
SYSTEM_THREAD(ENABLED);

// Tell the program which revision of Carloop you are using.
Carloop<CarloopRevision2> carloop;

// The data that is stored and loaded in permanent storage (EEPROM)
// including the current interval count and limit
Data data;
// The class that will read and write the data to EEPROM
Storage storage;
// Only store to EEPROM every so often
const auto STORAGE_PERIOD = 60 * 1000; /* every minute */
uint32_t lastStorageTime = 0;

// OBD constants for CAN

// CAN IDs for OBD messages
const auto OBD_CAN_REQUEST_ID    = 0x7E0;
const auto OBD_CAN_REPLY_ID      = 0x7E8;

// Modes (aka services) for OBD
const auto OBD_MODE_CURRENT_DATA = 0x01;

// OBD signals (aka PID) that can be requested
const auto OBD_PID_VEHICLE_SPEED = 0x0d;

// Time to wait for a reply for an OBD request
const auto OBD_TIMEOUT_MS = 10;

uint8_t vehicleSpeedKmh = 0;
uint32_t lastVehicleSpeedUpdateTime = 0;

// Called at boot
// Sets up the CAN bus and cloud functions
void setup()
{
  setupCloud();
  storage.load(data);

  // Configure the CAN bus speed for 500 kbps, the standard speed for the OBD-II port.
  // Other common speeds are 250 kbps and 1 Mbps.
  carloop.setCANSpeed(500000);

  // Connect to the CAN bus
  carloop.begin();
}

// Allow interacting with the Carloop remotely
void setupCloud()
{
  Particle.function("reset", resetIntervalCounter);
  Particle.function("limit", changeIntervalLimit);
  Particle.variable("count", data.intervalCounter);
}

// Reset the interval counter and store the zero value in EEPROM
int resetIntervalCounter(String)
{
  data.intervalCounter = 0;
  data.intervalReached = 0;
  storage.store(data);
  return 0;
}

// Set the interval upper limit and make sure the current value is below
// that. Store the values value in EEPROM
int changeIntervalLimit(String arg)
{
  long newLimit = arg.toInt();
  if (newLimit <= 0)
  {
    return -1;
  }

  data.intervalLimit = newLimit;
  checkIntervalLimit();
  storage.store(data);
  return 0;
}

// Called over and over
// Process new CAN messages here to update the vehicle speed, update
// the mileage and update the interval counter
void loop()
{
  updateSpeed();
  storeMileage();
  publishEvent();
  delay(100);
}

void updateSpeed()
{
  requestVehicleSpeed();
  waitForVehicleSpeedResponse();
}

void requestVehicleSpeed()
{
  CANMessage message;

  // A CAN message to request the vehicle speed
  message.id = OBD_CAN_REQUEST_ID;
  message.len = 8;

  // Data is an OBD request: get current value of the vehicle speed PID
  message.data[0] = 2; // 2 byte request
  message.data[1] = OBD_MODE_CURRENT_DATA;
  message.data[2] = OBD_PID_VEHICLE_SPEED;

  // Send the message on the bus!
  carloop.can().transmit(message);
}

void waitForVehicleSpeedResponse()
{
  uint32_t start = millis();
  bool received = false;
  while (millis() - start < OBD_TIMEOUT_MS)
  {
    CANMessage message;
    if (carloop.can().receive(message))
    {
      if (message.id == OBD_CAN_REPLY_ID &&
          message.data[2] == OBD_PID_VEHICLE_SPEED)
      {
        received = true;
        uint8_t newVehicleSpeedKhm = message.data[3];
        updateMileage(newVehicleSpeedKhm);
      }
    }
  }

  if (!received)
  {
    updateMileage(0);
  }
}

void updateMileage(uint8_t newVehicleSpeedKhm)
{
  double deltaMileage = computeDeltaMileage(newVehicleSpeedKhm);

  data.intervalCounter += deltaMileage;
  checkIntervalLimit();
}

double computeDeltaMileage(uint8_t newVehicleSpeedKhm)
{
  uint32_t now = millis();
  double deltaMileage = 0.0;

  // If the speed was previously 0 or newly 0, or timed out because the
  // car was off, just save the new speed value
  if (vehicleSpeedKmh > 0 && newVehicleSpeedKhm > 0)
  {
    // The car was previously driving and is still driving

    // Figure out the distance driven using the trapezoidal rule
    uint32_t msDiff = now - lastVehicleSpeedUpdateTime;
    // Calculate only if the difference is plausible
    if (msDiff < 1000) {
      // distance in km/h * ms
      uint32_t deltaDistance = msDiff * (vehicleSpeedKmh + newVehicleSpeedKhm) / 2;

      // Convert to miles
      // 1 kilometer per hour * ms = 1.72603109 × 10^-7 miles
      // km/h * ms * (1 h / 3600 s) * (0.621371 mi / 1 km) * (1 s / 1000 ms)
      deltaMileage = deltaDistance * 1.72603109e-7;
    }
  }

  vehicleSpeedKmh = newVehicleSpeedKhm;
  lastVehicleSpeedUpdateTime = now;
  return deltaMileage;
}

// If the interval limit is reached, mark it so we can publish an event
// when we come back to network range
void checkIntervalLimit()
{
  while (data.intervalCounter >= data.intervalLimit)
  {
    data.intervalCounter -= data.intervalLimit;
    data.intervalReached = 1;
  }
}

// Store data to EEPROM every so often
void storeMileage()
{
  if (millis() - lastStorageTime > STORAGE_PERIOD)
  {
    storage.store(data);
    lastStorageTime = millis();
  }
}

// If we reached the limit and we're connected to the cloud through WiFi
// or cellular, publish the event
void publishEvent()
{
  if (data.intervalReached && Particle.connected())
  {
    // Publish the event interval with the value set to the mileage that was reached
    Particle.publish("interval", String::format("%.0f", data.intervalLimit), PRIVATE);
    // Clear the flag avoid publishing the same event twice
    data.intervalReached = 0;
    storage.store(data);
  }
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you 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 .
 */

#ifndef INCLUDED_VCL_INC_GENERIC_GLYPHCACHE_HXX
#define INCLUDED_VCL_INC_GENERIC_GLYPHCACHE_HXX

#include <config_graphite.h>

#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H

#include <boost/shared_ptr.hpp>

#include <basebmp/bitmapdevice.hxx>
#include <com/sun/star/i18n/XBreakIterator.hpp>
#include <tools/gen.hxx>
#include <vcl/dllapi.h>
#include <vcl/metric.hxx>

#include <outfont.hxx>
#include <sallayout.hxx>

#include <unordered_map>

class FtFontInfo;
class GlyphCachePeer;
class GlyphData;
class GraphiteFaceWrapper;
class ImplFontOptions;
class PhysicalFontCollection;
class RawBitmap;
class ServerFont;
class ServerFontLayout;
class ServerFontLayoutEngine;
namespace basegfx { class B2DPolyPolygon; }
namespace vcl { struct FontCapabilities; }

class VCL_DLLPUBLIC GlyphCache
{
public:
    explicit                    GlyphCache( GlyphCachePeer& );
                                ~GlyphCache();

    static GlyphCache&      GetInstance();

    void                        AddFontFile( const OString& rNormalizedName,
                                             int nFaceNum, sal_IntPtr nFontId,
                                             const ImplDevFontAttributes&);
    void                        AnnounceFonts( PhysicalFontCollection* ) const;

    ServerFont*                 CacheFont( const FontSelectPattern& );
    void                        UncacheFont( ServerFont& );
    void                        ClearFontCache();
    void                        InvalidateAllGlyphs();

protected:
    GlyphCachePeer&             mrPeer;

private:
    friend class ServerFont;
    // used by ServerFont class only
    void                        AddedGlyph( ServerFont&, GlyphData& );
    void                        RemovingGlyph( GlyphData& );
    void                        UsingGlyph( ServerFont&, GlyphData& );
    void                        GrowNotify();

private:
    void                        GarbageCollect();

    // the GlyphCache's FontList matches a font request to a serverfont instance
    // the FontList key's mpFontData member is reinterpreted as integer font id
    struct IFSD_Equal{  bool operator()( const FontSelectPattern&, const FontSelectPattern& ) const; };
    struct IFSD_Hash{ size_t operator()( const FontSelectPattern& ) const; };
    typedef std::unordered_map<FontSelectPattern,ServerFont*,IFSD_Hash,IFSD_Equal > FontList;
    FontList                    maFontList;
    sal_uLong                       mnMaxSize;      // max overall cache size in bytes
    mutable sal_uLong               mnBytesUsed;
    mutable long                mnLruIndex;
    mutable int                 mnGlyphCount;
    ServerFont*                 mpCurrentGCFont;

    class FreetypeManager*      mpFtManager;
};

class GlyphMetric
{
public:
    GlyphMetric() : mnAdvanceWidth(0) {}

    Point                   GetOffset() const   { return maOffset; }
    Point                   GetDelta() const    { return maDelta; }
    Size                    GetSize() const     { return maSize; }
    long                    GetCharWidth() const { return mnAdvanceWidth; }

protected:
    friend class GlyphData;
    void                    SetOffset( int nX, int nY ) { maOffset = Point( nX, nY); }
    void                    SetDelta( int nX, int nY )  { maDelta = Point( nX, nY); }
    void                    SetSize( const Size& s )    { maSize = s; }
    void                    SetCharWidth( long nW )     { mnAdvanceWidth = nW; }

private:
    long                    mnAdvanceWidth;
    Point                   maDelta;
    Point                   maOffset;
    Size                    maSize;
};

// the glyph specific data needed by a GlyphCachePeer is usually trivial,
// not attaching it to the corresponding GlyphData would be overkill;
// this is currently only used by the headless (aka svp) plugin, where meInfo is
// basebmp::Format and mpData is SvpGcpHelper*
struct ExtGlyphData
{
    int     meInfo;
    void*   mpData;

    ExtGlyphData() : meInfo(0), mpData(NULL) {}
};

class GlyphData
{
public:
    GlyphData() : mnLruValue(0) {}

    const GlyphMetric&      GetMetric() const           { return maMetric; }
    Size                    GetSize() const             { return maMetric.GetSize(); }

    void                    SetSize( const Size& s)     { maMetric.SetSize( s ); }
    void                    SetOffset( int nX, int nY ) { maMetric.SetOffset( nX, nY ); }
    void                    SetDelta( int nX, int nY )  { maMetric.SetDelta( nX, nY ); }
    void                    SetCharWidth( long nW )     { maMetric.SetCharWidth( nW ); }

    void                    SetLruValue( int n ) const  { mnLruValue = n; }
    long                    GetLruValue() const         { return mnLruValue;}

    ExtGlyphData&           ExtDataRef()                { return maExtData; }
    const ExtGlyphData&     ExtDataRef() const          { return maExtData; }

private:
    GlyphMetric             maMetric;
    ExtGlyphData            maExtData;

    // used by GlyphCache for cache LRU algorithm
    mutable long            mnLruValue;
};

class VCL_DLLPUBLIC ServerFont
{
public:
    ServerFont( const FontSelectPattern&, FtFontInfo* );
    virtual ~ServerFont();

    const OString&      GetFontFileName() const;
    bool                TestFont() const { return mbFaceOk;}
    FT_Face             GetFtFace() const;
    int                 GetLoadFlags() const { return (mnLoadFlags & ~FT_LOAD_IGNORE_TRANSFORM); }
    void                SetFontOptions( boost::shared_ptr<ImplFontOptions> );
    boost::shared_ptr<ImplFontOptions> GetFontOptions() const;
    bool                NeedsArtificialBold() const { return mbArtBold; }
    bool                NeedsArtificialItalic() const { return mbArtItalic; }

    const FontSelectPattern&   GetFontSelData() const      { return maFontSelData; }

    void                FetchFontMetric( ImplFontMetricData&, long& rFactor ) const;
    const unsigned char* GetTable( const char* pName, sal_uLong* pLength );
    int                 GetEmUnits() const { return maFaceFT->units_per_EM;}
    const FT_Size_Metrics& GetMetricsFT() const { return maSizeFT->metrics; }
    const FontCharMapPtr GetFontCharMap() const;
    bool                GetFontCapabilities(vcl::FontCapabilities &) const;

    GlyphData&                  GetGlyphData( sal_GlyphId );
    const GlyphMetric&          GetGlyphMetric( sal_GlyphId aGlyphId )
                                { return GetGlyphData( aGlyphId ).GetMetric(); }
#if ENABLE_GRAPHITE
    virtual GraphiteFaceWrapper* GetGraphiteFace() const;
#endif

    sal_GlyphId         GetGlyphIndex( sal_UCS4 ) const;
    sal_GlyphId         GetRawGlyphIndex( sal_UCS4, sal_UCS4 = 0 ) const;
    sal_GlyphId         FixupGlyphIndex( sal_GlyphId aGlyphId, sal_UCS4 ) const;
    bool                GetGlyphOutline( sal_GlyphId aGlyphId, ::basegfx::B2DPolyPolygon& ) const;
    bool                GetAntialiasAdvice( void ) const;
    bool                GetGlyphBitmap1( sal_GlyphId aGlyphId, RawBitmap& ) const;
    bool                GetGlyphBitmap8( sal_GlyphId aGlyphId, RawBitmap& ) const;

private:
    friend class GlyphCache;
    friend class ServerFontLayout;
    friend class ImplServerFontEntry;
    friend class X11SalGraphics;
    friend class CairoTextRender;

    void                        AddRef() const      { ++mnRefCount; }
    long                        GetRefCount() const { return mnRefCount; }
    long                        Release() const;
    sal_uLong                       GetByteCount() const { return mnBytesUsed; }

    void                InitGlyphData( sal_GlyphId, GlyphData& ) const;
    void                GarbageCollect( long );
    void                        ReleaseFromGarbageCollect();

    int                 ApplyGlyphTransform( int nGlyphFlags, FT_GlyphRec_*, bool ) const;
    bool                ApplyGSUB( const FontSelectPattern& );

    ServerFontLayoutEngine* GetLayoutEngine();

    typedef std::unordered_map<int,GlyphData> GlyphList;
    mutable GlyphList           maGlyphList;

    const FontSelectPattern    maFontSelData;

    // used by GlyphCache for cache LRU algorithm
    mutable long                mnRefCount;
    mutable sal_uLong               mnBytesUsed;

    ServerFont*                 mpPrevGCFont;
    ServerFont*                 mpNextGCFont;

    // 16.16 fixed point values used for a rotated font
    long                        mnCos;
    long                        mnSin;

    bool                        mbCollectedZW;

    int                         mnWidth;
    int                         mnPrioEmbedded;
    int                         mnPrioAntiAlias;
    int                         mnPrioAutoHint;
    FtFontInfo*                 mpFontInfo;
    FT_Int                      mnLoadFlags;
    double                      mfStretch;
    FT_FaceRec_*                maFaceFT;
    FT_SizeRec_*                maSizeFT;

    boost::shared_ptr<ImplFontOptions> mpFontOptions;

    bool                        mbFaceOk;
    bool            mbArtItalic;
    bool            mbArtBold;
    bool            mbUseGamma;

    typedef std::unordered_map<int,int> GlyphSubstitution;
    GlyphSubstitution           maGlyphSubstitution;

    ServerFontLayoutEngine*     mpLayoutEngine;
};

// a class for cache entries for physical font instances that are based on serverfonts
class VCL_DLLPUBLIC ImplServerFontEntry : public ImplFontEntry
{
private:
    ServerFont*    mpServerFont;
    boost::shared_ptr<ImplFontOptions> mpFontOptions;
    bool           mbGotFontOptions;

public:
                   ImplServerFontEntry( FontSelectPattern& );
    virtual        ~ImplServerFontEntry();
    void           SetServerFont(ServerFont* p);
    void           HandleFontOptions();
};

class VCL_DLLPUBLIC ServerFontLayout : public GenericSalLayout
{
private:
    ServerFont&     mrServerFont;
    com::sun::star::uno::Reference<com::sun::star::i18n::XBreakIterator> mxBreak;

    // enforce proper copy semantic
    SAL_DLLPRIVATE  ServerFontLayout( const ServerFontLayout& );
    SAL_DLLPRIVATE  ServerFontLayout& operator=( const ServerFontLayout& );

public:
                    ServerFontLayout( ServerFont& );
    virtual bool    LayoutText( ImplLayoutArgs& ) SAL_OVERRIDE;
    virtual void    AdjustLayout( ImplLayoutArgs& ) SAL_OVERRIDE;
    virtual void    DrawText( SalGraphics& ) const SAL_OVERRIDE;
    void            setNeedFallback(ImplLayoutArgs& rArgs, sal_Int32 nIndex,
                        bool bRightToLeft);

    ServerFont&     GetServerFont() const   { return mrServerFont; }
};

class ServerFontLayoutEngine
{
public:
    virtual         ~ServerFontLayoutEngine() {}
    virtual bool    layout(ServerFontLayout&, ImplLayoutArgs&) = 0;
};

class GlyphCachePeer
{
protected:
                    GlyphCachePeer() : mnBytesUsed(0) {}
    virtual         ~GlyphCachePeer() {}

public:
    sal_Int32       GetByteCount() const { return mnBytesUsed; }
    virtual void    RemovingFont( ServerFont& ) {}
    virtual void    RemovingGlyph( GlyphData& ) {}

protected:
    sal_Int32       mnBytesUsed;
};

class VCL_DLLPUBLIC RawBitmap
{
public:
                    RawBitmap();
                    ~RawBitmap();
    bool            Rotate( int nAngle );

public:
    basebmp::RawMemorySharedArray mpBits;
    sal_uLong           mnAllocated;

    sal_uLong           mnWidth;
    sal_uLong           mnHeight;

    sal_uLong           mnScanlineSize;
    sal_uLong           mnBitCount;

    int             mnXOffset;
    int             mnYOffset;
};

#endif // INCLUDED_VCL_INC_GENERIC_GLYPHCACHE_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>vcl: whitespace cleanup on GlyphCache class definition<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you 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 .
 */

#ifndef INCLUDED_VCL_INC_GENERIC_GLYPHCACHE_HXX
#define INCLUDED_VCL_INC_GENERIC_GLYPHCACHE_HXX

#include <config_graphite.h>

#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H

#include <boost/shared_ptr.hpp>

#include <basebmp/bitmapdevice.hxx>
#include <com/sun/star/i18n/XBreakIterator.hpp>
#include <tools/gen.hxx>
#include <vcl/dllapi.h>
#include <vcl/metric.hxx>

#include <outfont.hxx>
#include <sallayout.hxx>

#include <unordered_map>

class FtFontInfo;
class GlyphCachePeer;
class GlyphData;
class GraphiteFaceWrapper;
class ImplFontOptions;
class PhysicalFontCollection;
class RawBitmap;
class ServerFont;
class ServerFontLayout;
class ServerFontLayoutEngine;
namespace basegfx { class B2DPolyPolygon; }
namespace vcl { struct FontCapabilities; }

class VCL_DLLPUBLIC GlyphCache
{
public:
    explicit                    GlyphCache( GlyphCachePeer& );
                                ~GlyphCache();

    static GlyphCache&          GetInstance();

    void                        AddFontFile( const OString& rNormalizedName,
                                             int nFaceNum, sal_IntPtr nFontId,
                                             const ImplDevFontAttributes&);
    void                        AnnounceFonts( PhysicalFontCollection* ) const;

    ServerFont*                 CacheFont( const FontSelectPattern& );
    void                        UncacheFont( ServerFont& );
    void                        ClearFontCache();
    void                        InvalidateAllGlyphs();

protected:
    GlyphCachePeer&             mrPeer;

private:
    friend class ServerFont;
    // used by ServerFont class only
    void                        AddedGlyph( ServerFont&, GlyphData& );
    void                        RemovingGlyph( GlyphData& );
    void                        UsingGlyph( ServerFont&, GlyphData& );
    void                        GrowNotify();

private:
    void                        GarbageCollect();

    // the GlyphCache's FontList matches a font request to a serverfont instance
    // the FontList key's mpFontData member is reinterpreted as integer font id
    struct IFSD_Equal{  bool operator()( const FontSelectPattern&, const FontSelectPattern& ) const; };
    struct IFSD_Hash{ size_t operator()( const FontSelectPattern& ) const; };
    typedef std::unordered_map<FontSelectPattern,ServerFont*,IFSD_Hash,IFSD_Equal > FontList;

    FontList                    maFontList;
    sal_uLong                   mnMaxSize;      // max overall cache size in bytes
    mutable sal_uLong           mnBytesUsed;
    mutable long                mnLruIndex;
    mutable int                 mnGlyphCount;
    ServerFont*                 mpCurrentGCFont;

    class FreetypeManager*      mpFtManager;
};

class GlyphMetric
{
public:
    GlyphMetric() : mnAdvanceWidth(0) {}

    Point                   GetOffset() const   { return maOffset; }
    Point                   GetDelta() const    { return maDelta; }
    Size                    GetSize() const     { return maSize; }
    long                    GetCharWidth() const { return mnAdvanceWidth; }

protected:
    friend class GlyphData;
    void                    SetOffset( int nX, int nY ) { maOffset = Point( nX, nY); }
    void                    SetDelta( int nX, int nY )  { maDelta = Point( nX, nY); }
    void                    SetSize( const Size& s )    { maSize = s; }
    void                    SetCharWidth( long nW )     { mnAdvanceWidth = nW; }

private:
    long                    mnAdvanceWidth;
    Point                   maDelta;
    Point                   maOffset;
    Size                    maSize;
};

// the glyph specific data needed by a GlyphCachePeer is usually trivial,
// not attaching it to the corresponding GlyphData would be overkill;
// this is currently only used by the headless (aka svp) plugin, where meInfo is
// basebmp::Format and mpData is SvpGcpHelper*
struct ExtGlyphData
{
    int     meInfo;
    void*   mpData;

    ExtGlyphData() : meInfo(0), mpData(NULL) {}
};

class GlyphData
{
public:
    GlyphData() : mnLruValue(0) {}

    const GlyphMetric&      GetMetric() const           { return maMetric; }
    Size                    GetSize() const             { return maMetric.GetSize(); }

    void                    SetSize( const Size& s)     { maMetric.SetSize( s ); }
    void                    SetOffset( int nX, int nY ) { maMetric.SetOffset( nX, nY ); }
    void                    SetDelta( int nX, int nY )  { maMetric.SetDelta( nX, nY ); }
    void                    SetCharWidth( long nW )     { maMetric.SetCharWidth( nW ); }

    void                    SetLruValue( int n ) const  { mnLruValue = n; }
    long                    GetLruValue() const         { return mnLruValue;}

    ExtGlyphData&           ExtDataRef()                { return maExtData; }
    const ExtGlyphData&     ExtDataRef() const          { return maExtData; }

private:
    GlyphMetric             maMetric;
    ExtGlyphData            maExtData;

    // used by GlyphCache for cache LRU algorithm
    mutable long            mnLruValue;
};

class VCL_DLLPUBLIC ServerFont
{
public:
    ServerFont( const FontSelectPattern&, FtFontInfo* );
    virtual ~ServerFont();

    const OString&      GetFontFileName() const;
    bool                TestFont() const { return mbFaceOk;}
    FT_Face             GetFtFace() const;
    int                 GetLoadFlags() const { return (mnLoadFlags & ~FT_LOAD_IGNORE_TRANSFORM); }
    void                SetFontOptions( boost::shared_ptr<ImplFontOptions> );
    boost::shared_ptr<ImplFontOptions> GetFontOptions() const;
    bool                NeedsArtificialBold() const { return mbArtBold; }
    bool                NeedsArtificialItalic() const { return mbArtItalic; }

    const FontSelectPattern&   GetFontSelData() const      { return maFontSelData; }

    void                FetchFontMetric( ImplFontMetricData&, long& rFactor ) const;
    const unsigned char* GetTable( const char* pName, sal_uLong* pLength );
    int                 GetEmUnits() const { return maFaceFT->units_per_EM;}
    const FT_Size_Metrics& GetMetricsFT() const { return maSizeFT->metrics; }
    const FontCharMapPtr GetFontCharMap() const;
    bool                GetFontCapabilities(vcl::FontCapabilities &) const;

    GlyphData&                  GetGlyphData( sal_GlyphId );
    const GlyphMetric&          GetGlyphMetric( sal_GlyphId aGlyphId )
                                { return GetGlyphData( aGlyphId ).GetMetric(); }
#if ENABLE_GRAPHITE
    virtual GraphiteFaceWrapper* GetGraphiteFace() const;
#endif

    sal_GlyphId         GetGlyphIndex( sal_UCS4 ) const;
    sal_GlyphId         GetRawGlyphIndex( sal_UCS4, sal_UCS4 = 0 ) const;
    sal_GlyphId         FixupGlyphIndex( sal_GlyphId aGlyphId, sal_UCS4 ) const;
    bool                GetGlyphOutline( sal_GlyphId aGlyphId, ::basegfx::B2DPolyPolygon& ) const;
    bool                GetAntialiasAdvice( void ) const;
    bool                GetGlyphBitmap1( sal_GlyphId aGlyphId, RawBitmap& ) const;
    bool                GetGlyphBitmap8( sal_GlyphId aGlyphId, RawBitmap& ) const;

private:
    friend class GlyphCache;
    friend class ServerFontLayout;
    friend class ImplServerFontEntry;
    friend class X11SalGraphics;
    friend class CairoTextRender;

    void                        AddRef() const      { ++mnRefCount; }
    long                        GetRefCount() const { return mnRefCount; }
    long                        Release() const;
    sal_uLong                       GetByteCount() const { return mnBytesUsed; }

    void                InitGlyphData( sal_GlyphId, GlyphData& ) const;
    void                GarbageCollect( long );
    void                        ReleaseFromGarbageCollect();

    int                 ApplyGlyphTransform( int nGlyphFlags, FT_GlyphRec_*, bool ) const;
    bool                ApplyGSUB( const FontSelectPattern& );

    ServerFontLayoutEngine* GetLayoutEngine();

    typedef std::unordered_map<int,GlyphData> GlyphList;
    mutable GlyphList           maGlyphList;

    const FontSelectPattern    maFontSelData;

    // used by GlyphCache for cache LRU algorithm
    mutable long                mnRefCount;
    mutable sal_uLong               mnBytesUsed;

    ServerFont*                 mpPrevGCFont;
    ServerFont*                 mpNextGCFont;

    // 16.16 fixed point values used for a rotated font
    long                        mnCos;
    long                        mnSin;

    bool                        mbCollectedZW;

    int                         mnWidth;
    int                         mnPrioEmbedded;
    int                         mnPrioAntiAlias;
    int                         mnPrioAutoHint;
    FtFontInfo*                 mpFontInfo;
    FT_Int                      mnLoadFlags;
    double                      mfStretch;
    FT_FaceRec_*                maFaceFT;
    FT_SizeRec_*                maSizeFT;

    boost::shared_ptr<ImplFontOptions> mpFontOptions;

    bool                        mbFaceOk;
    bool            mbArtItalic;
    bool            mbArtBold;
    bool            mbUseGamma;

    typedef std::unordered_map<int,int> GlyphSubstitution;
    GlyphSubstitution           maGlyphSubstitution;

    ServerFontLayoutEngine*     mpLayoutEngine;
};

// a class for cache entries for physical font instances that are based on serverfonts
class VCL_DLLPUBLIC ImplServerFontEntry : public ImplFontEntry
{
private:
    ServerFont*    mpServerFont;
    boost::shared_ptr<ImplFontOptions> mpFontOptions;
    bool           mbGotFontOptions;

public:
                   ImplServerFontEntry( FontSelectPattern& );
    virtual        ~ImplServerFontEntry();
    void           SetServerFont(ServerFont* p);
    void           HandleFontOptions();
};

class VCL_DLLPUBLIC ServerFontLayout : public GenericSalLayout
{
private:
    ServerFont&     mrServerFont;
    com::sun::star::uno::Reference<com::sun::star::i18n::XBreakIterator> mxBreak;

    // enforce proper copy semantic
    SAL_DLLPRIVATE  ServerFontLayout( const ServerFontLayout& );
    SAL_DLLPRIVATE  ServerFontLayout& operator=( const ServerFontLayout& );

public:
                    ServerFontLayout( ServerFont& );
    virtual bool    LayoutText( ImplLayoutArgs& ) SAL_OVERRIDE;
    virtual void    AdjustLayout( ImplLayoutArgs& ) SAL_OVERRIDE;
    virtual void    DrawText( SalGraphics& ) const SAL_OVERRIDE;
    void            setNeedFallback(ImplLayoutArgs& rArgs, sal_Int32 nIndex,
                        bool bRightToLeft);

    ServerFont&     GetServerFont() const   { return mrServerFont; }
};

class ServerFontLayoutEngine
{
public:
    virtual         ~ServerFontLayoutEngine() {}
    virtual bool    layout(ServerFontLayout&, ImplLayoutArgs&) = 0;
};

class GlyphCachePeer
{
protected:
                    GlyphCachePeer() : mnBytesUsed(0) {}
    virtual         ~GlyphCachePeer() {}

public:
    sal_Int32       GetByteCount() const { return mnBytesUsed; }
    virtual void    RemovingFont( ServerFont& ) {}
    virtual void    RemovingGlyph( GlyphData& ) {}

protected:
    sal_Int32       mnBytesUsed;
};

class VCL_DLLPUBLIC RawBitmap
{
public:
                    RawBitmap();
                    ~RawBitmap();
    bool            Rotate( int nAngle );

public:
    basebmp::RawMemorySharedArray mpBits;
    sal_uLong           mnAllocated;

    sal_uLong           mnWidth;
    sal_uLong           mnHeight;

    sal_uLong           mnScanlineSize;
    sal_uLong           mnBitCount;

    int             mnXOffset;
    int             mnYOffset;
};

#endif // INCLUDED_VCL_INC_GENERIC_GLYPHCACHE_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>#include <stdio.h>

int main(int argc, char* argv[]) {
	freopen("game.in", "r", stdin);
	freopen("game.out", "w+", stdout);
	int n, o, i, p;
	scanf("%i", &n);
	for(i = 1; i <= n; i++) {
		long long a, b;
		o = 0, p = 0;
		scanf("%lli %lli", &a, &b);
		while(a > b) {
			p++;
			a -= a / 2;
			if(!(a % 2)) a++;
		}
		printf("Case %i: %i", i, p);
	}
	return 0;
}
<commit_msg>commit via s script<commit_after>#include <stdio.h>

int main(int argc, char* argv[]) {
	freopen("game.in", "r", stdin);
	freopen("game.out", "w+", stdout);
	int n, o, i, p;
	scanf("%i", &n);
	for(i = 1; i <= n; i++) {
		long long a, b;
		o = 0, p = 0;
		scanf("%lli %lli", &a, &b);
		while(a > b) {
			p++;
			a -= a / 2;
			if(!(a % 2)) a++;
		}
		// ܳ˻Уȫ
		printf("Case %i: %i\n", i, p);
	}
	return 0;
}
<|endoftext|>


<commit_before>#include "common/grpc/common.h"
#include "common/http/headers.h"

#include "test/mocks/upstream/mocks.h"
#include "test/proto/helloworld.pb.h"
#include "test/test_common/utility.h"

#include "gtest/gtest.h"

namespace Envoy {
namespace Grpc {

TEST(GrpcCommonTest, GetGrpcStatus) {
  Http::TestHeaderMapImpl ok_trailers{{"grpc-status", "0"}};
  EXPECT_EQ(Status::Ok, Common::getGrpcStatus(ok_trailers).value());

  Http::TestHeaderMapImpl no_status_trailers{{"foo", "bar"}};
  EXPECT_FALSE(Common::getGrpcStatus(no_status_trailers).valid());

  Http::TestHeaderMapImpl aborted_trailers{{"grpc-status", "10"}};
  EXPECT_EQ(Status::Aborted, Common::getGrpcStatus(aborted_trailers).value());

  Http::TestHeaderMapImpl unauth_trailers{{"grpc-status", "16"}};
  EXPECT_EQ(Status::Unauthenticated, Common::getGrpcStatus(unauth_trailers).value());

  Http::TestHeaderMapImpl invalid_trailers{{"grpc-status", "-1"}};
  EXPECT_EQ(Status::InvalidCode, Common::getGrpcStatus(invalid_trailers).value());
}

TEST(GrpcCommonTest, GetGrpcMessage) {
  Http::TestHeaderMapImpl empty_trailers;
  EXPECT_EQ("", Common::getGrpcMessage(empty_trailers));

  Http::TestHeaderMapImpl error_trailers{{"grpc-message", "Some error"}};
  EXPECT_EQ("Some error", Common::getGrpcMessage(error_trailers));

  Http::TestHeaderMapImpl empty_error_trailers{{"grpc-message", ""}};
  EXPECT_EQ("", Common::getGrpcMessage(empty_error_trailers));
}

TEST(GrpcCommonTest, ChargeStats) {
  NiceMock<Upstream::MockClusterInfo> cluster;
  Common::chargeStat(cluster, "service", "method", true);
  EXPECT_EQ(1U, cluster.stats_store_.counter("grpc.service.method.success").value());
  EXPECT_EQ(0U, cluster.stats_store_.counter("grpc.service.method.failure").value());
  EXPECT_EQ(1U, cluster.stats_store_.counter("grpc.service.method.total").value());

  Common::chargeStat(cluster, "service", "method", false);
  EXPECT_EQ(1U, cluster.stats_store_.counter("grpc.service.method.success").value());
  EXPECT_EQ(1U, cluster.stats_store_.counter("grpc.service.method.failure").value());
  EXPECT_EQ(2U, cluster.stats_store_.counter("grpc.service.method.total").value());

  Http::TestHeaderMapImpl trailers;
  Http::HeaderEntry& status = trailers.insertGrpcStatus();
  status.value("0", 1);
  Common::chargeStat(cluster, "grpc", "service", "method", &status);
  EXPECT_EQ(1U, cluster.stats_store_.counter("grpc.service.method.0").value());
  EXPECT_EQ(2U, cluster.stats_store_.counter("grpc.service.method.success").value());
  EXPECT_EQ(1U, cluster.stats_store_.counter("grpc.service.method.failure").value());
  EXPECT_EQ(3U, cluster.stats_store_.counter("grpc.service.method.total").value());

  status.value("1", 1);
  Common::chargeStat(cluster, "grpc", "service", "method", &status);
  EXPECT_EQ(1U, cluster.stats_store_.counter("grpc.service.method.0").value());
  EXPECT_EQ(1U, cluster.stats_store_.counter("grpc.service.method.1").value());
  EXPECT_EQ(2U, cluster.stats_store_.counter("grpc.service.method.success").value());
  EXPECT_EQ(2U, cluster.stats_store_.counter("grpc.service.method.failure").value());
  EXPECT_EQ(4U, cluster.stats_store_.counter("grpc.service.method.total").value());
}

TEST(GrpcCommonTest, PrepareHeaders) {
  Http::MessagePtr message = Common::prepareHeaders("cluster", "service_name", "method_name");

  EXPECT_STREQ("POST", message->headers().Method()->value().c_str());
  EXPECT_STREQ("/service_name/method_name", message->headers().Path()->value().c_str());
  EXPECT_STREQ("cluster", message->headers().Host()->value().c_str());
  EXPECT_STREQ("application/grpc", message->headers().ContentType()->value().c_str());
}

TEST(GrpcCommonTest, ResolveServiceAndMethod) {
  std::string service;
  std::string method;
  Http::HeaderMapImpl headers;
  Http::HeaderEntry& path = headers.insertPath();
  path.value(std::string("/service_name/method_name"));
  EXPECT_TRUE(Common::resolveServiceAndMethod(&path, &service, &method));
  EXPECT_EQ("service_name", service);
  EXPECT_EQ("method_name", method);
  path.value(std::string(""));
  EXPECT_FALSE(Common::resolveServiceAndMethod(&path, &service, &method));
  path.value(std::string("/"));
  EXPECT_FALSE(Common::resolveServiceAndMethod(&path, &service, &method));
  path.value(std::string("//"));
  EXPECT_FALSE(Common::resolveServiceAndMethod(&path, &service, &method));
  path.value(std::string("/service_name"));
  EXPECT_FALSE(Common::resolveServiceAndMethod(&path, &service, &method));
  path.value(std::string("/service_name/"));
  EXPECT_FALSE(Common::resolveServiceAndMethod(&path, &service, &method));
}

TEST(GrpcCommonTest, GrpcToHttpStatus) {
  const std::vector<std::pair<Status::GrpcStatus, uint64_t>> test_set = {
      {Status::GrpcStatus::Ok, 200},
      {Status::GrpcStatus::Canceled, 499},
      {Status::GrpcStatus::Unknown, 500},
      {Status::GrpcStatus::InvalidArgument, 400},
      {Status::GrpcStatus::DeadlineExceeded, 504},
      {Status::GrpcStatus::NotFound, 404},
      {Status::GrpcStatus::AlreadyExists, 409},
      {Status::GrpcStatus::PermissionDenied, 403},
      {Status::GrpcStatus::ResourceExhausted, 429},
      {Status::GrpcStatus::FailedPrecondition, 400},
      {Status::GrpcStatus::Aborted, 409},
      {Status::GrpcStatus::OutOfRange, 400},
      {Status::GrpcStatus::Unimplemented, 501},
      {Status::GrpcStatus::Internal, 500},
      {Status::GrpcStatus::Unavailable, 503},
      {Status::GrpcStatus::DataLoss, 500},
      {Status::GrpcStatus::Unauthenticated, 401},
      {Status::GrpcStatus::InvalidCode, 500},
  };
  for (const auto& test_case : test_set) {
    EXPECT_EQ(test_case.second, Common::grpcToHttpStatus(test_case.first));
  }
}

TEST(GrpcCommonTest, HasGrpcContentType) {
  {
    Http::TestHeaderMapImpl headers{};
    EXPECT_FALSE(Common::hasGrpcContentType(headers));
  }
  auto isGrpcContentType = [](const std::string& s) {
    Http::TestHeaderMapImpl headers{{"content-type", s}};
    return Common::hasGrpcContentType(headers);
  };
  EXPECT_FALSE(isGrpcContentType(""));
  EXPECT_FALSE(isGrpcContentType("application/text"));
  EXPECT_TRUE(isGrpcContentType("application/grpc"));
  EXPECT_TRUE(isGrpcContentType("application/grpc+"));
  EXPECT_TRUE(isGrpcContentType("application/grpc+foo"));
  EXPECT_FALSE(isGrpcContentType("application/grpc-"));
  EXPECT_FALSE(isGrpcContentType("application/grpc-web"));
  EXPECT_FALSE(isGrpcContentType("application/grpc-web+foo"));
}

TEST(GrpcCommonTest, IsGrpcResponseHeader) {
  Http::TestHeaderMapImpl grpc_status_only{{":status", "500"}, {"grpc-status", "14"}};
  EXPECT_TRUE(Common::isGrpcResponseHeader(grpc_status_only, true));
  EXPECT_FALSE(Common::isGrpcResponseHeader(grpc_status_only, false));

  Http::TestHeaderMapImpl grpc_response_header{{":status", "200"},
                                               {"content-type", "application/grpc"}};
  EXPECT_FALSE(Common::isGrpcResponseHeader(grpc_response_header, true));
  EXPECT_TRUE(Common::isGrpcResponseHeader(grpc_response_header, false));

  Http::TestHeaderMapImpl json_response_header{{":status", "200"},
                                               {"content-type", "application/json"}};
  EXPECT_FALSE(Common::isGrpcResponseHeader(json_response_header, true));
  EXPECT_FALSE(Common::isGrpcResponseHeader(json_response_header, false));
}

} // namespace Grpc
} // namespace Envoy
<commit_msg>Improve Grpc::Common test coverage (#2494)<commit_after>#include "common/grpc/common.h"
#include "common/http/headers.h"
#include "common/http/message_impl.h"

#include "test/mocks/upstream/mocks.h"
#include "test/proto/helloworld.pb.h"
#include "test/test_common/utility.h"

#include "gtest/gtest.h"

namespace Envoy {
namespace Grpc {

TEST(GrpcCommonTest, GetGrpcStatus) {
  Http::TestHeaderMapImpl ok_trailers{{"grpc-status", "0"}};
  EXPECT_EQ(Status::Ok, Common::getGrpcStatus(ok_trailers).value());

  Http::TestHeaderMapImpl no_status_trailers{{"foo", "bar"}};
  EXPECT_FALSE(Common::getGrpcStatus(no_status_trailers).valid());

  Http::TestHeaderMapImpl aborted_trailers{{"grpc-status", "10"}};
  EXPECT_EQ(Status::Aborted, Common::getGrpcStatus(aborted_trailers).value());

  Http::TestHeaderMapImpl unauth_trailers{{"grpc-status", "16"}};
  EXPECT_EQ(Status::Unauthenticated, Common::getGrpcStatus(unauth_trailers).value());

  Http::TestHeaderMapImpl invalid_trailers{{"grpc-status", "-1"}};
  EXPECT_EQ(Status::InvalidCode, Common::getGrpcStatus(invalid_trailers).value());
}

TEST(GrpcCommonTest, GetGrpcMessage) {
  Http::TestHeaderMapImpl empty_trailers;
  EXPECT_EQ("", Common::getGrpcMessage(empty_trailers));

  Http::TestHeaderMapImpl error_trailers{{"grpc-message", "Some error"}};
  EXPECT_EQ("Some error", Common::getGrpcMessage(error_trailers));

  Http::TestHeaderMapImpl empty_error_trailers{{"grpc-message", ""}};
  EXPECT_EQ("", Common::getGrpcMessage(empty_error_trailers));
}

TEST(GrpcCommonTest, ChargeStats) {
  NiceMock<Upstream::MockClusterInfo> cluster;
  Common::chargeStat(cluster, "service", "method", true);
  EXPECT_EQ(1U, cluster.stats_store_.counter("grpc.service.method.success").value());
  EXPECT_EQ(0U, cluster.stats_store_.counter("grpc.service.method.failure").value());
  EXPECT_EQ(1U, cluster.stats_store_.counter("grpc.service.method.total").value());

  Common::chargeStat(cluster, "service", "method", false);
  EXPECT_EQ(1U, cluster.stats_store_.counter("grpc.service.method.success").value());
  EXPECT_EQ(1U, cluster.stats_store_.counter("grpc.service.method.failure").value());
  EXPECT_EQ(2U, cluster.stats_store_.counter("grpc.service.method.total").value());

  Http::TestHeaderMapImpl trailers;
  Http::HeaderEntry& status = trailers.insertGrpcStatus();
  status.value("0", 1);
  Common::chargeStat(cluster, "grpc", "service", "method", &status);
  EXPECT_EQ(1U, cluster.stats_store_.counter("grpc.service.method.0").value());
  EXPECT_EQ(2U, cluster.stats_store_.counter("grpc.service.method.success").value());
  EXPECT_EQ(1U, cluster.stats_store_.counter("grpc.service.method.failure").value());
  EXPECT_EQ(3U, cluster.stats_store_.counter("grpc.service.method.total").value());

  status.value("1", 1);
  Common::chargeStat(cluster, "grpc", "service", "method", &status);
  EXPECT_EQ(1U, cluster.stats_store_.counter("grpc.service.method.0").value());
  EXPECT_EQ(1U, cluster.stats_store_.counter("grpc.service.method.1").value());
  EXPECT_EQ(2U, cluster.stats_store_.counter("grpc.service.method.success").value());
  EXPECT_EQ(2U, cluster.stats_store_.counter("grpc.service.method.failure").value());
  EXPECT_EQ(4U, cluster.stats_store_.counter("grpc.service.method.total").value());
}

TEST(GrpcCommonTest, PrepareHeaders) {
  Http::MessagePtr message = Common::prepareHeaders("cluster", "service_name", "method_name");

  EXPECT_STREQ("POST", message->headers().Method()->value().c_str());
  EXPECT_STREQ("/service_name/method_name", message->headers().Path()->value().c_str());
  EXPECT_STREQ("cluster", message->headers().Host()->value().c_str());
  EXPECT_STREQ("application/grpc", message->headers().ContentType()->value().c_str());
}

TEST(GrpcCommonTest, ResolveServiceAndMethod) {
  std::string service;
  std::string method;
  Http::HeaderMapImpl headers;
  Http::HeaderEntry& path = headers.insertPath();
  path.value(std::string("/service_name/method_name"));
  EXPECT_TRUE(Common::resolveServiceAndMethod(&path, &service, &method));
  EXPECT_EQ("service_name", service);
  EXPECT_EQ("method_name", method);
  path.value(std::string(""));
  EXPECT_FALSE(Common::resolveServiceAndMethod(&path, &service, &method));
  path.value(std::string("/"));
  EXPECT_FALSE(Common::resolveServiceAndMethod(&path, &service, &method));
  path.value(std::string("//"));
  EXPECT_FALSE(Common::resolveServiceAndMethod(&path, &service, &method));
  path.value(std::string("/service_name"));
  EXPECT_FALSE(Common::resolveServiceAndMethod(&path, &service, &method));
  path.value(std::string("/service_name/"));
  EXPECT_FALSE(Common::resolveServiceAndMethod(&path, &service, &method));
}

TEST(GrpcCommonTest, GrpcToHttpStatus) {
  const std::vector<std::pair<Status::GrpcStatus, uint64_t>> test_set = {
      {Status::GrpcStatus::Ok, 200},
      {Status::GrpcStatus::Canceled, 499},
      {Status::GrpcStatus::Unknown, 500},
      {Status::GrpcStatus::InvalidArgument, 400},
      {Status::GrpcStatus::DeadlineExceeded, 504},
      {Status::GrpcStatus::NotFound, 404},
      {Status::GrpcStatus::AlreadyExists, 409},
      {Status::GrpcStatus::PermissionDenied, 403},
      {Status::GrpcStatus::ResourceExhausted, 429},
      {Status::GrpcStatus::FailedPrecondition, 400},
      {Status::GrpcStatus::Aborted, 409},
      {Status::GrpcStatus::OutOfRange, 400},
      {Status::GrpcStatus::Unimplemented, 501},
      {Status::GrpcStatus::Internal, 500},
      {Status::GrpcStatus::Unavailable, 503},
      {Status::GrpcStatus::DataLoss, 500},
      {Status::GrpcStatus::Unauthenticated, 401},
      {Status::GrpcStatus::InvalidCode, 500},
  };
  for (const auto& test_case : test_set) {
    EXPECT_EQ(test_case.second, Common::grpcToHttpStatus(test_case.first));
  }
}

TEST(GrpcCommonTest, HttpToGrpcStatus) {
  const std::vector<std::pair<uint64_t, Status::GrpcStatus>> test_set = {
      {400, Status::GrpcStatus::Internal},         {401, Status::GrpcStatus::Unauthenticated},
      {403, Status::GrpcStatus::PermissionDenied}, {404, Status::GrpcStatus::Unimplemented},
      {429, Status::GrpcStatus::Unavailable},      {502, Status::GrpcStatus::Unavailable},
      {503, Status::GrpcStatus::Unavailable},      {504, Status::GrpcStatus::Unavailable},
      {500, Status::GrpcStatus::Unknown},
  };
  for (const auto& test_case : test_set) {
    EXPECT_EQ(test_case.second, Common::httpToGrpcStatus(test_case.first));
  }
}

TEST(GrpcCommonTest, HasGrpcContentType) {
  {
    Http::TestHeaderMapImpl headers{};
    EXPECT_FALSE(Common::hasGrpcContentType(headers));
  }
  auto isGrpcContentType = [](const std::string& s) {
    Http::TestHeaderMapImpl headers{{"content-type", s}};
    return Common::hasGrpcContentType(headers);
  };
  EXPECT_FALSE(isGrpcContentType(""));
  EXPECT_FALSE(isGrpcContentType("application/text"));
  EXPECT_TRUE(isGrpcContentType("application/grpc"));
  EXPECT_TRUE(isGrpcContentType("application/grpc+"));
  EXPECT_TRUE(isGrpcContentType("application/grpc+foo"));
  EXPECT_FALSE(isGrpcContentType("application/grpc-"));
  EXPECT_FALSE(isGrpcContentType("application/grpc-web"));
  EXPECT_FALSE(isGrpcContentType("application/grpc-web+foo"));
}

TEST(GrpcCommonTest, IsGrpcResponseHeader) {
  Http::TestHeaderMapImpl grpc_status_only{{":status", "500"}, {"grpc-status", "14"}};
  EXPECT_TRUE(Common::isGrpcResponseHeader(grpc_status_only, true));
  EXPECT_FALSE(Common::isGrpcResponseHeader(grpc_status_only, false));

  Http::TestHeaderMapImpl grpc_response_header{{":status", "200"},
                                               {"content-type", "application/grpc"}};
  EXPECT_FALSE(Common::isGrpcResponseHeader(grpc_response_header, true));
  EXPECT_TRUE(Common::isGrpcResponseHeader(grpc_response_header, false));

  Http::TestHeaderMapImpl json_response_header{{":status", "200"},
                                               {"content-type", "application/json"}};
  EXPECT_FALSE(Common::isGrpcResponseHeader(json_response_header, true));
  EXPECT_FALSE(Common::isGrpcResponseHeader(json_response_header, false));
}

TEST(GrpcCommonTest, ValidateResponse) {
  {
    Http::ResponseMessageImpl response(
        Http::HeaderMapPtr{new Http::TestHeaderMapImpl{{":status", "200"}}});
    response.trailers(Http::HeaderMapPtr{new Http::TestHeaderMapImpl{{"grpc-status", "0"}}});
    EXPECT_NO_THROW(Common::validateResponse(response));
  }
  {
    Http::ResponseMessageImpl response(
        Http::HeaderMapPtr{new Http::TestHeaderMapImpl{{":status", "503"}}});
    EXPECT_THROW_WITH_MESSAGE(Common::validateResponse(response), Exception,
                              "non-200 response code");
  }
  {
    Http::ResponseMessageImpl response(
        Http::HeaderMapPtr{new Http::TestHeaderMapImpl{{":status", "200"}}});
    response.trailers(Http::HeaderMapPtr{new Http::TestHeaderMapImpl{{"grpc-status", "100"}}});
    EXPECT_THROW_WITH_MESSAGE(Common::validateResponse(response), Exception,
                              "bad grpc-status trailer");
  }
  {
    Http::ResponseMessageImpl response(
        Http::HeaderMapPtr{new Http::TestHeaderMapImpl{{":status", "200"}}});
    response.trailers(Http::HeaderMapPtr{new Http::TestHeaderMapImpl{{"grpc-status", "4"}}});
    EXPECT_THROW_WITH_MESSAGE(Common::validateResponse(response), Exception, "");
  }
  {
    Http::ResponseMessageImpl response(
        Http::HeaderMapPtr{new Http::TestHeaderMapImpl{{":status", "200"}}});
    response.trailers(Http::HeaderMapPtr{
        new Http::TestHeaderMapImpl{{"grpc-status", "4"}, {"grpc-message", "custom error"}}});
    EXPECT_THROW_WITH_MESSAGE(Common::validateResponse(response), Exception, "custom error");
  }
  {
    Http::ResponseMessageImpl response(Http::HeaderMapPtr{
        new Http::TestHeaderMapImpl{{":status", "200"}, {"grpc-status", "100"}}});
    EXPECT_THROW_WITH_MESSAGE(Common::validateResponse(response), Exception,
                              "bad grpc-status header");
  }
  {
    Http::ResponseMessageImpl response(
        Http::HeaderMapPtr{new Http::TestHeaderMapImpl{{":status", "200"}, {"grpc-status", "4"}}});
    EXPECT_THROW_WITH_MESSAGE(Common::validateResponse(response), Exception, "");
  }
  {
    Http::ResponseMessageImpl response(Http::HeaderMapPtr{new Http::TestHeaderMapImpl{
        {":status", "200"}, {"grpc-status", "4"}, {"grpc-message", "custom error"}}});
    EXPECT_THROW_WITH_MESSAGE(Common::validateResponse(response), Exception, "custom error");
  }
}

} // namespace Grpc
} // namespace Envoy
<|endoftext|>