<commit_before>#pragma once

#include <algorithm>   // copy, set_difference, set_intersection, set_union
#include <functional>  // hash
#include <initializer_list>
#include <iosfwd>
#include <iterator>    // inserter
#include <type_traits> // enable_if, is_integral
#include <utility>     // pair

#include <boost/container/flat_set.hpp>

#include "sdd/values_manager_fwd.hh"
#include "sdd/mem/ptr.hh"
#include "sdd/mem/unique.hh"
#include "sdd/util/hash.hh"
#include "sdd/values/values_traits.hh"

namespace sdd { namespace values {

/*------------------------------------------------------------------------------------------------*/

/// @brief A unified set of values, implemented with a sorted vector.
template <typename Value>
class flat_set final
{
public:

  /// @brief The type of the contained value.
  using value_type = Value;

  /// @internal
  /// @brief The type of the real container.
  using data_type = boost::container::flat_set<value_type>;

  /// @internal
  using unique_type = mem::unique<data_type>;

  /// @internal
  using ptr_type = mem::ptr<unique_type>;

  /// @brief The type of an iterator on a flat set of values.
  using const_iterator = typename data_type::const_iterator;

  /// @brief The type of an reverse iterator on a flat set of values.
  using const_reverse_iterator = typename data_type::const_reverse_iterator;

private:

  /// @brief A pointer to the unified set of values.
  ptr_type ptr_;

public:

  /// @brief Default copy constructor.
  flat_set(const flat_set&) = default;

  /// @brief Default copy operator.
  flat_set& operator=(const flat_set&) = default;

  /// @brief Default constructor.
  flat_set()
    : ptr_(empty_set())
  {}

  /// @brief Constructor with a range.
  template <typename InputIterator>
  flat_set(InputIterator begin, InputIterator end)
    : ptr_(create(begin, end))
  {}

  /// @brief Constructor with a initializer_list.
  flat_set(std::initializer_list<Value> values)
    : flat_set(values.begin(), values.end())
  {}

  /// @brief Constructor from a temporary data_type.
  flat_set(data_type&& fs)
    : ptr_(create(std::move(fs)))
  {}

  /// @brief Insert a value.
  std::pair<const_iterator, bool>
  insert(const Value& x)
  {
    data_type fs(ptr_->data());
    const auto insertion = fs.insert(x);
    if (insertion.second)
    {
      fs.shrink_to_fit();
      ptr_ = create(std::move(fs));
    }
    return insertion;
  }

  /// @brief Returns an iterator pointing to the first element in the flat set.
  const_iterator
  begin()
  const noexcept
  {
    return ptr_->data().cbegin();
  }

  /// @brief Returns an iterator pointing to the last element in the flat set.
  const_iterator
  end()
  const noexcept
  {
    return ptr_->data().cend();
  }

  /// @brief Returns an iterator pointing to the first element in the flat set.
  const_iterator
  cbegin()
  const noexcept
  {
    return ptr_->data().cbegin();
  }

  /// @brief Returns an iterator pointing to the last element in the flat set.
  const_iterator
  cend()
  const noexcept
  {
    return ptr_->data().cend();
  }

  /// @brief Returns a reverse iterator pointing to the last element in the flat set.
  const_reverse_iterator
  rbegin()
  const noexcept
  {
    return ptr_->data().crbegin();
  }

  /// @brief Returns a reverse iterator pointing to the first element in the flat set.
  const_reverse_iterator
  rend()
  const noexcept
  {
    return ptr_->data().crend();
  }

  /// @brief Returns a reverse iterator pointing to the first element in the flat set.
  const_reverse_iterator
  crbegin()
  const noexcept
  {
    return ptr_->data().crbegin();
  }

  /// @brief Returns a reverse iterator pointing to the last element in the flat set.
  const_reverse_iterator
  crend()
  const noexcept
  {
    return ptr_->data().crend();
  }

  /// @brief Tell if this set of values is empty.
  bool
  empty()
  const noexcept
  {
    return ptr_->data().empty();
  }

  /// @brief Get the number of contained values.
  std::size_t
  size()
  const noexcept
  {
    return ptr_->data().size();
  }

  /// @brief Find a value.
  const_iterator
  find(const Value& x)
  const
  {
    return ptr_->data().find(x);
  }

  /// @brief Erase a value.
  std::size_t
  erase(const Value& x)
  {
    data_type d(ptr_->data());
    const std::size_t nb_erased = d.erase(x);
    ptr_ = create(std::move(d));
    return nb_erased;
  }

  /// @brief
  const_iterator
  lower_bound(const Value& x)
  const
  {
    return ptr_->data().lower_bound(x);
  }

  /// @brief
  const_iterator
  upper_bound(const Value& x)
  const
  {
    return ptr_->data().upper_bound(x);
  }

  /// @internal
  /// @brief Get the pointer to the unified data.
  ptr_type
  ptr()
  const noexcept
  {
    return ptr_;
  }

  /// @internal
  static
  ptr_type
  empty_set()
  {
    return global_values<flat_set<Value>>().state.empty;
  }

  /// @brief Equality.
  ///
  /// O(1).
  friend
  bool
  operator==(const flat_set<Value>& lhs, const flat_set<Value>& rhs)
  noexcept
  {
    // Pointer equality.
    return lhs.ptr() == rhs.ptr();
  }

  /// @brief Inequality.
  ///
  /// O(1).
  friend
  bool
  operator!=(const flat_set<Value>& lhs, const flat_set<Value>& rhs)
  noexcept
  {
    // Pointer inequality.
    return not(lhs.ptr() == rhs.ptr());
  }

  /// @brief Less than comparison.
  ///
  /// O(1). The order on flat_set is arbitrary.
  friend
  bool
  operator<(const flat_set<Value>& lhs, const flat_set<Value>& rhs)
  noexcept
  {
    // Pointer comparison.
    return lhs.ptr() < rhs.ptr();
  }

private:

  /// @brief Create a smart pointer to unified set of values, from a pair of iterators.
  template <typename InputIterator>
  static
  ptr_type
  create(InputIterator begin, InputIterator end)
  {
    if (begin == end)
    {
      return empty_set();
    }
    else
    {
      return ptr_type(&unify(data_type(begin, end)));
    }
  }

  /// @brief Create a smart pointer to a unified set of values, from a boost::container::flat_set.
  static
  ptr_type
  create(data_type&& x)
  {
    if (x.empty())
    {
      return empty_set();
    }
    else
    {
      return ptr_type(&unify(std::move(x)));
    }
  }

