<commit_before>/*
 * Copyright (C) 2008-2011, Gostai S.A.S.
 *
 * This software is provided "as is" without warranty of any kind,
 * either expressed or implied, including but not limited to the
 * implied warranties of fitness for a particular purpose.
 *
 * See the LICENSE file for more information.
 */

#include <iostream>

#include <boost/thread/tss.hpp>

#include <libport/cassert>
#include <libport/compiler.hh>
#include <libport/containers.hh>
#include <libport/cstdio>
#include <libport/debug.hh>
#include <libport/escape.hh>
#include <libport/fnmatch.h>
#include <libport/foreach.hh>
#include <libport/format.hh>
#include <libport/ip-semaphore.hh>
#include <libport/lockable.hh>
#include <libport/pthread.h>
#include <libport/thread-data.hh>
#include <libport/tokenizer.hh>
#include <libport/unistd.h>
#include <libport/utime.hh>
#include <libport/windows.hh>
#include <sched/coroutine-data.hh>

#ifndef WIN32
# include <syslog.h>
#endif

#ifndef LIBPORT_DEBUG_DISABLE

GD_CATEGORY(Libport.Debug);

namespace libport
{
  LIBPORT_API boost::function0<local_data&> debugger_data;
  LIBPORT_API Debug* debugger = 0;
  LIBPORT_API Debug::levels::Level Debug::filter_(levels::log);

  local_data&
  debugger_data_thread_local()
  {
    static boost::thread_specific_ptr<local_data> storage;
    if (!storage.get())
      storage.reset(new local_data);
    return *storage;
  }

  local_data::local_data()
    : indent(0)
  {}

  namespace debug
  {
    // Whether categories are enabled by default.
    static bool default_category_state = true;

    LIBPORT_API void
    uninitialized_msg(const std::string& msg)
    {
      static bool dump =
        getenv("GD_LEVEL") && libport::streq(getenv("GD_LEVEL"), "DUMP");
      if (dump)
      {
        static bool tail = false;
        if (!tail++)
          std::cerr << "[Libport.Debug] "
                    << "Uninitialized debug, fallback to stderr." << std::endl;
        std::cerr << "[Libport.Debug] " << msg;
        if (msg.empty() || msg[msg.size() - 1] != '\n')
          std::cerr << std::endl;
      }
    }

    // Categories added so far.
    ATTRIBUTE_PURE
    categories_type&
    categories()
    {
      static categories_type categories;
      return categories;
    }

    namespace
    {

      ATTRIBUTE_PURE
      static unsigned
      current_pattern()
      {
        static unsigned current = 0;
        return current++;
      }

      // Patterns added so far.
      ATTRIBUTE_PURE
      patterns_type&
      patterns()
      {
        static patterns_type patterns;
        return patterns;
      }

      // Maximum category width.
      ATTRIBUTE_PURE
      size_t&
      categories_largest()
      {
        static size_t res = 0;
        return res;
      }

      inline
      bool
      match(Symbol globbing, Symbol string)
      {
        return fnmatch(globbing.name_get(), string.name_get()) == 0;
      }
    }

    /// Add a new category, look if it matches a pattern.
    Symbol
    add_category(Symbol name)
    {
      int order = -1;
      bool value = default_category_state;
      foreach (patterns_type::value_type& s, patterns())
        if (match(s.first, name)
            && order < int(s.second.second))
        {
          value = s.second.first;
          order = s.second.second;
        }

      categories()[name] = value;

      categories_largest() =
        std::max(categories_largest(), name.name_get().size());

      return name;
    }

    /// Add a new category pattern.
    int
    enable_category(Symbol pattern, bool enabled)
    {
      patterns()[pattern] = std::make_pair(enabled, current_pattern());
      foreach (categories_type::value_type& s, categories())
        if (match(pattern, s.first))
          s.second = enabled;

      return 42;
    }

    // Disable a new category pattern.
    int
    disable_category(Symbol pattern)
    {
      return enable_category(pattern, false);
    }

    // Enable/Disable a new category pattern with modifier.
    int
    auto_category(Symbol pattern)
    {
      std::string p = pattern.name_get();
      char modifier = p[0];
      if (modifier == '+' || modifier == '-')
        p = p.substr(1);
      else
        modifier = '+';
      return enable_category(Symbol(p), modifier == '+');
    }

    bool test_category(Symbol name)
    {
      return categories()[name];
    }

    typedef enum
    {
      ENABLE,
      DISABLE,
      AUTO,
    } category_modifier_type;

    /// Enable/disable categories, already seen or future.
    ///
    /// Called by GD_INIT.
    ///
    /// \param list  the value of the environment var (GD_CATEGORY, etc.).
    /// \param state the associated state (AUTO, ENABLE, DISABLE).
    static
    void
    set_category_state(const char* list, const category_modifier_type state)
    {
      // If the mode is "AUTO", then if the first specs is to enable
      // ("-...") then the default is to enable, otherwise disable.
      // If the mode if not AUTO, then the default is the converse of
      // the mode: ENABLE -> false, and DISABLE => true.
      default_category_state =
        state == AUTO ? (*list == '-') : (state != ENABLE);

      // Set all the existing categories to the default behavior
      // before running the per-pattern tests.
      foreach (categories_type::value_type& v, categories())
        v.second = default_category_state;

      std::string s(list); // Do not pass temporary to make_tokenizer.
      tokenizer_type t = make_tokenizer(s, ",");
      foreach (const std::string& elem, t)
      {
        Symbol pattern(elem);
        switch (state)
        {
          case ENABLE:
          case DISABLE:
            enable_category(pattern, state == ENABLE);
            break;
          case AUTO:
            auto_category(pattern);
            break;
        }
      }
    }
  }

  Debug::Debug()
    : locations_(getenv("GD_LOC"))
    , timestamps_(getenv("GD_TIME") || getenv("GD_TIMESTAMP_US"))
  {
    // Process enabled/disabled/auto categories in environment.
    if (const char* cp = getenv("GD_CATEGORY"))
      debug::set_category_state(cp, debug::AUTO);
    else
    {
      if (const char* cp = getenv("GD_ENABLE_CATEGORY"))
        debug::set_category_state(cp, debug::ENABLE);
      if (const char* cp = getenv("GD_DISABLE_CATEGORY"))
        debug::set_category_state(cp, debug::DISABLE);
    }

    if (const char* lvl_c = getenv("GD_LEVEL"))
      filter(lvl_c);
  }

  Debug::~Debug()
  {}

  void
  Debug::filter(levels::Level lvl)
  {
    filter_ = lvl;
  }

  void
  Debug::filter(const std::string& lvl)
  {
    if (lvl == "NONE" || lvl == "0")
      filter(Debug::levels::none);
    else if (lvl == "LOG" || lvl == "1")
      filter(Debug::levels::log);
    else if (lvl == "TRACE" || lvl == "2")
      filter(Debug::levels::trace);
    else if (lvl == "DEBUG" || lvl == "3")
      filter(Debug::levels::debug);
    else if (lvl == "DUMP" || lvl == "4")
      filter(Debug::levels::dump);
    else
      // Don't use GD_ABORT here, we can be in the debugger constructor!
      pabort("invalid debug level (NONE, LOG, TRACE, DEBUG, DUMP or [0-4])");
  }

  unsigned
  Debug::indentation() const
  {
    return debugger_data().indent;
  }

  Debug::levels::Level
  Debug::level()
  {
    return filter_;
  }

# ifdef LIBPORT_HAVE_IP_SEMAPHORE
  static IPSemaphore& sem()
  {
    static IPSemaphore res(1);
    return res;
  }
# endif

  void
  Debug::debug(const std::string& msg,
               types::Type type,
               debug::category_type category,
               const std::string& fun,
               const std::string& file,
               unsigned line)
  {
# ifdef LIBPORT_HAVE_IP_SEMAPHORE
    static bool useLock = getenv("GD_USE_LOCK") || getenv("GD_PID");
    libport::Finally f;
    if (useLock)
    {
      --sem();
      f << boost::bind(&IPSemaphore::operator++, boost::ref(sem()));
    }
# endif
    message(category, msg, type, fun, file, line);
  }

  Debug*
  Debug::push(debug::category_type category,
              const std::string& msg,
              const std::string& fun,
              const std::string& file,
              unsigned line)
  {
    message_push(category, msg, fun, file, line);
    return this;
  }

  std::string
  Debug::category_format(debug::category_type cat) const
  {
    std::string res = cat;
    size_t size = res.size();
    size_t largest = debug::categories_largest();
    if (size < largest)
    {
      size_t diff = largest - size;
      res = std::string(diff / 2, ' ')
        + res
        + std::string(diff / 2 + diff % 2, ' ');
    }

    return res;
  }

  namespace opts
  {

    void cb_debug_fun(const std::string& lvl)
    {
      GD_DEBUGGER->filter(lvl);
    }

    OptionValued::callback_type cb_debug(cb_debug_fun);

    OptionValue
    debug("set the debug level in NONE, LOG (default), TRACE, DEBUG, DUMP",
          "debug", 'd', "LEVEL", cb_debug);

  }

  ConsoleDebug::ConsoleDebug()
  {}

  namespace
  {
    inline
    std::string
    time()
    {
      static bool us = getenv("GD_TIMESTAMP_US");
      if (us)
        return string_cast(utime());
      time_t now = std::time(0);
      struct tm* ts = std::localtime(&now);
      char buf[80];
      strftime(buf, sizeof buf, "%a %Y-%m-%d %H:%M:%S %Z", ts);
      return buf;
    }

    inline
    std::string
    color(int color, bool bold = true)
    {
      static bool tty = isatty(STDERR_FILENO);
      static bool force = getenv("GD_COLOR");
      static bool force_disable = getenv("GD_NO_COLOR");
      return (((tty || force) && !force_disable)
              ? format("[33;0%s;%sm", bold ? 1 : 0, color)
              : "");
    }
  }

  static Debug::colors::Color
  msg_color(Debug::types::Type type)
  {
    switch (type)
    {
      case Debug::types::info:
        return Debug::colors::white;
      case Debug::types::warn:
        return Debug::colors::yellow;
      case Debug::types::error:
        return Debug::colors::red;
    };
    GD_UNREACHABLE();
  }

  void
  ConsoleDebug::message(debug::category_type category,
                        const std::string& msg,
                        types::Type type,
                        const std::string& fun,
                        const std::string& file,
                        unsigned line)
  {
    std::ostringstream ostr;
    Debug::colors::Color c = msg_color(type);
    if (timestamps())
      ostr << color(c) << time() << "    ";
    ostr << color(colors::purple)
         << "[" << category_format(category) << "] ";
    {
      static bool pid = getenv("GD_PID");
      if (pid)
        ostr << "[" << getpid() << "] ";
    }
#ifndef WIN32
    {
      static bool thread = getenv("GD_THREAD");
      if (thread)
        ostr << "[" << pthread_self() << "] ";
    }
#endif
    ostr << color(c);
    for (unsigned i = 0; i < debugger_data().indent; ++i)
      ostr << "  ";
    // As syslog would do, don't issue the users' \n.
    if (!msg.empty() && msg[msg.size() - 1] == '\n')
      ostr.write(msg.c_str(), msg.size() - 1);
    else
      ostr << msg;
    if (locations())
      ostr << color(colors::blue)
           << "    (" << fun << ", " << file << ":" << line << ")";
    ostr << color(colors::white);
    std::cerr << ostr.str() << std::endl;
  }

  void
  ConsoleDebug::message_push(debug::category_type category,
                             const std::string& msg,
                             const std::string& fun,
                             const std::string& file,
                             unsigned line)
  {
    debug(msg, types::info, category, fun, file, line);
    GD_INDENTATION_INC();
  }

  void
  ConsoleDebug::pop()
  {
    assert_gt(debugger_data().indent, 0u);
    GD_INDENTATION_DEC();
  }

  std::string gd_ihexdump(const unsigned char* data, unsigned size)
  {
    std::string res =
      format("\"%s\"",
             libport::escape(std::string((const char*)data, (size_t)(size))));

    bool first = true;
    for (unsigned i = 0; i < size; ++i)
    {
      if (first)
        first = false;
      else
        res += " ";
      // Cast to int, or boost::format will print the character.
      res += format("0x%x", static_cast<unsigned int>(data[i]));
    }
    return res;
  }

#ifndef WIN32
  /*-------------.
  | Syslog debug |
  `-------------*/

  SyslogDebug::SyslogDebug(const std::string& program)
  {
    openlog(strdup(program.c_str()), LOG_PID, LOG_DAEMON);
    syslog(LOG_INFO | LOG_DAEMON, "%s",
           format("Opening syslog session for '%s'", program).c_str());
  }

  SyslogDebug::~SyslogDebug()
  {
    syslog(LOG_INFO | LOG_DAEMON, "Closing syslog session.");
    closelog();
  }

  static
  int type_to_prio(Debug::types::Type t)
  {
    switch (t)
    {
#define CASE(In, Out)                           \
      case Debug::types::In: return Out; break
      CASE(info,  LOG_INFO);
      CASE(warn,  LOG_WARNING);
      CASE(error, LOG_ERR);
#undef CASE
    }
    // Pacify Gcc.
    libport::abort();
  }

