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

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

// Blueberry
#include <berryISelectionService.h>
#include <berryIWorkbenchWindow.h>

// Qmitk
#include "QmitkRegionGrowingView.h"

//! [cpp-includes]
// Qmitk
#include "QmitkPointListWidget.h"
#include "QmitkRenderWindow.h"

// MITK
#include "mitkImageAccessByItk.h"
#include "mitkITKImageImport.h"
#include "mitkProperties.h"
#include "mitkColorProperty.h"

// ITK
#include <itkConnectedThresholdImageFilter.h>
//! [cpp-includes]

// Qt
#include <QMessageBox>

const std::string QmitkRegionGrowingView::VIEW_ID = "org.mitk.views.example.regiongrowing";

QmitkRegionGrowingView::QmitkRegionGrowingView()
  : m_PointListWidget(NULL)
{
}

void QmitkRegionGrowingView::SetFocus()
{
  m_Controls.buttonPerformImageProcessing->setFocus();
}

void QmitkRegionGrowingView::CreateQtPartControl( QWidget *parent )
{
  // create GUI widgets from the Qt Designer's .ui file
  m_Controls.setupUi( parent );
  connect( m_Controls.buttonPerformImageProcessing, SIGNAL(clicked()), this, SLOT(DoImageProcessing()) );

  //! [cpp-createqtpartcontrol]
  // create a QmitkPointListWidget and add it to the widget created from .ui file
  m_PointListWidget = new QmitkPointListWidget();
  m_Controls.verticalLayout->addWidget(m_PointListWidget, 1);

  // retrieve a possibly existing IRenderWindowPart
  if (mitk::IRenderWindowPart* renderWindowPart = GetRenderWindowPart())
  {
    // let the point set widget know about the render window part (crosshair updates)
    RenderWindowPartActivated(renderWindowPart);
  }

  // create a new DataNode containing a PointSet with some interaction
  m_PointSet = mitk::PointSet::New();
  mitk::DataNode::Pointer pointSetNode = mitk::DataNode::New();
  pointSetNode->SetData( m_PointSet );
  pointSetNode->SetName("seed points for region growing");
  pointSetNode->SetProperty("helper object", mitk::BoolProperty::New(true) );
  pointSetNode->SetProperty("layer", mitk::IntProperty::New(1024) );

  // add the pointset to the data storage (for rendering and access by other modules)
  GetDataStorage()->Add( pointSetNode );

  // tell the GUI widget about the point set
  m_PointListWidget->SetPointSetNode( pointSetNode );
  //! [cpp-createqtpartcontrol]
}

void QmitkRegionGrowingView::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*source*/,
                                             const QList<mitk::DataNode::Pointer>& nodes )
{
  // iterate all selected objects, adjust warning visibility
  foreach( mitk::DataNode::Pointer node, nodes )
  {
    if( node.IsNotNull() && dynamic_cast<mitk::Image*>(node->GetData()) )
    {
      m_Controls.labelWarning->setVisible( false );
      m_Controls.buttonPerformImageProcessing->setEnabled( true );
      return;
    }
  }

  m_Controls.labelWarning->setVisible( true );
  m_Controls.buttonPerformImageProcessing->setEnabled( false );
}

void QmitkRegionGrowingView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart)
{
  // let the point set widget know about the slice navigation controllers
  // in the active render window part (crosshair updates)
  foreach(QmitkRenderWindow* renderWindow, renderWindowPart->GetQmitkRenderWindows().values())
  {
    m_PointListWidget->AddSliceNavigationController(renderWindow->GetSliceNavigationController());
  }
}

void QmitkRegionGrowingView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart)
{
  foreach(QmitkRenderWindow* renderWindow, renderWindowPart->GetQmitkRenderWindows().values())
  {
    m_PointListWidget->RemoveSliceNavigationController(renderWindow->GetSliceNavigationController());
  }
}

void QmitkRegionGrowingView::DoImageProcessing()
{
  QList<mitk::DataNode::Pointer> nodes = this->GetDataManagerSelection();
  if (nodes.empty()) return;

  mitk::DataNode* node = nodes.front();

  if (!node)
  {
    // Nothing selected. Inform the user and return
    QMessageBox::information( NULL, "Template", "Please load and select an image before starting image processing.");
    return;
  }

  // here we have a valid mitk::DataNode

  // a node itself is not very useful, we need its data item (the image)
  mitk::BaseData* data = node->GetData();
  if (data)
  {
    // test if this data item is an image or not (could also be a surface or something totally different)
    mitk::Image* image = dynamic_cast<mitk::Image*>( data );
    if (image)
    {
      //! [cpp-doimageprocessing]
      // So we have an image. Let's see if the user has set some seed points already
      if (m_PointSet->GetSize() == 0)
      {
        // no points there. Not good for region growing
        QMessageBox::information( NULL,
                                  "Region growing functionality",
                                  "Please set some seed points inside the image first.\n"
                                  "(hold Shift key and click left mouse button inside the image.)"
                                  );
        return;
      }

      // actually perform region growing. Here we have both an image and some seed points
      AccessByItk_1( image, ItkImageProcessing, image->GetGeometry() ) // some magic to call the correctly templated function
      //! [cpp-doimageprocessing]
    }
  }
}

//! [cpp-itkimageaccess]
template < typename TPixel, unsigned int VImageDimension >
void QmitkRegionGrowingView::ItkImageProcessing( itk::Image< TPixel, VImageDimension >* itkImage, mitk::BaseGeometry* imageGeometry )
{
  typedef itk::Image< TPixel, VImageDimension > InputImageType;
  typedef typename InputImageType::IndexType    IndexType;

  // instantiate an ITK region growing filter, set its parameters
  typedef itk::ConnectedThresholdImageFilter<InputImageType, InputImageType> RegionGrowingFilterType;
  typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New();
  regionGrower->SetInput( itkImage ); // don't forget this

  // determine a thresholding interval
  IndexType seedIndex;
  TPixel min( std::numeric_limits<TPixel>::max() );
  TPixel max( std::numeric_limits<TPixel>::min() );
  mitk::PointSet::PointsContainer* points = m_PointSet->GetPointSet()->GetPoints();
  for ( mitk::PointSet::PointsConstIterator pointsIterator = points->Begin();
        pointsIterator != points->End();
        ++pointsIterator )
  {
    // first test if this point is inside the image at all
    if ( !imageGeometry->IsInside( pointsIterator.Value()) )
    {
      continue;
    }

    // convert world coordinates to image indices
    imageGeometry->WorldToIndex( pointsIterator.Value(), seedIndex);

    // get the pixel value at this point
    TPixel currentPixelValue = itkImage->GetPixel( seedIndex );

    // adjust minimum and maximum values
    if (currentPixelValue > max)
      max = currentPixelValue;

    if (currentPixelValue < min)
      min = currentPixelValue;

    regionGrower->AddSeed( seedIndex );
  }

  MITK_INFO << "Values between " << min << " and " << max;

  min -= 30;
  max += 30;

  // set thresholds and execute filter
  regionGrower->SetLower( min );
  regionGrower->SetUpper( max );

  regionGrower->Update();

  mitk::Image::Pointer resultImage = mitk::ImportItkImage( regionGrower->GetOutput() );
  mitk::DataNode::Pointer newNode = mitk::DataNode::New();
  newNode->SetData( resultImage );

  // set some properties
  newNode->SetProperty("binary", mitk::BoolProperty::New(true));
  newNode->SetProperty("name", mitk::StringProperty::New("dumb segmentation"));
  newNode->SetProperty("color", mitk::ColorProperty::New(1.0,0.0,0.0));
  newNode->SetProperty("volumerendering", mitk::BoolProperty::New(true));
  newNode->SetProperty("layer", mitk::IntProperty::New(1));
  newNode->SetProperty("opacity", mitk::FloatProperty::New(0.5));

  // add result to data tree
  this->GetDataStorage()->Add( newNode );
  mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
//! [cpp-itkimageaccess]
<commit_msg>Replace ImportItkImage by CastToMitkImage to allow out-of-scope memory management.<commit_after>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

// Blueberry
#include <berryISelectionService.h>
#include <berryIWorkbenchWindow.h>

// Qmitk
#include "QmitkRegionGrowingView.h"

//! [cpp-includes]
// Qmitk
#include "QmitkPointListWidget.h"
#include "QmitkRenderWindow.h"

// MITK
#include "mitkImageAccessByItk.h"
#include "mitkITKImageImport.h"
#include "mitkProperties.h"
#include "mitkColorProperty.h"
#include "mitkImageCast.h"

// ITK
#include <itkConnectedThresholdImageFilter.h>
//! [cpp-includes]

// Qt
#include <QMessageBox>

const std::string QmitkRegionGrowingView::VIEW_ID = "org.mitk.views.example.regiongrowing";

QmitkRegionGrowingView::QmitkRegionGrowingView()
  : m_PointListWidget(NULL)
{
}

void QmitkRegionGrowingView::SetFocus()
{
  m_Controls.buttonPerformImageProcessing->setFocus();
}

void QmitkRegionGrowingView::CreateQtPartControl( QWidget *parent )
{
  // create GUI widgets from the Qt Designer's .ui file
  m_Controls.setupUi( parent );
  connect( m_Controls.buttonPerformImageProcessing, SIGNAL(clicked()), this, SLOT(DoImageProcessing()) );

  //! [cpp-createqtpartcontrol]
  // create a QmitkPointListWidget and add it to the widget created from .ui file
  m_PointListWidget = new QmitkPointListWidget();
  m_Controls.verticalLayout->addWidget(m_PointListWidget, 1);

  // retrieve a possibly existing IRenderWindowPart
  if (mitk::IRenderWindowPart* renderWindowPart = GetRenderWindowPart())
  {
    // let the point set widget know about the render window part (crosshair updates)
    RenderWindowPartActivated(renderWindowPart);
  }

  // create a new DataNode containing a PointSet with some interaction
  m_PointSet = mitk::PointSet::New();
  mitk::DataNode::Pointer pointSetNode = mitk::DataNode::New();
  pointSetNode->SetData( m_PointSet );
  pointSetNode->SetName("seed points for region growing");
  pointSetNode->SetProperty("helper object", mitk::BoolProperty::New(true) );
  pointSetNode->SetProperty("layer", mitk::IntProperty::New(1024) );

  // add the pointset to the data storage (for rendering and access by other modules)
  GetDataStorage()->Add( pointSetNode );

  // tell the GUI widget about the point set
  m_PointListWidget->SetPointSetNode( pointSetNode );
  //! [cpp-createqtpartcontrol]
}

void QmitkRegionGrowingView::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*source*/,
                                             const QList<mitk::DataNode::Pointer>& nodes )
{
  // iterate all selected objects, adjust warning visibility
  foreach( mitk::DataNode::Pointer node, nodes )
  {
    if( node.IsNotNull() && dynamic_cast<mitk::Image*>(node->GetData()) )
    {
      m_Controls.labelWarning->setVisible( false );
      m_Controls.buttonPerformImageProcessing->setEnabled( true );
      return;
    }
  }

  m_Controls.labelWarning->setVisible( true );
  m_Controls.buttonPerformImageProcessing->setEnabled( false );
}