  /// @brief Return the unfied version of a boost::container::flat_set.
  static
  unique_type&
  unify(data_type&& x)
  {
    auto& ut = global_values<flat_set<Value>>().state.unique_table;
    char* addr = ut.allocate(0 /*extra bytes*/);
    unique_type* u = new (addr) unique_type(std::move(x));
    return ut(u, 0);
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Will be used by sdd::manager.
template <typename Value>
struct flat_set_manager
{
  /// @brief The type of a unified flat_set.
  using unique_type = typename flat_set<Value>::unique_type;

  /// @brief The type of smart pointer to a unified flat_set.
  using ptr_type = typename flat_set<Value>::ptr_type;

  /// @brief The type of this manager's statistics.
  using statistics_type = mem::unique_table_statistics;

  /// @brief Manage the handler needed by ptr when a unified data is no longer referenced.
  struct ptr_handler
  {
    ptr_handler(mem::unique_table<unique_type>& ut)
    {
      mem::set_deletion_handler<unique_type>([&](const unique_type* u){ut.erase(u);});
    }

    ~ptr_handler()
    {
      mem::reset_deletion_handler<unique_type>();
    }
  } handler;

  /// @brief The set of unified flat_set.
  mem::unique_table<unique_type> unique_table;

  /// @brief The cached empty flat_set.
  const ptr_type empty;

  /// @brief Constructor.
  template <typename C>
  flat_set_manager(const C& configuration)
    : handler(unique_table)
    , unique_table(configuration.flat_set_unique_table_size)
    , empty(mk_empty())
  {}

  const statistics_type&
  statistics()
  const noexcept
  {
    return unique_table.stats();
  }

private:

  /// @brief Helper to construct the empty flat_set.
  ptr_type
  mk_empty()
  {
    char* addr = unique_table.allocate(0 /*extra bytes*/);
    unique_type* u = new (addr) unique_type;
    return ptr_type(&unique_table(u, 0));
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @related flat_set
///
/// Indicate to the library that flat_set needs to store a global state.
template <typename Value>
struct values_traits<flat_set<Value>>
{
  static constexpr bool stateful = true;
  static constexpr bool fast_iterable = true;
  using state_type = flat_set_manager<Value>;
  using builder = boost::container::flat_set<Value>;
};

/*------------------------------------------------------------------------------------------------*/

template <typename Value>
struct display_value
{
  void
  operator()(std::ostream& os, const Value& v)
  const
  {
    os << v;
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @brief Textual output of a flat_set
/// @related flat_set
template <typename Value>
std::enable_if_t<not std::is_integral<Value>::value, std::ostream&>
operator<<(std::ostream& os, const flat_set<Value>& fs)
{
  os << "{";
  if (not fs.empty())
  {
    std::copy(fs.cbegin(), std::prev(fs.cend()), std::ostream_iterator<Value>(os, ","));
    os << *std::prev(fs.cend());
  }
  return os << "}";
}

/// @brief Textual output of a flat_set
///
/// When Value is an integral type, consecutive values are displayed like 1..9.
/// @related flat_set
template <typename Value>
std::enable_if_t<std::is_integral<Value>::value, std::ostream&>
operator<<(std::ostream& os, const flat_set<Value>& fs)
{
  os << "{";
  if (fs.size() == 1)
  {
    os << *std::begin(fs);
  }
  else if ((fs.size() > 1))
  {
    auto cit = fs.cbegin();
    while (cit != fs.cend())
    {
      auto first = cit;
      while (std::next(cit) != fs.cend() and (*cit + 1) == (*std::next(cit))) // consecutive values
      {
        ++cit;
      }
      if (first == cit)
      {
        display_value<Value>()(os, *cit);
      }
      else
      {
        display_value<Value>()(os, *first);
        os << "..";
        display_value<Value>()(os, *cit);
      }
      if (std::next(cit) != fs.cend())
      {
        os << ",";
      }
      ++cit;
    }
  }
  return os << "}";
}

/*------------------------------------------------------------------------------------------------*/

/// @related flat_set
template <typename Value>
inline
flat_set<Value>
difference(const flat_set<Value>& lhs, const flat_set<Value>& rhs)
noexcept
{
  typename flat_set<Value>::data_type res;
  std::set_difference( lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()
                     , std::inserter(res, res.begin()));
  return {std::move(res)};
}

/*------------------------------------------------------------------------------------------------*/

/// @related flat_set
template <typename Value>
inline
flat_set<Value>
intersection(const flat_set<Value>& lhs, const flat_set<Value>& rhs)
noexcept
{
  typename flat_set<Value>::data_type res;
  std::set_intersection( lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()
                       , std::inserter(res, res.begin()));
  return {std::move(res)};
}

/*------------------------------------------------------------------------------------------------*/

/// @related flat_set
template <typename Value>
inline
flat_set<Value>
sum(const flat_set<Value>& lhs, const flat_set<Value>& rhs)
noexcept
{
  typename flat_set<Value>::data_type res;
  std::set_union( lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()
                , std::inserter(res, res.begin()));
  return {std::move(res)};
}

/*------------------------------------------------------------------------------------------------*/

}} // namespace sdd::values

namespace std {

/*------------------------------------------------------------------------------------------------*/

/// @brief Hash specialization for boost::container::flat_set
template <typename Key, typename Compare, typename Allocator>
struct hash<boost::container::flat_set<Key, Compare, Allocator>>
{
  std::size_t
  operator()(const boost::container::flat_set<Key, Compare, Allocator>& c)
  const noexcept
  {
    using namespace sdd::hash;
    return seed() (range(c));
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @brief Hash specialization for sdd::values::flat_set
template <typename Value>
struct hash<sdd::values::flat_set<Value>>
{
  std::size_t
  operator()(const sdd::values::flat_set<Value>& fs)
  const noexcept
  {
    return sdd::hash::seed(fs.ptr());
  }
};

/*------------------------------------------------------------------------------------------------*/

} // namespace std
<commit_msg>Add a count() method to flat_set<commit_after>#pragma once

#include <algorithm>   // copy, set_difference, set_intersection, set_union
#include <functional>  // hash
#include <initializer_list>
#include <iosfwd>
#include <iterator>    // inserter
#include <type_traits> // enable_if, is_integral
#include <utility>     // pair

#include <boost/container/flat_set.hpp>

#include "sdd/values_manager_fwd.hh"
#include "sdd/mem/ptr.hh"
#include "sdd/mem/unique.hh"
#include "sdd/util/hash.hh"
#include "sdd/values/values_traits.hh"

namespace sdd { namespace values {

/*------------------------------------------------------------------------------------------------*/

/// @brief A unified set of values, implemented with a sorted vector.
template <typename Value>
class flat_set final
{
public:

  /// @brief The type of the contained value.
  using value_type = Value;

  /// @internal
  /// @brief The type of the real container.
  using data_type = boost::container::flat_set<value_type>;

  /// @internal
  using unique_type = mem::unique<data_type>;

  /// @internal
  using ptr_type = mem::ptr<unique_type>;

  /// @brief The type of an iterator on a flat set of values.
  using const_iterator = typename data_type::const_iterator;

  /// @brief The type of an reverse iterator on a flat set of values.
  using const_reverse_iterator = typename data_type::const_reverse_iterator;

private:

  /// @brief A pointer to the unified set of values.
  ptr_type ptr_;

public:

  /// @brief Default copy constructor.
  flat_set(const flat_set&) = default;

  /// @brief Default copy operator.
  flat_set& operator=(const flat_set&) = default;

  /// @brief Default constructor.
  flat_set()
    : ptr_(empty_set())
  {}

  /// @brief Constructor with a range.
  template <typename InputIterator>
  flat_set(InputIterator begin, InputIterator end)
    : ptr_(create(begin, end))
  {}

  /// @brief Constructor with a initializer_list.
  flat_set(std::initializer_list<Value> values)
    : flat_set(values.begin(), values.end())
  {}

  /// @brief Constructor from a temporary data_type.
  flat_set(data_type&& fs)
    : ptr_(create(std::move(fs)))
  {}

  /// @brief Insert a value.
  std::pair<const_iterator, bool>
  insert(const Value& x)
  {
    data_type fs(ptr_->data());
    const auto insertion = fs.insert(x);
    if (insertion.second)
    {
      fs.shrink_to_fit();
      ptr_ = create(std::move(fs));
    }
    return insertion;
  }

  /// @brief Returns an iterator pointing to the first element in the flat set.
  const_iterator
  begin()
  const noexcept
  {
    return ptr_->data().cbegin();
  }

  /// @brief Returns an iterator pointing to the last element in the flat set.
  const_iterator
  end()
  const noexcept
  {
    return ptr_->data().cend();
  }

  /// @brief Returns an iterator pointing to the first element in the flat set.
  const_iterator
  cbegin()
  const noexcept
  {
    return ptr_->data().cbegin();
  }

  /// @brief Returns an iterator pointing to the last element in the flat set.
  const_iterator
  cend()
  const noexcept
  {
    return ptr_->data().cend();
  }

  /// @brief Returns a reverse iterator pointing to the last element in the flat set.
  const_reverse_iterator
  rbegin()
  const noexcept
  {
    return ptr_->data().crbegin();
  }

  /// @brief Returns a reverse iterator pointing to the first element in the flat set.
  const_reverse_iterator
  rend()
  const noexcept
  {
    return ptr_->data().crend();
  }

  /// @brief Returns a reverse iterator pointing to the first element in the flat set.
  const_reverse_iterator
  crbegin()
  const noexcept
  {
    return ptr_->data().crbegin();
  }

  /// @brief Returns a reverse iterator pointing to the last element in the flat set.
  const_reverse_iterator
  crend()
  const noexcept
  {
    return ptr_->data().crend();
  }

  /// @brief Tell if this set of values is empty.
  bool
  empty()
  const noexcept
  {
    return ptr_->data().empty();
  }

  /// @brief Get the number of contained values.
  std::size_t
  size()
  const noexcept
  {
    return ptr_->data().size();
  }

  /// @brief Find a value.
  const_iterator
  find(const Value& x)
  const
  {
    return ptr_->data().find(x);
  }

  /// @brief Returns the number of elements with key @p x
  std::size_t
  count(const Value& x)
  const noexcept
  {
    return ptr_->data().count(x);
  }

  /// @brief Erase a value.
  std::size_t
  erase(const Value& x)
  {
    data_type d(ptr_->data());
    const std::size_t nb_erased = d.erase(x);
    ptr_ = create(std::move(d));
    return nb_erased;
  }

  /// @brief
  const_iterator
  lower_bound(const Value& x)
  const
  {
    return ptr_->data().lower_bound(x);
  }

  /// @brief
  const_iterator
  upper_bound(const Value& x)
  const
  {
    return ptr_->data().upper_bound(x);
  }

  /// @internal
  /// @brief Get the pointer to the unified data.
  ptr_type
  ptr()
  const noexcept
  {
    return ptr_;
  }

  /// @internal
  static
  ptr_type
  empty_set()
  {
    return global_values<flat_set<Value>>().state.empty;
  }

  /// @brief Equality.
  ///
  /// O(1).
  friend
  bool
  operator==(const flat_set<Value>& lhs, const flat_set<Value>& rhs)
  noexcept
  {
    // Pointer equality.
    return lhs.ptr() == rhs.ptr();
  }

  /// @brief Inequality.
  ///
  /// O(1).
  friend
  bool
  operator!=(const flat_set<Value>& lhs, const flat_set<Value>& rhs)
  noexcept
  {
    // Pointer inequality.
    return not(lhs.ptr() == rhs.ptr());
  }

  /// @brief Less than comparison.
  ///
  /// O(1). The order on flat_set is arbitrary.
  friend
  bool
  operator<(const flat_set<Value>& lhs, const flat_set<Value>& rhs)
  noexcept
  {
    // Pointer comparison.
    return lhs.ptr() < rhs.ptr();
  }

private:

  /// @brief Create a smart pointer to unified set of values, from a pair of iterators.
  template <typename InputIterator>
  static
  ptr_type
  create(InputIterator begin, InputIterator end)
  {
    if (begin == end)
    {
      return empty_set();
    }
    else
    {
      return ptr_type(&unify(data_type(begin, end)));
    }
  }

  /// @brief Create a smart pointer to a unified set of values, from a boost::container::flat_set.
  static
  ptr_type
  create(data_type&& x)
  {
    if (x.empty())
    {
      return empty_set();
    }
    else
    {
      return ptr_type(&unify(std::move(x)));
    }
  }

  /// @brief Return the unfied version of a boost::container::flat_set.
  static
  unique_type&
  unify(data_type&& x)
  {
    auto& ut = global_values<flat_set<Value>>().state.unique_table;
    char* addr = ut.allocate(0 /*extra bytes*/);
    unique_type* u = new (addr) unique_type(std::move(x));
    return ut(u, 0);
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @brief Will be used by sdd::manager.
template <typename Value>
struct flat_set_manager
{
  /// @brief The type of a unified flat_set.
  using unique_type = typename flat_set<Value>::unique_type;

  /// @brief The type of smart pointer to a unified flat_set.
  using ptr_type = typename flat_set<Value>::ptr_type;

  /// @brief The type of this manager's statistics.
  using statistics_type = mem::unique_table_statistics;

  /// @brief Manage the handler needed by ptr when a unified data is no longer referenced.
  struct ptr_handler
  {
    ptr_handler(mem::unique_table<unique_type>& ut)
    {
      mem::set_deletion_handler<unique_type>([&](const unique_type* u){ut.erase(u);});
    }

    ~ptr_handler()
    {
      mem::reset_deletion_handler<unique_type>();
    }
  } handler;

  /// @brief The set of unified flat_set.
  mem::unique_table<unique_type> unique_table;

  /// @brief The cached empty flat_set.
  const ptr_type empty;

  /// @brief Constructor.
  template <typename C>
  flat_set_manager(const C& configuration)
    : handler(unique_table)
    , unique_table(configuration.flat_set_unique_table_size)
    , empty(mk_empty())
  {}

  const statistics_type&
  statistics()
  const noexcept
  {
    return unique_table.stats();
  }

private:

  /// @brief Helper to construct the empty flat_set.
  ptr_type
  mk_empty()
  {
    char* addr = unique_table.allocate(0 /*extra bytes*/);
    unique_type* u = new (addr) unique_type;
    return ptr_type(&unique_table(u, 0));
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @internal
/// @related flat_set
///
/// Indicate to the library that flat_set needs to store a global state.
template <typename Value>
struct values_traits<flat_set<Value>>
{
  static constexpr bool stateful = true;
  static constexpr bool fast_iterable = true;
  using state_type = flat_set_manager<Value>;
  using builder = boost::container::flat_set<Value>;
};

/*------------------------------------------------------------------------------------------------*/

template <typename Value>
struct display_value
{
  void
  operator()(std::ostream& os, const Value& v)
  const
  {
    os << v;
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @brief Textual output of a flat_set
/// @related flat_set
template <typename Value>
std::enable_if_t<not std::is_integral<Value>::value, std::ostream&>
operator<<(std::ostream& os, const flat_set<Value>& fs)
{
  os << "{";
  if (not fs.empty())
  {
    std::copy(fs.cbegin(), std::prev(fs.cend()), std::ostream_iterator<Value>(os, ","));
    os << *std::prev(fs.cend());
  }
  return os << "}";
}

/// @brief Textual output of a flat_set
///
/// When Value is an integral type, consecutive values are displayed like 1..9.
/// @related flat_set
template <typename Value>
std::enable_if_t<std::is_integral<Value>::value, std::ostream&>
operator<<(std::ostream& os, const flat_set<Value>& fs)
{
  os << "{";
  if (fs.size() == 1)
  {
    os << *std::begin(fs);
  }
  else if ((fs.size() > 1))
  {
    auto cit = fs.cbegin();
    while (cit != fs.cend())
    {
      auto first = cit;
      while (std::next(cit) != fs.cend() and (*cit + 1) == (*std::next(cit))) // consecutive values
      {
        ++cit;
      }
      if (first == cit)
      {
        display_value<Value>()(os, *cit);
      }
      else
      {
        display_value<Value>()(os, *first);
        os << "..";
        display_value<Value>()(os, *cit);
      }
      if (std::next(cit) != fs.cend())
      {
        os << ",";
      }
      ++cit;
    }
  }
  return os << "}";
}

/*------------------------------------------------------------------------------------------------*/

/// @related flat_set
template <typename Value>
inline
flat_set<Value>
difference(const flat_set<Value>& lhs, const flat_set<Value>& rhs)
noexcept
{
  typename flat_set<Value>::data_type res;
  std::set_difference( lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()
                     , std::inserter(res, res.begin()));
  return {std::move(res)};
}

/*------------------------------------------------------------------------------------------------*/

/// @related flat_set
template <typename Value>
inline
flat_set<Value>
intersection(const flat_set<Value>& lhs, const flat_set<Value>& rhs)
noexcept
{
  typename flat_set<Value>::data_type res;
  std::set_intersection( lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()
                       , std::inserter(res, res.begin()));
  return {std::move(res)};
}

/*------------------------------------------------------------------------------------------------*/

/// @related flat_set
template <typename Value>
inline
flat_set<Value>
sum(const flat_set<Value>& lhs, const flat_set<Value>& rhs)
noexcept
{
  typename flat_set<Value>::data_type res;
  std::set_union( lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()
                , std::inserter(res, res.begin()));
  return {std::move(res)};
}

/*------------------------------------------------------------------------------------------------*/

}} // namespace sdd::values

namespace std {

/*------------------------------------------------------------------------------------------------*/

/// @brief Hash specialization for boost::container::flat_set
template <typename Key, typename Compare, typename Allocator>
struct hash<boost::container::flat_set<Key, Compare, Allocator>>
{
  std::size_t
  operator()(const boost::container::flat_set<Key, Compare, Allocator>& c)
  const noexcept
  {
    using namespace sdd::hash;
    return seed() (range(c));
  }
};

/*------------------------------------------------------------------------------------------------*/

/// @brief Hash specialization for sdd::values::flat_set
template <typename Value>
struct hash<sdd::values::flat_set<Value>>
{
  std::size_t
  operator()(const sdd::values::flat_set<Value>& fs)
  const noexcept
  {
    return sdd::hash::seed(fs.ptr());
  }
};

/*------------------------------------------------------------------------------------------------*/

} // namespace std
<|endoftext|>


<commit_before>// @(#)root/thread:$Id$
// Author: Danilo Piparo August 2017

/*************************************************************************
 * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "RConfigure.h"

#include "ROOT/TTaskGroup.hxx"

#ifdef R__USE_IMT
#include "TROOT.h"
#include "tbb/task_group.h"
#include "tbb/task_arena.h"
#endif

#include <type_traits>

/**
\class ROOT::Experimental::TTaskGroup
\ingroup Parallelism
\brief A class to manage the asynchronous execution of work items.

A TTaskGroup represents concurrent execution of a group of tasks. Tasks may be dynamically added to the group as it is
executing.
*/

namespace ROOT {
namespace Internal {

tbb::task_group *CastToTG(void* p) {
   return (tbb::task_group *) p;
}

tbb::task_arena *CastToTA(void *p)
{
   return (tbb::task_arena *)p;
}

} // namespace Internal

namespace Experimental {

using namespace ROOT::Internal;

// in the constructor and destructor the casts are present in order to be able
// to be independent from the runtime used.
// This leaves the door open for other TTaskGroup implementations.

TTaskGroup::TTaskGroup()
{
#ifdef R__USE_IMT
   if (!ROOT::IsImplicitMTEnabled()) {
      throw std::runtime_error("Implicit parallelism not enabled. Cannot instantiate a TTaskGroup.");
   }
   fTaskContainer = ((void *)new tbb::task_group());
   fTaskArena = ((void *)new tbb::task_arena(ROOT::GetImplicitMTPoolSize()));
#endif
}

TTaskGroup::TTaskGroup(TTaskGroup &&other)
{
   *this = std::move(other);
}

TTaskGroup &TTaskGroup::operator=(TTaskGroup &&other)
{
   fTaskContainer = other.fTaskContainer;
   other.fTaskContainer = nullptr;
   fTaskArena = other.fTaskArena;
   fTaskArena = nullptr;
   fCanRun.store(other.fCanRun);
   return *this;
}

TTaskGroup::~TTaskGroup()
{
#ifdef R__USE_IMT
   if (!fTaskContainer)
      return;
   Wait();
   delete CastToTG(fTaskContainer);
   delete CastToTA(fTaskArena);
#endif
}

/////////////////////////////////////////////////////////////////////////////
/// Run operation in the internal task arena to implement work isolation, i.e.
/// prevent stealing of work items spawned by ancestors.
void TTaskGroup::ExecuteInIsolation(const std::function<void(void)> &operation)
{
#ifdef R__USE_IMT
   CastToTA(fTaskArena)->execute([&] { operation(); });
#else
   operation();
#endif
}

/////////////////////////////////////////////////////////////////////////////
/// Cancel all submitted tasks immediately.
void TTaskGroup::Cancel()
{
#ifdef R__USE_IMT
   fCanRun = false;
   ExecuteInIsolation([&] { CastToTG(fTaskContainer)->cancel(); });
   fCanRun = true;
#endif
}

/////////////////////////////////////////////////////////////////////////////
/// Add to the group an item of work which will be ran asynchronously.
/// Adding many small items of work to the TTaskGroup is not efficient,
/// unless they run for long enough. If the work to be done is little, look
/// try to express nested parallelism or resort to other constructs such as
/// the TThreadExecutor.
/// Trying to add a work item to the group while it is in waiting state
/// makes the method block.
void TTaskGroup::Run(const std::function<void(void)> &closure)
{
#ifdef R__USE_IMT
   while (!fCanRun)
      /* empty */;

   ExecuteInIsolation([&] { CastToTG(fTaskContainer)->run(closure); });
#else
   closure();
#endif
}

/////////////////////////////////////////////////////////////////////////////
/// Wait until all submitted items of work are completed. This method
/// is blocking.
void TTaskGroup::Wait()
{
#ifdef R__USE_IMT
   fCanRun = false;
   ExecuteInIsolation([&] { CastToTG(fTaskContainer)->wait(); });
   fCanRun = true;
#endif
}
} // namespace Experimental
} // namespace ROOT
<commit_msg>[IMT][NFC] TTaskGroup doc mentions potential runtime overhead when nesting<commit_after>// @(#)root/thread:$Id$
// Author: Danilo Piparo August 2017

/*************************************************************************
 * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "RConfigure.h"

#include "ROOT/TTaskGroup.hxx"

#ifdef R__USE_IMT
#include "TROOT.h"
#include "tbb/task_group.h"
#include "tbb/task_arena.h"
#endif

#include <type_traits>

/**
\class ROOT::Experimental::TTaskGroup
\ingroup Parallelism
\brief A class to manage the asynchronous execution of work items.

A TTaskGroup represents concurrent execution of a group of tasks.
Tasks may be dynamically added to the group as it is executing.
Nesting TTaskGroup instances may result in a runtime overhead.
*/

namespace ROOT {
namespace Internal {

tbb::task_group *CastToTG(void* p) {
   return (tbb::task_group *) p;
}

tbb::task_arena *CastToTA(void *p)
{
   return (tbb::task_arena *)p;
}

} // namespace Internal

namespace Experimental {

using namespace ROOT::Internal;

// in the constructor and destructor the casts are present in order to be able
// to be independent from the runtime used.
// This leaves the door open for other TTaskGroup implementations.

TTaskGroup::TTaskGroup()
{
#ifdef R__USE_IMT
   if (!ROOT::IsImplicitMTEnabled()) {
      throw std::runtime_error("Implicit parallelism not enabled. Cannot instantiate a TTaskGroup.");
   }
   fTaskContainer = ((void *)new tbb::task_group());
   fTaskArena = ((void *)new tbb::task_arena(ROOT::GetImplicitMTPoolSize()));
#endif
}

TTaskGroup::TTaskGroup(TTaskGroup &&other)
{
   *this = std::move(other);
}

TTaskGroup &TTaskGroup::operator=(TTaskGroup &&other)
{
   fTaskContainer = other.fTaskContainer;
   other.fTaskContainer = nullptr;
   fTaskArena = other.fTaskArena;
   fTaskArena = nullptr;
   fCanRun.store(other.fCanRun);
   return *this;
}

TTaskGroup::~TTaskGroup()
{
#ifdef R__USE_IMT
   if (!fTaskContainer)
      return;
   Wait();
   delete CastToTG(fTaskContainer);
   delete CastToTA(fTaskArena);
#endif
}

/////////////////////////////////////////////////////////////////////////////
/// Run operation in the internal task arena to implement work isolation, i.e.
/// prevent stealing of work items spawned by ancestors.
void TTaskGroup::ExecuteInIsolation(const std::function<void(void)> &operation)
{
#ifdef R__USE_IMT
   CastToTA(fTaskArena)->execute([&] { operation(); });
#else
   operation();
#endif
}

/////////////////////////////////////////////////////////////////////////////
/// Cancel all submitted tasks immediately.
void TTaskGroup::Cancel()
{
#ifdef R__USE_IMT
   fCanRun = false;
   ExecuteInIsolation([&] { CastToTG(fTaskContainer)->cancel(); });
   fCanRun = true;
#endif
}

/////////////////////////////////////////////////////////////////////////////
/// Add to the group an item of work which will be ran asynchronously.
/// Adding many small items of work to the TTaskGroup is not efficient,
/// unless they run for long enough. If the work to be done is little, look
/// try to express nested parallelism or resort to other constructs such as
/// the TThreadExecutor.
/// Trying to add a work item to the group while it is in waiting state
/// makes the method block.
void TTaskGroup::Run(const std::function<void(void)> &closure)
{
#ifdef R__USE_IMT
   while (!fCanRun)
      /* empty */;

   ExecuteInIsolation([&] { CastToTG(fTaskContainer)->run(closure); });
#else
   closure();
#endif
}

/////////////////////////////////////////////////////////////////////////////
/// Wait until all submitted items of work are completed. This method
/// is blocking.
void TTaskGroup::Wait()
{
#ifdef R__USE_IMT
   fCanRun = false;
   ExecuteInIsolation([&] { CastToTG(fTaskContainer)->wait(); });
   fCanRun = true;
#endif
}
} // namespace Experimental
} // namespace ROOT
<|endoftext|>


<commit_before>#ifndef MI_TYPE_2_STR_HPP
#define MI_TYPE_2_STR_HPP 1
#include <string>
namespace mi {

	template <typename T>
	std::string type2str( void );
	//	template <typename T>
	//std::string type2str( void );

	template <>
	std::string type2str<char>( void ) {
		return std::string ("char");
	}

	template <>
	std::string type2str<unsigned char>( void ) {
		return std::string ("uchar");
	}

	template <>
	std::string type2str<short>( void ) {
		return std::string ("short");
	}

	template <>
	std::string type2str<unsigned short>( void ) {
		return std::string ("ushort");
	}

	template <>
	std::string type2str<int>( void ) {
		return std::string ("int");
	}

	template <>
	std::string type2str<unsigned int>( void ) {
		return std::string ("uint");
	}

	template <>
	std::string type2str<float>( void ) {
		return std::string ("float");
	}

	template <>
	std::string type2str<double>( void ) {
		return std::string ("double");
	}
}

#endif// MI_TYPE_2_STR_HPP
<commit_msg>snapshot<commit_after>#ifndef MI_TYPE_2_STR_HPP
#define MI_TYPE_2_STR_HPP 1
#include <string>
namespace mi {
	template <typename T>
	std::string type2str( void );

	template <>
	std::string type2str<char>( void ) {
		return std::string ("char");
	}

	template <>
	std::string type2str<unsigned char>( void ) {
		return std::string ("uchar");
	}

	template <>
	std::string type2str<short>( void ) {
		return std::string ("short");
	}

	template <>
	std::string type2str<unsigned short>( void ) {
		return std::string ("ushort");
	}

	template <>
	std::string type2str<int>( void ) {
		return std::string ("int");
	}

	template <>
	std::string type2str<unsigned int>( void ) {
		return std::string ("uint");
	}

	template <>
	std::string type2str<float>( void ) {
		return std::string ("float");
	}

	template <>
	std::string type2str<double>( void ) {
		return std::string ("double");
	}
}

#endif// MI_TYPE_2_STR_HPP
<|endoftext|>


<commit_before>#include "chr/FileSystem.h"
#include "chr/ResourceBuffer.h"

using namespace std;

namespace chr
{
  bool hasFileResources()
  {
    #if defined(CHR_FS_APK) || defined(CHR_FS_RC) || defined(CHR_FS_JS_EMBED) || defined(CHR_FS_JS_PRELOAD)
      return false;
    #else
      return true;
    #endif
  }

  bool hasMemoryResources()
  {
    #if defined(CHR_FS_APK) || defined(CHR_FS_RC) || defined(CHR_FS_JS_EMBED) || defined(CHR_FS_JS_PRELOAD)
      return true;
    #else
      return false;
    #endif
  }

  fs::path getResourceFilePath(const fs::path &relativePath)
  {
    fs::path basePath;

    #if defined(CHR_FS_APK) || defined(CHR_FS_RC)
      return "";
    #elif defined(CHR_FS_BUNDLE)
      CFBundleRef bundle = CFBundleGetMainBundle();
      CFURLRef url = CFBundleCopyBundleURL(bundle);
      CFStringRef tmp = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);

      CFIndex length = CFStringGetLength(tmp);
      CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
      char *buffer = (char*)malloc(maxSize);
      CFStringGetCString(tmp, buffer, maxSize, kCFStringEncodingUTF8);

      basePath = fs::path(buffer);

      CFRelease(url);
      CFRelease(tmp);
      free(buffer);
    #elif defined(CHR_PLATFORM_ANDROID) // TODO: CONSIDER "GENERALIZING" TO LINUX
      static char buf[PATH_MAX];
      auto len = readlink("/proc/self/exe", buf, PATH_MAX - 1);
      assert(len > 0);
      basePath = fs::path(string(buf, len)).parent_path();
    #elif defined(CHR_PLATFORM_EMSCRIPTEN)
      basePath = "res";
    #else
      basePath = fs::current_path() / "res";
    #endif

    return basePath / relativePath;
  }

  shared_ptr<ResourceBuffer> getResourceBuffer(const fs::path &relativePath)
  {
    #if !defined(CHR_FS_APK) && !defined(CHR_FS_RC) && !defined(CHR_FS_JS_EMBED) && !defined(CHR_FS_JS_PRELOAD)
      return nullptr;
    #endif

    auto buffer = make_shared<ResourceBuffer>();
    buffer->lock(relativePath);

    return buffer;
  }

  shared_ptr<istream> getResourceStream(const fs::path &relativePath)
  {
    istream *stream;

    if (chr::hasMemoryResources())
    {
      auto memoryBuffer = chr::getResourceBuffer(relativePath);

      if (memoryBuffer)
      {
        stream = new imemstream(memoryBuffer); // FIXME: PROBLEMATIC BECAUSE memoryBuffer WILL BE DEALLOCATED
      }
      else
      {
        return nullptr;
      }
    }
    else if (chr::hasFileResources())
    {
      stream = new fs::ifstream(chr::getResourceFilePath(relativePath), ifstream::binary);
    }

    return shared_ptr<istream>(stream);
  }
}
<commit_msg>ADAPTING FILESYSTEM FOR OSX APPS BUILT WITH XCODE<commit_after>#include "chr/FileSystem.h"
#include "chr/ResourceBuffer.h"

using namespace std;

namespace chr
{
  bool hasFileResources()
  {
    #if defined(CHR_FS_APK) || defined(CHR_FS_RC) || defined(CHR_FS_JS_EMBED) || defined(CHR_FS_JS_PRELOAD)
      return false;
    #else
      return true;
    #endif
  }

  bool hasMemoryResources()
  {
    #if defined(CHR_FS_APK) || defined(CHR_FS_RC) || defined(CHR_FS_JS_EMBED) || defined(CHR_FS_JS_PRELOAD)
      return true;
    #else
      return false;
    #endif
  }

  fs::path getResourceFilePath(const fs::path &relativePath)
  {
    fs::path basePath;

    #if defined(CHR_FS_APK) || defined(CHR_FS_RC)
      return "";
    #elif defined(CHR_FS_BUNDLE)
      CFBundleRef bundle = CFBundleGetMainBundle();
      CFURLRef url = CFBundleCopyBundleURL(bundle);
      CFStringRef tmp = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);

      CFIndex length = CFStringGetLength(tmp);
      CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
      char *buffer = (char*)malloc(maxSize);
      CFStringGetCString(tmp, buffer, maxSize, kCFStringEncodingUTF8);

      basePath = fs::path(buffer);

      CFRelease(url);
      CFRelease(tmp);
      free(buffer);
    #elif defined(CHR_PLATFORM_ANDROID) // TODO: CONSIDER "GENERALIZING" TO LINUX
      static char buf[PATH_MAX];
      auto len = readlink("/proc/self/exe", buf, PATH_MAX - 1);
      assert(len > 0);
      basePath = fs::path(string(buf, len)).parent_path();
    #elif defined(CHR_PLATFORM_EMSCRIPTEN)
      basePath = "res";
    #else
      if (fs::exists(fs::current_path() / relativePath))
      {
        return fs::current_path() / relativePath; // RELEVANT WHEN BUILDING OSX APP WITH XCODE
      }
      else
      {
        return fs::current_path() / "res" / relativePath; // RELEVANT WHEN BUILDING WITH CLION OR COMMAND-LINE
      }
    #endif

    return basePath / relativePath;
  }

  shared_ptr<ResourceBuffer> getResourceBuffer(const fs::path &relativePath)
  {
    #if !defined(CHR_FS_APK) && !defined(CHR_FS_RC) && !defined(CHR_FS_JS_EMBED) && !defined(CHR_FS_JS_PRELOAD)
      return nullptr;
    #endif

    auto buffer = make_shared<ResourceBuffer>();
    buffer->lock(relativePath);

    return buffer;
  }

  shared_ptr<istream> getResourceStream(const fs::path &relativePath)
  {
    istream *stream;

    if (chr::hasMemoryResources())
    {
      auto memoryBuffer = chr::getResourceBuffer(relativePath);

      if (memoryBuffer)
      {
        stream = new imemstream(memoryBuffer); // FIXME: PROBLEMATIC BECAUSE memoryBuffer WILL BE DEALLOCATED
      }
      else
      {
        return nullptr;
      }
    }
    else if (chr::hasFileResources())
    {
      stream = new fs::ifstream(chr::getResourceFilePath(relativePath), ifstream::binary);
    }

    return shared_ptr<istream>(stream);
  }
}
<|endoftext|>


<commit_before>#include <iostream>
#include <stdlib.h>

#include <taglib/taglib.h>
#include <taglib/flacfile.h>
#include <taglib/xiphcomment.h>

#include "get_picture_data.cpp"

using namespace TagLib;

int main(int argc, char **argv) {
  if (
      ((argc < 2) || (argc > 3))
    ||
      ((argc == 3) && (argv[2] != "nopic"))
    ) {
    std::cout << "usage: " << argv[0] << " file [nopic]" << std::endl;
    exit(1);
  }
  char *filename = argv[1];

  FLAC::File file(filename);
  Ogg::XiphComment *tag = file.xiphComment(true);

  tag->setTitle("Title");
  tag->setArtist("Artist");
  tag->setAlbum("Album");
  tag->setComment("Comment");
  tag->setGenre("Pop");
  tag->setYear(2011);
  tag->setTrack(7);

  tag->addField("VERSION", "original");
  tag->addField("PERFORMER", "Performer");
  tag->addField("COPYRIGHT", "2011 Me, myself and I");
  tag->addField("LICENSE", "Any Use Permitted");
  tag->addField("ORGANIZATION", "Organization");
  tag->addField("DESCRIPTION", "Test file");
  tag->addField("LOCATION", "Earth");
  tag->addField("CONTACT", "Contact");

  tag->addField("MULTIPLE", "A");
  tag->addField("MULTIPLE", "B", false);

  if (argc == 2) {
    FLAC::Picture *picture = new FLAC::Picture();

    picture->setType(FLAC::Picture::FrontCover);
    picture->setMimeType("image/jpeg");
    picture->setDescription("Globe");
    picture->setWidth(90);
    picture->setHeight(90);
    picture->setColorDepth(8);
    picture->setNumColors(0);

    ByteVector data = getPictureData("globe_east_90.jpg");
    picture->setData(data);

    file.addPicture(picture);
  }

  file.save();
}

// vim: set filetype=cpp sw=2 ts=2 expandtab:
<commit_msg>revert changes to flac-create.cpp<commit_after>#include <iostream>
#include <stdlib.h>

#include <taglib/taglib.h>
#include <taglib/flacfile.h>
#include <taglib/xiphcomment.h>

#include "get_picture_data.cpp"

using namespace TagLib;

int main(int argc, char **argv) {
  if (argc != 2) {
    std::cout << "usage: " << argv[0] << " file" << std::endl;
    exit(1);
  }
  char *filename = argv[1];

  FLAC::File file(filename);
  Ogg::XiphComment *tag = file.xiphComment(true);

  tag->setTitle("Title");
  tag->setArtist("Artist");
  tag->setAlbum("Album");
  tag->setComment("Comment");
  tag->setGenre("Pop");
  tag->setYear(2011);
  tag->setTrack(7);

  tag->addField("VERSION", "original");
  tag->addField("PERFORMER", "Performer");
  tag->addField("COPYRIGHT", "2011 Me, myself and I");
  tag->addField("LICENSE", "Any Use Permitted");
  tag->addField("ORGANIZATION", "Organization");
  tag->addField("DESCRIPTION", "Test file");
  tag->addField("LOCATION", "Earth");
  tag->addField("CONTACT", "Contact");

  tag->addField("MULTIPLE", "A");
  tag->addField("MULTIPLE", "B", false);

  FLAC::Picture *picture = new FLAC::Picture();

  picture->setType(FLAC::Picture::FrontCover);
  picture->setMimeType("image/jpeg");
  picture->setDescription("Globe");
  picture->setWidth(90);
  picture->setHeight(90);
  picture->setColorDepth(8);
  picture->setNumColors(0);

  ByteVector data = getPictureData("globe_east_90.jpg");
  picture->setData(data);

  file.addPicture(picture);

  file.save();
}

// vim: set filetype=cpp sw=2 ts=2 expandtab:
<|endoftext|>


<commit_before>#include "drawRule.h"
#include "csscolorparser.hpp"
#include "geom.h" // for CLAMP
#include "platform.h"

#include <algorithm>
#include <cmath>
#include <map>
#include <utility>

namespace Tangram {

const StyleParam NONE;

const std::map<std::string, StyleParamKey> s_StyleParamMap = {
    {"none", StyleParamKey::none},
    {"order", StyleParamKey::order},
    {"extrude", StyleParamKey::extrude},
    {"color", StyleParamKey::color},
    {"width", StyleParamKey::width},
    {"cap", StyleParamKey::cap},
    {"join", StyleParamKey::join},
    {"outline:color", StyleParamKey::outline_color},
    {"outline:width", StyleParamKey::outline_width},
    {"outline:cap", StyleParamKey::outline_cap},
    {"outline:join", StyleParamKey::outline_join},
};

StyleParam::StyleParam(const std::string& _key, const std::string& _value) {
    auto it = s_StyleParamMap.find(_key);
    if (it == s_StyleParamMap.end()) {
        logMsg("Unknown StyleParam %s:%s\n", _key.c_str(), _value.c_str());
        key = StyleParamKey::none;
        value = none_type{};
        return;
    }

    key = it->second;

    switch (key) {
    case StyleParamKey::extrude:
        if (_value == "true") { value = std::make_pair(NAN, NAN); }
        else if (_value == "false") { value = std::make_pair(0.0f, 0.0f) ; }
        else {
            float f1, f2;
            int num = std::sscanf(_value.c_str(), "%f, %f", &f1, &f2);
            switch(num) {
                case 1:
                    value = std::make_pair(f1, NAN);
                    break;
                case 2:
                    value = std::make_pair(f1, f2);
                    break;
                case 0:
                default:
                    logMsg("Warning: Badly formed extrude parameter.\n");
                    break;
            }
        }
        break;
    case StyleParamKey::order:
        value = static_cast<int32_t>(std::stoi(_value));
        break;
    case StyleParamKey::width:
    case StyleParamKey::outline_width:
        value = static_cast<float>(std::stof(_value));
        break;
    case StyleParamKey::color:
    case StyleParamKey::outline_color:
        value = DrawRule::parseColor(_value);
        break;
    case StyleParamKey::cap:
    case StyleParamKey::outline_cap:
        value = CapTypeFromString(_value);
        break;
    case StyleParamKey::join:
    case StyleParamKey::outline_join:
        value = JoinTypeFromString(_value);
        break;
    default:
        value = none_type{};
    }
}

std::string StyleParam::toString() const {
    // TODO: cap, join and color toString()
    switch (key) {
    case StyleParamKey::extrude: {
        auto p = value.get<std::pair<float, float>>();
        return "extrude : (" + std::to_string(p.first) + ", " + std::to_string(p.second) + ")";
    }
    case StyleParamKey::order:
        return std::to_string(value.get<int32_t>());
    case StyleParamKey::width:
    case StyleParamKey::outline_width:
        return std::to_string(value.get<float>());
    case StyleParamKey::color:
    case StyleParamKey::outline_color:
        return std::to_string(value.get<Color>().getInt());
    case StyleParamKey::cap:
    case StyleParamKey::outline_cap:
        return std::to_string(static_cast<int>(value.get<CapTypes>()));
    case StyleParamKey::join:
    case StyleParamKey::outline_join:
        return std::to_string(static_cast<int>(value.get<CapTypes>()));
    case StyleParamKey::none:
        break;
    }
    return "undefined";
}

DrawRule::DrawRule(const std::string& _style, const std::vector<StyleParam>& _parameters) :
    style(_style),
    parameters(_parameters) {

    // Parameters within each rule must be sorted lexicographically by key to merge correctly
    std::sort(parameters.begin(), parameters.end());

}

DrawRule DrawRule::merge(DrawRule& _other) const {

    decltype(parameters) merged;

    auto myIt = parameters.begin(), myEnd = parameters.end();
    auto otherIt = _other.parameters.begin(), otherEnd = _other.parameters.end();
    while (myIt != myEnd && otherIt != otherEnd) {
        if (*myIt < *otherIt) {
            merged.push_back(*myIt++);
        } else if (*otherIt < *myIt) {
            merged.push_back(std::move(*otherIt++));
        } else {
            merged.push_back(*otherIt++);
            myIt++;
        }
    }
    while (myIt != myEnd) { merged.push_back(*myIt++); }
    while (otherIt != otherEnd) { merged.push_back(std::move(*otherIt++)); }

    return { style, merged };
}

std::string DrawRule::toString() const {

    std::string str = "{\n";

    for (auto& p : parameters) {
         str += "    { " + std::to_string(static_cast<int>(p.key)) + ", " + p.toString() + " }\n";
    }

    str += "}\n";

    return str;
}

const StyleParam&  DrawRule::findParameter(StyleParamKey _key) const {

    auto it = std::lower_bound(parameters.begin(), parameters.end(), _key,
                               [](auto& p, auto& k) { return p.key < k; });

    if (it != parameters.end() && it->key == _key) {
        return *it;
    }
    return NONE;
}

bool DrawRule::operator<(const DrawRule& _rhs) const {
    return style < _rhs.style;
}

Color DrawRule::parseColor(const std::string& _color) {
    Color color;

    if (isdigit(_color.front())) {
        // try to parse as comma-separated rgba components
        float r, g, b, a = 1.;
        if (sscanf(_color.c_str(), "%f,%f,%f,%f", &r, &g, &b, &a) >= 3) {
            color = Color {
                static_cast<uint8_t>(CLAMP((r * 255.), 0, 255)),
                static_cast<uint8_t>(CLAMP((g * 255.), 0, 255)),
                static_cast<uint8_t>(CLAMP((b * 255.), 0, 255)),
                CLAMP(a, 0, 1)
            };
        }
    } else {
        // parse as css color or #hex-num
        color = CSSColorParser::parse(_color);
    }
    return color;
}

}
<commit_msg>slightly better StyleParam::toString<commit_after>#include "drawRule.h"
#include "csscolorparser.hpp"
#include "geom.h" // for CLAMP
#include "platform.h"

#include <algorithm>
#include <cmath>
#include <map>
#include <utility>

namespace Tangram {

const StyleParam NONE;

const std::map<std::string, StyleParamKey> s_StyleParamMap = {
    {"none", StyleParamKey::none},
    {"order", StyleParamKey::order},
    {"extrude", StyleParamKey::extrude},
    {"color", StyleParamKey::color},
    {"width", StyleParamKey::width},
    {"cap", StyleParamKey::cap},
    {"join", StyleParamKey::join},
    {"outline:color", StyleParamKey::outline_color},
    {"outline:width", StyleParamKey::outline_width},
    {"outline:cap", StyleParamKey::outline_cap},
    {"outline:join", StyleParamKey::outline_join},
};

StyleParam::StyleParam(const std::string& _key, const std::string& _value) {
    auto it = s_StyleParamMap.find(_key);
    if (it == s_StyleParamMap.end()) {
        logMsg("Unknown StyleParam %s:%s\n", _key.c_str(), _value.c_str());
        key = StyleParamKey::none;
        value = none_type{};
        return;
    }

    key = it->second;

    switch (key) {
    case StyleParamKey::extrude:
        if (_value == "true") { value = std::make_pair(NAN, NAN); }
        else if (_value == "false") { value = std::make_pair(0.0f, 0.0f) ; }
        else {
            float f1, f2;
            int num = std::sscanf(_value.c_str(), "%f, %f", &f1, &f2);
            switch(num) {
                case 1:
                    value = std::make_pair(f1, NAN);
                    break;
                case 2:
                    value = std::make_pair(f1, f2);
                    break;
                case 0:
                default:
                    logMsg("Warning: Badly formed extrude parameter.\n");
                    break;
            }
        }
        break;
    case StyleParamKey::order:
        value = static_cast<int32_t>(std::stoi(_value));
        break;
    case StyleParamKey::width:
    case StyleParamKey::outline_width:
        value = static_cast<float>(std::stof(_value));
        break;
    case StyleParamKey::color:
    case StyleParamKey::outline_color:
        value = DrawRule::parseColor(_value);
        break;
    case StyleParamKey::cap:
    case StyleParamKey::outline_cap:
        value = CapTypeFromString(_value);
        break;
    case StyleParamKey::join:
    case StyleParamKey::outline_join:
        value = JoinTypeFromString(_value);
        break;
    default:
        value = none_type{};
    }
}

std::string StyleParam::toString() const {
    // TODO: cap, join and color toString()
    switch (key) {
    case StyleParamKey::extrude: {
        if (!value.is<std::pair<float, float>>()) break;
        auto p = value.get<std::pair<float, float>>();
        return "extrude : (" + std::to_string(p.first) + ", " + std::to_string(p.second) + ")";
    }
    case StyleParamKey::order:
        if (!value.is<int32_t>()) break;
        return "order : " + std::to_string(value.get<int32_t>());
    case StyleParamKey::width:
    case StyleParamKey::outline_width:
        if (!value.is<float>()) break;
        return "width : " + std::to_string(value.get<float>());
    case StyleParamKey::color:
    case StyleParamKey::outline_color:
        if (!value.is<Color>()) break;
        return "color : " + std::to_string(value.get<Color>().getInt());
    case StyleParamKey::cap:
    case StyleParamKey::outline_cap:
        if (!value.is<CapTypes>()) break;
        return "cap : " + std::to_string(static_cast<int>(value.get<CapTypes>()));
    case StyleParamKey::join:
    case StyleParamKey::outline_join:
        if (!value.is<JoinTypes>()) break;
        return "join : " + std::to_string(static_cast<int>(value.get<JoinTypes>()));
    case StyleParamKey::none:
        break;
    }
    return "undefined";
}

DrawRule::DrawRule(const std::string& _style, const std::vector<StyleParam>& _parameters) :
    style(_style),
    parameters(_parameters) {

    // Parameters within each rule must be sorted lexicographically by key to merge correctly
    std::sort(parameters.begin(), parameters.end());

}

DrawRule DrawRule::merge(DrawRule& _other) const {

    decltype(parameters) merged;

    auto myIt = parameters.begin(), myEnd = parameters.end();
    auto otherIt = _other.parameters.begin(), otherEnd = _other.parameters.end();
    while (myIt != myEnd && otherIt != otherEnd) {
        if (*myIt < *otherIt) {
            merged.push_back(*myIt++);
        } else if (*otherIt < *myIt) {
            merged.push_back(std::move(*otherIt++));
        } else {
            merged.push_back(*otherIt++);
            myIt++;
        }
    }
    while (myIt != myEnd) { merged.push_back(*myIt++); }
    while (otherIt != otherEnd) { merged.push_back(std::move(*otherIt++)); }

    return { style, merged };
}

std::string DrawRule::toString() const {

    std::string str = "{\n";

    for (auto& p : parameters) {
         str += "    { " + std::to_string(static_cast<int>(p.key)) + ", " + p.toString() + " }\n";
    }

    str += "}\n";

    return str;
}

const StyleParam&  DrawRule::findParameter(StyleParamKey _key) const {

    auto it = std::lower_bound(parameters.begin(), parameters.end(), _key,
                               [](auto& p, auto& k) { return p.key < k; });

    if (it != parameters.end() && it->key == _key) {
        return *it;
    }
    return NONE;
}

bool DrawRule::operator<(const DrawRule& _rhs) const {
    return style < _rhs.style;
}

Color DrawRule::parseColor(const std::string& _color) {
    Color color;

    if (isdigit(_color.front())) {
        // try to parse as comma-separated rgba components
        float r, g, b, a = 1.;
        if (sscanf(_color.c_str(), "%f,%f,%f,%f", &r, &g, &b, &a) >= 3) {
            color = Color {
                static_cast<uint8_t>(CLAMP((r * 255.), 0, 255)),
                static_cast<uint8_t>(CLAMP((g * 255.), 0, 255)),
                static_cast<uint8_t>(CLAMP((b * 255.), 0, 255)),
                CLAMP(a, 0, 1)
            };
        }
    } else {
        // parse as css color or #hex-num
        color = CSSColorParser::parse(_color);
    }
    return color;
}

}
<|endoftext|>


<commit_before>#include "output_https.h"
#include <mist/procs.h>

namespace Mist{
  mbedtls_entropy_context OutHTTPS::entropy;
  mbedtls_ctr_drbg_context OutHTTPS::ctr_drbg;
  mbedtls_ssl_config OutHTTPS::sslConf;
  mbedtls_x509_crt OutHTTPS::srvcert;
  mbedtls_pk_context OutHTTPS::pkey;

  void OutHTTPS::init(Util::Config *cfg){
    Output::init(cfg);
    capa["name"] = "HTTPS";
    capa["friendly"] = "HTTPS (HTTP+TLS)";
    capa["desc"] = "HTTPS connection handler, provides all enabled HTTP-based outputs";
    capa["provides"] = "HTTP";
    capa["protocol"] = "https://";
    capa["required"]["cert"]["name"] = "Certificate";
    capa["required"]["cert"]["help"] = "(Root) certificate(s) file(s) to append to chain";
    capa["required"]["cert"]["option"] = "--cert";
    capa["required"]["cert"]["short"] = "C";
    capa["required"]["cert"]["default"] = "";
    capa["required"]["cert"]["type"] = "str";
    capa["required"]["key"]["name"] = "Key";
    capa["required"]["key"]["help"] = "Private key for SSL";
    capa["required"]["key"]["option"] = "--key";
    capa["required"]["key"]["short"] = "K";
    capa["required"]["key"]["default"] = "";
    capa["required"]["key"]["type"] = "str";

    capa["optional"]["wrappers"]["name"] = "Active players";
    capa["optional"]["wrappers"]["help"] = "Which players are attempted and in what order.";
    capa["optional"]["wrappers"]["default"] = "";
    capa["optional"]["wrappers"]["type"] = "ord_multi_sel";
    capa["optional"]["wrappers"]["allowed"].append("html5");
    capa["optional"]["wrappers"]["allowed"].append("videojs");
    capa["optional"]["wrappers"]["allowed"].append("dashjs");
    capa["optional"]["wrappers"]["allowed"].append("flash_strobe");
    capa["optional"]["wrappers"]["allowed"].append("silverlight");
    capa["optional"]["wrappers"]["allowed"].append("img");
    capa["optional"]["wrappers"]["option"] = "--wrappers";
    capa["optional"]["wrappers"]["short"] = "w";
    cfg->addConnectorOptions(4433, capa);
    config = cfg;
  }

  OutHTTPS::OutHTTPS(Socket::Connection &C) : Output(C){
    int ret;
    mbedtls_net_init(&client_fd);
    client_fd.fd = C.getSocket();
    mbedtls_ssl_init(&ssl);
    if ((ret = mbedtls_ctr_drbg_reseed(&ctr_drbg, (const unsigned char *)"child", 5)) != 0){
      FAIL_MSG("Could not reseed");
      C.close();
      return;
    }

    // Set up the SSL connection
    if ((ret = mbedtls_ssl_setup(&ssl, &sslConf)) != 0){
      FAIL_MSG("Could not set up SSL connection");
      C.close();
      return;
    }

    // Inform mbedtls how we'd like to use the connection (uses default bio handlers)
    // We tell it to use non-blocking IO here
    mbedtls_net_set_nonblock(&client_fd);
    mbedtls_ssl_set_bio(&ssl, &client_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
    // do the SSL handshake
    while ((ret = mbedtls_ssl_handshake(&ssl)) != 0){
      if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){
        char error_buf[200];
        mbedtls_strerror(ret, error_buf, 200);
        MEDIUM_MSG("Could not handshake, SSL error: %s (%d)", error_buf, ret);
        C.close();
        return;
      }else{
        Util::sleep(100);
      }
    }
    HIGH_MSG("Started SSL connection handler");
  }

  int OutHTTPS::run(){
    unsigned char buf[1024 * 4]; // 4k internal buffer
    int ret;

    // Start a MistOutHTTP process, connected to this SSL connection
    int fderr = 2;
    int fd[2];
    if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fd) != 0){
      FAIL_MSG("Could not open anonymous socket for SSL<->HTTP connection!");
      return 1;
    }
    std::deque<std::string> args;
    args.push_back(Util::getMyPath() + "MistOutHTTP");
    args.push_back("--ip");
    args.push_back(myConn.getHost());
    args.push_back("");
    Util::Procs::socketList.insert(fd[0]);
    pid_t http_proc = Util::Procs::StartPiped(args, &(fd[1]), &(fd[1]), &fderr);
    close(fd[1]);
    if (http_proc < 2){
      FAIL_MSG("Could not spawn MistOutHTTP process for SSL connection!");
      return 1;
    }
    Socket::Connection http(fd[0]);
    http.setBlocking(false);
    Socket::Buffer &http_buf = http.Received();

    // pass data back and forth between the SSL connection and HTTP process while connected
    while (config->is_active && http){
      bool activity = false;
      // attempt to read SSL data, pass to HTTP
      ret = mbedtls_ssl_read(&ssl, buf, sizeof(buf));
      if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){
        if (ret <= 0){
          HIGH_MSG("SSL disconnect!");
          break;
        }
        // we received ret bytes of data to pass on. Do so.
        activity = true;
        http.SendNow((const char *)buf, ret);
      }

      // attempt to read HTTP data, pass to SSL
      if (http.spool() || http_buf.size()){
        // We have data - pass it on
        activity = true;
        while (http_buf.size() && http){
          int todo = http_buf.get().size();
          int done = 0;
          while (done < todo){
            ret = mbedtls_ssl_write(&ssl, (const unsigned char*)http_buf.get().data() + done, todo - done);
            if (ret == MBEDTLS_ERR_NET_CONN_RESET || ret == MBEDTLS_ERR_SSL_CLIENT_RECONNECT){
              HIGH_MSG("SSL disconnect!");
              http.close();
              break;
            }
            if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){
              done += ret;
            }else{
              Util::sleep(50);
            }
          }
          http_buf.get().clear();
        }
      }
      if (!activity){
        Util::sleep(50);
      }
    }
    // close the HTTP process (close stdio, kill its PID)
    http.close();
    Util::Procs::Stop(http_proc);
    uint16_t waiting = 0;
    while (++waiting < 100){
      if (!Util::Procs::isRunning(http_proc)){break;}
      Util::sleep(100);
    }
    return 0;
  }

  OutHTTPS::~OutHTTPS(){
    HIGH_MSG("Ending SSL connection handler");
    // close when we're done
    mbedtls_ssl_close_notify(&ssl);
    mbedtls_ssl_free(&ssl);
    mbedtls_net_free(&client_fd);
    myConn.close();
  }

  /// Listens for HTTPS requests, accepting them and connecting them to a HTTP socket
  void OutHTTPS::listener(Util::Config &conf, int (*callback)(Socket::Connection &S)){
    if (config->getOption("cert", true).size() < 2 || config->getOption("key", true).size() < 2){
      FAIL_MSG("The cert/key required options were not passed!");
      return;
    }

    //Declare and set up all required mbedtls structures
    int ret;
    mbedtls_ssl_config_init(&sslConf);
    mbedtls_entropy_init(&entropy);
    mbedtls_pk_init(&pkey);
    mbedtls_x509_crt_init(&srvcert);
    mbedtls_ctr_drbg_init(&ctr_drbg);

    // seed the rng
    if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *)"MistServer", 10)) != 0){
      FAIL_MSG("Could not seed the random number generator!");
    }

    //Read certificate chain(s) from cmdline option(s)
    JSON::Value certs = config->getOption("cert", true);
    jsonForEach(certs, it){
      if (it->asStringRef().size()){//Ignore empty entries (default is empty)
        ret = mbedtls_x509_crt_parse_file(&srvcert, it->asStringRef().c_str());
        if (ret != 0){
          WARN_MSG("Could not load any certificates from file: %s", it->asStringRef().c_str());
        }
      }
    }

    //Read key from cmdline option
    ret = mbedtls_pk_parse_keyfile(&pkey, config->getString("key").c_str(), 0);
    if (ret != 0){
      FAIL_MSG("Could not load any keys from file: %s", config->getString("key").c_str());
      return;
    }

    if ((ret = mbedtls_ssl_config_defaults(&sslConf, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0){
      FAIL_MSG("SSL config defaults failed");
      return;
    }
    mbedtls_ssl_conf_rng(&sslConf, mbedtls_ctr_drbg_random, &ctr_drbg);
    mbedtls_ssl_conf_ca_chain(&sslConf, srvcert.next, NULL);
    if ((ret = mbedtls_ssl_conf_own_cert(&sslConf, &srvcert, &pkey)) != 0){
      FAIL_MSG("SSL config own certificate failed");
      return;
    }

    Output::listener(conf, callback);

    //Free all the mbedtls structures
    mbedtls_x509_crt_free(&srvcert);
    mbedtls_pk_free(&pkey);
    mbedtls_ssl_config_free(&sslConf);
    mbedtls_ctr_drbg_free(&ctr_drbg);
    mbedtls_entropy_free(&entropy);
  }
}

<commit_msg>HTTPS speed optimize<commit_after>#include "output_https.h"
#include <mist/procs.h>

namespace Mist{
  mbedtls_entropy_context OutHTTPS::entropy;
  mbedtls_ctr_drbg_context OutHTTPS::ctr_drbg;
  mbedtls_ssl_config OutHTTPS::sslConf;
  mbedtls_x509_crt OutHTTPS::srvcert;
  mbedtls_pk_context OutHTTPS::pkey;

  void OutHTTPS::init(Util::Config *cfg){
    Output::init(cfg);
    capa["name"] = "HTTPS";
    capa["friendly"] = "HTTPS (HTTP+TLS)";
    capa["desc"] = "HTTPS connection handler, provides all enabled HTTP-based outputs";
    capa["provides"] = "HTTP";
    capa["protocol"] = "https://";
    capa["required"]["cert"]["name"] = "Certificate";
    capa["required"]["cert"]["help"] = "(Root) certificate(s) file(s) to append to chain";
    capa["required"]["cert"]["option"] = "--cert";
    capa["required"]["cert"]["short"] = "C";
    capa["required"]["cert"]["default"] = "";
    capa["required"]["cert"]["type"] = "str";
    capa["required"]["key"]["name"] = "Key";
    capa["required"]["key"]["help"] = "Private key for SSL";
    capa["required"]["key"]["option"] = "--key";
    capa["required"]["key"]["short"] = "K";
    capa["required"]["key"]["default"] = "";
    capa["required"]["key"]["type"] = "str";

    capa["optional"]["wrappers"]["name"] = "Active players";
    capa["optional"]["wrappers"]["help"] = "Which players are attempted and in what order.";
    capa["optional"]["wrappers"]["default"] = "";
    capa["optional"]["wrappers"]["type"] = "ord_multi_sel";
    capa["optional"]["wrappers"]["allowed"].append("html5");
    capa["optional"]["wrappers"]["allowed"].append("videojs");
    capa["optional"]["wrappers"]["allowed"].append("dashjs");
    capa["optional"]["wrappers"]["allowed"].append("flash_strobe");
    capa["optional"]["wrappers"]["allowed"].append("silverlight");
    capa["optional"]["wrappers"]["allowed"].append("img");
    capa["optional"]["wrappers"]["option"] = "--wrappers";
    capa["optional"]["wrappers"]["short"] = "w";
    cfg->addConnectorOptions(4433, capa);
    config = cfg;
  }

  OutHTTPS::OutHTTPS(Socket::Connection &C) : Output(C){
    int ret;
    mbedtls_net_init(&client_fd);
    client_fd.fd = C.getSocket();
    mbedtls_ssl_init(&ssl);
    if ((ret = mbedtls_ctr_drbg_reseed(&ctr_drbg, (const unsigned char *)"child", 5)) != 0){
      FAIL_MSG("Could not reseed");
      C.close();
      return;
    }

    // Set up the SSL connection
    if ((ret = mbedtls_ssl_setup(&ssl, &sslConf)) != 0){
      FAIL_MSG("Could not set up SSL connection");
      C.close();
      return;
    }

    // Inform mbedtls how we'd like to use the connection (uses default bio handlers)
    // We tell it to use non-blocking IO here
    mbedtls_net_set_nonblock(&client_fd);
    mbedtls_ssl_set_bio(&ssl, &client_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
    // do the SSL handshake
    while ((ret = mbedtls_ssl_handshake(&ssl)) != 0){
      if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){
        char error_buf[200];
        mbedtls_strerror(ret, error_buf, 200);
        MEDIUM_MSG("Could not handshake, SSL error: %s (%d)", error_buf, ret);
        C.close();
        return;
      }else{
        Util::sleep(100);
      }
    }
    HIGH_MSG("Started SSL connection handler");
  }

  int OutHTTPS::run(){
    unsigned char buf[1024 * 4]; // 4k internal buffer
    int ret;

    // Start a MistOutHTTP process, connected to this SSL connection
    int fderr = 2;
    int fd[2];
    if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fd) != 0){
      FAIL_MSG("Could not open anonymous socket for SSL<->HTTP connection!");
      return 1;
    }
    std::deque<std::string> args;
    args.push_back(Util::getMyPath() + "MistOutHTTP");
    args.push_back("--ip");
    args.push_back(myConn.getHost());
    args.push_back("");
    Util::Procs::socketList.insert(fd[0]);
    pid_t http_proc = Util::Procs::StartPiped(args, &(fd[1]), &(fd[1]), &fderr);
    close(fd[1]);
    if (http_proc < 2){
      FAIL_MSG("Could not spawn MistOutHTTP process for SSL connection!");
      return 1;
    }
    Socket::Connection http(fd[0]);
    http.setBlocking(false);
    Socket::Buffer &http_buf = http.Received();
    http_buf.splitter.clear();

    // pass data back and forth between the SSL connection and HTTP process while connected
    while (config->is_active && http){
      bool activity = false;
      // attempt to read SSL data, pass to HTTP
      ret = mbedtls_ssl_read(&ssl, buf, sizeof(buf));
      if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){
        if (ret <= 0){
          HIGH_MSG("SSL disconnect!");
          break;
        }
        // we received ret bytes of data to pass on. Do so.
        activity = true;
        http.SendNow((const char *)buf, ret);
      }

      // attempt to read HTTP data, pass to SSL
      if (http.spool() || http_buf.size()){
        // We have data - pass it on
        activity = true;
        while (http_buf.size() && http){
          int todo = http_buf.get().size();
          int done = 0;
          while (done < todo){
            ret = mbedtls_ssl_write(&ssl, (const unsigned char*)http_buf.get().data() + done, todo - done);
            if (ret == MBEDTLS_ERR_NET_CONN_RESET || ret == MBEDTLS_ERR_SSL_CLIENT_RECONNECT){
              HIGH_MSG("SSL disconnect!");
              http.close();
              break;
            }
            if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){
              done += ret;
            }else{
              Util::sleep(50);
            }
          }
          http_buf.get().clear();
        }
      }
      if (!activity){
        Util::sleep(50);
      }
    }
    // close the HTTP process (close stdio, kill its PID)
    http.close();
    Util::Procs::Stop(http_proc);
    uint16_t waiting = 0;
    while (++waiting < 100){
      if (!Util::Procs::isRunning(http_proc)){break;}
      Util::sleep(100);
    }
    return 0;
  }