  void
  SyslogDebug::message(debug::category_type category,
                       const std::string& msg,
                       types::Type type,
                       const std::string& fun,
                       const std::string& file,
                       unsigned line)
  {
    std::stringstream s;
    s << "[" << category_format(category) << "] ";
    for (unsigned i = 0; i < debugger_data().indent; ++i)
      s << "  ";
    // As syslog would do, don't issue the users' \n.
    if (!msg.empty() && msg[msg.size() - 1] == '\n')
      s.write(msg.c_str(), msg.size() - 1);
    else
      s << msg;
    if (locations())
      s << "    (" << fun << ", " << file << ":" << line << ")";
    int prio = type_to_prio(type) | LOG_DAEMON;
    syslog(prio, "%s", s.str().c_str());
  }

  void
  SyslogDebug::message_push(debug::category_type category,
                            const std::string& msg,
                            const std::string& fun,
                            const std::string& file,
                            unsigned line)
  {
    debug(msg, types::info, category, fun, file, line);
    GD_INDENTATION_INC();
  }

  void
  SyslogDebug::pop()
  {
    assert_gt(debugger_data().indent, 0u);
    GD_INDENTATION_DEC();
  }
#endif

  boost::function0<Debug*> make_debugger;

  namespace debug
  {
    void clear()
    {
#if FIXME
      delete debugger();
#endif
    }
  }
}

#endif
<commit_msg>help: fit in 80 cols.<commit_after>/*
 * Copyright (C) 2008-2011, Gostai S.A.S.
 *
 * This software is provided "as is" without warranty of any kind,
 * either expressed or implied, including but not limited to the
 * implied warranties of fitness for a particular purpose.
 *
 * See the LICENSE file for more information.
 */

#include <iostream>

#include <boost/thread/tss.hpp>

#include <libport/cassert>
#include <libport/compiler.hh>
#include <libport/containers.hh>
#include <libport/cstdio>
#include <libport/debug.hh>
#include <libport/escape.hh>
#include <libport/fnmatch.h>
#include <libport/foreach.hh>
#include <libport/format.hh>
#include <libport/ip-semaphore.hh>
#include <libport/lockable.hh>
#include <libport/pthread.h>
#include <libport/thread-data.hh>
#include <libport/tokenizer.hh>
#include <libport/unistd.h>
#include <libport/utime.hh>
#include <libport/windows.hh>
#include <sched/coroutine-data.hh>

#ifndef WIN32
# include <syslog.h>
#endif

#ifndef LIBPORT_DEBUG_DISABLE

GD_CATEGORY(Libport.Debug);

namespace libport
{
  LIBPORT_API boost::function0<local_data&> debugger_data;
  LIBPORT_API Debug* debugger = 0;
  LIBPORT_API Debug::levels::Level Debug::filter_(levels::log);

  local_data&
  debugger_data_thread_local()
  {
    static boost::thread_specific_ptr<local_data> storage;
    if (!storage.get())
      storage.reset(new local_data);
    return *storage;
  }

  local_data::local_data()
    : indent(0)
  {}

  namespace debug
  {
    // Whether categories are enabled by default.
    static bool default_category_state = true;

    LIBPORT_API void
    uninitialized_msg(const std::string& msg)
    {
      static bool dump =
        getenv("GD_LEVEL") && libport::streq(getenv("GD_LEVEL"), "DUMP");
      if (dump)
      {
        static bool tail = false;
        if (!tail++)
          std::cerr << "[Libport.Debug] "
                    << "Uninitialized debug, fallback to stderr." << std::endl;
        std::cerr << "[Libport.Debug] " << msg;
        if (msg.empty() || msg[msg.size() - 1] != '\n')
          std::cerr << std::endl;
      }
    }

    // Categories added so far.
    ATTRIBUTE_PURE
    categories_type&
    categories()
    {
      static categories_type categories;
      return categories;
    }

    namespace
    {

      ATTRIBUTE_PURE
      static unsigned
      current_pattern()
      {
        static unsigned current = 0;
        return current++;
      }

      // Patterns added so far.
      ATTRIBUTE_PURE
      patterns_type&
      patterns()
      {
        static patterns_type patterns;
        return patterns;
      }

      // Maximum category width.
      ATTRIBUTE_PURE
      size_t&
      categories_largest()
      {
        static size_t res = 0;
        return res;
      }

      inline
      bool
      match(Symbol globbing, Symbol string)
      {
        return fnmatch(globbing.name_get(), string.name_get()) == 0;
      }
    }

    /// Add a new category, look if it matches a pattern.
    Symbol
    add_category(Symbol name)
    {
      int order = -1;
      bool value = default_category_state;
      foreach (patterns_type::value_type& s, patterns())
        if (match(s.first, name)
            && order < int(s.second.second))
        {
          value = s.second.first;
          order = s.second.second;
        }

      categories()[name] = value;

      categories_largest() =
        std::max(categories_largest(), name.name_get().size());

      return name;
    }

    /// Add a new category pattern.
    int
    enable_category(Symbol pattern, bool enabled)
    {
      patterns()[pattern] = std::make_pair(enabled, current_pattern());
      foreach (categories_type::value_type& s, categories())
        if (match(pattern, s.first))
          s.second = enabled;

      return 42;
    }

    // Disable a new category pattern.
    int
    disable_category(Symbol pattern)
    {
      return enable_category(pattern, false);
    }

    // Enable/Disable a new category pattern with modifier.
    int
    auto_category(Symbol pattern)
    {
      std::string p = pattern.name_get();
      char modifier = p[0];
      if (modifier == '+' || modifier == '-')
        p = p.substr(1);
      else
        modifier = '+';
      return enable_category(Symbol(p), modifier == '+');
    }

    bool test_category(Symbol name)
    {
      return categories()[name];
    }

    typedef enum
    {
      ENABLE,
      DISABLE,
      AUTO,
    } category_modifier_type;

    /// Enable/disable categories, already seen or future.
    ///
    /// Called by GD_INIT.
    ///
    /// \param list  the value of the environment var (GD_CATEGORY, etc.).
    /// \param state the associated state (AUTO, ENABLE, DISABLE).
    static
    void
    set_category_state(const char* list, const category_modifier_type state)
    {
      // If the mode is "AUTO", then if the first specs is to enable
      // ("-...") then the default is to enable, otherwise disable.
      // If the mode if not AUTO, then the default is the converse of
      // the mode: ENABLE -> false, and DISABLE => true.
      default_category_state =
        state == AUTO ? (*list == '-') : (state != ENABLE);

      // Set all the existing categories to the default behavior
      // before running the per-pattern tests.
      foreach (categories_type::value_type& v, categories())
        v.second = default_category_state;

      std::string s(list); // Do not pass temporary to make_tokenizer.
      tokenizer_type t = make_tokenizer(s, ",");
      foreach (const std::string& elem, t)
      {
        Symbol pattern(elem);
        switch (state)
        {
          case ENABLE:
          case DISABLE:
            enable_category(pattern, state == ENABLE);
            break;
          case AUTO:
            auto_category(pattern);
            break;
        }
      }
    }
  }

  Debug::Debug()
    : locations_(getenv("GD_LOC"))
    , timestamps_(getenv("GD_TIME") || getenv("GD_TIMESTAMP_US"))
  {
    // Process enabled/disabled/auto categories in environment.
    if (const char* cp = getenv("GD_CATEGORY"))
      debug::set_category_state(cp, debug::AUTO);
    else
    {
      if (const char* cp = getenv("GD_ENABLE_CATEGORY"))
        debug::set_category_state(cp, debug::ENABLE);
      if (const char* cp = getenv("GD_DISABLE_CATEGORY"))
        debug::set_category_state(cp, debug::DISABLE);
    }

    if (const char* lvl_c = getenv("GD_LEVEL"))
      filter(lvl_c);
  }

  Debug::~Debug()
  {}

  void
  Debug::filter(levels::Level lvl)
  {
    filter_ = lvl;
  }

  void
  Debug::filter(const std::string& lvl)
  {
    if (lvl == "NONE" || lvl == "0")
      filter(Debug::levels::none);
    else if (lvl == "LOG" || lvl == "1")
      filter(Debug::levels::log);
    else if (lvl == "TRACE" || lvl == "2")
      filter(Debug::levels::trace);
    else if (lvl == "DEBUG" || lvl == "3")
      filter(Debug::levels::debug);
    else if (lvl == "DUMP" || lvl == "4")
      filter(Debug::levels::dump);
    else
      // Don't use GD_ABORT here, we can be in the debugger constructor!
      pabort("invalid debug level (NONE, LOG, TRACE, DEBUG, DUMP or [0-4])");
  }

  unsigned
  Debug::indentation() const
  {
    return debugger_data().indent;
  }

  Debug::levels::Level
  Debug::level()
  {
    return filter_;
  }

# ifdef LIBPORT_HAVE_IP_SEMAPHORE
  static IPSemaphore& sem()
  {
    static IPSemaphore res(1);
    return res;
  }
# endif

  void
  Debug::debug(const std::string& msg,
               types::Type type,
               debug::category_type category,
               const std::string& fun,
               const std::string& file,
               unsigned line)
  {
# ifdef LIBPORT_HAVE_IP_SEMAPHORE
    static bool useLock = getenv("GD_USE_LOCK") || getenv("GD_PID");
    libport::Finally f;
    if (useLock)
    {
      --sem();
      f << boost::bind(&IPSemaphore::operator++, boost::ref(sem()));
    }
# endif
    message(category, msg, type, fun, file, line);
  }

  Debug*
  Debug::push(debug::category_type category,
              const std::string& msg,
              const std::string& fun,
              const std::string& file,
              unsigned line)
  {
    message_push(category, msg, fun, file, line);
    return this;
  }

  std::string
  Debug::category_format(debug::category_type cat) const
  {
    std::string res = cat;
    size_t size = res.size();
    size_t largest = debug::categories_largest();
    if (size < largest)
    {
      size_t diff = largest - size;
      res = std::string(diff / 2, ' ')
        + res
        + std::string(diff / 2 + diff % 2, ' ');
    }

    return res;
  }

  namespace opts
  {

    void cb_debug_fun(const std::string& lvl)
    {
      GD_DEBUGGER->filter(lvl);
    }

    OptionValued::callback_type cb_debug(cb_debug_fun);

    OptionValue
    debug("set log verbosity (NONE, LOG, TRACE, DEBUG, DUMP) [LOG]",
          "debug", 'd', "LEVEL", cb_debug);

  }

  ConsoleDebug::ConsoleDebug()
  {}

  namespace
  {
    inline
    std::string
    time()
    {
      static bool us = getenv("GD_TIMESTAMP_US");
      if (us)
        return string_cast(utime());
      time_t now = std::time(0);
      struct tm* ts = std::localtime(&now);
      char buf[80];
      strftime(buf, sizeof buf, "%a %Y-%m-%d %H:%M:%S %Z", ts);
      return buf;
    }

    inline
    std::string
    color(int color, bool bold = true)
    {
      static bool tty = isatty(STDERR_FILENO);
      static bool force = getenv("GD_COLOR");
      static bool force_disable = getenv("GD_NO_COLOR");
      return (((tty || force) && !force_disable)
              ? format("[33;0%s;%sm", bold ? 1 : 0, color)
              : "");
    }
  }

  static Debug::colors::Color
  msg_color(Debug::types::Type type)
  {
    switch (type)
    {
      case Debug::types::info:
        return Debug::colors::white;
      case Debug::types::warn:
        return Debug::colors::yellow;
      case Debug::types::error:
        return Debug::colors::red;
    };
    GD_UNREACHABLE();
  }

  void
  ConsoleDebug::message(debug::category_type category,
                        const std::string& msg,
                        types::Type type,
                        const std::string& fun,
                        const std::string& file,
                        unsigned line)
  {
    std::ostringstream ostr;
    Debug::colors::Color c = msg_color(type);
    if (timestamps())
      ostr << color(c) << time() << "    ";
    ostr << color(colors::purple)
         << "[" << category_format(category) << "] ";
    {
      static bool pid = getenv("GD_PID");
      if (pid)
        ostr << "[" << getpid() << "] ";
    }
#ifndef WIN32
    {
      static bool thread = getenv("GD_THREAD");
      if (thread)
        ostr << "[" << pthread_self() << "] ";
    }
#endif
    ostr << color(c);
    for (unsigned i = 0; i < debugger_data().indent; ++i)
      ostr << "  ";
    // As syslog would do, don't issue the users' \n.
    if (!msg.empty() && msg[msg.size() - 1] == '\n')
      ostr.write(msg.c_str(), msg.size() - 1);
    else
      ostr << msg;
    if (locations())
      ostr << color(colors::blue)
           << "    (" << fun << ", " << file << ":" << line << ")";
    ostr << color(colors::white);
    std::cerr << ostr.str() << std::endl;
  }