void QmitkRegionGrowingView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart)
{
  // let the point set widget know about the slice navigation controllers
  // in the active render window part (crosshair updates)
  foreach(QmitkRenderWindow* renderWindow, renderWindowPart->GetQmitkRenderWindows().values())
  {
    m_PointListWidget->AddSliceNavigationController(renderWindow->GetSliceNavigationController());
  }
}

void QmitkRegionGrowingView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart)
{
  foreach(QmitkRenderWindow* renderWindow, renderWindowPart->GetQmitkRenderWindows().values())
  {
    m_PointListWidget->RemoveSliceNavigationController(renderWindow->GetSliceNavigationController());
  }
}

void QmitkRegionGrowingView::DoImageProcessing()
{
  QList<mitk::DataNode::Pointer> nodes = this->GetDataManagerSelection();
  if (nodes.empty()) return;

  mitk::DataNode* node = nodes.front();

  if (!node)
  {
    // Nothing selected. Inform the user and return
    QMessageBox::information( NULL, "Template", "Please load and select an image before starting image processing.");
    return;
  }

  // here we have a valid mitk::DataNode

  // a node itself is not very useful, we need its data item (the image)
  mitk::BaseData* data = node->GetData();
  if (data)
  {
    // test if this data item is an image or not (could also be a surface or something totally different)
    mitk::Image* image = dynamic_cast<mitk::Image*>( data );
    if (image)
    {
      //! [cpp-doimageprocessing]
      // So we have an image. Let's see if the user has set some seed points already
      if (m_PointSet->GetSize() == 0)
      {
        // no points there. Not good for region growing
        QMessageBox::information( NULL,
                                  "Region growing functionality",
                                  "Please set some seed points inside the image first.\n"
                                  "(hold Shift key and click left mouse button inside the image.)"
                                  );
        return;
      }

      // actually perform region growing. Here we have both an image and some seed points
      AccessByItk_1( image, ItkImageProcessing, image->GetGeometry() ) // some magic to call the correctly templated function
      //! [cpp-doimageprocessing]
    }
  }
}

//! [cpp-itkimageaccess]
template < typename TPixel, unsigned int VImageDimension >
void QmitkRegionGrowingView::ItkImageProcessing( itk::Image< TPixel, VImageDimension >* itkImage, mitk::BaseGeometry* imageGeometry )
{
  typedef itk::Image< TPixel, VImageDimension > InputImageType;
  typedef typename InputImageType::IndexType    IndexType;

  // instantiate an ITK region growing filter, set its parameters
  typedef itk::ConnectedThresholdImageFilter<InputImageType, InputImageType> RegionGrowingFilterType;
  typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New();
  regionGrower->SetInput( itkImage ); // don't forget this

  // determine a thresholding interval
  IndexType seedIndex;
  TPixel min( std::numeric_limits<TPixel>::max() );
  TPixel max( std::numeric_limits<TPixel>::min() );
  mitk::PointSet::PointsContainer* points = m_PointSet->GetPointSet()->GetPoints();
  for ( mitk::PointSet::PointsConstIterator pointsIterator = points->Begin();
        pointsIterator != points->End();
        ++pointsIterator )
  {
    // first test if this point is inside the image at all
    if ( !imageGeometry->IsInside( pointsIterator.Value()) )
    {
      continue;
    }

    // convert world coordinates to image indices
    imageGeometry->WorldToIndex( pointsIterator.Value(), seedIndex);

    // get the pixel value at this point
    TPixel currentPixelValue = itkImage->GetPixel( seedIndex );

    // adjust minimum and maximum values
    if (currentPixelValue > max)
      max = currentPixelValue;

    if (currentPixelValue < min)
      min = currentPixelValue;

    regionGrower->AddSeed( seedIndex );
  }

  MITK_INFO << "Values between " << min << " and " << max;

  min -= 30;
  max += 30;

  // set thresholds and execute filter
  regionGrower->SetLower( min );
  regionGrower->SetUpper( max );

  regionGrower->Update();

  mitk::Image::Pointer resultImage;
  mitk::CastToMitkImage( regionGrower->GetOutput(), resultImage );
  mitk::DataNode::Pointer newNode = mitk::DataNode::New();
  newNode->SetData( resultImage );

  // set some properties
  newNode->SetProperty("binary", mitk::BoolProperty::New(true));
  newNode->SetProperty("name", mitk::StringProperty::New("dumb segmentation"));
  newNode->SetProperty("color", mitk::ColorProperty::New(1.0,0.0,0.0));
  newNode->SetProperty("volumerendering", mitk::BoolProperty::New(true));
  newNode->SetProperty("layer", mitk::IntProperty::New(1));
  newNode->SetProperty("opacity", mitk::FloatProperty::New(0.5));

  // add result to data tree
  this->GetDataStorage()->Add( newNode );
  mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
//! [cpp-itkimageaccess]
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtTest/QtTest>
#include <QDebug>

#include <multimedia/qmediaserviceprovider.h>

class MockMediaServiceProvider : public QMediaServiceProvider
{
    QMediaService* requestService(const QByteArray &type, const QMediaServiceProviderHint &)
    {
        Q_UNUSED(type);
        return 0;
    }

    void releaseService(QMediaService *service)
    {
        Q_UNUSED(service);
    }
};

class tst_QMediaServiceProvider : public QObject
{
    Q_OBJECT

private slots:
    void testDefaultProviderAvailable();
    void testObtainService();
    void testHasSupport();
    void testDevices();
    void testProviderHints();
};

void tst_QMediaServiceProvider::testDefaultProviderAvailable()
{
    // Must always be a default provider available
    QVERIFY(QMediaServiceProvider::defaultServiceProvider() != 0);
}

void tst_QMediaServiceProvider::testObtainService()
{
    QMediaServiceProvider *provider = QMediaServiceProvider::defaultServiceProvider();

    if (provider == 0)
        QSKIP("No default provider", SkipSingle);

    QMediaService *service = 0;

    // Player
    service = provider->requestService(Q_MEDIASERVICE_MEDIAPLAYER);
    QVERIFY(service != 0);
    provider->releaseService(service);
}

void tst_QMediaServiceProvider::testHasSupport()
{
    MockMediaServiceProvider mockProvider;
    QCOMPARE(mockProvider.hasSupport(QByteArray(Q_MEDIASERVICE_MEDIAPLAYER), "video/ogv", QStringList()),
             QtMedia::MaybeSupported);

    QMediaServiceProvider *provider = QMediaServiceProvider::defaultServiceProvider();

    if (provider == 0)
        QSKIP("No default provider", SkipSingle);

    QCOMPARE(provider->hasSupport(QByteArray(Q_MEDIASERVICE_MEDIAPLAYER), "video/ogv", QStringList()),
             QtMedia::MaybeSupported);

    QCOMPARE(provider->hasSupport(QByteArray("non existing service"), "video/ogv", QStringList()),
             QtMedia::NotSupported);
}

void tst_QMediaServiceProvider::testDevices()
{
    MockMediaServiceProvider mockProvider;
    QVERIFY(mockProvider.devices(QByteArray(Q_MEDIASERVICE_CAMERA)).isEmpty());
    QVERIFY(mockProvider.deviceDescription(QByteArray(Q_MEDIASERVICE_CAMERA),
                                           QByteArray()).isEmpty());

    QMediaServiceProvider *provider = QMediaServiceProvider::defaultServiceProvider();

    if (provider == 0)
        QSKIP("No default provider", SkipSingle);

    //how to test this properly?
    //it depends on plugins installed
    //QVERIFY(!provider->devices(QByteArray(Q_MEDIASERVICE_CAMERA)).isEmpty());

    QVERIFY(provider->devices(QByteArray("non existing service")).isEmpty());
}

void tst_QMediaServiceProvider::testProviderHints()
{
    {
        QMediaServiceProviderHint hint;
        QVERIFY(hint.isNull());
        QCOMPARE(hint.type(), QMediaServiceProviderHint::Null);
        QVERIFY(hint.device().isEmpty());
        QVERIFY(hint.mimeType().isEmpty());
        QVERIFY(hint.codecs().isEmpty());
        QCOMPARE(hint.features(), 0);
    }

    {
        QByteArray deviceName(QByteArray("testDevice"));
        QMediaServiceProviderHint hint(deviceName);
        QVERIFY(!hint.isNull());
        QCOMPARE(hint.type(), QMediaServiceProviderHint::Device);
        QCOMPARE(hint.device(), deviceName);
        QVERIFY(hint.mimeType().isEmpty());
        QVERIFY(hint.codecs().isEmpty());
        QCOMPARE(hint.features(), 0);
    }

    {
        QMediaServiceProviderHint hint(QMediaServiceProviderHint::LowLatencyPlayback);
        QVERIFY(!hint.isNull());
        QCOMPARE(hint.type(), QMediaServiceProviderHint::SupportedFeatures);
        QVERIFY(hint.device().isEmpty());
        QVERIFY(hint.mimeType().isEmpty());
        QVERIFY(hint.codecs().isEmpty());
        QCOMPARE(hint.features(), QMediaServiceProviderHint::LowLatencyPlayback);
    }

    {
        QString mimeType(QLatin1String("video/ogg"));
        QStringList codecs;
        codecs << "theora" << "vorbis";

        QMediaServiceProviderHint hint(mimeType,codecs);
        QVERIFY(!hint.isNull());
        QCOMPARE(hint.type(), QMediaServiceProviderHint::ContentType);
        QVERIFY(hint.device().isEmpty());
        QCOMPARE(hint.mimeType(), mimeType);
        QCOMPARE(hint.codecs(), codecs);

        QMediaServiceProviderHint hint2(hint);

        QVERIFY(!hint2.isNull());
        QCOMPARE(hint2.type(), QMediaServiceProviderHint::ContentType);
        QVERIFY(hint2.device().isEmpty());
        QCOMPARE(hint2.mimeType(), mimeType);
        QCOMPARE(hint2.codecs(), codecs);

        QMediaServiceProviderHint hint3;
        QVERIFY(hint3.isNull());
        hint3 = hint;
        QVERIFY(!hint3.isNull());
        QCOMPARE(hint3.type(), QMediaServiceProviderHint::ContentType);
        QVERIFY(hint3.device().isEmpty());
        QCOMPARE(hint3.mimeType(), mimeType);
        QCOMPARE(hint3.codecs(), codecs);

        QCOMPARE(hint, hint2);
        QCOMPARE(hint3, hint2);

        QMediaServiceProviderHint hint4(mimeType,codecs);
        QCOMPARE(hint, hint4);

        QMediaServiceProviderHint hint5(mimeType,QStringList());
        QVERIFY(hint != hint5);
    }
}

QTEST_MAIN(tst_QMediaServiceProvider)

#include "tst_qmediaserviceprovider.moc"
<commit_msg>Test for QMediaServiceProvider::hasSupport() with set of mock plugins<commit_after>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtTest/QtTest>
#include <QDebug>

#include <multimedia/qmediaserviceprovider.h>
#include <multimedia/qmediaserviceproviderplugin.h>
#include <multimedia/qmediapluginloader_p.h>
#include <multimedia/qmediaobject.h>
#include <multimedia/qmediaservice.h>
#include <multimedia/qmediaplayer.h>

class MockMediaService : public QMediaService
{
    Q_OBJECT
public:
    MockMediaService(const QString& name, QObject *parent = 0) : QMediaService(parent)
    { setObjectName(name); }
    ~MockMediaService() {}

    QMediaControl* control(const char *) const {return 0;}
};

class MockServicePlugin1 : public QMediaServiceProviderPlugin,
                           public QMediaServiceSupportedFormatsInterface,
                           public QMediaServiceSupportedDevicesInterface
{
    Q_OBJECT
    Q_INTERFACES(QMediaServiceSupportedFormatsInterface)
    Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
public:
    QStringList keys() const
    {
        return QStringList() <<
                QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER) <<
                QLatin1String(Q_MEDIASERVICE_CAMERA);
    }

    QMediaService* create(QString const& key)
    {
        if (keys().contains(key))
            return new MockMediaService("MockServicePlugin1");
        else
            return 0;
    }

    void release(QMediaService *service)
    {
        delete service;
    }

    QtMedia::SupportEstimate hasSupport(const QString &mimeType, const QStringList& codecs) const
    {
        if (codecs.contains(QLatin1String("mpeg4")))
            return QtMedia::NotSupported;

        if (mimeType == "audio/ogg") {
            return QtMedia::ProbablySupported;
        }

        return QtMedia::MaybeSupported;
    }

    QList<QByteArray> devices(const QByteArray &service) const
    {
        QList<QByteArray> res;
        if (service == QByteArray(Q_MEDIASERVICE_CAMERA))
            res << "camera1" << "camera2";
        return res;
    }

    QString deviceDescription(const QByteArray &service, const QByteArray &device)
    {
        if (devices(service).contains(device))
            return QString(device)+" description";
        else
            return QString();
    }
};