  OutHTTPS::~OutHTTPS(){
    HIGH_MSG("Ending SSL connection handler");
    // close when we're done
    mbedtls_ssl_close_notify(&ssl);
    mbedtls_ssl_free(&ssl);
    mbedtls_net_free(&client_fd);
    myConn.close();
  }

  /// Listens for HTTPS requests, accepting them and connecting them to a HTTP socket
  void OutHTTPS::listener(Util::Config &conf, int (*callback)(Socket::Connection &S)){
    if (config->getOption("cert", true).size() < 2 || config->getOption("key", true).size() < 2){
      FAIL_MSG("The cert/key required options were not passed!");
      return;
    }

    //Declare and set up all required mbedtls structures
    int ret;
    mbedtls_ssl_config_init(&sslConf);
    mbedtls_entropy_init(&entropy);
    mbedtls_pk_init(&pkey);
    mbedtls_x509_crt_init(&srvcert);
    mbedtls_ctr_drbg_init(&ctr_drbg);

    // seed the rng
    if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *)"MistServer", 10)) != 0){
      FAIL_MSG("Could not seed the random number generator!");
    }

    //Read certificate chain(s) from cmdline option(s)
    JSON::Value certs = config->getOption("cert", true);
    jsonForEach(certs, it){
      if (it->asStringRef().size()){//Ignore empty entries (default is empty)
        ret = mbedtls_x509_crt_parse_file(&srvcert, it->asStringRef().c_str());
        if (ret != 0){
          WARN_MSG("Could not load any certificates from file: %s", it->asStringRef().c_str());
        }
      }
    }

    //Read key from cmdline option
    ret = mbedtls_pk_parse_keyfile(&pkey, config->getString("key").c_str(), 0);
    if (ret != 0){
      FAIL_MSG("Could not load any keys from file: %s", config->getString("key").c_str());
      return;
    }

    if ((ret = mbedtls_ssl_config_defaults(&sslConf, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0){
      FAIL_MSG("SSL config defaults failed");
      return;
    }
    mbedtls_ssl_conf_rng(&sslConf, mbedtls_ctr_drbg_random, &ctr_drbg);
    mbedtls_ssl_conf_ca_chain(&sslConf, srvcert.next, NULL);
    if ((ret = mbedtls_ssl_conf_own_cert(&sslConf, &srvcert, &pkey)) != 0){
      FAIL_MSG("SSL config own certificate failed");
      return;
    }

    Output::listener(conf, callback);

    //Free all the mbedtls structures
    mbedtls_x509_crt_free(&srvcert);
    mbedtls_pk_free(&pkey);
    mbedtls_ssl_config_free(&sslConf);
    mbedtls_ctr_drbg_free(&ctr_drbg);
    mbedtls_entropy_free(&entropy);
  }
}