  void
  ConsoleDebug::message_push(debug::category_type category,
                             const std::string& msg,
                             const std::string& fun,
                             const std::string& file,
                             unsigned line)
  {
    debug(msg, types::info, category, fun, file, line);
    GD_INDENTATION_INC();
  }

  void
  ConsoleDebug::pop()
  {
    assert_gt(debugger_data().indent, 0u);
    GD_INDENTATION_DEC();
  }

  std::string gd_ihexdump(const unsigned char* data, unsigned size)
  {
    std::string res =
      format("\"%s\"",
             libport::escape(std::string((const char*)data, (size_t)(size))));

    bool first = true;
    for (unsigned i = 0; i < size; ++i)
    {
      if (first)
        first = false;
      else
        res += " ";
      // Cast to int, or boost::format will print the character.
      res += format("0x%x", static_cast<unsigned int>(data[i]));
    }
    return res;
  }

#ifndef WIN32
  /*-------------.
  | Syslog debug |
  `-------------*/

  SyslogDebug::SyslogDebug(const std::string& program)
  {
    openlog(strdup(program.c_str()), LOG_PID, LOG_DAEMON);
    syslog(LOG_INFO | LOG_DAEMON, "%s",
           format("Opening syslog session for '%s'", program).c_str());
  }

  SyslogDebug::~SyslogDebug()
  {
    syslog(LOG_INFO | LOG_DAEMON, "Closing syslog session.");
    closelog();
  }

  static
  int type_to_prio(Debug::types::Type t)
  {
    switch (t)
    {
#define CASE(In, Out)                           \
      case Debug::types::In: return Out; break
      CASE(info,  LOG_INFO);
      CASE(warn,  LOG_WARNING);
      CASE(error, LOG_ERR);
#undef CASE
    }
    // Pacify Gcc.
    libport::abort();
  }

  void
  SyslogDebug::message(debug::category_type category,
                       const std::string& msg,
                       types::Type type,
                       const std::string& fun,
                       const std::string& file,
                       unsigned line)
  {
    std::stringstream s;
    s << "[" << category_format(category) << "] ";
    for (unsigned i = 0; i < debugger_data().indent; ++i)
      s << "  ";
    // As syslog would do, don't issue the users' \n.
    if (!msg.empty() && msg[msg.size() - 1] == '\n')
      s.write(msg.c_str(), msg.size() - 1);
    else
      s << msg;
    if (locations())
      s << "    (" << fun << ", " << file << ":" << line << ")";
    int prio = type_to_prio(type) | LOG_DAEMON;
    syslog(prio, "%s", s.str().c_str());
  }

  void
  SyslogDebug::message_push(debug::category_type category,
                            const std::string& msg,
                            const std::string& fun,
                            const std::string& file,
                            unsigned line)
  {
    debug(msg, types::info, category, fun, file, line);
    GD_INDENTATION_INC();
  }

  void
  SyslogDebug::pop()
  {
    assert_gt(debugger_data().indent, 0u);
    GD_INDENTATION_DEC();
  }
#endif

  boost::function0<Debug*> make_debugger;

  namespace debug
  {
    void clear()
    {
#if FIXME
      delete debugger();
#endif
    }
  }
}

#endif
<|endoftext|>


<commit_before>// Copyright (C) 2017 Gabriel Gouvine - All Rights Reserved

#include "inc_bipart.h"
#include "coarsener.h"

namespace minipart {

class ThresholdQueue {
 public:
  ThresholdQueue(const IncBipart &inc)
    : inc_(inc) {}

  bool empty() const {
    return pos_gain_.empty()
        && zero_gain_.empty()
        && neg_gain_.empty();
  }

  Node pop() {
    while (!pos_gain_.empty()) {
      Node n = pos_gain_.back();
      pos_gain_.pop_back();
      if (inc_.gain(n) > 0) return n;
      else zero_gain_.push_back(n);
    }
    while (!zero_gain_.empty()) {
      Node n = zero_gain_.back();
      zero_gain_.pop_back();
      if (inc_.gain(n) >= 0) return n;
      else neg_gain_.push_back(n);
    }
    Node n = neg_gain_.back();
    neg_gain_.pop_back();
    return n;
  }

  void push(Node n) {
    Weight g = inc_.gain(n);
    if (g > 0) {
      pos_gain_.push_back(n);
    }
    else if (g == 0) {
      zero_gain_.push_back(n);
    }
    else {
      neg_gain_.push_back(n);
    }
  }

  void clear() {
    pos_gain_.clear();
    zero_gain_.clear();
    neg_gain_.clear();
  }

 private:
  std::vector<Node> pos_gain_;
  std::vector<Node> zero_gain_;
  std::vector<Node> neg_gain_;
  const IncBipart &inc_;
};

class FMQueue {
 public:
  FMQueue(const IncBipart &inc)
    : inc_(inc)
    , last_bucket_(0) {
    max_gain_ = 0;
    for (Node n : inc.nodes()) {
      Weight loc_gain = 0;
      for (Edge e : inc.edges(n)) {
        loc_gain += inc.hypergraph().weight(e);
      }
      max_gain_ = std::max(loc_gain, max_gain_);
    }
    buckets_.resize(2 * max_gain_ + 1);
  }

  bool empty() {
    while (true) {
      while (last_bucket_ != 0 && buckets_[last_bucket_].empty()) {
        --last_bucket_;
      }
      if (buckets_[last_bucket_].empty()) return true;
      Node n = buckets_[last_bucket_].back();
      Weight bucket = inc_.gain(n) + max_gain_;
      if (bucket != last_bucket_) {
        buckets_[last_bucket_].pop_back();
        buckets_[bucket].push_back(n);
        last_bucket_ = std::max(last_bucket_, bucket);
      }
      else return false;
    }
  }

  Node pop() {
    empty(); // Consume all out-of-place nodes
    Node n = buckets_[last_bucket_].back();
    buckets_[last_bucket_].pop_back();
    return n;
  }

  void push(Node n) {
    Weight bucket = max_gain_ + inc_.gain(n);
    buckets_[bucket].push_back(n);
    last_bucket_ = std::max(bucket, last_bucket_);
  }

  void clear() {
    for (auto & bucket : buckets_) {
      bucket.clear();
    }
  }

 private:
  const IncBipart &inc_;
  std::vector<std::vector<Node> > buckets_;
  Weight last_bucket_;
  Weight max_gain_;
};

void random_placement_pass(IncBipart &inc, std::minstd_rand &rgen) {
  std::bernoulli_distribution dist;
  for (auto n : inc.nodes()) {
    if (dist(rgen)) inc.move(n);
  }

  std::vector<Node> nodes (inc.nodes().begin(), inc.nodes().end());
  std::shuffle(nodes.begin(), nodes.end(), rgen);
  for (auto n : nodes) {
    bool m = inc.mapping(n);
    if (inc.overflow(m)) inc.move(n);
  }
}

void traction_placement_pass(IncBipart &inc, std::minstd_rand &rgen) {
  // Initialize with random nodes
  std::vector<Node> nodes (inc.nodes().begin(), inc.nodes().end());
  std::shuffle(nodes.begin(), nodes.end(), rgen);

  // Put a limit on possible quadratic complexity: better fail early
  std::size_t moves_left = 2 * inc.nNodes();

  ThresholdQueue q(inc);
  while (!inc.legal() && moves_left) {
    // Pick one node
    if (nodes.empty()) break;
    q.push(nodes.back());
    nodes.pop_back();

    // Pull other nodes with it
    do {
      Node n = q.pop();
      if (!inc.overflow(inc.mapping(n))) continue;
      inc.move(n, [&](Node o, Weight w) { q.push(o); });
    } while (!q.empty() && --moves_left);
  }
}

template <typename Queue>
void positive_gain_pass(IncBipart &inc, std::minstd_rand &rgen) {
  std::vector<Node> nodes (inc.nodes().begin(), inc.nodes().end());
  std::shuffle(nodes.begin(), nodes.end(), rgen);

  Queue q(inc);

  for (auto n : nodes) {
    q.push_back(n);
  }

  while (!q.empty()) {
    Node n = q.pop();
    if (inc.gain(n) <= 0) continue;
    inc.tryMove(n, [&](Node o, Weight w) { q.push(o); });
  }
}

template <typename Queue>
void non_negative_gain_pass(IncBipart &inc, std::minstd_rand &rgen, const int max_zero_gain_moves = 1) {
  std::vector<Node> nodes (inc.nodes().begin(), inc.nodes().end());
  std::shuffle(nodes.begin(), nodes.end(), rgen);

  Queue q(inc);
  std::vector<int> zero_gain_moves(inc.nNodes(), 0);

  for (auto n : nodes) {
    q.push(n);
  }

  while (!q.empty()) {
    Node n = q.pop();
    Weight g = inc.gain(n);
    if (g < 0) break;
    if (g == 0 && ++zero_gain_moves[n.id] > max_zero_gain_moves) continue;
    inc.tryMove(n, [&](Node o, int w) { q.push(o); });
  }
}

template <typename Queue>
void probing_pass(IncBipart &inc, const std::vector<Node> &nodes, int max_moves_per_probe) {
  Queue q(inc);
  std::vector<Node> trail;

  for (auto n : nodes) {
    auto best_cost = inc.cost();
    int moves_left = max_moves_per_probe;

    q.push(n);
    while (!q.empty() && --moves_left) {
      Node n = q.pop();
      if (inc.tryMove(n, [&](Node o, int w) { q.push(o); })) {
        trail.push_back(n);
      }

      if (inc.cost() < best_cost) {
        trail.clear();
        moves_left = max_moves_per_probe;
        best_cost = inc.cost();
      }
    }

    for (Node n : trail) {
      inc.move(n);
    }

    q.clear();
    trail.clear();
  }
}

template <typename Queue>
void probing_pass(IncBipart &inc, std::minstd_rand &rgen, int max_moves_per_probe) {
  std::vector<Node> nodes;
  for (Node n : inc.nodes()) {
    bool frontier = false;
    for (Edge e : inc.edges(n)) {
      frontier |= inc.cut(e);
    }
    if (frontier) nodes.push_back(n);
  }
  std::shuffle(nodes.begin(), nodes.end(), rgen);
  probing_pass<Queue>(inc, nodes, max_moves_per_probe);
}

void move_all(IncBipart &inc, Edge e, const std::vector<char> &dest) {
  std::size_t i = 0;
  for (Node n : inc.nodes(e)) {
    if (inc.mapping(n) != dest[i++]) {
      inc.move(n);
    }
  }
}

void edge_centric_pass(IncBipart &inc, const std::vector<Edge> &edges) {
  for (Edge e : edges) {
    // Try to move the entire edge in both directions
    std::vector<char> initial;
    for (Node n : inc.nodes(e)) {
      initial.push_back(inc.mapping(n));
    }
    std::int64_t best_cost = inc.cost();
    int best_result = -1;
    for (int i = 0; i < 2; ++i) {
      move_all(inc, e, std::vector<char>(inc.nodes(e).size(), i));
      if (inc.cost() < best_cost) {
        best_cost = inc.cost();
        best_result = i;
      }
    }
    // Keep the best result found
    if (best_result == -1) {
      move_all(inc, e, initial);
    }
    else if (best_result == 0) {
      move_all(inc, e, std::vector<char>(inc.nodes(e).size(), 0));
    }
  }
}

void edge_centric_pass(IncBipart &inc, std::minstd_rand &rgen) {
  std::vector<Edge> edges;
  for (auto e : inc.edges()) {
    if (inc.cut(e)) edges.push_back(e);
  }
  std::shuffle(edges.begin(), edges.end(), rgen);
  edge_centric_pass(inc, edges);
}

void place(IncBipart &inc, std::minstd_rand &rgen) {
  random_placement_pass(inc, rgen);
}

void optimize(IncBipart &inc, std::minstd_rand &rgen) {
  non_negative_gain_pass<ThresholdQueue>(inc, rgen);
  probing_pass<ThresholdQueue>(inc, rgen, 5);
  //edge_centric_pass(inc, rgen);
}

std::vector<Mapping> place(const Problem &pb, int n_starts, std::minstd_rand &rgen) {
  std::vector<Mapping> mappings;
  for (int i = 0; i < n_starts; ++i) {
    IncBipart inc(pb);
    place(inc, rgen);
    if (!inc.legal()) continue;
    mappings.push_back(inc.mapping());
  }

  return mappings;
}

void optimize(const Problem &pb, std::vector<Mapping> &mappings, std::minstd_rand &rgen) {
  for (Mapping &m : mappings) {
    IncBipart inc(pb, m);
    optimize(inc, rgen);
    m = inc.mapping();
  }
}

void coarsen_recurse(const Problem &pb, std::vector<Mapping> &mappings, std::minstd_rand &rgen);
void optimize_recurse(const Problem &pb, std::vector<Mapping> &mappings, std::minstd_rand &rgen);

void coarsen_recurse(const Problem &pb, std::vector<Mapping> &mappings, std::minstd_rand &rgen) {
  Coarsening coarsening = inferCoarsening(mappings);
  if (coarsening.nNodesOut() < 10 || coarsening.nNodesOut() >= 0.95 * pb.hypergraph.nNodes()) {
    return;
  }

  Problem c_pb = coarsening(pb);
  std::vector<Mapping> c_mappings;

  for (const Mapping &m : mappings) {
    auto c_m = coarsening(m);
    assert (computeBipartCost(pb.hypergraph, m) == computeBipartCost(c_pb.hypergraph, c_m));
    c_mappings.push_back(c_m);
  }
  optimize_recurse(c_pb, c_mappings, rgen);
  for (std::size_t i = 0; i < mappings.size(); ++i) {
    mappings[i] = coarsening.reverse(c_mappings[i]);
  }
}

void optimize_recurse(const Problem &pb, std::vector<Mapping> &mappings, std::minstd_rand &rgen) {
  optimize(pb, mappings, rgen);
  coarsen_recurse(pb, mappings, rgen);
}

std::vector<Mapping> solve(const Problem &pb, const SolverOptions &options) {
  std::minstd_rand rgen(options.seed == 0 ? 1 : options.seed);

  std::vector<Mapping> mappings = place(pb, options.n_starts, rgen);

  const int n_cycles = 3;
  for (int i = 0; i < n_cycles; ++i) {
    optimize_recurse(pb, mappings, rgen);
  }

  return mappings;
}

} // End namespace minipart