class MockServicePlugin2 : public QMediaServiceProviderPlugin,
                            public QMediaServiceSupportedFormatsInterface
{
    Q_OBJECT
    Q_INTERFACES(QMediaServiceSupportedFormatsInterface)
public:
    QStringList keys() const
    {
        return QStringList() << QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER);
    }

    QMediaService* create(QString const& key)
    {
        if (keys().contains(key))
            return new MockMediaService("MockServicePlugin2");
        else
            return 0;
    }

    void release(QMediaService *service)
    {
        delete service;
    }

    QtMedia::SupportEstimate hasSupport(const QString &mimeType, const QStringList& codecs) const
    {
        Q_UNUSED(codecs);

        if (mimeType == "audio/wav")
            return QtMedia::PreferedService;

        return QtMedia::NotSupported;
    }
};

class MockServicePlugin3 : public QMediaServiceProviderPlugin,
                            public QMediaServiceSupportedDevicesInterface
{
    Q_OBJECT
    Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
public:
    QStringList keys() const
    {
        return QStringList() <<
               QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER) <<
               QLatin1String(Q_MEDIASERVICE_CAMERA) <<
               QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE);
    }

    QMediaService* create(QString const& key)
    {
        if (keys().contains(key))
            return new MockMediaService("MockServicePlugin3");
        else
            return 0;
    }

    void release(QMediaService *service)
    {
        delete service;
    }

    QList<QByteArray> devices(const QByteArray &service) const
    {
        QList<QByteArray> res;
        if (service == QByteArray(Q_MEDIASERVICE_CAMERA))
            res << "camera3" << "camera4";
        else if (service == QByteArray(Q_MEDIASERVICE_AUDIOSOURCE))
            res << "audiosource1" << "audiosource2";

        return res;
    }

    QString deviceDescription(const QByteArray &service, const QByteArray &device)
    {
        if (devices(service).contains(device))
            return QString(device)+" description";
        else
            return QString();
    }
};

class MockMediaServiceProvider : public QMediaServiceProvider
{
    QMediaService* requestService(const QByteArray &type, const QMediaServiceProviderHint &)
    {
        Q_UNUSED(type);
        return 0;
    }

    void releaseService(QMediaService *service)
    {
        Q_UNUSED(service);
    }
};

class tst_QMediaServiceProvider : public QObject
{
    Q_OBJECT

public slots:
    void initTestCase();

private slots:
    void testDefaultProviderAvailable();
    void testObtainService();
    void testHasSupport();
    void testDevices();
    void testProviderHints();

private:
    QObjectList plugins;
};

void tst_QMediaServiceProvider::initTestCase()
{
    plugins << new MockServicePlugin1;
    plugins << new MockServicePlugin2;
    plugins << new MockServicePlugin3;

    QMediaPluginLoader::setStaticPlugins(QLatin1String("/mediaservice"), plugins);
}

void tst_QMediaServiceProvider::testDefaultProviderAvailable()
{
    // Must always be a default provider available
    QVERIFY(QMediaServiceProvider::defaultServiceProvider() != 0);
}

void tst_QMediaServiceProvider::testObtainService()
{
    QMediaServiceProvider *provider = QMediaServiceProvider::defaultServiceProvider();

    if (provider == 0)
        QSKIP("No default provider", SkipSingle);

    QMediaService *service = 0;

    QTest::ignoreMessage(QtWarningMsg, "Load static plugins for \"/mediaservice/\" ");
    // Player
    service = provider->requestService(Q_MEDIASERVICE_MEDIAPLAYER);
    QVERIFY(service != 0);
    provider->releaseService(service);
}

void tst_QMediaServiceProvider::testHasSupport()
{
    MockMediaServiceProvider mockProvider;
    QCOMPARE(mockProvider.hasSupport(QByteArray(Q_MEDIASERVICE_MEDIAPLAYER), "video/ogv", QStringList()),
             QtMedia::MaybeSupported);

    QMediaServiceProvider *provider = QMediaServiceProvider::defaultServiceProvider();

    if (provider == 0)
        QSKIP("No default provider", SkipSingle);

    QCOMPARE(provider->hasSupport(QByteArray(Q_MEDIASERVICE_MEDIAPLAYER), "video/ogv", QStringList()),
             QtMedia::MaybeSupported);

    QCOMPARE(provider->hasSupport(QByteArray(Q_MEDIASERVICE_MEDIAPLAYER), "audio/ogg", QStringList()),
             QtMedia::ProbablySupported);

    //while the service returns PreferredService, provider should return ProbablySupported
    QCOMPARE(provider->hasSupport(QByteArray(Q_MEDIASERVICE_MEDIAPLAYER), "audio/wav", QStringList()),
             QtMedia::ProbablySupported);

    //even while all the plugins with "hasSupport" returned NotSupported,
    //MockServicePlugin3 has no "hasSupport" interface, so MaybeSupported
    QCOMPARE(provider->hasSupport(QByteArray(Q_MEDIASERVICE_MEDIAPLAYER), "video/avi",
                                  QStringList() << "mpeg4"),
             QtMedia::MaybeSupported);

    QCOMPARE(provider->hasSupport(QByteArray("non existing service"), "video/ogv", QStringList()),
             QtMedia::NotSupported);

    QCOMPARE(QMediaPlayer::hasSupport("video/ogv"), QtMedia::MaybeSupported);
    QCOMPARE(QMediaPlayer::hasSupport("audio/ogg"), QtMedia::ProbablySupported);
    QCOMPARE(QMediaPlayer::hasSupport("audio/wav"), QtMedia::ProbablySupported);
}

void tst_QMediaServiceProvider::testDevices()
{
    MockMediaServiceProvider mockProvider;
    QVERIFY(mockProvider.devices(QByteArray(Q_MEDIASERVICE_CAMERA)).isEmpty());
    QVERIFY(mockProvider.deviceDescription(QByteArray(Q_MEDIASERVICE_CAMERA),
                                           QByteArray()).isEmpty());

    QMediaServiceProvider *provider = QMediaServiceProvider::defaultServiceProvider();

    QVERIFY(!provider->devices(QByteArray(Q_MEDIASERVICE_CAMERA)).isEmpty());

    QVERIFY(provider->devices(QByteArray("non existing service")).isEmpty());
}