<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkImageToImageFilter.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$
  Thanks:    Thanks to C. Charles Law who developed this class.

Copyright (c) 1993-1995 Ken Martin, Will Schroeder, Bill Lorensen.

This software is copyrighted by Ken Martin, Will Schroeder and Bill Lorensen.
The following terms apply to all files associated with the software unless
explicitly disclaimed in individual files. This copyright specifically does
not apply to the related textbook "The Visualization Toolkit" ISBN
013199837-4 published by Prentice Hall which is covered by its own copyright.

The authors hereby grant permission to use, copy, and distribute this
software and its documentation for any purpose, provided that existing
copyright notices are retained in all copies and that this notice is included
verbatim in any distributions. Additionally, the authors grant permission to
modify this software and its documentation for any purpose, provided that
such modifications are not distributed without the explicit consent of the
authors and that existing copyright notices are retained in all copies. Some
of the algorithms implemented by this software are patented, observe all
applicable patent law.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=========================================================================*/
#include "vtkImageToImageFilter.h"

//----------------------------------------------------------------------------
vtkImageToImageFilter::vtkImageToImageFilter()
{
  this->Bypass = 0;
  this->BypassWasOn = 0;
  this->Threader = vtkMultiThreader::New();
  this->NumberOfThreads = this->Threader->GetNumberOfThreads();
}