<commit_msg>Swap pass; very good result, high computation time<commit_after>// Copyright (C) 2017 Gabriel Gouvine - All Rights Reserved

#include "inc_bipart.h"
#include "coarsener.h"

namespace minipart {

class ThresholdQueue {
 public:
  ThresholdQueue(const IncBipart &inc)
    : inc_(inc) {}

  bool empty() const {
    return pos_gain_.empty()
        && zero_gain_.empty()
        && neg_gain_.empty();
  }

  Node pop() {
    while (!pos_gain_.empty()) {
      Node n = pos_gain_.back();
      pos_gain_.pop_back();
      if (inc_.gain(n) > 0) return n;
      else zero_gain_.push_back(n);
    }
    while (!zero_gain_.empty()) {
      Node n = zero_gain_.back();
      zero_gain_.pop_back();
      if (inc_.gain(n) >= 0) return n;
      else neg_gain_.push_back(n);
    }
    Node n = neg_gain_.back();
    neg_gain_.pop_back();
    return n;
  }

  void push(Node n) {
    Weight g = inc_.gain(n);
    if (g > 0) {
      pos_gain_.push_back(n);
    }
    else if (g == 0) {
      zero_gain_.push_back(n);
    }
    else {
      neg_gain_.push_back(n);
    }
  }

  void clear() {
    pos_gain_.clear();
    zero_gain_.clear();
    neg_gain_.clear();
  }

 private:
  std::vector<Node> pos_gain_;
  std::vector<Node> zero_gain_;
  std::vector<Node> neg_gain_;
  const IncBipart &inc_;
};

class FMQueue {
 public:
  FMQueue(const IncBipart &inc)
    : inc_(inc)
    , last_bucket_(0) {
    max_gain_ = 0;
    for (Node n : inc.nodes()) {
      Weight loc_gain = 0;
      for (Edge e : inc.edges(n)) {
        loc_gain += inc.hypergraph().weight(e);
      }
      max_gain_ = std::max(loc_gain, max_gain_);
    }
    buckets_.resize(2 * max_gain_ + 1);
  }

  bool empty() {
    while (true) {
      while (last_bucket_ != 0 && buckets_[last_bucket_].empty()) {
        --last_bucket_;
      }
      if (buckets_[last_bucket_].empty()) return true;
      Node n = buckets_[last_bucket_].back();
      Weight bucket = inc_.gain(n) + max_gain_;
      if (bucket != last_bucket_) {
        buckets_[last_bucket_].pop_back();
        buckets_[bucket].push_back(n);
        last_bucket_ = std::max(last_bucket_, bucket);
      }
      else return false;
    }
  }

  Node pop() {
    empty(); // Consume all out-of-place nodes
    Node n = buckets_[last_bucket_].back();
    buckets_[last_bucket_].pop_back();
    return n;
  }

  void push(Node n) {
    Weight bucket = max_gain_ + inc_.gain(n);
    buckets_[bucket].push_back(n);
    last_bucket_ = std::max(bucket, last_bucket_);
  }

  void clear() {
    for (auto & bucket : buckets_) {
      bucket.clear();
    }
  }

 private:
  const IncBipart &inc_;
  std::vector<std::vector<Node> > buckets_;
  Weight last_bucket_;
  Weight max_gain_;
};

void random_placement_pass(IncBipart &inc, std::minstd_rand &rgen) {
  std::bernoulli_distribution dist;
  for (auto n : inc.nodes()) {
    if (dist(rgen)) inc.move(n);
  }

  std::vector<Node> nodes (inc.nodes().begin(), inc.nodes().end());
  std::shuffle(nodes.begin(), nodes.end(), rgen);
  for (auto n : nodes) {
    bool m = inc.mapping(n);
    if (inc.overflow(m)) inc.move(n);
  }
}

void traction_placement_pass(IncBipart &inc, std::minstd_rand &rgen) {
  // Initialize with random nodes
  std::vector<Node> nodes (inc.nodes().begin(), inc.nodes().end());
  std::shuffle(nodes.begin(), nodes.end(), rgen);

  // Put a limit on possible quadratic complexity: better fail early
  std::size_t moves_left = 2 * inc.nNodes();

  ThresholdQueue q(inc);
  while (!inc.legal() && moves_left) {
    // Pick one node
    if (nodes.empty()) break;
    q.push(nodes.back());
    nodes.pop_back();

    // Pull other nodes with it
    do {
      Node n = q.pop();
      if (!inc.overflow(inc.mapping(n))) continue;
      inc.move(n, [&](Node o, Weight w) { q.push(o); });
    } while (!q.empty() && --moves_left);
  }
}

template <typename Queue>
void positive_gain_pass(IncBipart &inc, std::minstd_rand &rgen) {
  std::vector<Node> nodes (inc.nodes().begin(), inc.nodes().end());
  std::shuffle(nodes.begin(), nodes.end(), rgen);

  Queue q(inc);

  for (auto n : nodes) {
    q.push_back(n);
  }

  while (!q.empty()) {
    Node n = q.pop();
    if (inc.gain(n) <= 0) continue;
    inc.tryMove(n, [&](Node o, Weight w) { q.push(o); });
  }
}

template <typename Queue>
void non_negative_gain_pass(IncBipart &inc, std::minstd_rand &rgen, const int max_zero_gain_moves = 1) {
  std::vector<Node> nodes (inc.nodes().begin(), inc.nodes().end());
  std::shuffle(nodes.begin(), nodes.end(), rgen);

  Queue q(inc);
  std::vector<int> zero_gain_moves(inc.nNodes(), 0);

  for (auto n : nodes) {
    q.push(n);
  }

  while (!q.empty()) {
    Node n = q.pop();
    Weight g = inc.gain(n);
    if (g < 0) break;
    if (g == 0 && ++zero_gain_moves[n.id] > max_zero_gain_moves) continue;
    inc.tryMove(n, [&](Node o, int w) { q.push(o); });
  }
}

template <typename Queue>
void probing_pass(IncBipart &inc, const std::vector<Node> &nodes, int max_moves_per_probe) {
  Queue q(inc);
  std::vector<Node> trail;

  for (auto n : nodes) {
    auto best_cost = inc.cost();
    int moves_left = max_moves_per_probe;

    q.push(n);
    while (!q.empty() && --moves_left) {
      Node n = q.pop();
      if (inc.tryMove(n, [&](Node o, int w) { q.push(o); })) {
        trail.push_back(n);
      }

      if (inc.cost() < best_cost) {
        trail.clear();
        moves_left = max_moves_per_probe;
        best_cost = inc.cost();
      }
    }

    for (Node n : trail) {
      inc.move(n);
    }

    q.clear();
    trail.clear();
  }
}

template <typename Queue>
void probing_pass(IncBipart &inc, std::minstd_rand &rgen, int max_moves_per_probe) {
  std::vector<Node> nodes;
  for (Node n : inc.nodes()) {
    bool frontier = false;
    for (Edge e : inc.edges(n)) {
      frontier |= inc.cut(e);
    }
    if (frontier) nodes.push_back(n);
  }
  std::shuffle(nodes.begin(), nodes.end(), rgen);
  probing_pass<Queue>(inc, nodes, max_moves_per_probe);
}

void move_all(IncBipart &inc, Edge e, const std::vector<char> &dest) {
  std::size_t i = 0;
  for (Node n : inc.nodes(e)) {
    if (inc.mapping(n) != dest[i++]) {
      inc.move(n);
    }
  }
}

void edge_centric_pass(IncBipart &inc, const std::vector<Edge> &edges) {
  for (Edge e : edges) {
    // Try to move the entire edge in both directions
    std::vector<char> initial;
    for (Node n : inc.nodes(e)) {
      initial.push_back(inc.mapping(n));
    }
    std::int64_t best_cost = inc.cost();
    int best_result = -1;
    for (int i = 0; i < 2; ++i) {
      move_all(inc, e, std::vector<char>(inc.nodes(e).size(), i));
      if (inc.legal() && inc.cost() < best_cost) {
        best_cost = inc.cost();
        best_result = i;
      }
    }
    // Keep the best result found
    if (best_result == -1) {
      move_all(inc, e, initial);
    }
    else if (best_result == 0) {
      move_all(inc, e, std::vector<char>(inc.nodes(e).size(), 0));
    }
  }
}

void edge_centric_pass(IncBipart &inc, std::minstd_rand &rgen) {
  std::vector<Edge> edges;
  for (auto e : inc.edges()) {
    if (inc.cut(e)) edges.push_back(e);
  }
  std::shuffle(edges.begin(), edges.end(), rgen);
  edge_centric_pass(inc, edges);
}

void trySwap(IncBipart &inc, Node n1, Node n2) {
  if (n1 == n2) return;
  if (inc.mapping(n1) == inc.mapping(n2)) return;
  if (inc.gain(n1) + inc.gain(n2) <= 0) return;

  std::int64_t cost = inc.cost();
  inc.move(n1);
  inc.move(n2);
  if (inc.cost() > cost || !inc.legal()) {
    inc.move(n1);
    inc.move(n2);
  }
}

void tryAllSwaps(IncBipart &inc, const std::vector<Node> &nodes) {
  for (Node n1 : nodes) {
    if (inc.gain(n1) <= 0) continue;
    if (inc.canMove(n1)) {
      inc.move(n1);
      continue;
    }
    for (Node n2 : nodes) {
      trySwap(inc, n1, n2);
    }
  }
}

void swap_pass(IncBipart &inc, std::minstd_rand &rgen) {
  // TODO: strict runtime limit to avoid quadratic blowup

  bool positive_gain_found = false;
  for (Node n : inc.nodes()) {
    positive_gain_found |= (inc.gain(n) > 0);
  }
  if (!positive_gain_found) return;

  std::vector<Node> nodes(inc.nodes().begin(), inc.nodes().end());
  std::shuffle(nodes.begin(), nodes.end(), rgen);

  tryAllSwaps(inc, nodes);
}

void place(IncBipart &inc, std::minstd_rand &rgen) {
  random_placement_pass(inc, rgen);
}

void optimize(IncBipart &inc, std::minstd_rand &rgen) {
  non_negative_gain_pass<ThresholdQueue>(inc, rgen);
  non_negative_gain_pass<ThresholdQueue>(inc, rgen);
  probing_pass<ThresholdQueue>(inc, rgen, 5);
  //edge_centric_pass(inc, rgen);
  swap_pass(inc, rgen);
}

std::vector<Mapping> place(const Problem &pb, int n_starts, std::minstd_rand &rgen) {
  std::vector<Mapping> mappings;
  for (int i = 0; i < n_starts; ++i) {
    IncBipart inc(pb);
    place(inc, rgen);
    if (!inc.legal()) continue;
    mappings.push_back(inc.mapping());
  }

  return mappings;
}

void optimize(const Problem &pb, std::vector<Mapping> &mappings, std::minstd_rand &rgen) {
  for (Mapping &m : mappings) {
    IncBipart inc(pb, m);
    optimize(inc, rgen);
    m = inc.mapping();
  }
}

void coarsen_recurse(const Problem &pb, std::vector<Mapping> &mappings, std::minstd_rand &rgen);
void optimize_recurse(const Problem &pb, std::vector<Mapping> &mappings, std::minstd_rand &rgen);

void coarsen_recurse(const Problem &pb, std::vector<Mapping> &mappings, std::minstd_rand &rgen) {
  Coarsening coarsening = inferCoarsening(mappings);
  if (coarsening.nNodesOut() < 10 || coarsening.nNodesOut() >= 0.95 * pb.hypergraph.nNodes()) {
    return;
  }

  Problem c_pb = coarsening(pb);
  std::vector<Mapping> c_mappings;

  for (const Mapping &m : mappings) {
    auto c_m = coarsening(m);
    assert (computeBipartCost(pb.hypergraph, m) == computeBipartCost(c_pb.hypergraph, c_m));
    c_mappings.push_back(c_m);
  }
  optimize_recurse(c_pb, c_mappings, rgen);
  for (std::size_t i = 0; i < mappings.size(); ++i) {
    mappings[i] = coarsening.reverse(c_mappings[i]);
  }
}

void optimize_recurse(const Problem &pb, std::vector<Mapping> &mappings, std::minstd_rand &rgen) {
  optimize(pb, mappings, rgen);
  coarsen_recurse(pb, mappings, rgen);
}

std::vector<Mapping> solve(const Problem &pb, const SolverOptions &options) {
  std::minstd_rand rgen(options.seed == 0 ? 1 : options.seed);

  std::vector<Mapping> mappings = place(pb, options.n_starts, rgen);

  const int n_cycles = 3;
  for (int i = 0; i < n_cycles; ++i) {
    optimize_recurse(pb, mappings, rgen);
  }

  return mappings;
}

} // End namespace minipart