void tst_QMediaServiceProvider::testProviderHints()
{
    {
        QMediaServiceProviderHint hint;
        QVERIFY(hint.isNull());
        QCOMPARE(hint.type(), QMediaServiceProviderHint::Null);
        QVERIFY(hint.device().isEmpty());
        QVERIFY(hint.mimeType().isEmpty());
        QVERIFY(hint.codecs().isEmpty());
        QCOMPARE(hint.features(), 0);
    }

    {
        QByteArray deviceName(QByteArray("testDevice"));
        QMediaServiceProviderHint hint(deviceName);
        QVERIFY(!hint.isNull());
        QCOMPARE(hint.type(), QMediaServiceProviderHint::Device);
        QCOMPARE(hint.device(), deviceName);
        QVERIFY(hint.mimeType().isEmpty());
        QVERIFY(hint.codecs().isEmpty());
        QCOMPARE(hint.features(), 0);
    }

    {
        QMediaServiceProviderHint hint(QMediaServiceProviderHint::LowLatencyPlayback);
        QVERIFY(!hint.isNull());
        QCOMPARE(hint.type(), QMediaServiceProviderHint::SupportedFeatures);
        QVERIFY(hint.device().isEmpty());
        QVERIFY(hint.mimeType().isEmpty());
        QVERIFY(hint.codecs().isEmpty());
        QCOMPARE(hint.features(), QMediaServiceProviderHint::LowLatencyPlayback);
    }

    {
        QString mimeType(QLatin1String("video/ogg"));
        QStringList codecs;
        codecs << "theora" << "vorbis";

        QMediaServiceProviderHint hint(mimeType,codecs);
        QVERIFY(!hint.isNull());
        QCOMPARE(hint.type(), QMediaServiceProviderHint::ContentType);
        QVERIFY(hint.device().isEmpty());
        QCOMPARE(hint.mimeType(), mimeType);
        QCOMPARE(hint.codecs(), codecs);

        QMediaServiceProviderHint hint2(hint);

        QVERIFY(!hint2.isNull());
        QCOMPARE(hint2.type(), QMediaServiceProviderHint::ContentType);
        QVERIFY(hint2.device().isEmpty());
        QCOMPARE(hint2.mimeType(), mimeType);
        QCOMPARE(hint2.codecs(), codecs);

        QMediaServiceProviderHint hint3;
        QVERIFY(hint3.isNull());
        hint3 = hint;
        QVERIFY(!hint3.isNull());
        QCOMPARE(hint3.type(), QMediaServiceProviderHint::ContentType);
        QVERIFY(hint3.device().isEmpty());
        QCOMPARE(hint3.mimeType(), mimeType);
        QCOMPARE(hint3.codecs(), codecs);

        QCOMPARE(hint, hint2);
        QCOMPARE(hint3, hint2);

        QMediaServiceProviderHint hint4(mimeType,codecs);
        QCOMPARE(hint, hint4);

        QMediaServiceProviderHint hint5(mimeType,QStringList());
        QVERIFY(hint != hint5);
    }
}

QTEST_MAIN(tst_QMediaServiceProvider)

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


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2013.  All rights reserved
 */
#ifndef _Stroika_Foundation_Containers_Private_PatchingDataStructures_LinkedList_inl_
#define _Stroika_Foundation_Containers_Private_PatchingDataStructures_LinkedList_inl_  1

#include    "../../../Debug/Assertions.h"

namespace   Stroika {
    namespace   Foundation {
        namespace   Containers {
            namespace   Private {
                namespace   PatchingDataStructures {