//----------------------------------------------------------------------------
vtkImageToImageFilter::~vtkImageToImageFilter()
{
  this->Threader->Delete();
}

//----------------------------------------------------------------------------
void vtkImageToImageFilter::PrintSelf(ostream& os, vtkIndent indent)
{
  os << indent << "Bypass: " << this->Bypass << "\n";
  os << indent << "NumberOfThreads: " << this->NumberOfThreads << "\n";

  vtkImageSource::PrintSelf(os,indent);
}

//----------------------------------------------------------------------------
void vtkImageToImageFilter::SetInput(vtkImageData *input)
{
  this->vtkProcessObject::SetInput(0, input);
}

//----------------------------------------------------------------------------
vtkImageData *vtkImageToImageFilter::GetInput()
{
  if (this->NumberOfInputs < 1)
    {
    return NULL;
    }

  return (vtkImageData *)(this->Inputs[0]);
}

//----------------------------------------------------------------------------
// This method can be overriden in a subclass to compute the output
// Information: WholeExtent, Spacing, Origin, ScalarType and
// NumberOfScalarComponents.
void vtkImageToImageFilter::ExecuteInformation()
{
  vtkImageData *input = this->GetInput();
  vtkImageData *output = this->GetOutput();

  // Make sure the Input has been set.
  if ( input == NULL || output == NULL)
    {
    vtkErrorMacro(<< "UpdateInformation: Input is not set.");
    return;
    }

  // Set the dafaults.
  // Setting defaults will modify the data if the sublcass overrides the
  // defaults.  But this should be OK because ExecuteTime (and UpdateTime)
  // should be out of date anyway if this method is being called.

  // Done in superclass now.
  //output->CopyInformation(input);

  if (this->Bypass == 0)
    {
    // for legacy
    this->LegacyHack = 1;
    this->ExecuteImageInformation();
    if (this->LegacyHack)
      {
      vtkWarningMacro("ExecuteImageInformation will not be supported in the future.\n"
        << "You should write an ExecuteInformation(vtkImageData*, vtkImageData*)");
      return;
      }

    this->ExecuteInformation(input, output);
    }
  else
    {
    output->CopyInformation( input );
    }
}
//----------------------------------------------------------------------------
void vtkImageToImageFilter::ExecuteInformation(
           vtkImageData *vtkNotUsed(inData), vtkImageData *vtkNotUsed(outData))
{
}

