<commit_before>/*
  This file is part of libkdepim.

  Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org>
  Copyright (c) 2002 David Jarvie <software@astrojar.org.uk>
  Copyright (c) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
  Copyright (c) 2004 Tobias Koenig <tokoe@kde.org>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; see the file COPYING.LIB.  If not, write to
  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  Boston, MA 02110-1301, USA.
*/

//krazy:excludeall=qclasses as we want to subclass from QComboBox, not KComboBox

#include "kdateedit.h"

#include <KCalendarSystem>
#include <KDebug>
#include <KGlobal>
#include <KGlobalSettings>
#include <KLocale>

#include <QAbstractItemView>
#include <QApplication>
#include <QCompleter>
#include <QEvent>
#include <QKeyEvent>
#include <QLineEdit>
#include <QMouseEvent>
#include <QValidator>

using namespace KPIM;

class DateValidator : public QValidator
{
  public:
    DateValidator( const QStringList &keywords, QWidget *parent )
      : QValidator( parent ), mKeywords( keywords )
    {}

    virtual State validate( QString &str, int & ) const
    {
      int length = str.length();

      // empty string is intermediate so one can clear the edit line and start from scratch
      if ( length <= 0 ) {
        return Intermediate;
      }

      if ( mKeywords.contains( str.toLower() ) ) {
        return Acceptable;
      }

      bool ok = false;
      KGlobal::locale()->readDate( str, &ok );
      if ( ok ) {
        return Acceptable;
      } else {
        return Intermediate;
      }
    }

  private:
    QStringList mKeywords;
};

KDateEdit::KDateEdit( QWidget *parent )
  : QComboBox( parent ), mReadOnly( false ), mDiscardNextMousePress( false )
{
  // need at least one entry for popup to work
  setMaxCount( 1 );
  setEditable( true );

  mDate = QDate::currentDate();
  QString today = KGlobal::locale()->formatDate( mDate, KLocale::ShortDate );

  addItem( today );
  setCurrentIndex( 0 );

  connect( lineEdit(), SIGNAL(returnPressed()),
           this, SLOT(lineEnterPressed()) );
  connect( this, SIGNAL(textChanged(QString)),
           SLOT(slotTextChanged(QString)) );

  mPopup = new KDatePickerPopup( KDatePickerPopup::DatePicker | KDatePickerPopup::Words,
                                 QDate::currentDate(), this );
  mPopup->hide();
  mPopup->installEventFilter( this );

  connect( mPopup, SIGNAL(dateChanged(QDate)),
           SLOT(dateSelected(QDate)) );

  // handle keyword entry
  setupKeywords();
  lineEdit()->installEventFilter( this );

  setValidator( new DateValidator( mKeywordMap.keys(), this ) );

  mTextChanged = false;
}

KDateEdit::~KDateEdit()
{
}

void KDateEdit::setDate( const QDate &date )
{
  assignDate( date );
  updateView();
}

QDate KDateEdit::date() const
{
  return mDate;
}

void KDateEdit::setReadOnly( bool readOnly )
{
  mReadOnly = readOnly;
  lineEdit()->setReadOnly( readOnly );
}

bool KDateEdit::isReadOnly() const
{
  return mReadOnly;
}

void KDateEdit::showPopup()
{
  if ( mReadOnly ) {
    return;
  }

  QRect desk = KGlobalSettings::desktopGeometry( this );

  QPoint popupPoint = mapToGlobal( QPoint( 0, 0 ) );

  int dateFrameHeight = mPopup->sizeHint().height();
  if ( popupPoint.y() + height() + dateFrameHeight > desk.bottom() ) {
    popupPoint.setY( popupPoint.y() - dateFrameHeight );
  } else {
    popupPoint.setY( popupPoint.y() + height() );
  }

  int dateFrameWidth = mPopup->sizeHint().width();
  if ( popupPoint.x() + dateFrameWidth > desk.right() ) {
    popupPoint.setX( desk.right() - dateFrameWidth );
  }

  if ( popupPoint.x() < desk.left() ) {
    popupPoint.setX( desk.left() );
  }

  if ( popupPoint.y() < desk.top() ) {
    popupPoint.setY( desk.top() );
  }

  if ( mDate.isValid() ) {
    mPopup->setDate( mDate );
  } else {
    mPopup->setDate( QDate::currentDate() );
  }

  mPopup->popup( popupPoint );

  // The combo box is now shown pressed. Make it show not pressed again
  // by causing its (invisible) list box to emit a 'selected' signal.
  // First, ensure that the list box contains the date currently displayed.
  QDate date = parseDate();
  assignDate( date );
  updateView();

  // Now, simulate an Enter to unpress it
  QAbstractItemView *lb = view();
  if ( lb ) {
    lb->setCurrentIndex( lb->model()->index( 0, 0 ) );
    QKeyEvent *keyEvent =
      new QKeyEvent( QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier );
    QApplication::postEvent( lb, keyEvent );
  }
}

void KDateEdit::dateSelected( const QDate &date )
{
  if ( assignDate( date ) ) {
    updateView();
    emit dateChanged( date );
    emit dateEntered( date );

    if ( date.isValid() ) {
      mPopup->hide();
    }
  }
}

void KDateEdit::lineEnterPressed()
{
  bool replaced = false;

  QDate date = parseDate( &replaced );

  if ( assignDate( date ) ) {
    if ( replaced ) {
      updateView();
    }

    emit dateChanged( date );
    emit dateEntered( date );
  }
}

QDate KDateEdit::parseDate( bool *replaced ) const
{
  QString text = currentText();
  QDate result;

  if ( replaced ) {
    (*replaced) = false;
  }

  if ( text.isEmpty() ) {
    result = QDate();
  } else if ( mKeywordMap.contains( text.toLower() ) ) {
    QDate today = QDate::currentDate();
    int i = mKeywordMap[ text.toLower() ];
    if ( i == 30 ) {
      today = today.addMonths( 1 );
    } else if ( i >= 100 ) {
      /* A day name has been entered. Convert to offset from today.
       * This uses some math tricks to figure out the offset in days
       * to the next date the given day of the week occurs. There
       * are two cases, that the new day is >= the current day, which means
       * the new day has not occurred yet or that the new day < the current day,
       * which means the new day is already passed (so we need to find the
       * day in the next week).
       */
      i -= 100;
      int currentDay = today.dayOfWeek();
      if ( i >= currentDay ) {
        i -= currentDay;
      } else {
        i += 7 - currentDay;
      }
    }

    result = today.addDays( i );
    if ( replaced ) {
      (*replaced) = true;
    }
  } else {
    result = KGlobal::locale()->readDate( text );
  }

  return result;
}

void KDateEdit::focusOutEvent( QFocusEvent *e )
{
  if ( mTextChanged ) {
    lineEnterPressed();
    mTextChanged = false;
  }
  QComboBox::focusOutEvent( e );
}

void KDateEdit::keyPressEvent(QKeyEvent* e)
{
  QDate date;

  if ( !mReadOnly ) {
    switch ( e->key() ) {
    case Qt::Key_Up:
      date = parseDate();
      if (!date.isValid()) break;
      date = date.addDays( 1 );
      break;
    case Qt::Key_Down:
      date = parseDate();
      if (!date.isValid()) break;
      date = date.addDays( -1 );
      break;
    case Qt::Key_Plus:
    case Qt::Key_PageUp:
      date = parseDate();
      if (!date.isValid()) break;
      date = date.addMonths( 1 );
      break;
    case Qt::Key_Minus:
    case Qt::Key_PageDown:
      date = parseDate();
      if (!date.isValid()) break;
      date = date.addMonths( -1 );
      break;
    case Qt::Key_Equal:
      date = QDate::currentDate();
      break;
    }

    if ( date.isValid() && assignDate( date ) ) {
      e->accept();
      updateView();
      emit dateChanged( date );
      emit dateEntered( date );
      return;
    }
  }

  QComboBox::keyPressEvent( e );
}

bool KDateEdit::eventFilter( QObject *object, QEvent *event )
{
  if ( object == lineEdit() ) {
    // We only process the focus out event if the text has changed
    // since we got focus
    if ( ( event->type() == QEvent::FocusOut ) && mTextChanged ) {
      lineEnterPressed();
      mTextChanged = false;
    } else if ( event->type() == QEvent::KeyPress ) {
      // Up and down arrow keys step the date
      QKeyEvent *keyEvent = (QKeyEvent *)event;

      if ( keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter ) {
        lineEnterPressed();
        return true;
      }
    }
  } else {
    // It's a date picker event
    switch ( event->type() ) {
    case QEvent::MouseButtonDblClick:
    case QEvent::MouseButtonPress:
    {
      QMouseEvent *mouseEvent = (QMouseEvent*)event;
      if ( !mPopup->rect().contains( mouseEvent->pos() ) ) {
        QPoint globalPos = mPopup->mapToGlobal( mouseEvent->pos() );
        if ( QApplication::widgetAt( globalPos ) == this ) {
          // The date picker is being closed by a click on the
          // KDateEdit widget. Avoid popping it up again immediately.
          mDiscardNextMousePress = true;
        }
      }

      break;
    }
    default:
      break;
    }
  }

  return false;
}

void KDateEdit::mousePressEvent( QMouseEvent *event )
{
  if ( event->button() == Qt::LeftButton && mDiscardNextMousePress ) {
    mDiscardNextMousePress = false;
    return;
  }

  QComboBox::mousePressEvent( event );
}

void KDateEdit::slotTextChanged( const QString & )
{
  QDate date = parseDate();

  if ( assignDate( date ) ) {
    emit dateChanged( date );
  }

  mTextChanged = true;
}

void KDateEdit::setupKeywords()
{
  // Create the keyword list. This will be used to match against when the user
  // enters information.
  mKeywordMap.insert( i18nc( "the day after today", "tomorrow" ), 1 );
  mKeywordMap.insert( i18nc( "this day", "today" ), 0 );
  mKeywordMap.insert( i18nc( "the day before today", "yesterday" ), -1 );
  mKeywordMap.insert( i18nc( "the week after this week", "next week" ), 7 );
  mKeywordMap.insert( i18nc( "the month after this month", "next month" ), 30 );

  QString dayName;
  for ( int i = 1; i <= 7; ++i ) {
    dayName = KGlobal::locale()->calendar()->weekDayName( i ).toLower();
    mKeywordMap.insert( dayName, i + 100 );
  }

  QCompleter *comp = new QCompleter( mKeywordMap.keys(), this );
  comp->setCaseSensitivity( Qt::CaseInsensitive );
  comp->setCompletionMode( QCompleter::InlineCompletion );
  setCompleter( comp );
}

bool KDateEdit::assignDate( const QDate &date )
{
  mDate = date;
  mTextChanged = false;
  return true;
}

void KDateEdit::updateView()
{
  QString dateString;
  if ( mDate.isValid() ) {
    dateString = KGlobal::locale()->formatDate( mDate, KLocale::ShortDate );
  }

  // We do not want to generate a signal here,
  // since we explicitly setting the date
  bool blocked = signalsBlocked();
  blockSignals( true );
  removeItem( 0 );
  insertItem( 0, dateString );
  blockSignals( blocked );
}

#include "kdateedit.moc"
<commit_msg>Fix broken format in KDateEdit<commit_after>/*
  This file is part of libkdepim.

  Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org>
  Copyright (c) 2002 David Jarvie <software@astrojar.org.uk>
  Copyright (c) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
  Copyright (c) 2004 Tobias Koenig <tokoe@kde.org>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; see the file COPYING.LIB.  If not, write to
  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  Boston, MA 02110-1301, USA.
*/

//krazy:excludeall=qclasses as we want to subclass from QComboBox, not KComboBox

#include "kdateedit.h"

#include <KCalendarSystem>
#include <KDebug>
#include <KGlobal>
#include <KGlobalSettings>
#include <KLocale>

#include <QAbstractItemView>
#include <QApplication>
#include <QCompleter>
#include <QEvent>
#include <QKeyEvent>
#include <QLineEdit>
#include <QMouseEvent>
#include <QValidator>

using namespace KPIM;

class DateValidator : public QValidator
{
  public:
    DateValidator( const QStringList &keywords, QWidget *parent )
      : QValidator( parent ), mKeywords( keywords )
    {}