                    /*
                    ********************************************************************************
                    ***************************** LinkedList_Patch<T> ******************************
                    ********************************************************************************
                    */
                    template    <typename   T>
                    inline  LinkedList_Patch<T>::LinkedList_Patch ()
                        : inherited ()
                        , fActiveIteratorsListHead_ (nullptr)
                    {
                        Invariant ();
                    }
                    template    <typename   T>
                    inline  LinkedList_Patch<T>::LinkedList_Patch (const LinkedList_Patch<T>& from)
                        : inherited (from)
                        , fActiveIteratorsListHead_ (nullptr)    // Don't copy the list of iterators - would be trouble with backpointers!
                        // Could clone but that would do no good, since nobody else would have pointers to them
                    {
                        Invariant ();
                    }
                    template    <typename   T>
                    inline  LinkedList_Patch<T>::~LinkedList_Patch ()
                    {
                        Require (fActiveIteratorsListHead_ == nullptr);
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::Invariant () const
                    {
#if     qDebug
                        Invariant_ ();
                        InvariantOnIterators_ ();
#endif
                    }
                    template    <typename   T>
                    inline  bool    LinkedList_Patch<T>::HasActiveIterators () const
                    {
                        return bool (fActiveIteratorsListHead_ != nullptr);
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::PatchViewsAdd (const Link* link) const
                    {
                        RequireNotNull (link);
                        for (auto v = fActiveIteratorsListHead_; v != nullptr; v = v->_fNextActiveIterator) {
                            v->PatchAdd (link);
                        }
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::PatchViewsRemove (const Link* link) const
                    {
                        RequireNotNull (link);
                        for (auto v = fActiveIteratorsListHead_; v != nullptr; v = v->_fNextActiveIterator) {
                            v->PatchRemove (link);
                        }
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::PatchViewsRemoveAll () const
                    {
                        for (auto v = fActiveIteratorsListHead_; v != nullptr; v = v->_fNextActiveIterator) {
                            v->PatchRemoveAll ();
                        }
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::TwoPhaseIteratorPatcherPass1 (Link* oldI, Memory::SmallStackBuffer<ForwardIterator*>* items2Patch) const
                    {
                        for (auto ai = fActiveIteratorsListHead_; ai != nullptr; ai = ai->_fNextActiveIterator) {
                            ai->TwoPhaseIteratorPatcherPass1 (oldI, items2Patch);
                        }
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::TwoPhaseIteratorPatcherPass2 (const Memory::SmallStackBuffer<ForwardIterator*>* items2Patch, Link* newI)
                    {
                        for (size_t i = 0; i < items2Patch->GetSize (); ++i) {
                            (*items2Patch)[i]->TwoPhaseIteratorPatcherPass2 (newI);
                        }
                    }
                    template    <typename   T>
                    inline  LinkedList_Patch<T>& LinkedList_Patch<T>::operator= (const LinkedList_Patch<T>& rhs)
                    {
                        /*
                         * Don't copy the rhs iterators, and don't do assignments when we have active iterators.
                         * If this is to be supported at some future date, well need to work on our patching.
                         */
                        Assert (not (HasActiveIterators ()));   // cuz copy of LinkedList does not copy iterators...
                        inherited::operator= (rhs);
                        return (*this);
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::Prepend (T item)
                    {
                        Invariant ();
                        inherited::Prepend (item);
                        PatchViewsAdd (this->_fHead);
                        Invariant ();
                    }
                    //tmphack - must fix to have in  base class and just hook for patch here
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::Append (T item)
                    {
                        if (this->_fHead == nullptr) {
                            Prepend (item);
                        }
                        else {
                            Link* last = this->_fHead;
                            for (; last->fNext != nullptr; last = last->fNext)
                                ;
                            Assert (last != nullptr);
                            Assert (last->fNext == nullptr);
                            last->fNext = new Link (item, nullptr);
                            PatchViewsAdd (last->fNext);
                        }
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::RemoveFirst ()
                    {
                        Invariant ();
                        PatchViewsRemove (this->_fHead);
                        inherited::RemoveFirst ();
                        Invariant ();
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::RemoveAll ()
                    {
                        Invariant ();
                        inherited::RemoveAll ();
                        PatchViewsRemoveAll ();
                        Invariant ();
                    }
                    template    <typename   T>
                    void    LinkedList_Patch<T>::Remove (T item)
                    {
                        Invariant ();
                        T current;
                        for (ForwardIterator it (*this); it.More (&current, true); ) {
                            if (current == item) {
                                this->RemoveAt (it);
                                break;
                            }
                        }
                        Invariant ();
                    }
                    template    <typename   T>
                    void    LinkedList_Patch<T>::RemoveAt (const ForwardIterator& i)
                    {
                        Require (not i.Done ());
                        Invariant ();
                        Memory::SmallStackBuffer<ForwardIterator*>   items2Patch (0);
                        TwoPhaseIteratorPatcherPass1 (const_cast<Link*> (i._fCurrent), &items2Patch);
                        Link*   next = i._fCurrent->fNext;
                        this->inherited::RemoveAt (i);
                        TwoPhaseIteratorPatcherPass2 (&items2Patch, next);
                        Invariant ();
                    }
                    template    <typename   T>
                    void    LinkedList_Patch<T>::AddBefore (const ForwardIterator& i, T newValue)
                    {
                        Invariant ();

                        //tmphack
                        const Link*     prev = nullptr;
                        if ((this->_fHead != nullptr) and (this->_fHead != i._fCurrent)) {
                            for (prev = this->_fHead; prev->fNext != i._fCurrent; prev = prev->fNext) {
                                AssertNotNull (prev);    // cuz that would mean _fCurrent not in DoublyLinkedList!!!
                            }
                        }

                        bool    isPrevNull = (prev == nullptr);
                        inherited::AddBefore (i, newValue);
                        /// WAG - VERY LIKELY WRONG BELOIW - MUST CLENAUP - LGP -2013-06-17
                        if (isPrevNull) {
                            this->PatchViewsAdd (this->_fHead);       // Will adjust fPrev
                        }
                        else {
                            this->PatchViewsAdd (prev->fNext);       // Will adjust fPrev
                        }
                        Invariant ();
                    }
                    template    <typename   T>
                    void    LinkedList_Patch<T>::AddAfter (const ForwardIterator& i, T newValue)
                    {
                        Invariant ();
                        inherited::AddAfter (i, newValue);
                        this->PatchViewsAdd (i._fCurrent->fNext);
                        Invariant ();
                    }
#if     qDebug
                    template    <typename   T>
                    void    LinkedList_Patch<T>::Invariant_ () const
                    {
                        inherited::Invariant_ ();
                        /*
                         *      Be sure each iterator points back to us. Thats about all we can test from
                         *  here since we cannot call each iterators Invariant(). That would be
                         *  nice, but sadly when this Invariant_ () is called from LinkedList<T> the
                         *  iterators themselves may not have been patched, so they'll be out of
                         *  date. Instead, so that in local shadow of Invariant() done in LinkedList_Patch<T>
                         *  so only called when WE call Invariant().
                         */
                        for (auto v = fActiveIteratorsListHead_; v != nullptr; v = v->_fNextActiveIterator) {
                            Assert (v->_fData == this);
                        }
                    }
                    template    <typename   T>
                    void    LinkedList_Patch<T>::InvariantOnIterators_ () const
                    {
                        /*
                         *      Only here can we iterate over each iterator and calls its Invariant()
                         *  since now we've completed any needed patching.
                         */
                        for (auto v = fActiveIteratorsListHead_; v != nullptr; v = v->_fNextActiveIterator) {
                            Assert (v->_fData == this);
                            v->Invariant ();
                        }
                    }
#endif

                    /*
                    ********************************************************************************
                    ******************** LinkedList_Patch<T>::ForwardIterator **********************
                    ********************************************************************************
                    */
                    template    <typename   T>
                    inline  LinkedList_Patch<T>::ForwardIterator::ForwardIterator (const LinkedList_Patch<T>& data)
                        : inherited (data)
                        , _fNextActiveIterator (data.fActiveIteratorsListHead_)
                        //, fPrev (nullptr)         // means invalid or fData->_fHead == _fCurrent ...
                    {
                        const_cast<LinkedList_Patch<T>*> (&data)->fActiveIteratorsListHead_ = this;
                        this->Invariant ();
                    }
                    template    <typename   T>
                    inline  LinkedList_Patch<T>::ForwardIterator::ForwardIterator (const ForwardIterator& from)
                        : inherited (from)
                        , _fNextActiveIterator (from.GetPatchingContainer_ ().fActiveIteratorsListHead_)
                        //, fPrev (from.fPrev)
                    {
                        from.Invariant ();
                        GetPatchingContainer_ ().fActiveIteratorsListHead_ = this;
                        this->Invariant ();
                    }
                    template    <typename   T>
                    inline  LinkedList_Patch<T>::ForwardIterator::~ForwardIterator ()
                    {
                        this->Invariant ();
                        if (GetPatchingContainer_ ().fActiveIteratorsListHead_ == this) {
                            GetPatchingContainer_ ().fActiveIteratorsListHead_ = _fNextActiveIterator;
                        }
                        else {
                            auto    v = GetPatchingContainer_ ().fActiveIteratorsListHead_;
                            for (; v->_fNextActiveIterator != this; v = v->_fNextActiveIterator) {
                                AssertNotNull (v);
                                AssertNotNull (v->_fNextActiveIterator);
                            }
                            AssertNotNull (v);
                            Assert (v->_fNextActiveIterator == this);
                            v->_fNextActiveIterator = _fNextActiveIterator;
                        }
                    }
                    template    <typename   T>
                    inline  typename LinkedList_Patch<T>::ForwardIterator&    LinkedList_Patch<T>::ForwardIterator::operator= (const ForwardIterator& rhs)
                    {
                        this->Invariant ();

                        /*
                         *      If the fData field has not changed, then we can leave alone our iterator linkage.
                         *  Otherwise, we must remove ourselves from the old, and add ourselves to the new.
                         */
                        if (&GetPatchingContainer_ () != &rhs.GetPatchingContainer_ ()) {

                            /*
                             * Remove from old.
                             */
                            if (GetPatchingContainer_ ().fActiveIteratorsListHead_ == this) {
                                GetPatchingContainer_ ().fActiveIteratorsListHead_ = _fNextActiveIterator;
                            }
                            else {
                                auto v = GetPatchingContainer_ ().fActiveIteratorsListHead_;
                                for (; v->_fNextActiveIterator != this; v = v->_fNextActiveIterator) {
                                    AssertNotNull (v);
                                    AssertNotNull (v->_fNextActiveIterator);
                                }
                                AssertNotNull (v);
                                Assert (v->_fNextActiveIterator == this);
                                v->_fNextActiveIterator = _fNextActiveIterator;
                            }

                            /*
                             * Add to new.
                             */
                            fData = rhs.fData;
                            _fNextActiveIterator = rhs.GetPatchingContainer_ ().fActiveIteratorsListHead_;
                            GetPatchingContainer_ ().fActiveIteratorsListHead_ = this;
                        }

                        fData = rhs.fData;
                        //fPrev = rhs.fPrev;

                        inherited::operator= (rhs);

                        this->Invariant ();
                        return *this;
                    }
                    template    <typename   T>
                    inline  bool    LinkedList_Patch<T>::ForwardIterator::More (T* current, bool advance)
                    {
                        this->Invariant ();

                        if (advance) {
                            /*
                             * We could already be done since after the last Done() call, we could
                             * have done a removeall.
                             */
                            if (not this->_fSuppressMore and this->_fCurrent != nullptr) {
                                //fPrev = this->_fCurrent;
                                this->_fCurrent = this->_fCurrent->fNext;

                            }
                            this->_fSuppressMore = false;
                        }
                        this->Invariant ();
                        if ((current != nullptr) and (not this->Done ())) {
                            *current = this->_fCurrent->fItem;
                        }
                        return (not this->Done ());
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::ForwardIterator::PatchAdd (const Link* link)
                    {
                        /*
                         *      link is the new link just added. If it was just after current, then
                         *  there is no problem - we will soon hit it. If it was well before current
                         *  (ie before prev) then there is still no problem. If it is the new
                         *  previous, we just adjust our previous.
                         */
                        RequireNotNull (link);
                        if (link->fNext == this->_fCurrent) {
                            //fPrev = link;
                        }
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::ForwardIterator::PatchRemove (const Link* link)
                    {
                        RequireNotNull (link);

                        /*
                         *  There are basicly three cases:
                         *
                         *  (1)     We remove the current. In this case, we just advance current to the next
                         *          item (prev is already all set), and set _fSuppressMore since we are advanced
                         *          to the next item.
                         *  (2)     We remove our previous. Technically this poses no problems, except then
                         *          our previos pointer is invalid. We could recompute it, but that would
                         *          involve rescanning the list from the beginning - slow. And we probably
                         *          will never need the next pointer (unless we get a remove current call).
                         *          So just set it to nullptr, which conventionally means no valid value.
                         *          It will be recomputed if needed.
                         *  (3)     We are deleting some other value. No probs.
                         */
                        if (this->_fCurrent == link) {
                            this->_fCurrent = this->_fCurrent->fNext;
                            // fPrev remains the same - right now it points to a bad item, since
                            // PatchRemove() called before the actual removal, but right afterwards
                            // it will point to our new _fCurrent.
                            this->_fSuppressMore = true;         // Since we advanced cursor...
                        }
                        //else if (fPrev == link) {
                        //    fPrev = nullptr;                    // real value recomputed later, if needed
                        //}
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::ForwardIterator::PatchRemoveAll ()
                    {
                        this->_fCurrent = nullptr;
                        //fPrev = nullptr;
                        Ensure (this->Done ());
                    }
                    template    <typename T>
                    void    LinkedList_Patch<T>::ForwardIterator::TwoPhaseIteratorPatcherPass1 (Link* oldI, Memory::SmallStackBuffer<ForwardIterator*>* items2Patch)
                    {
                        if (this->_fCurrent == oldI) {
                            items2Patch->push_back (this);
                        }
                    }
                    template    <typename T>
                    void    LinkedList_Patch<T>::ForwardIterator::TwoPhaseIteratorPatcherPass2 (Link* newI)
                    {
                        this->_fSuppressMore = true;
                        this->_fCurrent = newI;
                    }
                    template    <typename T>
                    inline  const LinkedList_Patch<T>&  LinkedList_Patch<T>::ForwardIterator::GetPatchingContainer_ () const
                    {
                        AssertMember (this->_fData, LinkedList_Patch<T>);
                        return *static_cast<const LinkedList_Patch<T>*> (this->_fData);
                    }
                    template    <typename T>
                    inline  LinkedList_Patch<T>&    LinkedList_Patch<T>::ForwardIterator::GetPatchingContainer_ ()
                    {
                        AssertMember (this->_fData, LinkedList_Patch<T>);
                        return *static_cast<LinkedList_Patch<T>*> (const_cast<DataStructures::LinkedList<T>*> (this->_fData));
                    }
#if     qDebug
                    template    <typename   T>
                    void    LinkedList_Patch<T>::ForwardIterator::Invariant_ () const
                    {
                        inherited::Invariant_ ();

                        /*
                         *  fPrev could be nullptr, but if it isn't then its next must be _fCurrent.
                         */
                        //Assert ((fPrev == nullptr) or (fPrev->fNext == this->_fCurrent));
                    }
#endif

                }
            }
        }
    }
}
#endif /* _Stroika_Foundation_Containers_Private_PatchingDataStructures_LinkedList_inl_ */
<commit_msg>nearly done with this pass of LinkedList<T> cleanups<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2013.  All rights reserved
 */
#ifndef _Stroika_Foundation_Containers_Private_PatchingDataStructures_LinkedList_inl_
#define _Stroika_Foundation_Containers_Private_PatchingDataStructures_LinkedList_inl_  1

#include    "../../../Debug/Assertions.h"

namespace   Stroika {
    namespace   Foundation {
        namespace   Containers {
            namespace   Private {
                namespace   PatchingDataStructures {

                    /*
                    ********************************************************************************
                    ***************************** LinkedList_Patch<T> ******************************
                    ********************************************************************************
                    */
                    template    <typename   T>
                    inline  LinkedList_Patch<T>::LinkedList_Patch ()
                        : inherited ()
                        , fActiveIteratorsListHead_ (nullptr)
                    {
                        Invariant ();
                    }
                    template    <typename   T>
                    inline  LinkedList_Patch<T>::LinkedList_Patch (const LinkedList_Patch<T>& from)
                        : inherited (from)
                        , fActiveIteratorsListHead_ (nullptr)    // Don't copy the list of iterators - would be trouble with backpointers!
                        // Could clone but that would do no good, since nobody else would have pointers to them
                    {
                        Invariant ();
                    }
                    template    <typename   T>
                    inline  LinkedList_Patch<T>::~LinkedList_Patch ()
                    {
                        Require (fActiveIteratorsListHead_ == nullptr);
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::Invariant () const
                    {
#if     qDebug
                        Invariant_ ();
                        InvariantOnIterators_ ();
#endif
                    }
                    template    <typename   T>
                    inline  bool    LinkedList_Patch<T>::HasActiveIterators () const
                    {
                        return bool (fActiveIteratorsListHead_ != nullptr);
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::PatchViewsAdd (const Link* link) const
                    {
                        RequireNotNull (link);
                        for (auto v = fActiveIteratorsListHead_; v != nullptr; v = v->_fNextActiveIterator) {
                            v->PatchAdd (link);
                        }
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::PatchViewsRemove (const Link* link) const
                    {
                        RequireNotNull (link);
                        for (auto v = fActiveIteratorsListHead_; v != nullptr; v = v->_fNextActiveIterator) {
                            v->PatchRemove (link);
                        }
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::PatchViewsRemoveAll () const
                    {
                        for (auto v = fActiveIteratorsListHead_; v != nullptr; v = v->_fNextActiveIterator) {
                            v->PatchRemoveAll ();
                        }
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::TwoPhaseIteratorPatcherPass1 (Link* oldI, Memory::SmallStackBuffer<ForwardIterator*>* items2Patch) const
                    {
                        for (auto ai = fActiveIteratorsListHead_; ai != nullptr; ai = ai->_fNextActiveIterator) {
                            ai->TwoPhaseIteratorPatcherPass1 (oldI, items2Patch);
                        }
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::TwoPhaseIteratorPatcherPass2 (const Memory::SmallStackBuffer<ForwardIterator*>* items2Patch, Link* newI)
                    {
                        for (size_t i = 0; i < items2Patch->GetSize (); ++i) {
                            (*items2Patch)[i]->TwoPhaseIteratorPatcherPass2 (newI);
                        }
                    }
                    template    <typename   T>
                    inline  LinkedList_Patch<T>& LinkedList_Patch<T>::operator= (const LinkedList_Patch<T>& rhs)
                    {
                        /*
                         * Don't copy the rhs iterators, and don't do assignments when we have active iterators.
                         * If this is to be supported at some future date, well need to work on our patching.
                         */
                        Assert (not (HasActiveIterators ()));   // cuz copy of LinkedList does not copy iterators...
                        inherited::operator= (rhs);
                        return (*this);
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::Prepend (T item)
                    {
                        Invariant ();
                        inherited::Prepend (item);
                        PatchViewsAdd (this->_fHead);
                        Invariant ();
                    }
                    //tmphack - must fix to have in  base class and just hook for patch here
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::Append (T item)
                    {
                        if (this->_fHead == nullptr) {
                            Prepend (item);
                        }
                        else {
                            Link* last = this->_fHead;
                            for (; last->fNext != nullptr; last = last->fNext)
                                ;
                            Assert (last != nullptr);
                            Assert (last->fNext == nullptr);
                            last->fNext = new Link (item, nullptr);
                            PatchViewsAdd (last->fNext);
                        }
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::RemoveFirst ()
                    {
                        Invariant ();
                        PatchViewsRemove (this->_fHead);
                        inherited::RemoveFirst ();
                        Invariant ();
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::RemoveAll ()
                    {
                        Invariant ();
                        inherited::RemoveAll ();
                        PatchViewsRemoveAll ();
                        Invariant ();
                    }
                    template    <typename   T>
                    void    LinkedList_Patch<T>::Remove (T item)
                    {
                        Invariant ();
                        T current;
                        for (ForwardIterator it (*this); it.More (&current, true); ) {
                            if (current == item) {
                                this->RemoveAt (it);
                                break;
                            }
                        }
                        Invariant ();
                    }
                    template    <typename   T>
                    void    LinkedList_Patch<T>::RemoveAt (const ForwardIterator& i)
                    {
                        Require (not i.Done ());
                        Invariant ();
                        Memory::SmallStackBuffer<ForwardIterator*>   items2Patch (0);
                        TwoPhaseIteratorPatcherPass1 (const_cast<Link*> (i._fCurrent), &items2Patch);
                        Link*   next = i._fCurrent->fNext;
                        this->inherited::RemoveAt (i);
                        TwoPhaseIteratorPatcherPass2 (&items2Patch, next);
                        Invariant ();
                    }
                    template    <typename   T>
                    void    LinkedList_Patch<T>::AddBefore (const ForwardIterator& i, T newValue)
                    {
                        Invariant ();

                        //tmphack
                        const Link*     prev = nullptr;
                        if ((this->_fHead != nullptr) and (this->_fHead != i._fCurrent)) {
                            for (prev = this->_fHead; prev->fNext != i._fCurrent; prev = prev->fNext) {
                                AssertNotNull (prev);    // cuz that would mean _fCurrent not in DoublyLinkedList!!!
                            }
                        }

                        bool    isPrevNull = (prev == nullptr);
                        inherited::AddBefore (i, newValue);
                        /// WAG - VERY LIKELY WRONG BELOIW - MUST CLENAUP - LGP -2013-06-17
                        if (isPrevNull) {
                            this->PatchViewsAdd (this->_fHead);       // Will adjust fPrev
                        }
                        else {
                            this->PatchViewsAdd (prev->fNext);       // Will adjust fPrev
                        }
                        Invariant ();
                    }
                    template    <typename   T>
                    void    LinkedList_Patch<T>::AddAfter (const ForwardIterator& i, T newValue)
                    {
                        Invariant ();
                        inherited::AddAfter (i, newValue);
                        this->PatchViewsAdd (i._fCurrent->fNext);
                        Invariant ();
                    }
#if     qDebug
                    template    <typename   T>
                    void    LinkedList_Patch<T>::Invariant_ () const
                    {
                        inherited::Invariant_ ();
                        /*
                         *      Be sure each iterator points back to us. Thats about all we can test from
                         *  here since we cannot call each iterators Invariant(). That would be
                         *  nice, but sadly when this Invariant_ () is called from LinkedList<T> the
                         *  iterators themselves may not have been patched, so they'll be out of
                         *  date. Instead, so that in local shadow of Invariant() done in LinkedList_Patch<T>
                         *  so only called when WE call Invariant().
                         */
                        for (auto v = fActiveIteratorsListHead_; v != nullptr; v = v->_fNextActiveIterator) {
                            Assert (v->_fData == this);
                        }
                    }
                    template    <typename   T>
                    void    LinkedList_Patch<T>::InvariantOnIterators_ () const
                    {
                        /*
                         *      Only here can we iterate over each iterator and calls its Invariant()
                         *  since now we've completed any needed patching.
                         */
                        for (auto v = fActiveIteratorsListHead_; v != nullptr; v = v->_fNextActiveIterator) {
                            Assert (v->_fData == this);
                            v->Invariant ();
                        }
                    }
#endif

                    /*
                    ********************************************************************************
                    ******************** LinkedList_Patch<T>::ForwardIterator **********************
                    ********************************************************************************
                    */
                    template    <typename   T>
                    inline  LinkedList_Patch<T>::ForwardIterator::ForwardIterator (const LinkedList_Patch<T>& data)
                        : inherited (data)
                        , _fNextActiveIterator (data.fActiveIteratorsListHead_)
                        //, fPrev (nullptr)         // means invalid or fData->_fHead == _fCurrent ...
                    {
                        const_cast<LinkedList_Patch<T>*> (&data)->fActiveIteratorsListHead_ = this;
                        this->Invariant ();
                    }
                    template    <typename   T>
                    inline  LinkedList_Patch<T>::ForwardIterator::ForwardIterator (const ForwardIterator& from)
                        : inherited (from)
                        , _fNextActiveIterator (from.GetPatchingContainer_ ().fActiveIteratorsListHead_)
                        //, fPrev (from.fPrev)
                    {
                        from.Invariant ();
                        GetPatchingContainer_ ().fActiveIteratorsListHead_ = this;
                        this->Invariant ();
                    }
                    template    <typename   T>
                    inline  LinkedList_Patch<T>::ForwardIterator::~ForwardIterator ()
                    {
                        this->Invariant ();
                        if (GetPatchingContainer_ ().fActiveIteratorsListHead_ == this) {
                            GetPatchingContainer_ ().fActiveIteratorsListHead_ = _fNextActiveIterator;
                        }
                        else {
                            auto    v = GetPatchingContainer_ ().fActiveIteratorsListHead_;
                            for (; v->_fNextActiveIterator != this; v = v->_fNextActiveIterator) {
                                AssertNotNull (v);
                                AssertNotNull (v->_fNextActiveIterator);
                            }
                            AssertNotNull (v);
                            Assert (v->_fNextActiveIterator == this);
                            v->_fNextActiveIterator = _fNextActiveIterator;
                        }
                    }
                    template    <typename   T>
                    inline  typename LinkedList_Patch<T>::ForwardIterator&    LinkedList_Patch<T>::ForwardIterator::operator= (const ForwardIterator& rhs)
                    {
                        this->Invariant ();

                        /*
                         *      If the fData field has not changed, then we can leave alone our iterator linkage.
                         *  Otherwise, we must remove ourselves from the old, and add ourselves to the new.
                         */
                        if (&GetPatchingContainer_ () != &rhs.GetPatchingContainer_ ()) {

                            /*
                             * Remove from old.
                             */
                            if (GetPatchingContainer_ ().fActiveIteratorsListHead_ == this) {
                                GetPatchingContainer_ ().fActiveIteratorsListHead_ = _fNextActiveIterator;
                            }
                            else {
                                auto v = GetPatchingContainer_ ().fActiveIteratorsListHead_;
                                for (; v->_fNextActiveIterator != this; v = v->_fNextActiveIterator) {
                                    AssertNotNull (v);
                                    AssertNotNull (v->_fNextActiveIterator);
                                }
                                AssertNotNull (v);
                                Assert (v->_fNextActiveIterator == this);
                                v->_fNextActiveIterator = _fNextActiveIterator;
                            }

                            /*
                             * Add to new.
                             */
                            _fNextActiveIterator = rhs.GetPatchingContainer_ ().fActiveIteratorsListHead_;
                            GetPatchingContainer_ ().fActiveIteratorsListHead_ = this;
                        }

                        //fPrev = rhs.fPrev;

                        inherited::operator= (rhs);

                        this->Invariant ();
                        return *this;
                    }
                    template    <typename   T>
                    inline  bool    LinkedList_Patch<T>::ForwardIterator::More (T* current, bool advance)
                    {
                        this->Invariant ();

                        if (advance) {
                            /*
                             * We could already be done since after the last Done() call, we could
                             * have done a removeall.
                             */
                            if (not this->_fSuppressMore and this->_fCurrent != nullptr) {
                                //fPrev = this->_fCurrent;
                                this->_fCurrent = this->_fCurrent->fNext;

                            }
                            this->_fSuppressMore = false;
                        }
                        this->Invariant ();
                        if ((current != nullptr) and (not this->Done ())) {
                            *current = this->_fCurrent->fItem;
                        }
                        return (not this->Done ());
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::ForwardIterator::PatchAdd (const Link* link)
                    {
                        /*
                         *      link is the new link just added. If it was just after current, then
                         *  there is no problem - we will soon hit it. If it was well before current
                         *  (ie before prev) then there is still no problem. If it is the new
                         *  previous, we just adjust our previous.
                         */
                        RequireNotNull (link);
                        if (link->fNext == this->_fCurrent) {
                            //fPrev = link;
                        }
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::ForwardIterator::PatchRemove (const Link* link)
                    {
                        RequireNotNull (link);

                        /*
                         *  There are basicly three cases:
                         *
                         *  (1)     We remove the current. In this case, we just advance current to the next
                         *          item (prev is already all set), and set _fSuppressMore since we are advanced
                         *          to the next item.
                         *  (2)     We remove our previous. Technically this poses no problems, except then
                         *          our previos pointer is invalid. We could recompute it, but that would
                         *          involve rescanning the list from the beginning - slow. And we probably
                         *          will never need the next pointer (unless we get a remove current call).
                         *          So just set it to nullptr, which conventionally means no valid value.
                         *          It will be recomputed if needed.
                         *  (3)     We are deleting some other value. No probs.
                         */
                        if (this->_fCurrent == link) {
                            this->_fCurrent = this->_fCurrent->fNext;
                            // fPrev remains the same - right now it points to a bad item, since
                            // PatchRemove() called before the actual removal, but right afterwards
                            // it will point to our new _fCurrent.
                            this->_fSuppressMore = true;         // Since we advanced cursor...
                        }
                        //else if (fPrev == link) {
                        //    fPrev = nullptr;                    // real value recomputed later, if needed
                        //}
                    }
                    template    <typename   T>
                    inline  void    LinkedList_Patch<T>::ForwardIterator::PatchRemoveAll ()
                    {
                        this->_fCurrent = nullptr;
                        //fPrev = nullptr;
                        Ensure (this->Done ());
                    }
                    template    <typename T>
                    void    LinkedList_Patch<T>::ForwardIterator::TwoPhaseIteratorPatcherPass1 (Link* oldI, Memory::SmallStackBuffer<ForwardIterator*>* items2Patch)
                    {
                        if (this->_fCurrent == oldI) {
                            items2Patch->push_back (this);
                        }
                    }
                    template    <typename T>
                    void    LinkedList_Patch<T>::ForwardIterator::TwoPhaseIteratorPatcherPass2 (Link* newI)
                    {
                        this->_fSuppressMore = true;
                        this->_fCurrent = newI;
                    }
                    template    <typename T>
                    inline  const LinkedList_Patch<T>&  LinkedList_Patch<T>::ForwardIterator::GetPatchingContainer_ () const
                    {
                        AssertMember (this->_fData, LinkedList_Patch<T>);
                        return *static_cast<const LinkedList_Patch<T>*> (this->_fData);
                    }
                    template    <typename T>
                    inline  LinkedList_Patch<T>&    LinkedList_Patch<T>::ForwardIterator::GetPatchingContainer_ ()
                    {
                        AssertMember (this->_fData, LinkedList_Patch<T>);
                        return *static_cast<LinkedList_Patch<T>*> (const_cast<DataStructures::LinkedList<T>*> (this->_fData));
                    }
#if     qDebug
                    template    <typename   T>
                    void    LinkedList_Patch<T>::ForwardIterator::Invariant_ () const
                    {
                        inherited::Invariant_ ();

                        /*
                         *  fPrev could be nullptr, but if it isn't then its next must be _fCurrent.
                         */
                        //Assert ((fPrev == nullptr) or (fPrev->fNext == this->_fCurrent));
                    }
#endif

                }
            }
        }
    }
}
#endif /* _Stroika_Foundation_Containers_Private_PatchingDataStructures_LinkedList_inl_ */
<|endoftext|>


<commit_before><commit_msg>Fix the build of view_examples for Linux.<commit_after><|endoftext|>


<commit_before>/*
 * Listener on a control datagram socket.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "lb_control.hxx"
#include "lb_instance.hxx"
#include "lb_config.hxx"
#include "lb_stats.hxx"
#include "control_server.hxx"
#include "address_edit.h"
#include "failure.hxx"
#include "tpool.hxx"
#include "pool.hxx"

#include <daemon/log.h>
#include <socket/address.h>

#include <string.h>
#include <stdlib.h>

static void
enable_node(const struct lb_instance *instance,
          const char *payload, size_t length)
{
    const char *colon = (const char *)memchr(payload, ':', length);
    if (colon == NULL || colon == payload || colon == payload + length - 1) {
        daemon_log(3, "malformed FADE_NODE control packet: no port\n");
        return;
    }

    const AutoRewindPool auto_rewind(*tpool);

    char *node_name = p_strndup(tpool, payload, length);
    char *port_string = node_name + (colon - payload);
    *port_string++ = 0;

    const lb_node_config *node = instance->config->FindNode(node_name);
    if (node == NULL) {
        daemon_log(3, "unknown node in FADE_NODE control packet\n");
        return;
    }

    char *endptr;
    unsigned port = strtoul(port_string, &endptr, 10);
    if (port == 0 || *endptr != 0) {
        daemon_log(3, "malformed FADE_NODE control packet: port is not a number\n");
        return;
    }

    const struct sockaddr *with_port =
        sockaddr_set_port(tpool,
                          node->address, node->address.GetSize(),
                          port);

    char buffer[64];
    socket_address_to_string(buffer, sizeof(buffer), with_port,
                             node->address.GetSize());
    daemon_log(4, "enabling node %s (%s)\n", node_name, buffer);

    failure_unset({with_port, node->address.GetSize()}, FAILURE_OK);
}

static void
fade_node(const struct lb_instance *instance,
          const char *payload, size_t length)
{
    const char *colon = (const char *)memchr(payload, ':', length);
    if (colon == NULL || colon == payload || colon == payload + length - 1) {
        daemon_log(3, "malformed FADE_NODE control packet: no port\n");
        return;
    }

    const AutoRewindPool auto_rewind(*tpool);

    char *node_name = p_strndup(tpool, payload, length);
    char *port_string = node_name + (colon - payload);
    *port_string++ = 0;

    const lb_node_config *node = instance->config->FindNode(node_name);
    if (node == NULL) {
        daemon_log(3, "unknown node in FADE_NODE control packet\n");
        return;
    }

    char *endptr;
    unsigned port = strtoul(port_string, &endptr, 10);
    if (port == 0 || *endptr != 0) {
        daemon_log(3, "malformed FADE_NODE control packet: port is not a number\n");
        return;
    }

    const struct sockaddr *with_port =
        sockaddr_set_port(tpool,
                          node->address, node->address.GetSize(),
                          port);

    char buffer[64];
    socket_address_to_string(buffer, sizeof(buffer), with_port,
                             node->address.GetSize());
    daemon_log(4, "fading node %s (%s)\n", node_name, buffer);

    /* set status "FADE" for 3 hours */
    failure_set({with_port, node->address.GetSize()}, FAILURE_FADE, 3 * 3600);
}

G_GNUC_CONST
static const char *
failure_status_to_string(enum failure_status status)
{
    switch (status) {
    case FAILURE_OK:
        return "ok";

    case FAILURE_FADE:
        return "fade";

    case FAILURE_RESPONSE:
    case FAILURE_FAILED:
    case FAILURE_MONITOR:
        break;
    }

    return "error";
}

static bool
node_status_response(ControlServer *server, struct pool *pool,
                     SocketAddress address,
                     const char *payload, size_t length, const char *status,
                     GError **error_r)
{
    size_t status_length = strlen(status);

    size_t response_length = length + 1 + status_length;
    char *response = PoolAlloc<char>(*tpool, response_length);
    memcpy(response, payload, length);
    response[length] = 0;
    memcpy(response + length + 1, status, status_length);

    return control_server_reply(server, pool, address,
                                CONTROL_NODE_STATUS, response, response_length,
                                error_r);
}

static void
query_node_status(LbControl *control,
                  const char *payload, size_t length,
                  SocketAddress address)
{
    if (address.GetSize() == 0) {
        daemon_log(3, "got NODE_STATUS from unbound client socket\n");
        return;
    }

    const char *colon = (const char *)memchr(payload, ':', length);
    if (colon == NULL || colon == payload || colon == payload + length - 1) {
        node_status_response(control->server, tpool, address,
                             payload, length, "malformed", NULL);
        daemon_log(3, "malformed NODE_STATUS control packet: no port\n");
        return;
    }

    const AutoRewindPool auto_rewind(*tpool);

    char *node_name = p_strndup(tpool, payload, length);
    char *port_string = node_name + (colon - payload);
    *port_string++ = 0;

    const lb_node_config *node =
        control->instance->config->FindNode(node_name);
    if (node == NULL) {
        node_status_response(control->server, tpool, address,
                             payload, length, "unknown", NULL);
        daemon_log(3, "unknown node in NODE_STATUS control packet\n");
        return;
    }

    char *endptr;
    unsigned port = strtoul(port_string, &endptr, 10);
    if (port == 0 || *endptr != 0) {
        node_status_response(control->server, tpool, address,
                             payload, length, "malformed", NULL);
        daemon_log(3, "malformed NODE_STATUS control packet: port is not a number\n");
        return;
    }

    const struct sockaddr *with_port =
        sockaddr_set_port(tpool,
                          node->address, node->address.GetSize(),
                          port);

    char buffer[64];
    socket_address_to_string(buffer, sizeof(buffer), with_port,
                             node->address.GetSize());

    enum failure_status status =
        failure_get_status({with_port, node->address.GetSize()});
    const char *s = failure_status_to_string(status);

    GError *error = NULL;
    if (!node_status_response(control->server, tpool, address,
                              payload, length, s,
                              &error)) {
        daemon_log(3, "%s\n", error->message);
        g_error_free(error);
    }
}

static void
query_stats(LbControl *control, SocketAddress address)
{
    struct beng_control_stats stats;
    lb_get_stats(control->instance, &stats);

    const AutoRewindPool auto_rewind(*tpool);

    GError *error = NULL;
    if (!control_server_reply(control->server, tpool,
                              address,
                              CONTROL_STATS, &stats, sizeof(stats),
                              &error)) {
        daemon_log(3, "%s\n", error->message);
        g_error_free(error);
    }
}

static void
lb_control_packet(enum beng_control_command command,
                  const void *payload, size_t payload_length,
                  SocketAddress address,
                  void *ctx)
{
    auto *control = (LbControl *)ctx;

    switch (command) {
    case CONTROL_NOP:
    case CONTROL_TCACHE_INVALIDATE:
    case CONTROL_FADE_CHILDREN:
        break;

    case CONTROL_ENABLE_NODE:
        enable_node(control->instance, (const char *)payload, payload_length);
        break;

    case CONTROL_FADE_NODE:
        fade_node(control->instance, (const char *)payload, payload_length);
        break;

    case CONTROL_NODE_STATUS:
        query_node_status(control, (const char *)payload, payload_length,
                          address);
        break;

    case CONTROL_DUMP_POOLS:
        pool_dump_tree(control->instance->pool);
        break;

    case CONTROL_STATS:
        query_stats(control, address);
        break;

    case CONTROL_VERBOSE:
        if (payload_length == 1)
            daemon_log_config.verbose = *(const uint8_t *)payload;
        break;
    }
}

static void
lb_control_error(GError *error, gcc_unused void *ctx)
{
    daemon_log(2, "%s\n", error->message);
    g_error_free(error);
}

static constexpr struct control_handler lb_control_handler = {
    nullptr,
    lb_control_packet,
    lb_control_error,
};

LbControl *
lb_control_new(struct lb_instance *instance,
               const struct lb_control_config *config,
               GError **error_r)
{
    auto *control = new LbControl();
    control->instance = instance;

    control->server =
        control_server_new(config->bind_address,
                           &lb_control_handler, control,
                           error_r);
    if (control->server == NULL) {
        delete control;
        return NULL;
    }

    return control;
}

void
lb_control_free(LbControl *control)
{
    control_server_free(control->server);

    delete control;
}

void
lb_control_enable(LbControl *control)
{
    control_server_enable(control->server);
}

void
lb_control_disable(LbControl *control)
{
    control_server_disable(control->server);
}
<commit_msg>lb_control: use nullptr instead of NULL<commit_after>/*
 * Listener on a control datagram socket.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "lb_control.hxx"
#include "lb_instance.hxx"
#include "lb_config.hxx"
#include "lb_stats.hxx"
#include "control_server.hxx"
#include "address_edit.h"
#include "failure.hxx"
#include "tpool.hxx"
#include "pool.hxx"

#include <daemon/log.h>
#include <socket/address.h>

#include <string.h>
#include <stdlib.h>

static void
enable_node(const struct lb_instance *instance,
          const char *payload, size_t length)
{
    const char *colon = (const char *)memchr(payload, ':', length);
    if (colon == nullptr || colon == payload || colon == payload + length - 1) {
        daemon_log(3, "malformed FADE_NODE control packet: no port\n");
        return;
    }

    const AutoRewindPool auto_rewind(*tpool);

    char *node_name = p_strndup(tpool, payload, length);
    char *port_string = node_name + (colon - payload);
    *port_string++ = 0;

    const lb_node_config *node = instance->config->FindNode(node_name);
    if (node == nullptr) {
        daemon_log(3, "unknown node in FADE_NODE control packet\n");
        return;
    }

    char *endptr;
    unsigned port = strtoul(port_string, &endptr, 10);
    if (port == 0 || *endptr != 0) {
        daemon_log(3, "malformed FADE_NODE control packet: port is not a number\n");
        return;
    }

    const struct sockaddr *with_port =
        sockaddr_set_port(tpool,
                          node->address, node->address.GetSize(),
                          port);

    char buffer[64];
    socket_address_to_string(buffer, sizeof(buffer), with_port,
                             node->address.GetSize());
    daemon_log(4, "enabling node %s (%s)\n", node_name, buffer);

    failure_unset({with_port, node->address.GetSize()}, FAILURE_OK);
}

static void
fade_node(const struct lb_instance *instance,
          const char *payload, size_t length)
{
    const char *colon = (const char *)memchr(payload, ':', length);
    if (colon == nullptr || colon == payload || colon == payload + length - 1) {
        daemon_log(3, "malformed FADE_NODE control packet: no port\n");
        return;
    }

    const AutoRewindPool auto_rewind(*tpool);

    char *node_name = p_strndup(tpool, payload, length);
    char *port_string = node_name + (colon - payload);
    *port_string++ = 0;

    const lb_node_config *node = instance->config->FindNode(node_name);
    if (node == nullptr) {
        daemon_log(3, "unknown node in FADE_NODE control packet\n");
        return;
    }

    char *endptr;
    unsigned port = strtoul(port_string, &endptr, 10);
    if (port == 0 || *endptr != 0) {
        daemon_log(3, "malformed FADE_NODE control packet: port is not a number\n");
        return;
    }

    const struct sockaddr *with_port =
        sockaddr_set_port(tpool,
                          node->address, node->address.GetSize(),
                          port);

    char buffer[64];
    socket_address_to_string(buffer, sizeof(buffer), with_port,
                             node->address.GetSize());
    daemon_log(4, "fading node %s (%s)\n", node_name, buffer);

    /* set status "FADE" for 3 hours */
    failure_set({with_port, node->address.GetSize()}, FAILURE_FADE, 3 * 3600);
}

G_GNUC_CONST
static const char *
failure_status_to_string(enum failure_status status)
{
    switch (status) {
    case FAILURE_OK:
        return "ok";

    case FAILURE_FADE:
        return "fade";

    case FAILURE_RESPONSE:
    case FAILURE_FAILED:
    case FAILURE_MONITOR:
        break;
    }

    return "error";
}

static bool
node_status_response(ControlServer *server, struct pool *pool,
                     SocketAddress address,
                     const char *payload, size_t length, const char *status,
                     GError **error_r)
{
    size_t status_length = strlen(status);

    size_t response_length = length + 1 + status_length;
    char *response = PoolAlloc<char>(*tpool, response_length);
    memcpy(response, payload, length);
    response[length] = 0;
    memcpy(response + length + 1, status, status_length);

    return control_server_reply(server, pool, address,
                                CONTROL_NODE_STATUS, response, response_length,
                                error_r);
}

static void
query_node_status(LbControl *control,
                  const char *payload, size_t length,
                  SocketAddress address)
{
    if (address.GetSize() == 0) {
        daemon_log(3, "got NODE_STATUS from unbound client socket\n");
        return;
    }

    const char *colon = (const char *)memchr(payload, ':', length);
    if (colon == nullptr || colon == payload || colon == payload + length - 1) {
        node_status_response(control->server, tpool, address,
                             payload, length, "malformed", nullptr);
        daemon_log(3, "malformed NODE_STATUS control packet: no port\n");
        return;
    }

    const AutoRewindPool auto_rewind(*tpool);

    char *node_name = p_strndup(tpool, payload, length);
    char *port_string = node_name + (colon - payload);
    *port_string++ = 0;

    const lb_node_config *node =
        control->instance->config->FindNode(node_name);
    if (node == nullptr) {
        node_status_response(control->server, tpool, address,
                             payload, length, "unknown", nullptr);
        daemon_log(3, "unknown node in NODE_STATUS control packet\n");
        return;
    }

    char *endptr;
    unsigned port = strtoul(port_string, &endptr, 10);
    if (port == 0 || *endptr != 0) {
        node_status_response(control->server, tpool, address,
                             payload, length, "malformed", nullptr);
        daemon_log(3, "malformed NODE_STATUS control packet: port is not a number\n");
        return;
    }

    const struct sockaddr *with_port =
        sockaddr_set_port(tpool,
                          node->address, node->address.GetSize(),
                          port);

    char buffer[64];
    socket_address_to_string(buffer, sizeof(buffer), with_port,
                             node->address.GetSize());

    enum failure_status status =
        failure_get_status({with_port, node->address.GetSize()});
    const char *s = failure_status_to_string(status);

    GError *error = nullptr;
    if (!node_status_response(control->server, tpool, address,
                              payload, length, s,
                              &error)) {
        daemon_log(3, "%s\n", error->message);
        g_error_free(error);
    }
}

static void
query_stats(LbControl *control, SocketAddress address)
{
    struct beng_control_stats stats;
    lb_get_stats(control->instance, &stats);

    const AutoRewindPool auto_rewind(*tpool);

    GError *error = nullptr;
    if (!control_server_reply(control->server, tpool,
                              address,
                              CONTROL_STATS, &stats, sizeof(stats),
                              &error)) {
        daemon_log(3, "%s\n", error->message);
        g_error_free(error);
    }
}

static void
lb_control_packet(enum beng_control_command command,
                  const void *payload, size_t payload_length,
                  SocketAddress address,
                  void *ctx)
{
    auto *control = (LbControl *)ctx;

    switch (command) {
    case CONTROL_NOP:
    case CONTROL_TCACHE_INVALIDATE:
    case CONTROL_FADE_CHILDREN:
        break;

    case CONTROL_ENABLE_NODE:
        enable_node(control->instance, (const char *)payload, payload_length);
        break;

    case CONTROL_FADE_NODE:
        fade_node(control->instance, (const char *)payload, payload_length);
        break;

    case CONTROL_NODE_STATUS:
        query_node_status(control, (const char *)payload, payload_length,
                          address);
        break;

    case CONTROL_DUMP_POOLS:
        pool_dump_tree(control->instance->pool);
        break;

    case CONTROL_STATS:
        query_stats(control, address);
        break;

    case CONTROL_VERBOSE:
        if (payload_length == 1)
            daemon_log_config.verbose = *(const uint8_t *)payload;
        break;
    }
}

static void
lb_control_error(GError *error, gcc_unused void *ctx)
{
    daemon_log(2, "%s\n", error->message);
    g_error_free(error);
}

static constexpr struct control_handler lb_control_handler = {
    nullptr,
    lb_control_packet,
    lb_control_error,
};

LbControl *
lb_control_new(struct lb_instance *instance,
               const struct lb_control_config *config,
               GError **error_r)
{
    auto *control = new LbControl();
    control->instance = instance;

    control->server =
        control_server_new(config->bind_address,
                           &lb_control_handler, control,
                           error_r);
    if (control->server == nullptr) {
        delete control;
        return nullptr;
    }

    return control;
}

void
lb_control_free(LbControl *control)
{
    control_server_free(control->server);

    delete control;
}

void
lb_control_enable(LbControl *control)
{
    control_server_enable(control->server);
}

void
lb_control_disable(LbControl *control)
{
    control_server_disable(control->server);
}
<|endoftext|>