//----------------------------------------------------------------------------
int vtkImageToImageFilter::ComputeDivisionExtents(vtkDataObject *output,
						  int idx, int numDivisions)
{
  vtkImageData *input = this->GetInput();
  int actualSplits;
  int *outExt, inExt[6];

  if (input == NULL)
    {
    vtkErrorMacro("No input");
    return 0;
    }

  outExt = this->GetOutput()->GetUpdateExtent();
  actualSplits = this->SplitExtent(this->ExecuteExtent, outExt,
				   idx, numDivisions);

  if (idx < actualSplits)
    { // yes this is a valid piece.
    this->ComputeRequiredInputUpdateExtent(inExt, this->ExecuteExtent);
    input->SetUpdateExtent(inExt);
    return 1;
    }
  else
    {
    // We could not split to this piece.
    return 0;
    }
}

//----------------------------------------------------------------------------
// This method can be overriden in a subclass to compute the input
// UpdateExtent needed to generate the output UpdateExtent.
// By default the input is set to the same as the output before this
// method is called.
void vtkImageToImageFilter::ComputeRequiredInputUpdateExtent(int inExt[6],
						     int outExt[6])
{
  memcpy(inExt,outExt,sizeof(int)*6);
}

struct vtkImageThreadStruct
{
  vtkImageToImageFilter *Filter;
  vtkImageData   *Input;
  vtkImageData   *Output;
};