    virtual State validate( QString &str, int & ) const
    {
      int length = str.length();

      // empty string is intermediate so one can clear the edit line and start from scratch
      if ( length <= 0 ) {
        return Intermediate;
      }

      if ( mKeywords.contains( str.toLower() ) ) {
        return Acceptable;
      }

      bool ok = false;
      KGlobal::locale()->readDate( str, &ok );
      if ( ok ) {
        return Acceptable;
      } else {
        return Intermediate;
      }
    }

  private:
    QStringList mKeywords;
};

KDateEdit::KDateEdit( QWidget *parent )
  : QComboBox( parent ), mReadOnly( false ), mDiscardNextMousePress( false )
{
  // need at least one entry for popup to work
  setMaxCount( 1 );
  setEditable( true );

  mDate = QDate::currentDate();
  QString today = KGlobal::locale()->formatDate( mDate, KLocale::ShortDate );

  addItem( today );
  setCurrentIndex( 0 );

  connect( lineEdit(), SIGNAL(returnPressed()),
           this, SLOT(lineEnterPressed()) );
  connect( this, SIGNAL(textChanged(QString)),
           SLOT(slotTextChanged(QString)) );

  mPopup = new KDatePickerPopup( KDatePickerPopup::DatePicker | KDatePickerPopup::Words,
                                 QDate::currentDate(), this );
  mPopup->hide();
  mPopup->installEventFilter( this );

  connect( mPopup, SIGNAL(dateChanged(QDate)),
           SLOT(dateSelected(QDate)) );

  // handle keyword entry
  setupKeywords();
  lineEdit()->installEventFilter( this );

  setValidator( new DateValidator( mKeywordMap.keys(), this ) );

  mTextChanged = false;
}

KDateEdit::~KDateEdit()
{
}

void KDateEdit::setDate( const QDate &date )
{
  assignDate( date );
  updateView();
}

QDate KDateEdit::date() const
{
  return mDate;
}

void KDateEdit::setReadOnly( bool readOnly )
{
  mReadOnly = readOnly;
  lineEdit()->setReadOnly( readOnly );
}

bool KDateEdit::isReadOnly() const
{
  return mReadOnly;
}

void KDateEdit::showPopup()
{
  if ( mReadOnly ) {
    return;
  }

  QRect desk = KGlobalSettings::desktopGeometry( this );

  QPoint popupPoint = mapToGlobal( QPoint( 0, 0 ) );

  int dateFrameHeight = mPopup->sizeHint().height();
  if ( popupPoint.y() + height() + dateFrameHeight > desk.bottom() ) {
    popupPoint.setY( popupPoint.y() - dateFrameHeight );
  } else {
    popupPoint.setY( popupPoint.y() + height() );
  }

  int dateFrameWidth = mPopup->sizeHint().width();
  if ( popupPoint.x() + dateFrameWidth > desk.right() ) {
    popupPoint.setX( desk.right() - dateFrameWidth );
  }

  if ( popupPoint.x() < desk.left() ) {
    popupPoint.setX( desk.left() );
  }

  if ( popupPoint.y() < desk.top() ) {
    popupPoint.setY( desk.top() );
  }

  if ( mDate.isValid() ) {
    mPopup->setDate( mDate );
  } else {
    mPopup->setDate( QDate::currentDate() );
  }

  mPopup->popup( popupPoint );

  // The combo box is now shown pressed. Make it show not pressed again
  // by causing its (invisible) list box to emit a 'selected' signal.
  // First, ensure that the list box contains the date currently displayed.
  QDate date = parseDate();
  assignDate( date );
  updateView();

  // Now, simulate an Enter to unpress it
  QAbstractItemView *lb = view();
  if ( lb ) {
    lb->setCurrentIndex( lb->model()->index( 0, 0 ) );
    QKeyEvent *keyEvent =
      new QKeyEvent( QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier );
    QApplication::postEvent( lb, keyEvent );
  }
}

void KDateEdit::dateSelected( const QDate &date )
{
  if ( assignDate( date ) ) {
    updateView();
    emit dateChanged( date );
    emit dateEntered( date );

    if ( date.isValid() ) {
      mPopup->hide();
    }
  }
}

void KDateEdit::lineEnterPressed()
{
  bool replaced = false;

  QDate date = parseDate( &replaced );

  if ( assignDate( date ) ) {
    if ( replaced ) {
      updateView();
    }

    emit dateChanged( date );
    emit dateEntered( date );
  }
}

QDate KDateEdit::parseDate( bool *replaced ) const
{
  QString text = currentText();
  QDate result;

  if ( replaced ) {
    (*replaced) = false;
  }

  if ( text.isEmpty() ) {
    result = QDate();
  } else if ( mKeywordMap.contains( text.toLower() ) ) {
    QDate today = QDate::currentDate();
    int i = mKeywordMap[ text.toLower() ];
    if ( i == 30 ) {
      today = today.addMonths( 1 );
    } else if ( i >= 100 ) {
      /* A day name has been entered. Convert to offset from today.
       * This uses some math tricks to figure out the offset in days
       * to the next date the given day of the week occurs. There
       * are two cases, that the new day is >= the current day, which means
       * the new day has not occurred yet or that the new day < the current day,
       * which means the new day is already passed (so we need to find the
       * day in the next week).
       */
      i -= 100;
      int currentDay = today.dayOfWeek();
      if ( i >= currentDay ) {
        i -= currentDay;
      } else {
        i += 7 - currentDay;
      }
    }

    result = today.addDays( i );
    if ( replaced ) {
      (*replaced) = true;
    }
  } else {
    result = KGlobal::locale()->readDate( text );
  }

  return result;
}

void KDateEdit::focusOutEvent( QFocusEvent *e )
{
  if ( mTextChanged ) {
    lineEnterPressed();
    mTextChanged = false;
  }
  QComboBox::focusOutEvent( e );
}

void KDateEdit::keyPressEvent(QKeyEvent* e)
{
  QDate date;

  if ( !mReadOnly ) {
    switch ( e->key() ) {
    case Qt::Key_Up:
      date = parseDate();
      if (!date.isValid()) break;
      date = date.addDays( 1 );
      break;
    case Qt::Key_Down:
      date = parseDate();
      if (!date.isValid()) break;
      date = date.addDays( -1 );
      break;
    case Qt::Key_PageUp:
      date = parseDate();
      if (!date.isValid()) break;
      date = date.addMonths( 1 );
      break;
    case Qt::Key_PageDown:
      date = parseDate();
      if (!date.isValid()) break;
      date = date.addMonths( -1 );
      break;
    case Qt::Key_Equal:
      date = QDate::currentDate();
      break;
    }

    if ( date.isValid() && assignDate( date ) ) {
      e->accept();
      updateView();
      emit dateChanged( date );
      emit dateEntered( date );
      return;
    }
  }

  QComboBox::keyPressEvent( e );
}

bool KDateEdit::eventFilter( QObject *object, QEvent *event )
{
  if ( object == lineEdit() ) {
    // We only process the focus out event if the text has changed
    // since we got focus
    if ( ( event->type() == QEvent::FocusOut ) && mTextChanged ) {
      lineEnterPressed();
      mTextChanged = false;
    } else if ( event->type() == QEvent::KeyPress ) {
      // Up and down arrow keys step the date
      QKeyEvent *keyEvent = (QKeyEvent *)event;

      if ( keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter ) {
        lineEnterPressed();
        return true;
      }
    }
  } else {
    // It's a date picker event
    switch ( event->type() ) {
    case QEvent::MouseButtonDblClick:
    case QEvent::MouseButtonPress:
    {
      QMouseEvent *mouseEvent = (QMouseEvent*)event;
      if ( !mPopup->rect().contains( mouseEvent->pos() ) ) {
        QPoint globalPos = mPopup->mapToGlobal( mouseEvent->pos() );
        if ( QApplication::widgetAt( globalPos ) == this ) {
          // The date picker is being closed by a click on the
          // KDateEdit widget. Avoid popping it up again immediately.
          mDiscardNextMousePress = true;
        }
      }

      break;
    }
    default:
      break;
    }
  }

  return false;
}

void KDateEdit::mousePressEvent( QMouseEvent *event )
{
  if ( event->button() == Qt::LeftButton && mDiscardNextMousePress ) {
    mDiscardNextMousePress = false;
    return;
  }

  QComboBox::mousePressEvent( event );
}

void KDateEdit::slotTextChanged( const QString & )
{
  QDate date = parseDate();

  if ( assignDate( date ) ) {
    emit dateChanged( date );
  }

  mTextChanged = true;
}

void KDateEdit::setupKeywords()
{
  // Create the keyword list. This will be used to match against when the user
  // enters information.
  mKeywordMap.insert( i18nc( "the day after today", "tomorrow" ), 1 );
  mKeywordMap.insert( i18nc( "this day", "today" ), 0 );
  mKeywordMap.insert( i18nc( "the day before today", "yesterday" ), -1 );
  mKeywordMap.insert( i18nc( "the week after this week", "next week" ), 7 );
  mKeywordMap.insert( i18nc( "the month after this month", "next month" ), 30 );

  QString dayName;
  for ( int i = 1; i <= 7; ++i ) {
    dayName = KGlobal::locale()->calendar()->weekDayName( i ).toLower();
    mKeywordMap.insert( dayName, i + 100 );
  }

  QCompleter *comp = new QCompleter( mKeywordMap.keys(), this );
  comp->setCaseSensitivity( Qt::CaseInsensitive );
  comp->setCompletionMode( QCompleter::InlineCompletion );
  setCompleter( comp );
}

bool KDateEdit::assignDate( const QDate &date )
{
  mDate = date;
  mTextChanged = false;
  return true;
}

void KDateEdit::updateView()
{
  QString dateString;
  if ( mDate.isValid() ) {
    dateString = KGlobal::locale()->formatDate( mDate, KLocale::ShortDate );
  }

  // We do not want to generate a signal here,
  // since we explicitly setting the date
  bool blocked = signalsBlocked();
  blockSignals( true );
  removeItem( 0 );
  insertItem( 0, dateString );
  blockSignals( blocked );
}

#include "kdateedit.moc"
<|endoftext|>


<commit_before>/*
 * Configuration.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef BENG_LB_CONFIG_H
#define BENG_LB_CONFIG_H

#include "address_list.h"
#include "sticky.h"
#include "ssl_config.hxx"

#include <http/status.h>

#include <glib.h>

#include <map>
#include <list>
#include <vector>
#include <string>

struct pool;

enum lb_protocol {
    LB_PROTOCOL_HTTP,
    LB_PROTOCOL_TCP,
};

struct lb_control_config {
    const struct address_envelope *envelope;

    lb_control_config():envelope(nullptr) {}
};

struct lb_monitor_config {
    std::string name;

    /**
     * Time in seconds between two monitor checks.
     */
    unsigned interval;

    /**
     * If the monitor does not produce a result after this timeout
     * [seconds], it is assumed to be negative.
     */
    unsigned timeout;

    enum class Type {
        NONE,
        PING,
        CONNECT,
        TCP_EXPECT,
    } type;

    /**
     * The timeout for establishing a connection.  Only applicable for
     * #Type::TCP_EXPECT.  0 means no special setting present.
     */
    unsigned connect_timeout;

    /**
     * For #Type::TCP_EXPECT: a string that is sent to the peer
     * after the connection has been established.  May be empty.
     */
    std::string send;

    /**
     * For #Type::TCP_EXPECT: a string that is expected to be
     * received from the peer after the #send string has been sent.
     */
    std::string expect;

    /**
     * For #Type::TCP_EXPECT: if that string is received from the
     * peer (instead of #expect), then the node is assumed to be
     * shutting down gracefully, and will only get sticky requests.
     */
    std::string fade_expect;

    lb_monitor_config(const char *_name)
        :name(_name),
         interval(10), timeout(0),
         type(Type::NONE),
         connect_timeout(0) {}
};

struct lb_node_config {
    std::string name;

    const struct address_envelope *envelope;

    /**
     * The Tomcat "jvmRoute" setting of this node.  It is used for
     * #STICKY_JVM_ROUTE.
     */
    std::string jvm_route;

    lb_node_config(const char *_name,
                   const struct address_envelope *_envelope=nullptr)
        :name(_name),
         envelope(_envelope) {}
};