<|endoftext|>


<commit_before>/**
 * @cond ___LICENSE___
 *
 * Copyright (c) 2016 Koen Visscher, Paul Visscher and individual contributors.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @endcond
 */

#include "manager/applicationManager.h"
#include "manager/systemManager.h"

#include "common/program.h"

Program::Program( S32 argc, char **argv ) noexcept
    : mIsInitialised( false ),
      mArgc( argc ),
      mArgv( argv )
{
}

Program::~Program()
{
    Shutdown();
}

void Program::Update()
{
    if ( !mIsInitialised )
    {
        Init();
    }

    SystemManager::Get()->GetManagers()->system->Update();
}

void Program::Init()
{
    // Create a system manager and provide it for global access
    // using the service locater pattern.
    SystemManager *systemManager = new SystemManager( mArgc, mArgv );

    // Provide our service locator
    SystemManager::Get( systemManager );

    systemManager->RegisterManagers();

    systemManager->Initialise();

    mIsInitialised = true;
}

bool Program::IsRunning() const noexcept
{
    if ( !mIsInitialised )
    {
        return true;
    }

    return SystemManager::Get()->GetManagers()->application->IsRunning();
}

std::map<std::string, docopt::value> Program::ParseCLI( const std::string &usage, bool help /*= true*/,
                                                        bool optionsFirst /*= false */ ) const
{
    return docopt::docopt( usage, { mArgv + 1, mArgv + mArgc }, help,
                           SystemManager::Get()->GetManagers()->application->GetVersion(), optionsFirst );
}

void Program::Shutdown()
{
    SystemManager::Get()->Release();
}<commit_msg>Relocated manager registration<commit_after>/**
 * @cond ___LICENSE___
 *
 * Copyright (c) 2016 Koen Visscher, Paul Visscher and individual contributors.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @endcond
 */

#include "manager/applicationManager.h"
#include "manager/systemManager.h"

#include "common/program.h"

Program::Program( S32 argc, char **argv ) noexcept
    : mIsInitialised( false ),
      mArgc( argc ),
      mArgv( argv )
{
    // Create a system manager and provide it for global access
    // using the service locater pattern.
    SystemManager *systemManager = new SystemManager( mArgc, mArgv );

    // Provide our service locator
    SystemManager::Get( systemManager );

    systemManager->RegisterManagers();
}

Program::~Program()
{
    Shutdown();
}

void Program::Update()
{
    if ( !mIsInitialised )
    {
        Init();
    }

    SystemManager::Get()->GetManagers()->system->Update();
}

void Program::Init()
{
    systemManager->Initialise();

    mIsInitialised = true;
}

bool Program::IsRunning() const noexcept
{
    if ( !mIsInitialised )
    {
        return true;
    }

    return SystemManager::Get()->GetManagers()->application->IsRunning();
}

std::map<std::string, docopt::value> Program::ParseCLI( const std::string &usage, bool help /*= true*/,
                                                        bool optionsFirst /*= false */ ) const
{
    return docopt::docopt( usage, { mArgv + 1, mArgv + mArgc }, help,
                           SystemManager::Get()->GetManagers()->application->GetVersion(), optionsFirst );
}

void Program::Shutdown()
{
    SystemManager::Get()->Release();
}<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "nl_bond.h"

#include <cassert>
#include <glog/logging.h>
#include <linux/if_bridge.h>
#include <netlink/route/link.h>
#include <netlink/route/link/bonding.h>

#include "cnetlink.h"
#include "nl_output.h"
#include "sai.h"

namespace basebox {

nl_bond::nl_bond(cnetlink *nl) : swi(nullptr), nl(nl) {}

void nl_bond::clear() noexcept {
  lag_members.clear();
  ifi2lag.clear();
}

uint32_t nl_bond::get_lag_id(rtnl_link *bond) {
  assert(bond);

  return get_lag_id(rtnl_link_get_ifindex(bond));
}

uint32_t nl_bond::get_lag_id(int ifindex) {
  auto it = ifi2lag.find(ifindex);
  if (it == ifi2lag.end()) {
    VLOG(1) << __FUNCTION__ << ": lag_id not found for if=" << ifindex;
    return 0;
  }

  return it->second;
}

std::set<uint32_t> nl_bond::get_members(rtnl_link *bond) {
  auto it = ifi2lag.find(rtnl_link_get_ifindex(bond));
  if (it == ifi2lag.end()) {
    LOG(WARNING) << __FUNCTION__ << ": lag does not exist for "
                 << OBJ_CAST(bond);
    return {};
  }

  auto mem_it = lag_members.find(it->second);
  if (mem_it == lag_members.end()) {
    LOG(WARNING) << __FUNCTION__ << ": lag does not exist for "
                 << OBJ_CAST(bond);
    return {};
  }

  return mem_it->second;
}

std::set<uint32_t> nl_bond::get_members_by_port_id(uint32_t port_id) {
  auto mem_it = lag_members.find(port_id);
  if (mem_it == lag_members.end()) {
    LOG(WARNING) << __FUNCTION__
                 << ": lag does not exist for port_id=" << port_id;
    return {};
  }

  return mem_it->second;
}

int nl_bond::update_lag(rtnl_link *old_link, rtnl_link *new_link) {
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  VLOG(1) << __FUNCTION__ << ": updating bond interface ";
  int rv = 0;

  uint8_t o_mode, n_mode;
  uint32_t lag_id = nl->get_port_id(new_link);

  if (lag_id == 0) {
    rv = add_lag(new_link);
    if (rv < 0)
      return rv;

    lag_id = rv;
  }

  rv = rtnl_link_bond_get_mode(old_link, &o_mode);
  if (rv < 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get mode for "
            << OBJ_CAST(new_link);
  }

  rv = rtnl_link_bond_get_mode(new_link, &n_mode);
  if (rv < 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get mode for "
            << OBJ_CAST(new_link);
  }

  if (o_mode != n_mode) {
    VLOG(1) << __FUNCTION__ << ": bond mode updated "
            << static_cast<uint32_t>(n_mode);
    rv = swi->lag_set_mode(lag_id, n_mode);

    if (rv < 0) {
      VLOG(1) << __FUNCTION__ << ": failed to set active state for "
              << OBJ_CAST(new_link);
    }

    return 0;
  }

  add_l3_address(new_link);
#endif

  return 0;
}

int nl_bond::add_lag(rtnl_link *bond) {
  uint32_t lag_id = 0;
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  uint8_t mode;

  rtnl_link_bond_get_mode(bond, &mode);

  assert(bond);
  rv = swi->lag_create(&lag_id, rtnl_link_get_name(bond), mode);
  if (rv < 0) {
    LOG(ERROR) << __FUNCTION__ << ": failed to create lag for "
               << OBJ_CAST(bond);
    return rv;
  }

  rv = lag_id;

  auto rv_emp =
      ifi2lag.emplace(std::make_pair(rtnl_link_get_ifindex(bond), lag_id));

  if (!rv_emp.second) {
    VLOG(1) << __FUNCTION__
            << ": lag exists with lag_id=" << rv_emp.first->second
            << " for bond " << OBJ_CAST(bond);
    rv = rv_emp.first->second;

    if (lag_id != rv_emp.first->second)
      swi->lag_remove(lag_id);
  }

#endif

  return rv;
}

int nl_bond::remove_lag(rtnl_link *bond) {
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  int rv = 0;
  auto it = ifi2lag.find(rtnl_link_get_ifindex(bond));

  if (it == ifi2lag.end()) {
    LOG(WARNING) << __FUNCTION__ << ": lag does not exist for "
                 << OBJ_CAST(bond);
    return -ENODEV;
  }

  rv = swi->lag_remove(it->second);
  if (rv < 0) {
    LOG(ERROR) << __FUNCTION__
               << ": failed to remove lag with lag_id=" << it->second
               << " for bond " << OBJ_CAST(bond);
    return rv;
  }

  ifi2lag.erase(it);
#endif

  return 0;
}

int nl_bond::add_lag_member(rtnl_link *bond, rtnl_link *link) {
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  uint32_t lag_id;
  uint8_t state = 0;

  auto it = ifi2lag.find(rtnl_link_get_ifindex(bond));
  if (it == ifi2lag.end()) {
    VLOG(1) << __FUNCTION__ << ": no lag_id found creating new for "
            << OBJ_CAST(bond);

    rv = add_lag(bond);
    if (rv < 0)
      return rv;

    lag_id = rv;
  } else {
    lag_id = it->second;
  }

  uint32_t port_id = nl->get_port_id(link);
  if (port_id == 0) {
    VLOG(1) << __FUNCTION__ << ": ignoring port " << OBJ_CAST(link);
    return -EINVAL;
  }

  auto mem_it = lag_members.find(it->second);
  if (mem_it == lag_members.end()) { // No ports in lag
    std::set<uint32_t> members;
    members.insert(port_id);
    auto lm_rv = lag_members.emplace(lag_id, members);
  } else {
    mem_it->second.insert(port_id);
  }

  rv = rtnl_link_bond_slave_get_state(link, &state);
  if (rv < 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get slave state for "
            << OBJ_CAST(link);
  }

  rv = swi->lag_add_member(lag_id, port_id);
  if (rv < 0) {
    LOG(ERROR) << __FUNCTION__ << ": failed add member " << port_id;
    return -EINVAL;
  }

  rv = swi->lag_set_member_active(lag_id, port_id, state == 0);
  if (rv < 0) {
    LOG(ERROR) << __FUNCTION__ << ": failed set active member " << port_id;
    return -EINVAL;
  }

  if (rtnl_link_get_master(bond)) {
    // check bridge attachement
    auto br_link = nl->get_link(rtnl_link_get_ifindex(bond), AF_BRIDGE);
    if (br_link) {
      VLOG(2) << __FUNCTION__
              << ": bond was already bridge slave: " << OBJ_CAST(br_link);
      nl->link_created(br_link);

      auto new_state = rtnl_link_bridge_get_port_state(br_link);
      std::string state;

      switch (new_state) {
      case BR_STATE_FORWARDING:
        state = "forward";
        break;
      case BR_STATE_BLOCKING:
        state = "block";
        break;
      case BR_STATE_DISABLED:
        state = "disable";
        break;
      case BR_STATE_LISTENING:
        state = "listen";
        break;
      case BR_STATE_LEARNING:
        state = "learn";
        break;
      default:
        VLOG(1) << __FUNCTION__ << ": stp state change not supported";
        return rv;
      }

      swi->ofdpa_stg_state_port_set(port_id, state);
    }

    rv = nl->set_bridge_port_vlan_tpid(br_link);
    if (rv < 0)
      LOG(ERROR) << __FUNCTION__
                 << ": failed to set egress TPID entry for port "
                 << OBJ_CAST(link);
  } else {
    std::deque<uint16_t> vlans;

    nl->get_vlans(rtnl_link_get_ifindex(bond), &vlans);
    for (auto vid : vlans) {
      swi->ingress_port_vlan_add(port_id, vid, false);
      swi->egress_port_vlan_add(port_id, vid, false);
    }
  }

  // XXX FIXME check for vlan interfaces
  // Adding an IP address here will ensure that every slave that is
  // added will retry to add the address. It will not be written to the
  // ASIC, but repeated messages will be seen
  // This should be done in ::add_lag, but for currently unknown reasons
  // this fails when the lag has no members yet. So keep it here for now.
  add_l3_address(bond);
#endif

  return rv;
}

int nl_bond::remove_lag_member(rtnl_link *link) {
  assert(link);

  int master_id = rtnl_link_get_master(link);
  auto master = nl->get_link(master_id, AF_UNSPEC);

  return remove_lag_member(master, link);
}

int nl_bond::remove_lag_member(rtnl_link *bond, rtnl_link *link) {
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  auto it = ifi2lag.find(rtnl_link_get_ifindex(bond));
  if (it == ifi2lag.end()) {
    LOG(FATAL) << __FUNCTION__ << ": no lag_id found for " << OBJ_CAST(bond);
  }

  uint32_t port_id = nl->get_port_id(link);
  if (port_id == 0) {
    VLOG(1) << __FUNCTION__ << ": ignore invalid lag port " << OBJ_CAST(link);
    return -EINVAL;
  }

  auto lm_rv = lag_members.find(it->second);
  if (lm_rv == lag_members.end()) {
    VLOG(1) << __FUNCTION__ << ": ignore invalid attached port "
            << OBJ_CAST(link);
    return -EINVAL;
  }

  rv = swi->lag_remove_member(it->second, port_id);
  lag_members.erase(lm_rv);

  if (nl->is_bridge_interface(bond)) {
    swi->ofdpa_stg_state_port_set(port_id, "forward");

    auto br_link = nl->get_link(rtnl_link_get_ifindex(bond), AF_BRIDGE);
    rv = nl->unset_bridge_port_vlan_tpid(br_link);

    if (rv < 0)
      LOG(ERROR) << __FUNCTION__
                 << ": failed to set egress TPID entry for port "
                 << OBJ_CAST(link);
  } else {
    std::deque<uint16_t> vlans;

    nl->get_vlans(rtnl_link_get_ifindex(bond), &vlans);

    if (lm_rv->second.empty())
      remove_l3_address(bond);

    if (nl->is_bridge_interface(bond))
      swi->ofdpa_stg_state_port_set(port_id, "forward");

    for (auto vid : vlans) {
      swi->ingress_port_vlan_remove(port_id, vid, false);
      swi->egress_port_vlan_remove(port_id, vid);
    }
  }
#endif

  return rv;
}

int nl_bond::update_lag_member(rtnl_link *old_slave, rtnl_link *new_slave) {
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  assert(new_slave);

  int rv;
  uint8_t new_state;
  uint8_t old_state;

  int n_master_id = rtnl_link_get_master(new_slave);
  int o_master_id = rtnl_link_get_master(old_slave);
  auto new_master = nl->get_link(n_master_id, AF_UNSPEC);
  auto old_master = nl->get_link(o_master_id, AF_UNSPEC);
  auto port_id = nl->get_port_id(new_slave);

  if (old_master != new_master || new_master == 0) {
    return -EINVAL;
  }

  rv = rtnl_link_bond_slave_get_state(new_slave, &new_state);
  if (rv != 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get state";
    return -EINVAL;
  }
  rtnl_link_bond_slave_get_state(old_slave, &old_state);
  if (rv != 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get state";
    return -EINVAL;
  }

  rv = swi->lag_set_member_active(nl->get_port_id(new_master), port_id,
                                  new_state == 0);
#endif
  return 0;
}

int nl_bond::add_l3_address(rtnl_link *link) {
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  assert(link);

  std::deque<rtnl_addr *> addresses;
  nl->get_l3_addrs(link, &addresses);

  for (auto i : addresses) {
    LOG(INFO) << __FUNCTION__ << ": adding address=" << OBJ_CAST(i);

    rv = nl->add_l3_addr(i);
    if (rv < 0)
      LOG(ERROR) << __FUNCTION__ << ":failed to add l3 address " << OBJ_CAST(i)
                 << " to " << OBJ_CAST(link);
  }
  LOG(INFO) << __FUNCTION__ << ": added l3 addresses to bond "
            << OBJ_CAST(link);

#endif
  return rv;
}

int nl_bond::remove_l3_address(rtnl_link *link) {
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  assert(link);

  std::deque<rtnl_addr *> addresses;
  nl->get_l3_addrs(link, &addresses);

  for (auto i : addresses) {
    rv = nl->del_l3_addr(i);
    if (rv < 0)
      LOG(ERROR) << __FUNCTION__ << ":failed to remove l3 address from "
                 << OBJ_CAST(link);
  }

#endif
  return rv;
}

} // namespace basebox
<commit_msg>nl_bond: change API to reflect STP setting; only set state in the case that bridge is stp_state > 0<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "nl_bond.h"