// this mess is really a simple function. All it does is call
// the ThreadedExecute method after setting the correct
// extent for this thread. Its just a pain to calculate
// the correct extent.
VTK_THREAD_RETURN_TYPE vtkImageThreadedExecute( void *arg )
{
  vtkImageThreadStruct *str;
  int ext[6], splitExt[6], total;
  int threadId, threadCount;

  threadId = ((ThreadInfoStruct *)(arg))->ThreadID;
  threadCount = ((ThreadInfoStruct *)(arg))->NumberOfThreads;

  str = (vtkImageThreadStruct *)(((ThreadInfoStruct *)(arg))->UserData);
  memcpy(ext,str->Filter->GetExecuteExtent(), sizeof(int)*6);

  // execute the actual method with appropriate extent
  // first find out how many pieces extent can be split into.
  total = str->Filter->SplitExtent(splitExt, ext, threadId, threadCount);
  //total = 1;

  if (threadId < total)
    {
    str->Filter->ThreadedExecute(str->Input, str->Output, splitExt, threadId);
    }
  // else
  //   {
  //   otherwise don't use this thread. Sometimes the threads dont
  //   break up very well and it is just as efficient to leave a
  //   few threads idle.
  //   }

  return VTK_THREAD_RETURN_VALUE;
}

void vtkImageToImageFilter::StreamExecuteStart()
{
  vtkImageData *output = this->GetOutput();

  // We need to be careful if Bypass has been toggled.
  if (this->Bypass == 0)
    {
    if (this->BypassWasOn)
      {
      // We were bypassing this filter (causing pointers to be copied from
      // input to output), now we are not bypassing the filter.  Need to reset
      // the output so we do not use the "copied" references.
      output->GetPointData()->Initialize();
      this->BypassWasOn = 0;
      }
    }
  else
    {
    this->BypassWasOn = 1;
    }

  //
  // Call vtkImageSource's StreamExecuteStart
  //
  this->vtkImageSource::StreamExecuteStart();
}

//----------------------------------------------------------------------------
// This is the superclasses style of Execute method.  Convert it into
// an imaging style Execute method.
void vtkImageToImageFilter::Execute()
{
  if (this->Bypass == 0)
    {
    this->Execute(this->GetInput(), this->GetOutput());
    }
  else
    {
    vtkImageData *inData = this->GetInput();

    if ( inData == NULL)
      {
      vtkErrorMacro("No Input.");
      return;
      }
    this->GetOutput()->GetPointData()->PassData(inData->GetPointData());
    this->GetOutput()->SetExtent( this->GetInput()->GetExtent() );
    }
}

//----------------------------------------------------------------------------
// This assumes that there is no overlap of pieces.
// Not a good assumption! OH Well....
int vtkImageToImageFilter::GetNumberOfStreamDivisions()
{
  vtkImageData *input = this->GetInput();
  float fraction;
  int *ext;
  int num;

  if (input == NULL)
    {
    return 1;
    }

  // What fraction of whole extent is the OUTPUT UpdateExtent.
  ext = this->GetOutput()->GetWholeExtent();
  fraction = (ext[1]-ext[0]+1) * (ext[3]-ext[2]+1) * (ext[5]-ext[4]+1);
  ext = this->GetOutput()->GetUpdateExtent();
  fraction = (ext[1]-ext[0]+1)*(ext[3]-ext[2]+1)*(ext[5]-ext[4]+1) / fraction;

  // Estimated memory size is of the WholeExtent
  num =(int)(fraction *
	     input->GetEstimatedWholeMemorySize() / input->GetMemoryLimit());

  if (num < 1)
    {
    return 1;
    }
  return num;
}

//----------------------------------------------------------------------------
void vtkImageToImageFilter::Execute(vtkImageData *inData,
				    vtkImageData *outData)
{
  vtkImageThreadStruct str;

  str.Filter = this;
  str.Input = inData;
  str.Output = outData;

  this->Threader->SetNumberOfThreads(this->NumberOfThreads);

  // setup threading and the invoke threadedExecute
  this->Threader->SetSingleMethod(vtkImageThreadedExecute, &str);
  this->Threader->SingleMethodExecute();
}

//----------------------------------------------------------------------------
// The execute method created by the subclass.
void vtkImageToImageFilter::ThreadedExecute(vtkImageData *vtkNotUsed(inData),
				     vtkImageData *vtkNotUsed(outData),
				     int extent[6], int vtkNotUsed(threadId))
{
  extent = extent;
  vtkErrorMacro("subclass should override this method!!!");
}

//----------------------------------------------------------------------------
// For streaming and threads.  Splits output update extent into num pieces.
// This method needs to be called num times.  Results must not overlap for
// consistent starting extent.  Subclass can override this method.
// This method returns the number of peices resulting from a successful split.
// This can be from 1 to "total".
// If 1 is returned, the extent cannot be split.
int vtkImageToImageFilter::SplitExtent(int splitExt[6], int startExt[6],
				       int num, int total)
{
  int splitAxis;
  int min, max;

  vtkDebugMacro("SplitExtent: ( " << startExt[0] << ", " << startExt[1] << ", "
		<< startExt[2] << ", " << startExt[3] << ", "
		<< startExt[4] << ", " << startExt[5] << "), "
		<< num << " of " << total);

  // start with same extent
  memcpy(splitExt, startExt, 6 * sizeof(int));

  splitAxis = 2;
  min = startExt[4];
  max = startExt[5];
  while (min == max)
    {
    splitAxis--;
    if (splitAxis < 0)
      { // cannot split
      vtkDebugMacro("  Cannot Split");
      return 1;
      }
    min = startExt[splitAxis*2];
    max = startExt[splitAxis*2+1];
    }

  // determine the actual number of pieces that will be generated
  int range = max - min + 1;
  int valuesPerThread = (int)ceil(range/(double)total);
  int maxThreadIdUsed = (int)ceil(range/(double)valuesPerThread) - 1;
  if (num < maxThreadIdUsed)
    {
    splitExt[splitAxis*2] = splitExt[splitAxis*2] + num*valuesPerThread;
    splitExt[splitAxis*2+1] = splitExt[splitAxis*2] + valuesPerThread - 1;
    }
  if (num == maxThreadIdUsed)
    {
    splitExt[splitAxis*2] = splitExt[splitAxis*2] + num*valuesPerThread;
    }

  vtkDebugMacro("  Split Piece: ( " <<splitExt[0]<< ", " <<splitExt[1]<< ", "
		<< splitExt[2] << ", " << splitExt[3] << ", "
		<< splitExt[4] << ", " << splitExt[5] << ")");

  return maxThreadIdUsed + 1;
}

//----------------------------------------------------------------------------
void vtkImageToImageFilter::SetInputMemoryLimit(int limit)
{
  vtkImageData *input = this->GetInput();

  if ( input == NULL)
    {
    vtkErrorMacro("Input must be set before InputMemoryLimit.");
    }

  input->SetMemoryLimit(limit);
}