struct lb_member_config {
    const struct lb_node_config *node;

    unsigned port;

    lb_member_config():node(nullptr), port(0) {}
};

struct lb_fallback_config {
    http_status_t status;

    /**
     * The "Location" response header.
     */
    std::string location;

    std::string message;

    bool IsDefined() const {
        return !location.empty() || !message.empty();
    }
};

struct lb_cluster_config {
    std::string name;

    /**
     * The protocol that is spoken on this cluster.
     */
    enum lb_protocol protocol;

    bool mangle_via;

    struct lb_fallback_config fallback;

    enum sticky_mode sticky_mode;

    std::string session_cookie;

    const struct lb_monitor_config *monitor;

    std::vector<lb_member_config> members;

    /**
     * A list of node addresses.
     */
    struct address_list address_list;

    lb_cluster_config(const char *_name)
        :name(_name),
         protocol(LB_PROTOCOL_HTTP),
         mangle_via(false),
         sticky_mode(STICKY_NONE),
         session_cookie("beng_proxy_session"),
         monitor(nullptr) {}

    /**
     * Returns the member index of the node with the specified
     * jvm_route value, or -1 if not found.
     */
    gcc_pure
    int FindJVMRoute(const char *jvm_route) const;
};

struct lb_attribute_reference {
    enum class Type {
        METHOD,
        URI,
        HEADER,
    } type;

    std::string name;

    template<typename N>
    lb_attribute_reference(Type _type, N &&_name)
        :type(_type), name(std::forward<N>(_name)) {}
};

struct lb_branch_config;

struct lb_goto {
    const lb_cluster_config *cluster;
    const lb_branch_config *branch;

    lb_goto()
        :cluster(nullptr), branch(nullptr) {}

    lb_goto(lb_cluster_config *_cluster)
        :cluster(_cluster), branch(nullptr) {}

    lb_goto(lb_branch_config *_branch)
        :cluster(nullptr), branch(_branch) {}

    bool IsDefined() const {
        return cluster != nullptr || branch != nullptr;
    }

    gcc_pure
    lb_protocol GetProtocol() const;

    gcc_pure
    const char *GetName() const;
};

struct lb_condition_config {
    lb_attribute_reference attribute_reference;

    enum class Operator {
        EQUALS,
        REGEX,
    };

    Operator op;

    bool negate;

    std::string string;
    GRegex *regex;

    lb_condition_config(lb_attribute_reference &&a, bool _negate,
                        const char *_string)
        :attribute_reference(std::move(a)), op(Operator::EQUALS),
         negate(_negate), string(_string) {}

    lb_condition_config(lb_attribute_reference &&a, bool _negate,
                        GRegex *_regex)
        :attribute_reference(std::move(a)), op(Operator::REGEX),
         negate(_negate), regex(_regex) {}

    lb_condition_config(const lb_condition_config &other)
        :attribute_reference(other.attribute_reference),
         op(other.op), negate(other.negate),
         string(other.string),
         regex(other.op == Operator::REGEX
               ? g_regex_ref(other.regex)
               : nullptr) {}

    lb_condition_config(lb_condition_config &&other)
        :attribute_reference(std::move(other.attribute_reference)),
         op(other.op), negate(other.negate),
         string(std::move(other.string)),
         regex(other.regex) {
        other.regex = nullptr;
    }

    ~lb_condition_config() {
        if (regex != nullptr)
            g_regex_unref(regex);
    }

    gcc_pure
    bool Match(const char *value) const {
        switch (op) {
        case Operator::EQUALS:
            return (string == value) ^ negate;
            break;

        case Operator::REGEX:
            return g_regex_match(regex, value, GRegexMatchFlags(0),
                                 nullptr) ^ negate;
            break;
        }

        gcc_unreachable();
    }
};

struct lb_goto_if_config {
    lb_condition_config condition;

    lb_goto destination;

    lb_goto_if_config(lb_condition_config &&c, lb_goto d)
        :condition(std::move(c)), destination(d) {}
};

/**
 * An object that distributes connections or requests to the "real"
 * cluster.
 */
struct lb_branch_config {
    std::string name;

    lb_goto fallback;

    std::list<lb_goto_if_config> conditions;

    lb_branch_config(const char *_name)
        :name(_name) {}

    bool HasFallback() const {
        return fallback.IsDefined();
    }

    lb_protocol GetProtocol() const {
        return fallback.GetProtocol();
    }
};

inline lb_protocol
lb_goto::GetProtocol() const
{
    assert(IsDefined());

    return cluster != nullptr
        ? cluster->protocol
        : branch->GetProtocol();
}

inline const char *
lb_goto::GetName() const
{
    assert(IsDefined());

    return cluster != nullptr
        ? cluster->name.c_str()
        : branch->name.c_str();
}

struct lb_listener_config {
    std::string name;

    const struct address_envelope *envelope;

    lb_goto destination;

    bool ssl;

    struct ssl_config ssl_config;

    lb_listener_config(const char *_name)
        :name(_name),
         envelope(nullptr),
         ssl(false) {
    }
};

struct lb_config {
    std::list<lb_control_config> controls;

    std::map<std::string, lb_monitor_config> monitors;

    std::map<std::string, lb_node_config> nodes;

    std::map<std::string, lb_cluster_config> clusters;
    std::map<std::string, lb_branch_config> branches;

    std::list<lb_listener_config> listeners;