#include <cassert>
#include <glog/logging.h>
#include <linux/if_bridge.h>
#include <netlink/route/link.h>
#include <netlink/route/link/bonding.h>

#include "cnetlink.h"
#include "nl_output.h"
#include "sai.h"

namespace basebox {

nl_bond::nl_bond(cnetlink *nl) : swi(nullptr), nl(nl) {}

void nl_bond::clear() noexcept {
  lag_members.clear();
  ifi2lag.clear();
}

uint32_t nl_bond::get_lag_id(rtnl_link *bond) {
  assert(bond);

  return get_lag_id(rtnl_link_get_ifindex(bond));
}

uint32_t nl_bond::get_lag_id(int ifindex) {
  auto it = ifi2lag.find(ifindex);
  if (it == ifi2lag.end()) {
    VLOG(1) << __FUNCTION__ << ": lag_id not found for if=" << ifindex;
    return 0;
  }

  return it->second;
}

std::set<uint32_t> nl_bond::get_members(rtnl_link *bond) {
  auto it = ifi2lag.find(rtnl_link_get_ifindex(bond));
  if (it == ifi2lag.end()) {
    LOG(WARNING) << __FUNCTION__ << ": lag does not exist for "
                 << OBJ_CAST(bond);
    return {};
  }

  auto mem_it = lag_members.find(it->second);
  if (mem_it == lag_members.end()) {
    LOG(WARNING) << __FUNCTION__ << ": lag does not exist for "
                 << OBJ_CAST(bond);
    return {};
  }

  return mem_it->second;
}

std::set<uint32_t> nl_bond::get_members_by_port_id(uint32_t port_id) {
  auto mem_it = lag_members.find(port_id);
  if (mem_it == lag_members.end()) {
    LOG(WARNING) << __FUNCTION__
                 << ": lag does not exist for port_id=" << port_id;
    return {};
  }

  return mem_it->second;
}

int nl_bond::update_lag(rtnl_link *old_link, rtnl_link *new_link) {
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  VLOG(1) << __FUNCTION__ << ": updating bond interface ";
  int rv = 0;

  uint8_t o_mode, n_mode;
  uint32_t lag_id = nl->get_port_id(new_link);

  if (lag_id == 0) {
    rv = add_lag(new_link);
    if (rv < 0)
      return rv;

    lag_id = rv;
  }

  rv = rtnl_link_bond_get_mode(old_link, &o_mode);
  if (rv < 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get mode for "
            << OBJ_CAST(new_link);
  }

  rv = rtnl_link_bond_get_mode(new_link, &n_mode);
  if (rv < 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get mode for "
            << OBJ_CAST(new_link);
  }

  if (o_mode != n_mode) {
    VLOG(1) << __FUNCTION__ << ": bond mode updated "
            << static_cast<uint32_t>(n_mode);
    rv = swi->lag_set_mode(lag_id, n_mode);

    if (rv < 0) {
      VLOG(1) << __FUNCTION__ << ": failed to set active state for "
              << OBJ_CAST(new_link);
    }

    return 0;
  }

  add_l3_address(new_link);
#endif

  return 0;
}

int nl_bond::add_lag(rtnl_link *bond) {
  uint32_t lag_id = 0;
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  uint8_t mode;

  rtnl_link_bond_get_mode(bond, &mode);

  assert(bond);
  rv = swi->lag_create(&lag_id, rtnl_link_get_name(bond), mode);
  if (rv < 0) {
    LOG(ERROR) << __FUNCTION__ << ": failed to create lag for "
               << OBJ_CAST(bond);
    return rv;
  }

  rv = lag_id;

  auto rv_emp =
      ifi2lag.emplace(std::make_pair(rtnl_link_get_ifindex(bond), lag_id));

  if (!rv_emp.second) {
    VLOG(1) << __FUNCTION__
            << ": lag exists with lag_id=" << rv_emp.first->second
            << " for bond " << OBJ_CAST(bond);
    rv = rv_emp.first->second;

    if (lag_id != rv_emp.first->second)
      swi->lag_remove(lag_id);
  }

#endif

  return rv;
}

int nl_bond::remove_lag(rtnl_link *bond) {
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  int rv = 0;
  auto it = ifi2lag.find(rtnl_link_get_ifindex(bond));

  if (it == ifi2lag.end()) {
    LOG(WARNING) << __FUNCTION__ << ": lag does not exist for "
                 << OBJ_CAST(bond);
    return -ENODEV;
  }

  rv = swi->lag_remove(it->second);
  if (rv < 0) {
    LOG(ERROR) << __FUNCTION__
               << ": failed to remove lag with lag_id=" << it->second
               << " for bond " << OBJ_CAST(bond);
    return rv;
  }

  ifi2lag.erase(it);
#endif

  return 0;
}

int nl_bond::add_lag_member(rtnl_link *bond, rtnl_link *link) {
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  uint32_t lag_id;
  uint8_t state = 0;

  auto it = ifi2lag.find(rtnl_link_get_ifindex(bond));
  if (it == ifi2lag.end()) {
    VLOG(1) << __FUNCTION__ << ": no lag_id found creating new for "
            << OBJ_CAST(bond);

    rv = add_lag(bond);
    if (rv < 0)
      return rv;

    lag_id = rv;
  } else {
    lag_id = it->second;
  }

  uint32_t port_id = nl->get_port_id(link);
  if (port_id == 0) {
    VLOG(1) << __FUNCTION__ << ": ignoring port " << OBJ_CAST(link);
    return -EINVAL;
  }

  auto mem_it = lag_members.find(it->second);
  if (mem_it == lag_members.end()) { // No ports in lag
    std::set<uint32_t> members;
    members.insert(port_id);
    auto lm_rv = lag_members.emplace(lag_id, members);
  } else {
    mem_it->second.insert(port_id);
  }

  rv = rtnl_link_bond_slave_get_state(link, &state);
  if (rv < 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get slave state for "
            << OBJ_CAST(link);
  }

  rv = swi->lag_add_member(lag_id, port_id);
  if (rv < 0) {
    LOG(ERROR) << __FUNCTION__ << ": failed add member " << port_id;
    return -EINVAL;
  }

  rv = swi->lag_set_member_active(lag_id, port_id, state == 0);
  if (rv < 0) {
    LOG(ERROR) << __FUNCTION__ << ": failed set active member " << port_id;
    return -EINVAL;
  }

  if (rtnl_link_get_master(bond)) {
    // check bridge attachement
    auto br_link = nl->get_link(rtnl_link_get_ifindex(bond), AF_BRIDGE);
    if (br_link) {
      VLOG(2) << __FUNCTION__
              << ": bond was already bridge slave: " << OBJ_CAST(br_link);
      nl->link_created(br_link);

      if (nl->get_bridge_stp_state() == 0)
        return rv;

      auto new_state = rtnl_link_bridge_get_port_state(br_link);
      std::string state;

      switch (new_state) {
      case BR_STATE_FORWARDING:
        state = "forward";
        break;
      case BR_STATE_BLOCKING:
        state = "block";
        break;
      case BR_STATE_DISABLED:
        state = "disable";
        break;
      case BR_STATE_LISTENING:
        state = "listen";
        break;
      case BR_STATE_LEARNING:
        state = "learn";
        break;
      default:
        VLOG(1) << __FUNCTION__ << ": stp state change not supported";
        return rv;
      }

      swi->ofdpa_global_stp_state_port_set(port_id, state);
    }

    rv = nl->set_bridge_port_vlan_tpid(br_link);
    if (rv < 0)
      LOG(ERROR) << __FUNCTION__
                 << ": failed to set egress TPID entry for port "
                 << OBJ_CAST(link);
  } else {
    std::deque<uint16_t> vlans;

    nl->get_vlans(rtnl_link_get_ifindex(bond), &vlans);
    for (auto vid : vlans) {
      swi->ingress_port_vlan_add(port_id, vid, false);
      swi->egress_port_vlan_add(port_id, vid, false);
    }
  }

  // XXX FIXME check for vlan interfaces
  // Adding an IP address here will ensure that every slave that is
  // added will retry to add the address. It will not be written to the
  // ASIC, but repeated messages will be seen
  // This should be done in ::add_lag, but for currently unknown reasons
  // this fails when the lag has no members yet. So keep it here for now.
  add_l3_address(bond);
#endif

  return rv;
}

int nl_bond::remove_lag_member(rtnl_link *link) {
  assert(link);

  int master_id = rtnl_link_get_master(link);
  auto master = nl->get_link(master_id, AF_UNSPEC);

  return remove_lag_member(master, link);
}

int nl_bond::remove_lag_member(rtnl_link *bond, rtnl_link *link) {
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  auto it = ifi2lag.find(rtnl_link_get_ifindex(bond));
  if (it == ifi2lag.end()) {
    LOG(FATAL) << __FUNCTION__ << ": no lag_id found for " << OBJ_CAST(bond);
  }

  uint32_t port_id = nl->get_port_id(link);
  if (port_id == 0) {
    VLOG(1) << __FUNCTION__ << ": ignore invalid lag port " << OBJ_CAST(link);
    return -EINVAL;
  }

  auto lm_rv = lag_members.find(it->second);
  if (lm_rv == lag_members.end()) {
    VLOG(1) << __FUNCTION__ << ": ignore invalid attached port "
            << OBJ_CAST(link);
    return -EINVAL;
  }

  rv = swi->lag_remove_member(it->second, port_id);
  lag_members.erase(lm_rv);

  if (nl->is_bridge_interface(bond)) {
    swi->ofdpa_global_stp_state_port_set(port_id, "forward");

    auto br_link = nl->get_link(rtnl_link_get_ifindex(bond), AF_BRIDGE);
    rv = nl->unset_bridge_port_vlan_tpid(br_link);

    if (rv < 0)
      LOG(ERROR) << __FUNCTION__
                 << ": failed to set egress TPID entry for port "
                 << OBJ_CAST(link);
  } else {
    std::deque<uint16_t> vlans;

    nl->get_vlans(rtnl_link_get_ifindex(bond), &vlans);

    if (lm_rv->second.empty())
      remove_l3_address(bond);

    if (nl->is_bridge_interface(bond))
      swi->ofdpa_global_stp_state_port_set(port_id, "forward");

    for (auto vid : vlans) {
      swi->ingress_port_vlan_remove(port_id, vid, false);
      swi->egress_port_vlan_remove(port_id, vid);
    }
  }
#endif

  return rv;
}

int nl_bond::update_lag_member(rtnl_link *old_slave, rtnl_link *new_slave) {
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  assert(new_slave);

  int rv;
  uint8_t new_state;
  uint8_t old_state;

  int n_master_id = rtnl_link_get_master(new_slave);
  int o_master_id = rtnl_link_get_master(old_slave);
  auto new_master = nl->get_link(n_master_id, AF_UNSPEC);
  auto old_master = nl->get_link(o_master_id, AF_UNSPEC);
  auto port_id = nl->get_port_id(new_slave);

  if (old_master != new_master || new_master == 0) {
    return -EINVAL;
  }

  rv = rtnl_link_bond_slave_get_state(new_slave, &new_state);
  if (rv != 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get state";
    return -EINVAL;
  }
  rtnl_link_bond_slave_get_state(old_slave, &old_state);
  if (rv != 0) {
    VLOG(1) << __FUNCTION__ << ": failed to get state";
    return -EINVAL;
  }

  rv = swi->lag_set_member_active(nl->get_port_id(new_master), port_id,
                                  new_state == 0);
#endif
  return 0;
}

int nl_bond::add_l3_address(rtnl_link *link) {
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  assert(link);

  std::deque<rtnl_addr *> addresses;
  nl->get_l3_addrs(link, &addresses);

  for (auto i : addresses) {
    LOG(INFO) << __FUNCTION__ << ": adding address=" << OBJ_CAST(i);

    rv = nl->add_l3_addr(i);
    if (rv < 0)
      LOG(ERROR) << __FUNCTION__ << ":failed to add l3 address " << OBJ_CAST(i)
                 << " to " << OBJ_CAST(link);
  }
  LOG(INFO) << __FUNCTION__ << ": added l3 addresses to bond "
            << OBJ_CAST(link);

#endif
  return rv;
}

int nl_bond::remove_l3_address(rtnl_link *link) {
  int rv = 0;
#ifdef HAVE_RTNL_LINK_BOND_GET_MODE
  assert(link);

  std::deque<rtnl_addr *> addresses;
  nl->get_l3_addrs(link, &addresses);

  for (auto i : addresses) {
    rv = nl->del_l3_addr(i);
    if (rv < 0)
      LOG(ERROR) << __FUNCTION__ << ":failed to remove l3 address from "
                 << OBJ_CAST(link);
  }

#endif
  return rv;
}

} // namespace basebox
<|endoftext|>