//----------------------------------------------------------------------------
long vtkImageToImageFilter::GetInputMemoryLimit()
{
  vtkImageData *input = this->GetInput();

  if ( input == NULL)
    {
    vtkErrorMacro("Input must be set before you can get InputMemoryLimit.");
    return 1000000;
    }

  return input->GetMemoryLimit();
}
<commit_msg>FIX: More fixes for Bypass mode.  We were reallocating our input accidently.<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkImageToImageFilter.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$
  Thanks:    Thanks to C. Charles Law who developed this class.

Copyright (c) 1993-1995 Ken Martin, Will Schroeder, Bill Lorensen.

This software is copyrighted by Ken Martin, Will Schroeder and Bill Lorensen.
The following terms apply to all files associated with the software unless
explicitly disclaimed in individual files. This copyright specifically does
not apply to the related textbook "The Visualization Toolkit" ISBN
013199837-4 published by Prentice Hall which is covered by its own copyright.

The authors hereby grant permission to use, copy, and distribute this
software and its documentation for any purpose, provided that existing
copyright notices are retained in all copies and that this notice is included
verbatim in any distributions. Additionally, the authors grant permission to
modify this software and its documentation for any purpose, provided that
such modifications are not distributed without the explicit consent of the
authors and that existing copyright notices are retained in all copies. Some
of the algorithms implemented by this software are patented, observe all
applicable patent law.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=========================================================================*/
#include "vtkImageToImageFilter.h"

//----------------------------------------------------------------------------
vtkImageToImageFilter::vtkImageToImageFilter()
{
  this->Bypass = 0;
  this->BypassWasOn = 0;
  this->Threader = vtkMultiThreader::New();
  this->NumberOfThreads = this->Threader->GetNumberOfThreads();
}

//----------------------------------------------------------------------------
vtkImageToImageFilter::~vtkImageToImageFilter()
{
  this->Threader->Delete();
}

//----------------------------------------------------------------------------
void vtkImageToImageFilter::PrintSelf(ostream& os, vtkIndent indent)
{
  os << indent << "Bypass: " << this->Bypass << "\n";
  os << indent << "NumberOfThreads: " << this->NumberOfThreads << "\n";

  vtkImageSource::PrintSelf(os,indent);
}

//----------------------------------------------------------------------------
void vtkImageToImageFilter::SetInput(vtkImageData *input)
{
  this->vtkProcessObject::SetInput(0, input);
}

//----------------------------------------------------------------------------
vtkImageData *vtkImageToImageFilter::GetInput()
{
  if (this->NumberOfInputs < 1)
    {
    return NULL;
    }

  return (vtkImageData *)(this->Inputs[0]);
}

//----------------------------------------------------------------------------
// This method can be overriden in a subclass to compute the output
// Information: WholeExtent, Spacing, Origin, ScalarType and
// NumberOfScalarComponents.
void vtkImageToImageFilter::ExecuteInformation()
{
  vtkImageData *input = this->GetInput();
  vtkImageData *output = this->GetOutput();

  // Make sure the Input has been set.
  if ( input == NULL || output == NULL)
    {
    vtkErrorMacro(<< "UpdateInformation: Input is not set.");
    return;
    }

  // Set the dafaults.
  // Setting defaults will modify the data if the sublcass overrides the
  // defaults.  But this should be OK because ExecuteTime (and UpdateTime)
  // should be out of date anyway if this method is being called.

  // Done in superclass now.
  //output->CopyInformation(input);

  if (this->Bypass == 0)
    {
    // for legacy
    this->LegacyHack = 1;
    this->ExecuteImageInformation();
    if (this->LegacyHack)
      {
      vtkWarningMacro("ExecuteImageInformation will not be supported in the future.\n"
        << "You should write an ExecuteInformation(vtkImageData*, vtkImageData*)");
      return;
      }

    this->ExecuteInformation(input, output);
    }
  else
    {
    output->CopyInformation( input );
    }
}
//----------------------------------------------------------------------------
void vtkImageToImageFilter::ExecuteInformation(
           vtkImageData *vtkNotUsed(inData), vtkImageData *vtkNotUsed(outData))
{
}

//----------------------------------------------------------------------------
int vtkImageToImageFilter::ComputeDivisionExtents(vtkDataObject *output,
						  int idx, int numDivisions)
{
  vtkImageData *input = this->GetInput();
  int actualSplits;
  int *outExt, inExt[6];

  if (input == NULL)
    {
    vtkErrorMacro("No input");
    return 0;
    }

  outExt = this->GetOutput()->GetUpdateExtent();
  actualSplits = this->SplitExtent(this->ExecuteExtent, outExt,
				   idx, numDivisions);

  if (idx < actualSplits)
    { // yes this is a valid piece.
    this->ComputeRequiredInputUpdateExtent(inExt, this->ExecuteExtent);
    input->SetUpdateExtent(inExt);
    return 1;
    }
  else
    {
    // We could not split to this piece.
    return 0;
    }
}

//----------------------------------------------------------------------------
// This method can be overriden in a subclass to compute the input
// UpdateExtent needed to generate the output UpdateExtent.
// By default the input is set to the same as the output before this
// method is called.
void vtkImageToImageFilter::ComputeRequiredInputUpdateExtent(int inExt[6],
						     int outExt[6])
{
  memcpy(inExt,outExt,sizeof(int)*6);
}

struct vtkImageThreadStruct
{
  vtkImageToImageFilter *Filter;
  vtkImageData   *Input;
  vtkImageData   *Output;
};

// this mess is really a simple function. All it does is call
// the ThreadedExecute method after setting the correct
// extent for this thread. Its just a pain to calculate
// the correct extent.
VTK_THREAD_RETURN_TYPE vtkImageThreadedExecute( void *arg )
{
  vtkImageThreadStruct *str;
  int ext[6], splitExt[6], total;
  int threadId, threadCount;

  threadId = ((ThreadInfoStruct *)(arg))->ThreadID;
  threadCount = ((ThreadInfoStruct *)(arg))->NumberOfThreads;

  str = (vtkImageThreadStruct *)(((ThreadInfoStruct *)(arg))->UserData);
  memcpy(ext,str->Filter->GetExecuteExtent(), sizeof(int)*6);

  // execute the actual method with appropriate extent
  // first find out how many pieces extent can be split into.
  total = str->Filter->SplitExtent(splitExt, ext, threadId, threadCount);
  //total = 1;

  if (threadId < total)
    {
    str->Filter->ThreadedExecute(str->Input, str->Output, splitExt, threadId);
    }
  // else
  //   {
  //   otherwise don't use this thread. Sometimes the threads dont
  //   break up very well and it is just as efficient to leave a
  //   few threads idle.
  //   }

  return VTK_THREAD_RETURN_VALUE;
}

void vtkImageToImageFilter::StreamExecuteStart()
{
  vtkImageData *output = this->GetOutput();

  // We need to be careful if Bypass has been toggled.
  if (this->Bypass == 0)
    {
    if (this->BypassWasOn)
      {
      // We were bypassing this filter (causing pointers to be copied from
      // input to output), now we are not bypassing the filter.  Need to reset
      // the output so we do not use the "copied" references.
      output->ReleaseData();
      this->BypassWasOn = 0;
      }
    //
    // Call vtkImageSource's StreamExecuteStart
    //
    this->vtkImageSource::StreamExecuteStart();
    }
  else
    {
    this->BypassWasOn = 1;
    }

}

//----------------------------------------------------------------------------
// This is the superclasses style of Execute method.  Convert it into
// an imaging style Execute method.
void vtkImageToImageFilter::Execute()
{
  if (this->Bypass == 0)
    {
    this->Execute(this->GetInput(), this->GetOutput());
    }
  else
    {
    vtkImageData *inData = this->GetInput();

    if ( inData == NULL)
      {
      vtkErrorMacro("No Input.");
      return;
      }
    this->GetOutput()->GetPointData()->PassData(inData->GetPointData());
    this->GetOutput()->SetExtent( this->GetInput()->GetExtent() );
    }
}

//----------------------------------------------------------------------------
// This assumes that there is no overlap of pieces.
// Not a good assumption! OH Well....
int vtkImageToImageFilter::GetNumberOfStreamDivisions()
{
  vtkImageData *input = this->GetInput();
  float fraction;
  int *ext;
  int num;

  if (input == NULL)
    {
    return 1;
    }

  // What fraction of whole extent is the OUTPUT UpdateExtent.
  ext = this->GetOutput()->GetWholeExtent();
  fraction = (ext[1]-ext[0]+1) * (ext[3]-ext[2]+1) * (ext[5]-ext[4]+1);
  ext = this->GetOutput()->GetUpdateExtent();
  fraction = (ext[1]-ext[0]+1)*(ext[3]-ext[2]+1)*(ext[5]-ext[4]+1) / fraction;

  // Estimated memory size is of the WholeExtent
  num =(int)(fraction *
	     input->GetEstimatedWholeMemorySize() / input->GetMemoryLimit());

  if (num < 1)
    {
    return 1;
    }
  return num;
}

//----------------------------------------------------------------------------
void vtkImageToImageFilter::Execute(vtkImageData *inData,
				    vtkImageData *outData)
{
  vtkImageThreadStruct str;

  str.Filter = this;
  str.Input = inData;
  str.Output = outData;

  this->Threader->SetNumberOfThreads(this->NumberOfThreads);

  // setup threading and the invoke threadedExecute
  this->Threader->SetSingleMethod(vtkImageThreadedExecute, &str);
  this->Threader->SingleMethodExecute();
}

//----------------------------------------------------------------------------
// The execute method created by the subclass.
void vtkImageToImageFilter::ThreadedExecute(vtkImageData *vtkNotUsed(inData),
				     vtkImageData *vtkNotUsed(outData),
				     int extent[6], int vtkNotUsed(threadId))
{
  extent = extent;
  vtkErrorMacro("subclass should override this method!!!");
}

//----------------------------------------------------------------------------
// For streaming and threads.  Splits output update extent into num pieces.
// This method needs to be called num times.  Results must not overlap for
// consistent starting extent.  Subclass can override this method.
// This method returns the number of peices resulting from a successful split.
// This can be from 1 to "total".
// If 1 is returned, the extent cannot be split.
int vtkImageToImageFilter::SplitExtent(int splitExt[6], int startExt[6],
				       int num, int total)
{
  int splitAxis;
  int min, max;

  vtkDebugMacro("SplitExtent: ( " << startExt[0] << ", " << startExt[1] << ", "
		<< startExt[2] << ", " << startExt[3] << ", "
		<< startExt[4] << ", " << startExt[5] << "), "
		<< num << " of " << total);

  // start with same extent
  memcpy(splitExt, startExt, 6 * sizeof(int));

  splitAxis = 2;
  min = startExt[4];
  max = startExt[5];
  while (min == max)
    {
    splitAxis--;
    if (splitAxis < 0)
      { // cannot split
      vtkDebugMacro("  Cannot Split");
      return 1;
      }
    min = startExt[splitAxis*2];
    max = startExt[splitAxis*2+1];
    }

  // determine the actual number of pieces that will be generated
  int range = max - min + 1;
  int valuesPerThread = (int)ceil(range/(double)total);
  int maxThreadIdUsed = (int)ceil(range/(double)valuesPerThread) - 1;
  if (num < maxThreadIdUsed)
    {
    splitExt[splitAxis*2] = splitExt[splitAxis*2] + num*valuesPerThread;
    splitExt[splitAxis*2+1] = splitExt[splitAxis*2] + valuesPerThread - 1;
    }
  if (num == maxThreadIdUsed)
    {
    splitExt[splitAxis*2] = splitExt[splitAxis*2] + num*valuesPerThread;
    }

  vtkDebugMacro("  Split Piece: ( " <<splitExt[0]<< ", " <<splitExt[1]<< ", "
		<< splitExt[2] << ", " << splitExt[3] << ", "
		<< splitExt[4] << ", " << splitExt[5] << ")");

  return maxThreadIdUsed + 1;
}

//----------------------------------------------------------------------------
void vtkImageToImageFilter::SetInputMemoryLimit(int limit)
{
  vtkImageData *input = this->GetInput();

  if ( input == NULL)
    {
    vtkErrorMacro("Input must be set before InputMemoryLimit.");
    }

  input->SetMemoryLimit(limit);
}

//----------------------------------------------------------------------------
long vtkImageToImageFilter::GetInputMemoryLimit()
{
  vtkImageData *input = this->GetInput();

  if ( input == NULL)
    {
    vtkErrorMacro("Input must be set before you can get InputMemoryLimit.");
    return 1000000;
    }

  return input->GetMemoryLimit();
}
<|endoftext|>