    template<typename T>
    gcc_pure
    const lb_monitor_config *FindMonitor(T &&t) const {
        const auto i = monitors.find(std::forward<T>(t));
        return i != monitors.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_node_config *FindNode(T &&t) const {
        const auto i = nodes.find(std::forward<T>(t));
        return i != nodes.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_cluster_config *FindCluster(T &&t) const {
        const auto i = clusters.find(std::forward<T>(t));
        return i != clusters.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_goto FindGoto(T &&t) const {
        lb_goto g;

        g.cluster = FindCluster(t);
        if (g.cluster == nullptr)
            g.branch = FindBranch(std::forward<T>(t));

        return g;
    }

    template<typename T>
    gcc_pure
    const lb_branch_config *FindBranch(T &&t) const {
        const auto i = branches.find(std::forward<T>(t));
        return i != branches.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_listener_config *FindListener(T &&t) const {
        for (const auto &i : listeners)
            if (i.name == t)
                return &i;

        return nullptr;
    }
};

G_GNUC_CONST
static inline GQuark
lb_config_quark(void)
{
    return g_quark_from_static_string("lb_config");
}

/**
 * Load and parse the specified configuration file.
 */
struct lb_config *
lb_config_load(struct pool *pool, const char *path, GError **error_r);

#endif
<commit_msg>lb_config: always initialise lb_condition_config::regex<commit_after>/*
 * Configuration.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef BENG_LB_CONFIG_H
#define BENG_LB_CONFIG_H

#include "address_list.h"
#include "sticky.h"
#include "ssl_config.hxx"

#include <http/status.h>

#include <glib.h>

#include <map>
#include <list>
#include <vector>
#include <string>

struct pool;

enum lb_protocol {
    LB_PROTOCOL_HTTP,
    LB_PROTOCOL_TCP,
};

struct lb_control_config {
    const struct address_envelope *envelope;

    lb_control_config():envelope(nullptr) {}
};

struct lb_monitor_config {
    std::string name;

    /**
     * Time in seconds between two monitor checks.
     */
    unsigned interval;

    /**
     * If the monitor does not produce a result after this timeout
     * [seconds], it is assumed to be negative.
     */
    unsigned timeout;

    enum class Type {
        NONE,
        PING,
        CONNECT,
        TCP_EXPECT,
    } type;

    /**
     * The timeout for establishing a connection.  Only applicable for
     * #Type::TCP_EXPECT.  0 means no special setting present.
     */
    unsigned connect_timeout;

    /**
     * For #Type::TCP_EXPECT: a string that is sent to the peer
     * after the connection has been established.  May be empty.
     */
    std::string send;

    /**
     * For #Type::TCP_EXPECT: a string that is expected to be
     * received from the peer after the #send string has been sent.
     */
    std::string expect;

    /**
     * For #Type::TCP_EXPECT: if that string is received from the
     * peer (instead of #expect), then the node is assumed to be
     * shutting down gracefully, and will only get sticky requests.
     */
    std::string fade_expect;

    lb_monitor_config(const char *_name)
        :name(_name),
         interval(10), timeout(0),
         type(Type::NONE),
         connect_timeout(0) {}
};

struct lb_node_config {
    std::string name;

    const struct address_envelope *envelope;

    /**
     * The Tomcat "jvmRoute" setting of this node.  It is used for
     * #STICKY_JVM_ROUTE.
     */
    std::string jvm_route;

    lb_node_config(const char *_name,
                   const struct address_envelope *_envelope=nullptr)
        :name(_name),
         envelope(_envelope) {}
};

struct lb_member_config {
    const struct lb_node_config *node;

    unsigned port;

    lb_member_config():node(nullptr), port(0) {}
};

struct lb_fallback_config {
    http_status_t status;

    /**
     * The "Location" response header.
     */
    std::string location;

    std::string message;

    bool IsDefined() const {
        return !location.empty() || !message.empty();
    }
};

struct lb_cluster_config {
    std::string name;

    /**
     * The protocol that is spoken on this cluster.
     */
    enum lb_protocol protocol;

    bool mangle_via;

    struct lb_fallback_config fallback;

    enum sticky_mode sticky_mode;

    std::string session_cookie;

    const struct lb_monitor_config *monitor;

    std::vector<lb_member_config> members;

    /**
     * A list of node addresses.
     */
    struct address_list address_list;

    lb_cluster_config(const char *_name)
        :name(_name),
         protocol(LB_PROTOCOL_HTTP),
         mangle_via(false),
         sticky_mode(STICKY_NONE),
         session_cookie("beng_proxy_session"),
         monitor(nullptr) {}

    /**
     * Returns the member index of the node with the specified
     * jvm_route value, or -1 if not found.
     */
    gcc_pure
    int FindJVMRoute(const char *jvm_route) const;
};

struct lb_attribute_reference {
    enum class Type {
        METHOD,
        URI,
        HEADER,
    } type;

    std::string name;

    template<typename N>
    lb_attribute_reference(Type _type, N &&_name)
        :type(_type), name(std::forward<N>(_name)) {}
};

struct lb_branch_config;

struct lb_goto {
    const lb_cluster_config *cluster;
    const lb_branch_config *branch;

    lb_goto()
        :cluster(nullptr), branch(nullptr) {}

    lb_goto(lb_cluster_config *_cluster)
        :cluster(_cluster), branch(nullptr) {}

    lb_goto(lb_branch_config *_branch)
        :cluster(nullptr), branch(_branch) {}

    bool IsDefined() const {
        return cluster != nullptr || branch != nullptr;
    }

    gcc_pure
    lb_protocol GetProtocol() const;

    gcc_pure
    const char *GetName() const;
};

struct lb_condition_config {
    lb_attribute_reference attribute_reference;

    enum class Operator {
        EQUALS,
        REGEX,
    };

    Operator op;

    bool negate;

    std::string string;
    GRegex *regex;

    lb_condition_config(lb_attribute_reference &&a, bool _negate,
                        const char *_string)
        :attribute_reference(std::move(a)), op(Operator::EQUALS),
         negate(_negate), string(_string), regex(nullptr) {}

    lb_condition_config(lb_attribute_reference &&a, bool _negate,
                        GRegex *_regex)
        :attribute_reference(std::move(a)), op(Operator::REGEX),
         negate(_negate), regex(_regex) {}

    lb_condition_config(const lb_condition_config &other)
        :attribute_reference(other.attribute_reference),
         op(other.op), negate(other.negate),
         string(other.string),
         regex(other.op == Operator::REGEX
               ? g_regex_ref(other.regex)
               : nullptr) {}

    lb_condition_config(lb_condition_config &&other)
        :attribute_reference(std::move(other.attribute_reference)),
         op(other.op), negate(other.negate),
         string(std::move(other.string)),
         regex(other.regex) {
        other.regex = nullptr;
    }

    ~lb_condition_config() {
        if (regex != nullptr)
            g_regex_unref(regex);
    }

    gcc_pure
    bool Match(const char *value) const {
        switch (op) {
        case Operator::EQUALS:
            return (string == value) ^ negate;
            break;

        case Operator::REGEX:
            return g_regex_match(regex, value, GRegexMatchFlags(0),
                                 nullptr) ^ negate;
            break;
        }

        gcc_unreachable();
    }
};

struct lb_goto_if_config {
    lb_condition_config condition;

    lb_goto destination;

    lb_goto_if_config(lb_condition_config &&c, lb_goto d)
        :condition(std::move(c)), destination(d) {}
};

/**
 * An object that distributes connections or requests to the "real"
 * cluster.
 */
struct lb_branch_config {
    std::string name;

    lb_goto fallback;

    std::list<lb_goto_if_config> conditions;

    lb_branch_config(const char *_name)
        :name(_name) {}

    bool HasFallback() const {
        return fallback.IsDefined();
    }

    lb_protocol GetProtocol() const {
        return fallback.GetProtocol();
    }
};

inline lb_protocol
lb_goto::GetProtocol() const
{
    assert(IsDefined());

    return cluster != nullptr
        ? cluster->protocol
        : branch->GetProtocol();
}

inline const char *
lb_goto::GetName() const
{
    assert(IsDefined());

    return cluster != nullptr
        ? cluster->name.c_str()
        : branch->name.c_str();
}

struct lb_listener_config {
    std::string name;

    const struct address_envelope *envelope;

    lb_goto destination;

    bool ssl;

    struct ssl_config ssl_config;

    lb_listener_config(const char *_name)
        :name(_name),
         envelope(nullptr),
         ssl(false) {
    }
};

struct lb_config {
    std::list<lb_control_config> controls;

    std::map<std::string, lb_monitor_config> monitors;

    std::map<std::string, lb_node_config> nodes;

    std::map<std::string, lb_cluster_config> clusters;
    std::map<std::string, lb_branch_config> branches;

    std::list<lb_listener_config> listeners;

    template<typename T>
    gcc_pure
    const lb_monitor_config *FindMonitor(T &&t) const {
        const auto i = monitors.find(std::forward<T>(t));
        return i != monitors.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_node_config *FindNode(T &&t) const {
        const auto i = nodes.find(std::forward<T>(t));
        return i != nodes.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_cluster_config *FindCluster(T &&t) const {
        const auto i = clusters.find(std::forward<T>(t));
        return i != clusters.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_goto FindGoto(T &&t) const {
        lb_goto g;

        g.cluster = FindCluster(t);
        if (g.cluster == nullptr)
            g.branch = FindBranch(std::forward<T>(t));

        return g;
    }

    template<typename T>
    gcc_pure
    const lb_branch_config *FindBranch(T &&t) const {
        const auto i = branches.find(std::forward<T>(t));
        return i != branches.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_listener_config *FindListener(T &&t) const {
        for (const auto &i : listeners)
            if (i.name == t)
                return &i;

        return nullptr;
    }
};

G_GNUC_CONST
static inline GQuark
lb_config_quark(void)
{
    return g_quark_from_static_string("lb_config");
}

/**
 * Load and parse the specified configuration file.
 */
struct lb_config *
lb_config_load(struct pool *pool, const char *path, GError **error_r);

#endif
<|endoftext|>


<commit_before>/*
 * Configuration.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef BENG_LB_CONFIG_H
#define BENG_LB_CONFIG_H

#include "address_list.hxx"
#include "sticky.h"
#include "ssl_config.hxx"
#include "net/AllocatedSocketAddress.hxx"

#include <http/status.h>

#include <glib.h>

#include <map>
#include <list>
#include <vector>
#include <string>

struct pool;
class Error;

enum lb_protocol {
    LB_PROTOCOL_HTTP,
    LB_PROTOCOL_TCP,
};

struct lb_control_config {
    AllocatedSocketAddress bind_address;
};

struct lb_monitor_config {
    std::string name;

    /**
     * Time in seconds between two monitor checks.
     */
    unsigned interval = 10;

    /**
     * If the monitor does not produce a result after this timeout
     * [seconds], it is assumed to be negative.
     */
    unsigned timeout = 0;

    enum class Type {
        NONE,
        PING,
        CONNECT,
        TCP_EXPECT,
    } type = Type::NONE;

    /**
     * The timeout for establishing a connection.  Only applicable for
     * #Type::TCP_EXPECT.  0 means no special setting present.
     */
    unsigned connect_timeout = 0;

    /**
     * For #Type::TCP_EXPECT: a string that is sent to the peer
     * after the connection has been established.  May be empty.
     */
    std::string send;

    /**
     * For #Type::TCP_EXPECT: a string that is expected to be
     * received from the peer after the #send string has been sent.
     */
    std::string expect;

    /**
     * For #Type::TCP_EXPECT: if that string is received from the
     * peer (instead of #expect), then the node is assumed to be
     * shutting down gracefully, and will only get sticky requests.
     */
    std::string fade_expect;

    explicit lb_monitor_config(const char *_name)
        :name(_name) {}
};

struct lb_node_config {
    std::string name;

    AllocatedSocketAddress address;

    /**
     * The Tomcat "jvmRoute" setting of this node.  It is used for
     * #STICKY_JVM_ROUTE.
     */
    std::string jvm_route;

    explicit lb_node_config(const char *_name)
        :name(_name) {}

    lb_node_config(const char *_name, AllocatedSocketAddress &&_address)
        :name(_name), address(std::move(_address)) {}

    lb_node_config(lb_node_config &&src)
        :name(std::move(src.name)), address(std::move(src.address)),
         jvm_route(std::move(src.jvm_route)) {}
};

struct lb_member_config {
    const struct lb_node_config *node = nullptr;

    unsigned port = 0;
};

struct lb_fallback_config {
    http_status_t status;

    /**
     * The "Location" response header.
     */
    std::string location;

    std::string message;

    bool IsDefined() const {
        return !location.empty() || !message.empty();
    }
};

struct lb_cluster_config {
    std::string name;

    /**
     * The protocol that is spoken on this cluster.
     */
    enum lb_protocol protocol = LB_PROTOCOL_HTTP;

    /**
     * Use the client's source IP for the connection to the backend?
     * This is implemented using IP_TRANSPARENT and requires the
     * "tproxy" Linux kernel module.
     */
    bool transparent_source = false;

    bool mangle_via = false;

    struct lb_fallback_config fallback;

    enum sticky_mode sticky_mode = STICKY_NONE;

    std::string session_cookie = "beng_proxy_session";

    const struct lb_monitor_config *monitor = nullptr;

    std::vector<lb_member_config> members;

    /**
     * A list of node addresses.
     */
    AddressList address_list;

    explicit lb_cluster_config(const char *_name)
        :name(_name) {}

    /**
     * Returns the member index of the node with the specified
     * jvm_route value, or -1 if not found.
     */
    gcc_pure
    int FindJVMRoute(const char *jvm_route) const;
};

struct lb_attribute_reference {
    enum class Type {
        METHOD,
        URI,
        HEADER,
    } type;

    std::string name;

    template<typename N>
    lb_attribute_reference(Type _type, N &&_name)
        :type(_type), name(std::forward<N>(_name)) {}
};

struct lb_branch_config;

struct lb_goto {
    const lb_cluster_config *cluster = nullptr;
    const lb_branch_config *branch = nullptr;

    lb_goto() = default;

    explicit lb_goto(lb_cluster_config *_cluster)
        :cluster(_cluster), branch(nullptr) {}

    explicit lb_goto(lb_branch_config *_branch)
        :cluster(nullptr), branch(_branch) {}

    bool IsDefined() const {
        return cluster != nullptr || branch != nullptr;
    }

    gcc_pure
    lb_protocol GetProtocol() const;

    gcc_pure
    const char *GetName() const;
};

struct lb_condition_config {
    lb_attribute_reference attribute_reference;

    enum class Operator {
        EQUALS,
        REGEX,
    };

    Operator op;

    bool negate;

    std::string string;
    GRegex *regex = nullptr;

    lb_condition_config(lb_attribute_reference &&a, bool _negate,
                        const char *_string)
        :attribute_reference(std::move(a)), op(Operator::EQUALS),
         negate(_negate), string(_string) {}

    lb_condition_config(lb_attribute_reference &&a, bool _negate,
                        GRegex *_regex)
        :attribute_reference(std::move(a)), op(Operator::REGEX),
         negate(_negate), regex(_regex) {}

    lb_condition_config(lb_condition_config &&other)
        :attribute_reference(std::move(other.attribute_reference)),
         op(other.op), negate(other.negate),
         string(std::move(other.string)),
         regex(other.regex) {
        other.regex = nullptr;
    }

    ~lb_condition_config() {
        if (regex != nullptr)
            g_regex_unref(regex);
    }

    lb_condition_config(const lb_condition_config &) = delete;
    lb_condition_config &operator=(const lb_condition_config &) = delete;

    gcc_pure
    bool Match(const char *value) const {
        switch (op) {
        case Operator::EQUALS:
            return (string == value) ^ negate;
            break;

        case Operator::REGEX:
            return g_regex_match(regex, value, GRegexMatchFlags(0),
                                 nullptr) ^ negate;
            break;
        }

        gcc_unreachable();
    }
};

struct lb_goto_if_config {
    lb_condition_config condition;

    lb_goto destination;

    lb_goto_if_config(lb_condition_config &&c, lb_goto d)
        :condition(std::move(c)), destination(d) {}
};

/**
 * An object that distributes connections or requests to the "real"
 * cluster.
 */
struct lb_branch_config {
    std::string name;

    lb_goto fallback;

    std::list<lb_goto_if_config> conditions;

    explicit lb_branch_config(const char *_name)
        :name(_name) {}

    lb_branch_config(lb_branch_config &&) = default;

    lb_branch_config(const lb_branch_config &) = delete;
    lb_branch_config &operator=(const lb_branch_config &) = delete;

    bool HasFallback() const {
        return fallback.IsDefined();
    }

    lb_protocol GetProtocol() const {
        return fallback.GetProtocol();
    }
};

inline lb_protocol
lb_goto::GetProtocol() const
{
    assert(IsDefined());

    return cluster != nullptr
        ? cluster->protocol
        : branch->GetProtocol();
}

inline const char *
lb_goto::GetName() const
{
    assert(IsDefined());

    return cluster != nullptr
        ? cluster->name.c_str()
        : branch->name.c_str();
}

struct lb_listener_config {
    std::string name;

    AllocatedSocketAddress bind_address;

    lb_goto destination;

    bool verbose_response = false;

    bool ssl = false;

    struct ssl_config ssl_config;

    explicit lb_listener_config(const char *_name)
        :name(_name) {}
};

struct lb_config {
    std::list<lb_control_config> controls;

    std::map<std::string, lb_monitor_config> monitors;

    std::map<std::string, lb_node_config> nodes;

    std::map<std::string, lb_cluster_config> clusters;
    std::map<std::string, lb_branch_config> branches;

    std::list<lb_listener_config> listeners;

    template<typename T>
    gcc_pure
    const lb_monitor_config *FindMonitor(T &&t) const {
        const auto i = monitors.find(std::forward<T>(t));
        return i != monitors.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_node_config *FindNode(T &&t) const {
        const auto i = nodes.find(std::forward<T>(t));
        return i != nodes.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_cluster_config *FindCluster(T &&t) const {
        const auto i = clusters.find(std::forward<T>(t));
        return i != clusters.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_goto FindGoto(T &&t) const {
        lb_goto g;

        g.cluster = FindCluster(t);
        if (g.cluster == nullptr)
            g.branch = FindBranch(std::forward<T>(t));

        return g;
    }

    template<typename T>
    gcc_pure
    const lb_branch_config *FindBranch(T &&t) const {
        const auto i = branches.find(std::forward<T>(t));
        return i != branches.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_listener_config *FindListener(T &&t) const {
        for (const auto &i : listeners)
            if (i.name == t)
                return &i;

        return nullptr;
    }
};

/**
 * Load and parse the specified configuration file.
 */
struct lb_config *
lb_config_load(struct pool *pool, const char *path, Error &error_r);

#endif
<commit_msg>lb_config: remove useless "break" statements<commit_after>/*
 * Configuration.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef BENG_LB_CONFIG_H
#define BENG_LB_CONFIG_H

#include "address_list.hxx"
#include "sticky.h"
#include "ssl_config.hxx"
#include "net/AllocatedSocketAddress.hxx"

#include <http/status.h>

#include <glib.h>

#include <map>
#include <list>
#include <vector>
#include <string>

struct pool;
class Error;

enum lb_protocol {
    LB_PROTOCOL_HTTP,
    LB_PROTOCOL_TCP,
};

struct lb_control_config {
    AllocatedSocketAddress bind_address;
};

struct lb_monitor_config {
    std::string name;

    /**
     * Time in seconds between two monitor checks.
     */
    unsigned interval = 10;

    /**
     * If the monitor does not produce a result after this timeout
     * [seconds], it is assumed to be negative.
     */
    unsigned timeout = 0;

    enum class Type {
        NONE,
        PING,
        CONNECT,
        TCP_EXPECT,
    } type = Type::NONE;

    /**
     * The timeout for establishing a connection.  Only applicable for
     * #Type::TCP_EXPECT.  0 means no special setting present.
     */
    unsigned connect_timeout = 0;

    /**
     * For #Type::TCP_EXPECT: a string that is sent to the peer
     * after the connection has been established.  May be empty.
     */
    std::string send;

    /**
     * For #Type::TCP_EXPECT: a string that is expected to be
     * received from the peer after the #send string has been sent.
     */
    std::string expect;

    /**
     * For #Type::TCP_EXPECT: if that string is received from the
     * peer (instead of #expect), then the node is assumed to be
     * shutting down gracefully, and will only get sticky requests.
     */
    std::string fade_expect;

    explicit lb_monitor_config(const char *_name)
        :name(_name) {}
};

struct lb_node_config {
    std::string name;

    AllocatedSocketAddress address;

    /**
     * The Tomcat "jvmRoute" setting of this node.  It is used for
     * #STICKY_JVM_ROUTE.
     */
    std::string jvm_route;

    explicit lb_node_config(const char *_name)
        :name(_name) {}

    lb_node_config(const char *_name, AllocatedSocketAddress &&_address)
        :name(_name), address(std::move(_address)) {}

    lb_node_config(lb_node_config &&src)
        :name(std::move(src.name)), address(std::move(src.address)),
         jvm_route(std::move(src.jvm_route)) {}
};

struct lb_member_config {
    const struct lb_node_config *node = nullptr;

    unsigned port = 0;
};

struct lb_fallback_config {
    http_status_t status;

    /**
     * The "Location" response header.
     */
    std::string location;

    std::string message;

    bool IsDefined() const {
        return !location.empty() || !message.empty();
    }
};

struct lb_cluster_config {
    std::string name;

    /**
     * The protocol that is spoken on this cluster.
     */
    enum lb_protocol protocol = LB_PROTOCOL_HTTP;

    /**
     * Use the client's source IP for the connection to the backend?
     * This is implemented using IP_TRANSPARENT and requires the
     * "tproxy" Linux kernel module.
     */
    bool transparent_source = false;

    bool mangle_via = false;

    struct lb_fallback_config fallback;

    enum sticky_mode sticky_mode = STICKY_NONE;

    std::string session_cookie = "beng_proxy_session";

    const struct lb_monitor_config *monitor = nullptr;

    std::vector<lb_member_config> members;

    /**
     * A list of node addresses.
     */
    AddressList address_list;

    explicit lb_cluster_config(const char *_name)
        :name(_name) {}

    /**
     * Returns the member index of the node with the specified
     * jvm_route value, or -1 if not found.
     */
    gcc_pure
    int FindJVMRoute(const char *jvm_route) const;
};

struct lb_attribute_reference {
    enum class Type {
        METHOD,
        URI,
        HEADER,
    } type;

    std::string name;

    template<typename N>
    lb_attribute_reference(Type _type, N &&_name)
        :type(_type), name(std::forward<N>(_name)) {}
};

struct lb_branch_config;

struct lb_goto {
    const lb_cluster_config *cluster = nullptr;
    const lb_branch_config *branch = nullptr;

    lb_goto() = default;

    explicit lb_goto(lb_cluster_config *_cluster)
        :cluster(_cluster), branch(nullptr) {}

    explicit lb_goto(lb_branch_config *_branch)
        :cluster(nullptr), branch(_branch) {}

    bool IsDefined() const {
        return cluster != nullptr || branch != nullptr;
    }

    gcc_pure
    lb_protocol GetProtocol() const;

    gcc_pure
    const char *GetName() const;
};

struct lb_condition_config {
    lb_attribute_reference attribute_reference;

    enum class Operator {
        EQUALS,
        REGEX,
    };

    Operator op;

    bool negate;

    std::string string;
    GRegex *regex = nullptr;

    lb_condition_config(lb_attribute_reference &&a, bool _negate,
                        const char *_string)
        :attribute_reference(std::move(a)), op(Operator::EQUALS),
         negate(_negate), string(_string) {}

    lb_condition_config(lb_attribute_reference &&a, bool _negate,
                        GRegex *_regex)
        :attribute_reference(std::move(a)), op(Operator::REGEX),
         negate(_negate), regex(_regex) {}

    lb_condition_config(lb_condition_config &&other)
        :attribute_reference(std::move(other.attribute_reference)),
         op(other.op), negate(other.negate),
         string(std::move(other.string)),
         regex(other.regex) {
        other.regex = nullptr;
    }

    ~lb_condition_config() {
        if (regex != nullptr)
            g_regex_unref(regex);
    }

    lb_condition_config(const lb_condition_config &) = delete;
    lb_condition_config &operator=(const lb_condition_config &) = delete;

    gcc_pure
    bool Match(const char *value) const {
        switch (op) {
        case Operator::EQUALS:
            return (string == value) ^ negate;

        case Operator::REGEX:
            return g_regex_match(regex, value, GRegexMatchFlags(0),
                                 nullptr) ^ negate;
        }

        gcc_unreachable();
    }
};

struct lb_goto_if_config {
    lb_condition_config condition;

    lb_goto destination;

    lb_goto_if_config(lb_condition_config &&c, lb_goto d)
        :condition(std::move(c)), destination(d) {}
};

/**
 * An object that distributes connections or requests to the "real"
 * cluster.
 */
struct lb_branch_config {
    std::string name;

    lb_goto fallback;

    std::list<lb_goto_if_config> conditions;

    explicit lb_branch_config(const char *_name)
        :name(_name) {}

    lb_branch_config(lb_branch_config &&) = default;

    lb_branch_config(const lb_branch_config &) = delete;
    lb_branch_config &operator=(const lb_branch_config &) = delete;

    bool HasFallback() const {
        return fallback.IsDefined();
    }

    lb_protocol GetProtocol() const {
        return fallback.GetProtocol();
    }
};

inline lb_protocol
lb_goto::GetProtocol() const
{
    assert(IsDefined());

    return cluster != nullptr
        ? cluster->protocol
        : branch->GetProtocol();
}

inline const char *
lb_goto::GetName() const
{
    assert(IsDefined());

    return cluster != nullptr
        ? cluster->name.c_str()
        : branch->name.c_str();
}

struct lb_listener_config {
    std::string name;

    AllocatedSocketAddress bind_address;

    lb_goto destination;

    bool verbose_response = false;

    bool ssl = false;

    struct ssl_config ssl_config;

    explicit lb_listener_config(const char *_name)
        :name(_name) {}
};

struct lb_config {
    std::list<lb_control_config> controls;

    std::map<std::string, lb_monitor_config> monitors;

    std::map<std::string, lb_node_config> nodes;

    std::map<std::string, lb_cluster_config> clusters;
    std::map<std::string, lb_branch_config> branches;

    std::list<lb_listener_config> listeners;

    template<typename T>
    gcc_pure
    const lb_monitor_config *FindMonitor(T &&t) const {
        const auto i = monitors.find(std::forward<T>(t));
        return i != monitors.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_node_config *FindNode(T &&t) const {
        const auto i = nodes.find(std::forward<T>(t));
        return i != nodes.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_cluster_config *FindCluster(T &&t) const {
        const auto i = clusters.find(std::forward<T>(t));
        return i != clusters.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_goto FindGoto(T &&t) const {
        lb_goto g;

        g.cluster = FindCluster(t);
        if (g.cluster == nullptr)
            g.branch = FindBranch(std::forward<T>(t));

        return g;
    }

    template<typename T>
    gcc_pure
    const lb_branch_config *FindBranch(T &&t) const {
        const auto i = branches.find(std::forward<T>(t));
        return i != branches.end()
            ? &i->second
            : nullptr;
    }

    template<typename T>
    gcc_pure
    const lb_listener_config *FindListener(T &&t) const {
        for (const auto &i : listeners)
            if (i.name == t)
                return &i;

        return nullptr;
    }
};

/**
 * Load and parse the specified configuration file.
 */
struct lb_config *
lb_config_load(struct pool *pool, const char *path, Error &error_r);

#endif
<|endoftext|>


<commit_before>#include "Array.h"
#include "Runtime.h"
#include "SnowString.h"
#include "Function.h"
#include <sstream>

#define DEFAULT_ARRAY_LENGTH 8

namespace snow {
	GC_ROOTS_IMPL(Array) {
		GC_SUPER(Object);

		GC_ROOT(m_Data);
		for (size_t i = 0; i < m_Length; ++i) {
			GC_ROOT(m_Data[i]);
		}
	}

	void Array::resize(size_t new_size) {
		// TODO: Grow more than strictly necessary to minimize allocations
		VALUE* old_pointer = m_Data;
		m_Data = new(kGarbage, kBlob) VALUE[new_size];
		m_AllocatedSize = new_size;
		if (m_Length != 0 && old_pointer) {
			memcpy(m_Data, old_pointer, m_Length*sizeof(VALUE));
		}
	}

	Array::Array(size_t preallocate_length) : Object(array_prototype()), m_Data(NULL), m_Length(0), m_AllocatedSize(0) {
		if (preallocate_length == 0)
			preallocate_length = DEFAULT_ARRAY_LENGTH;
		resize(preallocate_length * sizeof(VALUE));
	}

	Array::Array(const Array& other) : Object(array_prototype()), m_Data(other.m_Data), m_Length(other.m_Length), m_AllocatedSize(0) {
		resize(m_Length * sizeof(VALUE));
	}

	Array::Array(VALUE* existing_array, size_t len, bool copy) : Object(array_prototype()), m_Data(existing_array), m_Length(len), m_AllocatedSize(0) {
		if (copy)
			resize(m_Length * sizeof(VALUE));
	}

	void Array::ensure_length(size_t len) {
		ASSERT(!is_frozen());
		if (len * sizeof(VALUE) <= m_AllocatedSize)
			return;
		resize(len * sizeof(VALUE));
	}

	VALUE& Array::operator[](int64_t idx) {
		if (idx < 0)
			idx %= m_Length;
		if (idx >= (int64_t)m_Length)
		{
			ensure_length(idx+1);
			auto old_length = m_Length;
			m_Length = idx+1;
			for (size_t i = old_length; i < m_Length; ++i) {
				m_Data[i] = nil();
			}
		}
		return m_Data[idx];
	}

	VALUE Array::operator[](int64_t idx) const {
		if (idx < 0)
			idx = m_Length + idx;
		if (idx < 0 || idx >= (int64_t)m_Length)
			return nil();
		return m_Data[idx];
	}

	VALUE Array::push(VALUE val) {
		ensure_length(m_Length + 1);
		m_Data[m_Length] = val;
		++m_Length;
		return val;
	}

	VALUE Array::pop() {
		ASSERT(!is_frozen());
		if (m_Length == 0)
			return nil();
		VALUE val = m_Data[m_Length-1];
		--m_Length;
		return val;
	}

	VALUE Array::unshift(VALUE val) {
		ensure_length(m_Length+1);
		// XXX: For some reason, memmove doesn't work here. Investigate!
		// Also, this might be acceptable, since it's faster than memmove.
		for (size_t i = 0; i < m_Length; ++i) {
			m_Data[m_Length-i] = m_Data[m_Length-1-i];
		}
		++m_Length;
		m_Data[0] = val;
		return val;
	}

	VALUE Array::shift() {
		ASSERT(!is_frozen());
		if (m_Length == 0)
			return nil();
		VALUE val = m_Data[0];
		--m_Length;
		memmove(m_Data, &m_Data[1], m_Length);
		return val;
	}

	VALUE Array::va_call(VALUE self, uint64_t num_args, va_list& args) {
		if (num_args == 0)
			return this;
		if (num_args == 1) {
			int64_t index = integer(va_arg(args, VALUE));
			return (*this)[index];
		}
		// TODO: ranges and such.
		TRAP();
		return this;
	}

	static VALUE array_new(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		return new Array(args, num_args);
	}

	static VALUE array_get(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 1);
		auto array = object_cast<Array>(self);
		int64_t idx = integer(args[0]);
		return (*array)[idx];
	}

	static VALUE array_set(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 2);
		auto array = object_cast<Array>(self);
		int idx = integer(args[0]);
		VALUE new_value = args[1];
		return array->set_by_index(idx, new_value);
	}

	static VALUE array_each(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args >= 1);
		auto array = object_cast<Array>(self);

		VALUE closure = args[0];
		for (size_t i = 0; i < array->length(); ++i) {
			snow::call(NULL, closure, 2, (*array)[i], value((int64_t)i));
		}
		return self;
	}

	static VALUE array_length(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		auto array = object_cast<Array>(self);
		return value((int64_t)array->length());
	}

	static VALUE array_inspect(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		auto array = object_cast<Array>(self);
		std::stringstream ss;

		if (array->length() == 0) {
			ss << "()";
		} else {
			ss << "(";
			for (size_t i = 0; i < array->length() - 1; ++i) {
				ss << value_to_string((*array)[i]) << ", ";
			}
			ss << value_to_string((*array)[array->length()-1]) << ")";
		}

		return new String(ss.str());
	}

	static VALUE array_push(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 1);
		auto array = object_cast<Array>(self);
		VALUE val = args[0];
		array->push(val);
		return self;
	}

	static VALUE array_pop(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 0);
		auto array = object_cast<Array>(self);
		return array->pop();
	}

	static VALUE array_unshift(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 1);
		auto array = object_cast<Array>(self);
		VALUE val = args[0];
		return array->unshift(val);
	}

	static VALUE array_shift(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 0);
		auto array = object_cast<Array>(self);
		return array->shift();
	}

	Object* array_prototype() {
		static Object* proto = NULL;
		if (proto) return proto;
		proto = new(kMalloc) Object;
		proto->set_by_string("new", new Function(array_new));
		proto->set_by_string("get", new Function(array_get));
		proto->set_by_string("set", new Function(array_set));
		proto->set_by_string("each", new Function(array_each));
		proto->set_by_string("push", new Function(array_push));
		proto->set_by_string("pop", new Function(array_pop));
		proto->set_by_string("unshift", new Function(array_unshift));
		proto->set_by_string("shift", new Function(array_shift));
		proto->set_by_string("length", new Function(array_length));
		proto->set_by_string("inspect", new Function(array_inspect));
		proto->set_by_string("<<", new Function(array_push));
		return proto;
	}
}
<commit_msg>fixed Array.inspect to use inspect on its members. Breaks if the array is recursive.<commit_after>#include "Array.h"
#include "Runtime.h"
#include "SnowString.h"
#include "Function.h"
#include <sstream>

#define DEFAULT_ARRAY_LENGTH 8

namespace snow {
	GC_ROOTS_IMPL(Array) {
		GC_SUPER(Object);

		GC_ROOT(m_Data);
		for (size_t i = 0; i < m_Length; ++i) {
			GC_ROOT(m_Data[i]);
		}
	}

	void Array::resize(size_t new_size) {
		// TODO: Grow more than strictly necessary to minimize allocations
		VALUE* old_pointer = m_Data;
		m_Data = new(kGarbage, kBlob) VALUE[new_size];
		m_AllocatedSize = new_size;
		if (m_Length != 0 && old_pointer) {
			memcpy(m_Data, old_pointer, m_Length*sizeof(VALUE));
		}
	}

	Array::Array(size_t preallocate_length) : Object(array_prototype()), m_Data(NULL), m_Length(0), m_AllocatedSize(0) {
		if (preallocate_length == 0)
			preallocate_length = DEFAULT_ARRAY_LENGTH;
		resize(preallocate_length * sizeof(VALUE));
	}

	Array::Array(const Array& other) : Object(array_prototype()), m_Data(other.m_Data), m_Length(other.m_Length), m_AllocatedSize(0) {
		resize(m_Length * sizeof(VALUE));
	}

	Array::Array(VALUE* existing_array, size_t len, bool copy) : Object(array_prototype()), m_Data(existing_array), m_Length(len), m_AllocatedSize(0) {
		if (copy)
			resize(m_Length * sizeof(VALUE));
	}

	void Array::ensure_length(size_t len) {
		ASSERT(!is_frozen());
		if (len * sizeof(VALUE) <= m_AllocatedSize)
			return;
		resize(len * sizeof(VALUE));
	}

	VALUE& Array::operator[](int64_t idx) {
		if (idx < 0)
			idx %= m_Length;
		if (idx >= (int64_t)m_Length)
		{
			ensure_length(idx+1);
			auto old_length = m_Length;
			m_Length = idx+1;
			for (size_t i = old_length; i < m_Length; ++i) {
				m_Data[i] = nil();
			}
		}
		return m_Data[idx];
	}

	VALUE Array::operator[](int64_t idx) const {
		if (idx < 0)
			idx = m_Length + idx;
		if (idx < 0 || idx >= (int64_t)m_Length)
			return nil();
		return m_Data[idx];
	}

	VALUE Array::push(VALUE val) {
		ensure_length(m_Length + 1);
		m_Data[m_Length] = val;
		++m_Length;
		return val;
	}

	VALUE Array::pop() {
		ASSERT(!is_frozen());
		if (m_Length == 0)
			return nil();
		VALUE val = m_Data[m_Length-1];
		--m_Length;
		return val;
	}

	VALUE Array::unshift(VALUE val) {
		ensure_length(m_Length+1);
		// XXX: For some reason, memmove doesn't work here. Investigate!
		// Also, this might be acceptable, since it's faster than memmove.
		for (size_t i = 0; i < m_Length; ++i) {
			m_Data[m_Length-i] = m_Data[m_Length-1-i];
		}
		++m_Length;
		m_Data[0] = val;
		return val;
	}

	VALUE Array::shift() {
		ASSERT(!is_frozen());
		if (m_Length == 0)
			return nil();
		VALUE val = m_Data[0];
		--m_Length;
		memmove(m_Data, &m_Data[1], m_Length);
		return val;
	}

	VALUE Array::va_call(VALUE self, uint64_t num_args, va_list& args) {
		if (num_args == 0)
			return this;
		if (num_args == 1) {
			int64_t index = integer(va_arg(args, VALUE));
			return (*this)[index];
		}
		// TODO: ranges and such.
		TRAP();
		return this;
	}

	static VALUE array_new(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		return new Array(args, num_args);
	}

	static VALUE array_get(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 1);
		auto array = object_cast<Array>(self);
		int64_t idx = integer(args[0]);
		return (*array)[idx];
	}

	static VALUE array_set(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 2);
		auto array = object_cast<Array>(self);
		int idx = integer(args[0]);
		VALUE new_value = args[1];
		return array->set_by_index(idx, new_value);
	}

	static VALUE array_each(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args >= 1);
		auto array = object_cast<Array>(self);

		VALUE closure = args[0];
		for (size_t i = 0; i < array->length(); ++i) {
			snow::call(NULL, closure, 2, (*array)[i], value((int64_t)i));
		}
		return self;
	}

	static VALUE array_length(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		auto array = object_cast<Array>(self);
		return value((int64_t)array->length());
	}

	static VALUE array_inspect(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		auto array = object_cast<Array>(self);
		std::stringstream ss;

		ss << "@(";
		for (size_t i = 0; i < array->length(); ++i) {
			ss << value_to_string(snow::call_method((*array)[i], "inspect", 0));
			if (i != array->length() - 1)
				ss << ", ";
		}
		ss << ")";

		return new String(ss.str());
	}

	static VALUE array_push(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 1);
		auto array = object_cast<Array>(self);
		VALUE val = args[0];
		array->push(val);
		return self;
	}

	static VALUE array_pop(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 0);
		auto array = object_cast<Array>(self);
		return array->pop();
	}