<commit_before>// ***************************************************************************
//
//   Generated automatically by genwrapper.
//   Please DO NOT EDIT this file!
//
// ***************************************************************************

#include <osgIntrospection/ReflectionMacros>
#include <osgIntrospection/TypedMethodInfo>
#include <osgIntrospection/StaticMethodInfo>
#include <osgIntrospection/Attributes>

#include <osg/CopyOp>
#include <osg/Image>
#include <osg/NodeVisitor>
#include <osg/Object>
#include <osg/Shape>
#include <osgTerrain/TerrainNode>
#include <osgTerrain/TerrainTechnique>

// Must undefine IN and OUT macros defined in Windows headers
#ifdef IN
#undef IN
#endif
#ifdef OUT
#undef OUT
#endif

BEGIN_OBJECT_REFLECTOR(osgTerrain::TerrainNode)
	I_BaseType(osg::Group);
	I_Constructor0(____TerrainNode,
	               "",
	               "");
	I_ConstructorWithDefaults2(IN, const osgTerrain::TerrainNode &, x, , IN, const osg::CopyOp &, copyop, osg::CopyOp::SHALLOW_COPY,
	                           ____TerrainNode__C5_TerrainNode_R1__C5_osg_CopyOp_R1,
	                           "Copy constructor using CopyOp to manage deep vs shallow copy. ",
	                           "");
	I_Method0(osg::Object *, cloneType,
	          Properties::VIRTUAL,
	          __osg_Object_P1__cloneType,
	          "clone an object of the same type as the node. ",
	          "");
	I_Method1(osg::Object *, clone, IN, const osg::CopyOp &, copyop,
	          Properties::VIRTUAL,
	          __osg_Object_P1__clone__C5_osg_CopyOp_R1,
	          "return a clone of a node, with Object* return type. ",
	          "");
	I_Method1(bool, isSameKindAs, IN, const osg::Object *, obj,
	          Properties::VIRTUAL,
	          __bool__isSameKindAs__C5_osg_Object_P1,
	          "return true if this and obj are of the same kind of object. ",
	          "");
	I_Method0(const char *, className,
	          Properties::VIRTUAL,
	          __C5_char_P1__className,
	          "return the name of the node's class type. ",
	          "");
	I_Method0(const char *, libraryName,
	          Properties::VIRTUAL,
	          __C5_char_P1__libraryName,
	          "return the name of the node's library. ",
	          "");
	I_Method1(void, accept, IN, osg::NodeVisitor &, nv,
	          Properties::VIRTUAL,
	          __void__accept__osg_NodeVisitor_R1,
	          "Visitor Pattern : calls the apply method of a NodeVisitor with this node's type. ",
	          "");
	I_Method1(void, traverse, IN, osg::NodeVisitor &, nv,
	          Properties::VIRTUAL,
	          __void__traverse__osg_NodeVisitor_R1,
	          "Traverse downwards : calls children's accept method with NodeVisitor. ",
	          "");
	I_Method1(void, setHeightField, IN, osg::HeightField *, heightField,
	          Properties::NON_VIRTUAL,
	          __void__setHeightField__osg_HeightField_P1,
	          "Set the HeightField for this TerrainNode. ",
	          "If a Renderer is attached then this will be notified. ");
	I_Method0(osg::HeightField *, getHeightField,
	          Properties::NON_VIRTUAL,
	          __osg_HeightField_P1__getHeightField,
	          "Get the HeightField. ",
	          "");
	I_Method0(const osg::HeightField *, getHeightField,
	          Properties::NON_VIRTUAL,
	          __C5_osg_HeightField_P1__getHeightField,
	          "Get the const HeightField. ",
	          "");
	I_Method0(void, heightFieldHasBeenModified,
	          Properties::NON_VIRTUAL,
	          __void__heightFieldHasBeenModified,
	          "Tell the Renderer that the height field has been modified, so that any cached data will need updating. ",
	          "");
	I_Method1(void, setRenderer, IN, osgTerrain::TerrainTechnique *, renderer,
	          Properties::NON_VIRTUAL,
	          __void__setRenderer__osgTerrain_TerrainTechnique_P1,
	          "Set the Renderer. ",
	          "");
	I_Method0(osgTerrain::TerrainTechnique *, getRenderer,
	          Properties::NON_VIRTUAL,
	          __TerrainTechnique_P1__getRenderer,
	          "Get the Renderer. ",
	          "");
	I_Method0(const osgTerrain::TerrainTechnique *, getRenderer,
	          Properties::NON_VIRTUAL,
	          __C5_TerrainTechnique_P1__getRenderer,
	          "Get the const Renderer. ",
	          "");
	I_Method1(void, setBaseTextureImage, IN, osg::Image *, image,
	          Properties::NON_VIRTUAL,
	          __void__setBaseTextureImage__osg_Image_P1,
	          "",
	          "");
	I_Method0(osg::Image *, getBaseTextureImage,
	          Properties::NON_VIRTUAL,
	          __osg_Image_P1__getBaseTextureImage,
	          "",
	          "");
	I_Method0(const osg::Image *, getBaseTextureImage,
	          Properties::NON_VIRTUAL,
	          __C5_osg_Image_P1__getBaseTextureImage,
	          "",
	          "");
	I_Method1(void, setDetailTextureImage, IN, osg::Image *, image,
	          Properties::NON_VIRTUAL,
	          __void__setDetailTextureImage__osg_Image_P1,
	          "",
	          "");
	I_Method0(osg::Image *, getDetailTextureImage,
	          Properties::NON_VIRTUAL,
	          __osg_Image_P1__getDetailTextureImage,
	          "",
	          "");
	I_Method0(const osg::Image *, getDetailTextureImage,
	          Properties::NON_VIRTUAL,
	          __C5_osg_Image_P1__getDetailTextureImage,
	          "",
	          "");
	I_Method1(void, setCloudShadowTextureImage, IN, osg::Image *, image,
	          Properties::NON_VIRTUAL,
	          __void__setCloudShadowTextureImage__osg_Image_P1,
	          "",
	          "");
	I_Method0(osg::Image *, getCloudShadowTextureImage,
	          Properties::NON_VIRTUAL,
	          __osg_Image_P1__getCloudShadowTextureImage,
	          "",
	          "");
	I_Method0(const osg::Image *, getCloudShadowTextureImage,
	          Properties::NON_VIRTUAL,
	          __C5_osg_Image_P1__getCloudShadowTextureImage,
	          "",
	          "");
	I_Method1(void, setNormalMapImage, IN, osg::Image *, image,
	          Properties::NON_VIRTUAL,
	          __void__setNormalMapImage__osg_Image_P1,
	          "",
	          "");
	I_Method0(osg::Image *, getNormalMapImage,
	          Properties::NON_VIRTUAL,
	          __osg_Image_P1__getNormalMapImage,
	          "",
	          "");
	I_Method0(const osg::Image *, getNormalMapImage,
	          Properties::NON_VIRTUAL,
	          __C5_osg_Image_P1__getNormalMapImage,
	          "",
	          "");
	I_Method0(void, computeNormalMap,
	          Properties::NON_VIRTUAL,
	          __void__computeNormalMap,
	          "",
	          "");
	I_SimpleProperty(osg::Image *, BaseTextureImage,
	                 __osg_Image_P1__getBaseTextureImage,
	                 __void__setBaseTextureImage__osg_Image_P1);
	I_SimpleProperty(osg::Image *, CloudShadowTextureImage,
	                 __osg_Image_P1__getCloudShadowTextureImage,
	                 __void__setCloudShadowTextureImage__osg_Image_P1);
	I_SimpleProperty(osg::Image *, DetailTextureImage,
	                 __osg_Image_P1__getDetailTextureImage,
	                 __void__setDetailTextureImage__osg_Image_P1);
	I_SimpleProperty(osg::HeightField *, HeightField,
	                 __osg_HeightField_P1__getHeightField,
	                 __void__setHeightField__osg_HeightField_P1);
	I_SimpleProperty(osg::Image *, NormalMapImage,
	                 __osg_Image_P1__getNormalMapImage,
	                 __void__setNormalMapImage__osg_Image_P1);
	I_SimpleProperty(osgTerrain::TerrainTechnique *, Renderer,
	                 __TerrainTechnique_P1__getRenderer,
	                 __void__setRenderer__osgTerrain_TerrainTechnique_P1);
END_REFLECTOR

<commit_msg>Updated wrappers<commit_after>// ***************************************************************************
//
//   Generated automatically by genwrapper.
//   Please DO NOT EDIT this file!
//
// ***************************************************************************

#include <osgIntrospection/ReflectionMacros>
#include <osgIntrospection/TypedMethodInfo>
#include <osgIntrospection/StaticMethodInfo>
#include <osgIntrospection/Attributes>

#include <osg/CopyOp>
#include <osg/NodeVisitor>
#include <osg/Object>
#include <osgTerrain/Layer>
#include <osgTerrain/TerrainNode>
#include <osgTerrain/TerrainTechnique>

// Must undefine IN and OUT macros defined in Windows headers
#ifdef IN
#undef IN
#endif
#ifdef OUT
#undef OUT
#endif