	static VALUE array_unshift(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 1);
		auto array = object_cast<Array>(self);
		VALUE val = args[0];
		return array->unshift(val);
	}

	static VALUE array_shift(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 0);
		auto array = object_cast<Array>(self);
		return array->shift();
	}

	Object* array_prototype() {
		static Object* proto = NULL;
		if (proto) return proto;
		proto = new(kMalloc) Object;
		proto->set_by_string("new", new Function(array_new));
		proto->set_by_string("get", new Function(array_get));
		proto->set_by_string("set", new Function(array_set));
		proto->set_by_string("each", new Function(array_each));
		proto->set_by_string("push", new Function(array_push));
		proto->set_by_string("pop", new Function(array_pop));
		proto->set_by_string("unshift", new Function(array_unshift));
		proto->set_by_string("shift", new Function(array_shift));
		proto->set_by_string("length", new Function(array_length));
		proto->set_by_string("inspect", new Function(array_inspect));
		proto->set_by_string("<<", new Function(array_push));
		return proto;
	}
}
<|endoftext|>


<commit_before>#include "novo.h"

#include <novo/io.h>
#include <novo/entities/randomcubes.h>
#include <novo/entities/origin.h>
#include <novo/gfx/gl/names.h>
#include <novo/gfx/framebuffer.h>
#include <novo/gfx/window.h>

#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <boost/ptr_container/ptr_list.hpp>

#include <glbinding/Binding.h>

#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/quaternion.hpp>

#include <iostream>
#include <thread>

using namespace novo;
using namespace graphics;

using namespace std::chrono;
using namespace std::placeholders;
using std::string;
using std::bind;

using boost::program_options::variables_map;
using boost::format;
using boost::ptr_list;

static void glfwErrorCB(int code, const char* msg);
static void glErrorCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam);

format fmtTitle(APPNAME" - FPS: %d ::S: %d");

Novo::Novo(variables_map opts):
	window(new Window(800, 600, APPNAME))
{
	// GLFW
	glfwSetErrorCallback(glfwErrorCB);
	if(!glfwInit())
		throw std::runtime_error(string("GLFW init failed!"));

	// OpenGL / GLEW
	Window test(1,1, APPNAME " - GL Test");
	test.config.visible = false;
	test.config.debuging = true;
	test.open();
	test.bindContext();

	glbinding::Binding::initialize(false);

	window->config.resizable = false;
	window->config.GLversion.major = 3;
	window->config.GLversion.minor = 2;
	window->config.GLprofile = WindowConfig::Profile::CORE;
	window->config.debuging = true;
}

Novo::~Novo()
{
	window->close();
	glfwTerminate();
}

i32 Novo::run() {
	using namespace ::gl;

	window->open();
	window->bindContext();
	window->setVsync(false);

	glEnable(GL_DEBUG_OUTPUT);
	glDebugMessageCallback(&glErrorCB, this);

	///##################################
	///##################################
	sptr<Camera> cam(new Camera(vec3(0), 800, 600));
	screen = sptr<Framebuffer>(new Framebuffer(800, 600, cam, false));

	ptr_list<Drawable> objs;

	u32 size = 64;
	objs.push_back(new RandomCubes(size*size, size*size, vec3(0)));
	objs.push_back(new Origin(size, 8.0));

	///##################################
	///##################################

	func.mouse = bind(&Camera::mouseCallback, screen->getCamera().get(), _1, _2);

	con.focus = window->onFocus.connect(bind(&Novo::onFocus, this, _1));
	con.key = window->onKey.connect(bind(&Novo::onKey, this, _1, _2, _3, _4));
	con.mouse = window->onCursorMove.connect(func.mouse);
	con.mouseBTN = window->onMouseButton.connect(bind(&Novo::onMouseButton, this, _1, _2, _3));

	// Debug
	GLuint query = 0;
	glGenQueries(1, &query);

	steady_clock::time_point tick = steady_clock::now();
	i32 totalFrames = 0;
	while(window->isOpen()) {
		///##################################

		screen->getCamera()->moveUpdate(1);
		screen->bind();
		glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

		glBeginQuery(GL_SAMPLES_PASSED, query);

		///TODO glm::mat4_cast(glm::angleAxis(glm::sin((float)glfwGetTime()), vec3(0.0f, 0.0f, 1.0f)));

		for(Drawable& obj : objs)
			screen->render(&obj);

		glEndQuery(GL_SAMPLES_PASSED);

		//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

		screen->draw();

		///##################################
		window->present();
		window->poll();

		totalFrames++; // +1 Frame
		if(duration_cast<seconds>(steady_clock::now() - tick).count()) {

			GLuint samples;
			glGetQueryObjectuiv(query, GL_QUERY_RESULT, &samples);

			fmtTitle % totalFrames % samples;
			window->setTitle(fmtTitle.str());

			totalFrames = 0;
			tick = steady_clock::now();
		}
		//TODO: main loop sleep
	}

	return 0;
}