BEGIN_OBJECT_REFLECTOR(osgTerrain::TerrainNode)
	I_BaseType(osg::Group);
	I_Constructor0(____TerrainNode,
	               "",
	               "");
	I_ConstructorWithDefaults2(IN, const osgTerrain::TerrainNode &, x, , IN, const osg::CopyOp &, copyop, osg::CopyOp::SHALLOW_COPY,
	                           ____TerrainNode__C5_TerrainNode_R1__C5_osg_CopyOp_R1,
	                           "Copy constructor using CopyOp to manage deep vs shallow copy. ",
	                           "");
	I_Method0(osg::Object *, cloneType,
	          Properties::VIRTUAL,
	          __osg_Object_P1__cloneType,
	          "clone an object of the same type as the node. ",
	          "");
	I_Method1(osg::Object *, clone, IN, const osg::CopyOp &, copyop,
	          Properties::VIRTUAL,
	          __osg_Object_P1__clone__C5_osg_CopyOp_R1,
	          "return a clone of a node, with Object* return type. ",
	          "");
	I_Method1(bool, isSameKindAs, IN, const osg::Object *, obj,
	          Properties::VIRTUAL,
	          __bool__isSameKindAs__C5_osg_Object_P1,
	          "return true if this and obj are of the same kind of object. ",
	          "");
	I_Method0(const char *, className,
	          Properties::VIRTUAL,
	          __C5_char_P1__className,
	          "return the name of the node's class type. ",
	          "");
	I_Method0(const char *, libraryName,
	          Properties::VIRTUAL,
	          __C5_char_P1__libraryName,
	          "return the name of the node's library. ",
	          "");
	I_Method1(void, accept, IN, osg::NodeVisitor &, nv,
	          Properties::VIRTUAL,
	          __void__accept__osg_NodeVisitor_R1,
	          "Visitor Pattern : calls the apply method of a NodeVisitor with this node's type. ",
	          "");
	I_Method1(void, traverse, IN, osg::NodeVisitor &, nv,
	          Properties::VIRTUAL,
	          __void__traverse__osg_NodeVisitor_R1,
	          "Traverse downwards : calls children's accept method with NodeVisitor. ",
	          "");
	I_Method1(void, setTerrainTechnique, IN, osgTerrain::TerrainTechnique *, TerrainTechnique,
	          Properties::NON_VIRTUAL,
	          __void__setTerrainTechnique__osgTerrain_TerrainTechnique_P1,
	          "Set the TerrainTechnique. ",
	          "");
	I_Method0(osgTerrain::TerrainTechnique *, getTerrainTechnique,
	          Properties::NON_VIRTUAL,
	          __TerrainTechnique_P1__getTerrainTechnique,
	          "Get the TerrainTechnique. ",
	          "");
	I_Method0(const osgTerrain::TerrainTechnique *, getTerrainTechnique,
	          Properties::NON_VIRTUAL,
	          __C5_TerrainTechnique_P1__getTerrainTechnique,
	          "Get the const TerrainTechnique. ",
	          "");
	I_Method1(void, setHeightLayer, IN, osgTerrain::Layer *, layer,
	          Properties::NON_VIRTUAL,
	          __void__setHeightLayer__osgTerrain_Layer_P1,
	          "",
	          "");
	I_Method0(osgTerrain::Layer *, getHeightLayer,
	          Properties::NON_VIRTUAL,
	          __osgTerrain_Layer_P1__getHeightLayer,
	          "",
	          "");
	I_Method1(void, addColorLayer, IN, osgTerrain::Layer *, layer,
	          Properties::NON_VIRTUAL,
	          __void__addColorLayer__osgTerrain_Layer_P1,
	          "",
	          "");
	I_Method1(void, removeColorLayer, IN, osgTerrain::Layer *, layer,
	          Properties::NON_VIRTUAL,
	          __void__removeColorLayer__osgTerrain_Layer_P1,
	          "",
	          "");
	I_SimpleProperty(osgTerrain::Layer *, HeightLayer,
	                 __osgTerrain_Layer_P1__getHeightLayer,
	                 __void__setHeightLayer__osgTerrain_Layer_P1);
	I_SimpleProperty(osgTerrain::TerrainTechnique *, TerrainTechnique,
	                 __TerrainTechnique_P1__getTerrainTechnique,
	                 __void__setTerrainTechnique__osgTerrain_TerrainTechnique_P1);
END_REFLECTOR

<|endoftext|>


<commit_before>//
// Name: OptionsDlg.cpp
//
// Copyright (c) 2004 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//

#ifdef __GNUG__
	#pragma implementation "OptionsDlg.cpp"
#endif

#ifndef WX_PRECOMP
      #include "wx/wx.h"
#endif

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#include "vtui/Helper.h"	// for AddFilenamesToComboBox
#include "OptionsDlg.h"

// WDR: class implementations

//----------------------------------------------------------------------------
// OptionsDlg
//----------------------------------------------------------------------------

// WDR: event table for OptionsDlg

BEGIN_EVENT_TABLE(OptionsDlg,AutoDialog)
	EVT_INIT_DIALOG (OptionsDlg::OnInitDialog)
END_EVENT_TABLE()

OptionsDlg::OptionsDlg( wxWindow *parent, wxWindowID id, const wxString &title,
	const wxPoint &position, const wxSize& size, long style ) :
	AutoDialog( parent, id, title, position, size, style )
{
	// WDR: dialog function OptionsDialogFunc for OptionsDlg
	OptionsDialogFunc( this, TRUE );

	AddValidator(ID_FULLSCREEN, &m_bFullscreen);
//	AddValidator(ID_HTML_PANE, &m_bHtmlpane);
//	AddValidator(ID_FLOATING, &m_bFloatingToolbar);
	AddValidator(ID_TEXTURE_COMPRESSION, &m_bTextureCompression);
//	AddValidator(ID_SHADOWS, &m_bShadows);
	AddValidator(ID_DISABLE_MIPMAPS, &m_bDisableMipmaps);

	AddNumValidator(ID_SELECTION_CUTOFF, &m_fSelectionCutoff, 2);
	AddNumValidator(ID_SELECTION_RADIUS, &m_fMaxPickableInstanceRadius, 2);

	AddNumValidator(ID_PLANTSIZE, &m_fPlantScale, 2);
	AddValidator(ID_ONLY_AVAILABLE_SPECIES, &m_bOnlyAvailableSpecies);

	AddValidator(ID_CHOICE_CONTENT, &m_iContentFile);
	AddValidator(ID_CHOICE_CONTENT, &m_strContentFile);
}

void OptionsDlg::GetOptionsFrom(EnviroOptions &opt)
{
	m_bFullscreen = opt.m_bFullscreen;
//	m_bHtmlpane = opt.m_bHtmlpane;
//	m_bFloatingToolbar = opt.m_bFloatingToolbar;
	m_bTextureCompression = opt.m_bTextureCompression;
//	m_bSpeedTest = opt.m_bSpeedTest;
	m_bDisableMipmaps = opt.m_bDisableModelMipmaps;

	m_fSelectionCutoff = opt.m_fSelectionCutoff;
	m_fMaxPickableInstanceRadius = opt.m_fMaxPickableInstanceRadius;

	m_fPlantScale = opt.m_fPlantScale;
//	m_bShadows = opt.m_bShadows;
	m_bOnlyAvailableSpecies = opt.m_bOnlyAvailableSpecies;

	m_strContentFile = opt.m_strContentFile;
}

void OptionsDlg::PutOptionsTo(EnviroOptions &opt)
{
	opt.m_bFullscreen = m_bFullscreen;
//	opt.m_bHtmlpane = m_bHtmlpane;
//	opt.m_bFloatingToolbar = m_bFloatingToolbar;
	opt.m_bTextureCompression = m_bTextureCompression;
//	opt.m_bSpeedTest = m_bSpeedTest;
	opt.m_bDisableModelMipmaps = m_bDisableMipmaps;

	opt.m_fSelectionCutoff = m_fSelectionCutoff;
	opt.m_fMaxPickableInstanceRadius = m_fMaxPickableInstanceRadius;

	opt.m_fPlantScale = m_fPlantScale;
//	opt.m_bShadows = m_bShadows;
	opt.m_bOnlyAvailableSpecies = m_bOnlyAvailableSpecies;

	opt.m_strContentFile = m_strContentFile.mb_str();
}

// WDR: handler implementations for OptionsDlg

void OptionsDlg::OnInitDialog(wxInitDialogEvent& event)
{
	// Populate Content files choices
	vtStringArray &paths = g_Options.m_DataPaths;
	for (unsigned int i = 0; i < paths.size(); i++)
	{
		vtString path = paths[i];
		AddFilenamesToChoice(GetContent(), path, "*.vtco");
	}
	m_iContentFile = GetContent()->FindString(m_strContentFile);
	if (m_iContentFile != -1)
		GetContent()->SetSelection(m_iContentFile);

	wxWindow::OnInitDialog(event);
}

<commit_msg>re-ordered precompiled headers<commit_after>//
// Name: OptionsDlg.cpp
//
// Copyright (c) 2004 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//

#ifdef __GNUG__
	#pragma implementation "OptionsDlg.cpp"
#endif

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifndef WX_PRECOMP
      #include "wx/wx.h"
#endif

#include "vtui/Helper.h"	// for AddFilenamesToComboBox
#include "OptionsDlg.h"

// WDR: class implementations

//----------------------------------------------------------------------------
// OptionsDlg
//----------------------------------------------------------------------------

// WDR: event table for OptionsDlg

BEGIN_EVENT_TABLE(OptionsDlg,AutoDialog)
	EVT_INIT_DIALOG (OptionsDlg::OnInitDialog)
END_EVENT_TABLE()

OptionsDlg::OptionsDlg( wxWindow *parent, wxWindowID id, const wxString &title,
	const wxPoint &position, const wxSize& size, long style ) :
	AutoDialog( parent, id, title, position, size, style )
{
	// WDR: dialog function OptionsDialogFunc for OptionsDlg
	OptionsDialogFunc( this, TRUE );

	AddValidator(ID_FULLSCREEN, &m_bFullscreen);
//	AddValidator(ID_HTML_PANE, &m_bHtmlpane);
//	AddValidator(ID_FLOATING, &m_bFloatingToolbar);
	AddValidator(ID_TEXTURE_COMPRESSION, &m_bTextureCompression);
//	AddValidator(ID_SHADOWS, &m_bShadows);
	AddValidator(ID_DISABLE_MIPMAPS, &m_bDisableMipmaps);

	AddNumValidator(ID_SELECTION_CUTOFF, &m_fSelectionCutoff, 2);
	AddNumValidator(ID_SELECTION_RADIUS, &m_fMaxPickableInstanceRadius, 2);

	AddNumValidator(ID_PLANTSIZE, &m_fPlantScale, 2);
	AddValidator(ID_ONLY_AVAILABLE_SPECIES, &m_bOnlyAvailableSpecies);

	AddValidator(ID_CHOICE_CONTENT, &m_iContentFile);
	AddValidator(ID_CHOICE_CONTENT, &m_strContentFile);
}

void OptionsDlg::GetOptionsFrom(EnviroOptions &opt)
{
	m_bFullscreen = opt.m_bFullscreen;
//	m_bHtmlpane = opt.m_bHtmlpane;
//	m_bFloatingToolbar = opt.m_bFloatingToolbar;
	m_bTextureCompression = opt.m_bTextureCompression;
//	m_bSpeedTest = opt.m_bSpeedTest;
	m_bDisableMipmaps = opt.m_bDisableModelMipmaps;

	m_fSelectionCutoff = opt.m_fSelectionCutoff;
	m_fMaxPickableInstanceRadius = opt.m_fMaxPickableInstanceRadius;

	m_fPlantScale = opt.m_fPlantScale;
//	m_bShadows = opt.m_bShadows;
	m_bOnlyAvailableSpecies = opt.m_bOnlyAvailableSpecies;

	m_strContentFile = opt.m_strContentFile;
}

void OptionsDlg::PutOptionsTo(EnviroOptions &opt)
{
	opt.m_bFullscreen = m_bFullscreen;
//	opt.m_bHtmlpane = m_bHtmlpane;
//	opt.m_bFloatingToolbar = m_bFloatingToolbar;
	opt.m_bTextureCompression = m_bTextureCompression;
//	opt.m_bSpeedTest = m_bSpeedTest;
	opt.m_bDisableModelMipmaps = m_bDisableMipmaps;

	opt.m_fSelectionCutoff = m_fSelectionCutoff;
	opt.m_fMaxPickableInstanceRadius = m_fMaxPickableInstanceRadius;

	opt.m_fPlantScale = m_fPlantScale;
//	opt.m_bShadows = m_bShadows;
	opt.m_bOnlyAvailableSpecies = m_bOnlyAvailableSpecies;

	opt.m_strContentFile = m_strContentFile.mb_str();
}

// WDR: handler implementations for OptionsDlg

void OptionsDlg::OnInitDialog(wxInitDialogEvent& event)
{
	// Populate Content files choices
	vtStringArray &paths = g_Options.m_DataPaths;
	for (unsigned int i = 0; i < paths.size(); i++)
	{
		vtString path = paths[i];
		AddFilenamesToChoice(GetContent(), path, "*.vtco");
	}
	m_iContentFile = GetContent()->FindString(m_strContentFile);
	if (m_iContentFile != -1)
		GetContent()->SetSelection(m_iContentFile);

	wxWindow::OnInitDialog(event);
}

<|endoftext|>