void Novo::onFocus(bool state) {
	if(!state) {
		con.mouse.disconnect();
		window->setCursorMode(CursorMode::NORMAL);
	}
}

void Novo::onMouseButton(i32 key, i32 action, i32 mod) {
	if(action == GLFW_PRESS && window->onCursorMove.empty()) {
		con.mouse = window->onCursorMove.connect(func.mouse);
		window->setCursorMode(CursorMode::DISABLED);
		return;
	}
}

void Novo::onKey(i32 key, i32 scancode, i32 action, i32 mod) {
	sptr<Camera> cam = screen->getCamera();
	vec3 camVal = cam->getVelocity();

		switch(action) {
		case GLFW_PRESS:
			switch(key) {
				case GLFW_KEY_W:
					camVal.z++;
					break;
				case GLFW_KEY_S:
					camVal.z--;
					break;
				case GLFW_KEY_D:
					camVal.x--;
					break;
				case GLFW_KEY_A:
					camVal.x++;
					break;
				case GLFW_KEY_SPACE:
					camVal.y++;
					break;
				case GLFW_KEY_C:
					camVal.y--;
					break;
				case GLFW_KEY_ESCAPE:
					if(!con.mouse.connected())
						window->close();
					else {
						con.mouse.disconnect();
						window->setCursorMode(CursorMode::NORMAL);
					}
					return;

				case GLFW_KEY_TAB:
					static int e;
					if(++e > 5) e = 0;
					screen->getProgram().setUniform("effectFlag", e);
					break;

			}
			break;
		case GLFW_RELEASE:
			switch(key) {
				case GLFW_KEY_W:
					camVal.z--;
					break;
				case GLFW_KEY_S:
					camVal.z++;
					break;
				case GLFW_KEY_D:
					camVal.x++;
					break;
				case GLFW_KEY_A:
					camVal.x--;
					break;
				case GLFW_KEY_SPACE:
					camVal.y--;
					break;
				case GLFW_KEY_C:
					camVal.y++;
					break;
			}
			break;
	}
	cam->setVelocity(camVal);
}

static void glfwErrorCB(int code, const char* msg)
{
	std::cerr << string("[glfw]: ") << code << ": " << string(msg) << std::endl;
	std::cerr.flush();
}

static void glErrorCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void *userParam)
{
	using std::cerr;
	using std::endl;

	using namespace novo::gl::names;

	static format err("[%f] glError: %d::%s::%s::%s\n\t%s\n");

	cerr << (err % glfwGetTime() % id % debug[severity] % debug[source] % debug[type] % message);
	cerr.flush();
	/*
	cerr << "Stack trace:" << endl;
	cerr << saneStackTrace() << endl;
	cerr.flush();
	*/
	if(severity == GL_DEBUG_SEVERITY_HIGH)
		throw novo::NovoException("OpenGL critical error");
}
<commit_msg>GL: using more fitting name for debug callback<commit_after>#include "novo.h"

#include <novo/io.h>
#include <novo/entities/randomcubes.h>
#include <novo/entities/origin.h>
#include <novo/gfx/gl/names.h>
#include <novo/gfx/framebuffer.h>
#include <novo/gfx/window.h>

#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <boost/ptr_container/ptr_list.hpp>

#include <glbinding/Binding.h>

#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/quaternion.hpp>

#include <iostream>
#include <thread>

using namespace novo;
using namespace graphics;

using namespace std::chrono;
using namespace std::placeholders;
using std::string;
using std::bind;

using boost::program_options::variables_map;
using boost::format;
using boost::ptr_list;

static void glfwErrorCB(int code, const char* msg);
static void glDebugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam);

format fmtTitle(APPNAME" - FPS: %d ::S: %d");

Novo::Novo(variables_map opts):
	window(new Window(800, 600, APPNAME))
{
	// GLFW
	glfwSetErrorCallback(glfwErrorCB);
	if(!glfwInit())
		throw std::runtime_error(string("GLFW init failed!"));

	// OpenGL / GLEW
	Window test(1,1, APPNAME " - GL Test");
	test.config.visible = false;
	test.config.debuging = true;
	test.open();
	test.bindContext();

	glbinding::Binding::initialize(false);

	window->config.resizable = false;
	window->config.GLversion.major = 3;
	window->config.GLversion.minor = 2;
	window->config.GLprofile = WindowConfig::Profile::CORE;
	window->config.debuging = true;
}

Novo::~Novo()
{
	window->close();
	glfwTerminate();
}

i32 Novo::run() {
	using namespace ::gl;

	window->open();
	window->bindContext();
	window->setVsync(false);

	glEnable(GL_DEBUG_OUTPUT);
	glDebugMessageCallback(&glDebugCB, this);

	///##################################
	///##################################
	sptr<Camera> cam(new Camera(vec3(0), 800, 600));
	screen = sptr<Framebuffer>(new Framebuffer(800, 600, cam, false));

	ptr_list<Drawable> objs;

	u32 size = 64;
	objs.push_back(new RandomCubes(size*size, size*size, vec3(0)));
	objs.push_back(new Origin(size, 8.0));

	///##################################
	///##################################

	func.mouse = bind(&Camera::mouseCallback, screen->getCamera().get(), _1, _2);

	con.focus = window->onFocus.connect(bind(&Novo::onFocus, this, _1));
	con.key = window->onKey.connect(bind(&Novo::onKey, this, _1, _2, _3, _4));
	con.mouse = window->onCursorMove.connect(func.mouse);
	con.mouseBTN = window->onMouseButton.connect(bind(&Novo::onMouseButton, this, _1, _2, _3));

	// Debug
	GLuint query = 0;
	glGenQueries(1, &query);

	steady_clock::time_point tick = steady_clock::now();
	i32 totalFrames = 0;
	while(window->isOpen()) {
		///##################################

		screen->getCamera()->moveUpdate(1);
		screen->bind();
		glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

		glBeginQuery(GL_SAMPLES_PASSED, query);

		///TODO glm::mat4_cast(glm::angleAxis(glm::sin((float)glfwGetTime()), vec3(0.0f, 0.0f, 1.0f)));

		for(Drawable& obj : objs)
			screen->render(&obj);

		glEndQuery(GL_SAMPLES_PASSED);

		//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

		screen->draw();

		///##################################
		window->present();
		window->poll();

		totalFrames++; // +1 Frame
		if(duration_cast<seconds>(steady_clock::now() - tick).count()) {

			GLuint samples;
			glGetQueryObjectuiv(query, GL_QUERY_RESULT, &samples);

			fmtTitle % totalFrames % samples;
			window->setTitle(fmtTitle.str());

			totalFrames = 0;
			tick = steady_clock::now();
		}
		//TODO: main loop sleep
	}

	return 0;
}

void Novo::onFocus(bool state) {
	if(!state) {
		con.mouse.disconnect();
		window->setCursorMode(CursorMode::NORMAL);
	}
}

void Novo::onMouseButton(i32 key, i32 action, i32 mod) {
	if(action == GLFW_PRESS && window->onCursorMove.empty()) {
		con.mouse = window->onCursorMove.connect(func.mouse);
		window->setCursorMode(CursorMode::DISABLED);
		return;
	}
}

void Novo::onKey(i32 key, i32 scancode, i32 action, i32 mod) {
	sptr<Camera> cam = screen->getCamera();
	vec3 camVal = cam->getVelocity();

		switch(action) {
		case GLFW_PRESS:
			switch(key) {
				case GLFW_KEY_W:
					camVal.z++;
					break;
				case GLFW_KEY_S:
					camVal.z--;
					break;
				case GLFW_KEY_D:
					camVal.x--;
					break;
				case GLFW_KEY_A:
					camVal.x++;
					break;
				case GLFW_KEY_SPACE:
					camVal.y++;
					break;
				case GLFW_KEY_C:
					camVal.y--;
					break;
				case GLFW_KEY_ESCAPE:
					if(!con.mouse.connected())
						window->close();
					else {
						con.mouse.disconnect();
						window->setCursorMode(CursorMode::NORMAL);
					}
					return;

				case GLFW_KEY_TAB:
					static int e;
					if(++e > 5) e = 0;
					screen->getProgram().setUniform("effectFlag", e);
					break;

			}
			break;
		case GLFW_RELEASE:
			switch(key) {
				case GLFW_KEY_W:
					camVal.z--;
					break;
				case GLFW_KEY_S:
					camVal.z++;
					break;
				case GLFW_KEY_D:
					camVal.x++;
					break;
				case GLFW_KEY_A:
					camVal.x--;
					break;
				case GLFW_KEY_SPACE:
					camVal.y--;
					break;
				case GLFW_KEY_C:
					camVal.y++;
					break;
			}
			break;
	}
	cam->setVelocity(camVal);
}

static void glfwErrorCB(int code, const char* msg)
{
	std::cerr << string("[glfw]: ") << code << ": " << string(msg) << std::endl;
	std::cerr.flush();
}

static void glDebugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void *userParam)
{
	using std::cerr;
	using std::endl;

	using namespace novo::gl::names;

	static format err("[%f] gl: %d::%s::%s::%s\n\t%s\n");

	cerr << (err % glfwGetTime() % id % debug[severity] % debug[source] % debug[type] % message);
	cerr.flush();
	/*
	cerr << "Stack trace:" << endl;
	cerr << saneStackTrace() << endl;
	cerr.flush();
	*/
	if(severity == GL_DEBUG_SEVERITY_HIGH)
		throw novo::NovoException("OpenGL critical error");
}
<|endoftext|>


<commit_before>// Copyright (c) 2018 alex v
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "ntpclient.h"
#include "random.h"
#include "timedata.h"
#include "util.h"

#include <sstream>
#include <iomanip>

using namespace boost;
using namespace boost::asio;

int64_t CNtpClient::getTimestamp()
{
    time_t timeRecv = -1;

    io_service io_service;

    LogPrint("ntp", "[NTP] Opening socket to NTP server %s.\n", sHostName);
    try
    {

        ip::udp::resolver resolver(io_service);
        ip::udp::resolver::query query(boost::asio::ip::udp::v4(), sHostName, "ntp");

        ip::udp::endpoint receiver_endpoint = *resolver.resolve(query);

        ip::udp::socket socket(io_service);
        socket.open(ip::udp::v4());

        boost::array<unsigned char, 48> sendBuf = {{010,0,0,0,0,0,0,0,0}};

        socket.send_to(boost::asio::buffer(sendBuf), receiver_endpoint);

        boost::array<unsigned long, 1024> recvBuf;
        ip::udp::endpoint sender_endpoint;

        try
        {

            fd_set fileDescriptorSet;
            struct timeval timeStruct;

            timeStruct.tv_sec = GetArg("-ntptimeout", DEFAULT_NTP_TIMEOUT);
            timeStruct.tv_usec = 0;
            FD_ZERO(&fileDescriptorSet);

            int nativeSocket = socket.native();

            FD_SET(nativeSocket,&fileDescriptorSet);

            select(nativeSocket+1,&fileDescriptorSet,NULL,NULL,&timeStruct);

            if(!FD_ISSET(nativeSocket,&fileDescriptorSet))
            {

                LogPrint("ntp", "[NTP] Could not read socket from NTP server %s (Read timeout)\n", sHostName);

            }
            else
            {

                socket.receive_from(boost::asio::buffer(recvBuf), sender_endpoint);

                std::ostringstream oss;
                oss << std::hex << std::setfill('0');
                oss << std::setw(2) << (unsigned int)recvBuf[4];

                timeRecv = ntohl((time_t)recvBuf[4]);
                timeRecv-= 2208988800U;  // Substract 01/01/1970 == 2208988800U

                LogPrint("ntp", "[NTP] Received timestamp: %ll  (Raw: 0x%s)\n", (uint64_t)timeRecv, oss.str());

            }

        }
        catch (std::exception& e)
        {

             LogPrintf("[NTP] Could not read clock from NTP server %s (%s)\n", sHostName, e.what());

        }

    }
    catch (std::exception& e)
    {

        LogPrintf("[NTP] Could not open socket to NTP server %s (%s)\n", sHostName, e.what());

    }

    assert (sizeof(int64_t) >= sizeof(time_t));

    return *reinterpret_cast<int64_t*>(&timeRecv);
}

bool NtpClockSync() {
    LogPrintf("[NTP] Starting clock sync...\n");

    std::vector<std::string> vNtpServers;
    std::vector<int64_t> vResults;

    if (mapMultiArgs["-ntpserver"].size() > 0)
    {
        vNtpServers = mapMultiArgs["-ntpserver"];
    }
    else
    {
        vNtpServers = vDefaultNtpServers;
    }

    string sReport = "";
    string sPrevServer = "";
    int64_t nPrevMeasure = -1;

    random_shuffle(vNtpServers.begin(), vNtpServers.end(), GetRandInt);

    unsigned int nMeasureCount = 0;

    for(unsigned int i = 0; i < vNtpServers.size(); i++)
    {
        string s = vNtpServers[i];
        CNtpClient ntpClient(s);
        int64_t nTimestamp = ntpClient.getTimestamp();
        if(nTimestamp > -1)
        {
            int64_t nClockDrift = GetTimeNow() - nTimestamp;
            nMeasureCount++;

            // We push if its the first entry
            if(nMeasureCount == 1)
            {
                vResults.push_back(nClockDrift);

                string sign = "";
                if(nClockDrift > 0)
                    sign = "+";

                sReport += s + "[" + sign + to_string(nClockDrift) + "sec.] ";
            }
            // or if n.measure is odd, including prev measure too
            else if (nMeasureCount % 2 == 1)
            {
                vResults.push_back(nClockDrift);

                string sign = "";
                if(nClockDrift > 0)
                    sign = "+";

                sReport += s + "[" + sign + to_string(nClockDrift) + "sec.] ";

                vResults.push_back(nPrevMeasure);

                sign = "";
                if(nPrevMeasure > 0)
                    sign = "+";

                sReport += sPrevServer + "[" + sign + to_string(nPrevMeasure) + "sec.] ";
            }
            else
            {
                nPrevMeasure = nClockDrift;
                sPrevServer = s;
            }

            if (vResults.size() >= 5)
                break;
        }
    }

    assert(vResults.size() % 2 == 1 || vResults.size() == 0);

    unsigned int nMin = GetArg("-ntpminmeasures", MINIMUM_NTP_MEASURE);

    if (vResults.size() >= nMin)
    {
        LogPrintf("[NTP] Measured: %s\n", sReport);

        static CMedianFilter<int64_t> vNtpTimeOffsets(vResults.size(), 0);

        for(unsigned int i = 0; i < vResults.size(); i++)
        {
            vNtpTimeOffsets.input(vResults[i]);
        }

        SetNtpTimeOffset(vNtpTimeOffsets.median());

        LogPrintf("[NTP] Calculated offset from median: %d\n", GetNtpTimeOffset());
        return true;
    }
    else
    {
        return false;
    }
}
<commit_msg>changed return type to int64_t<commit_after>// Copyright (c) 2018 alex v
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "ntpclient.h"
#include "random.h"
#include "timedata.h"
#include "util.h"

#include <sstream>
#include <iomanip>

using namespace boost;
using namespace boost::asio;

int64_t CNtpClient::getTimestamp()
{
    time_t timeRecv = -1;

    io_service io_service;

    LogPrint("ntp", "[NTP] Opening socket to NTP server %s.\n", sHostName);
    try
    {

        ip::udp::resolver resolver(io_service);
        ip::udp::resolver::query query(boost::asio::ip::udp::v4(), sHostName, "ntp");

        ip::udp::endpoint receiver_endpoint = *resolver.resolve(query);

        ip::udp::socket socket(io_service);
        socket.open(ip::udp::v4());

        boost::array<unsigned char, 48> sendBuf = {{010,0,0,0,0,0,0,0,0}};

        socket.send_to(boost::asio::buffer(sendBuf), receiver_endpoint);

        boost::array<unsigned long, 1024> recvBuf;
        ip::udp::endpoint sender_endpoint;

        try
        {

            fd_set fileDescriptorSet;
            struct timeval timeStruct;

            timeStruct.tv_sec = GetArg("-ntptimeout", DEFAULT_NTP_TIMEOUT);
            timeStruct.tv_usec = 0;
            FD_ZERO(&fileDescriptorSet);

            int nativeSocket = socket.native();

            FD_SET(nativeSocket,&fileDescriptorSet);

            select(nativeSocket+1,&fileDescriptorSet,NULL,NULL,&timeStruct);

            if(!FD_ISSET(nativeSocket,&fileDescriptorSet))
            {

                LogPrint("ntp", "[NTP] Could not read socket from NTP server %s (Read timeout)\n", sHostName);

            }
            else
            {

                socket.receive_from(boost::asio::buffer(recvBuf), sender_endpoint);

                std::ostringstream oss;
                oss << std::hex << std::setfill('0');
                oss << std::setw(2) << (unsigned int)recvBuf[4];

                timeRecv = ntohl((time_t)recvBuf[4]);
                LogPrint("ntp", "[NTP] timeRecv: %ll \n", (uint64_t)timeRecv);

                timeRecv-= 2208988800U;  // Substract 01/01/1970 == 2208988800U

                LogPrint("ntp", "[NTP] Received timestamp: %ll  (Raw: 0x%s)\n", (uint64_t)timeRecv, oss.str());

            }

        }
        catch (std::exception& e)
        {

             LogPrintf("[NTP] Could not read clock from NTP server %s (%s)\n", sHostName, e.what());

        }

    }
    catch (std::exception& e)
    {

        LogPrintf("[NTP] Could not open socket to NTP server %s (%s)\n", sHostName, e.what());

    }

    assert (sizeof(int64_t) >= sizeof(time_t));

    return (int64_t)timeRecv;
}

bool NtpClockSync() {
    LogPrintf("[NTP] Starting clock sync...\n");

    std::vector<std::string> vNtpServers;
    std::vector<int64_t> vResults;

    if (mapMultiArgs["-ntpserver"].size() > 0)
    {
        vNtpServers = mapMultiArgs["-ntpserver"];
    }
    else
    {
        vNtpServers = vDefaultNtpServers;
    }

    string sReport = "";
    string sPrevServer = "";
    int64_t nPrevMeasure = -1;

    random_shuffle(vNtpServers.begin(), vNtpServers.end(), GetRandInt);

    unsigned int nMeasureCount = 0;

    for(unsigned int i = 0; i < vNtpServers.size(); i++)
    {
        string s = vNtpServers[i];
        CNtpClient ntpClient(s);
        int64_t nTimestamp = ntpClient.getTimestamp();
        if(nTimestamp > -1)
        {
            int64_t nClockDrift = GetTimeNow() - nTimestamp;
            nMeasureCount++;

            // We push if its the first entry
            if(nMeasureCount == 1)
            {
                vResults.push_back(nClockDrift);

                string sign = "";
                if(nClockDrift > 0)
                    sign = "+";

                sReport += s + "[" + sign + to_string(nClockDrift) + "sec.] ";
            }
            // or if n.measure is odd, including prev measure too
            else if (nMeasureCount % 2 == 1)
            {
                vResults.push_back(nClockDrift);

                string sign = "";
                if(nClockDrift > 0)
                    sign = "+";

                sReport += s + "[" + sign + to_string(nClockDrift) + "sec.] ";

                vResults.push_back(nPrevMeasure);

                sign = "";
                if(nPrevMeasure > 0)
                    sign = "+";

                sReport += sPrevServer + "[" + sign + to_string(nPrevMeasure) + "sec.] ";
            }
            else
            {
                nPrevMeasure = nClockDrift;
                sPrevServer = s;
            }

            if (vResults.size() >= 5)
                break;
        }
    }

    assert(vResults.size() % 2 == 1 || vResults.size() == 0);

    unsigned int nMin = GetArg("-ntpminmeasures", MINIMUM_NTP_MEASURE);

    if (vResults.size() >= nMin)
    {
        LogPrintf("[NTP] Measured: %s\n", sReport);

        static CMedianFilter<int64_t> vNtpTimeOffsets(vResults.size(), 0);

        for(unsigned int i = 0; i < vResults.size(); i++)
        {
            vNtpTimeOffsets.input(vResults[i]);
        }

        SetNtpTimeOffset(vNtpTimeOffsets.median());

        LogPrintf("[NTP] Calculated offset from median: %d\n", GetNtpTimeOffset());
        return true;
    }
    else
    {
        return false;
    }
}
<|endoftext|>


<commit_before>#include <cstdio>

#include <s2list.h>

static bool _invalidated = true;

class Button
{
public:
	int m_x, m_y;
	int m_width, m_height;
	bool m_hovering;
	bool m_down;

public:
	Button(int x, int y, int width = 120, int height = 30)
	{
		m_x = x;
		m_y = y;
		m_width = width;
		m_height = height;
		m_hovering = false;
		m_down = false;
	}

	~Button()
	{
	}

	bool Contains(int x, int y)
	{
		return (x >= m_x && x < m_x + m_width) && (y >= m_y && y < m_y + m_height);
	}

	void Draw(NVGcontext* vg)
	{
		nvgBeginPath(vg);
		nvgRect(vg, m_x, m_y, m_width, m_height);
		if (m_hovering) {
			if (m_down) {
				nvgFillColor(vg, nvgRGBA(255, 127, 0, 255));
			} else {
				nvgFillColor(vg, nvgRGBA(255, 255, 127, 255));
			}
		} else {
			nvgFillColor(vg, nvgRGBA(0, 127, 255, 255));
		}
		nvgFill(vg);
	}

	void MouseEnter()
	{
		m_hovering = true;
		m_down = false;
		_invalidated = true;
	}

	void MouseLeave()
	{
		m_hovering = false;
		m_down = false;
		_invalidated = true;
	}

	void MouseDown(int button)
	{
		if (button == GLFW_MOUSE_BUTTON_LEFT) {
			m_down = true;
			_invalidated = true;
		}
	}

	void MouseUp(int button)
	{
		if (button == GLFW_MOUSE_BUTTON_LEFT) {
			if (m_down) {
				Click();
			}
			m_down = false;
			_invalidated = true;
		}
	}

	void Click()
	{
		printf("Button was clicked!\n");
		m_x += 10;
		_invalidated = true;
	}
};

static s2::list<Button*> _buttons;

int main()
{
	/*
	lay_context lay;
	lay_init_context(&lay);

	lay_id root = lay_item(&lay);
	lay_set_size_xy(&lay, root, 1024, 768);
	lay_set_contain(&lay, root, LAY_ROW);

	lay_id master_list = lay_item(&lay);
	lay_set_size_xy(&lay, master_list, 400, 0);
	lay_set_behave(&lay, master_list, LAY_VFILL);
	lay_set_contain(&lay, master_list, LAY_COLUMN);
	lay_insert(&lay, root, master_list);

	lay_id content_view = lay_item(&lay);
	lay_set_behave(&lay, content_view, LAY_FILL);

	lay_run_context(&lay);
	lay_vec4 master_list_rect = lay_get_rect(&lay, master_list);
	lay_vec4 content_view_rect = lay_get_rect(&lay, content_view);

	printf("master: %d,%d -- %d,%d\n", master_list_rect[0], master_list_rect[1], master_list_rect[2], master_list_rect[3]);
	printf("content: %d,%d -- %d,%d\n", content_view_rect[0], content_view_rect[1], content_view_rect[2], content_view_rect[3]);

	//lay_reset_context(&lay);
	lay_destroy_context(&lay);

	return 0;
	*/

	_buttons.add(new Button(100, 100));
	_buttons.add(new Button(100, 150));
	_buttons.add(new Button(100, 200));
	_buttons.add(new Button(100, 250));

	return 0;
}
<commit_msg>Remove prototype<commit_after><|endoftext|>