<commit_before>#include "xchainer/cuda/cuda_backend.h"

#include <cuda_runtime.h>
#include <gtest/gtest.h>

#include "xchainer/context.h"
#include "xchainer/cuda/cuda_runtime.h"
#include "xchainer/device.h"

namespace xchainer {
namespace cuda {
namespace {

TEST(CudaBackendTest, GetDeviceCount) {
    Context ctx;
    int count = 0;
    CheckError(cudaGetDeviceCount(&count));
    EXPECT_EQ(count, CudaBackend(ctx).GetDeviceCount());
}

TEST(CudaBackendTest, GetDevice) {
    Context ctx;
    CudaBackend backend{ctx};
    {
        Device& device = backend.GetDevice(0);
        EXPECT_EQ(&backend, &device.backend());
        EXPECT_EQ(0, device.index());
    }
    {
        Device& device3 = backend.GetDevice(3);
        Device& device2 = backend.GetDevice(2);
        EXPECT_EQ(&backend, &device3.backend());
        EXPECT_EQ(3, device3.index());
        EXPECT_EQ(&backend, &device2.backend());
        EXPECT_EQ(2, device2.index());
    }
    {
        EXPECT_THROW(backend.GetDevice(-1), std::out_of_range);
        EXPECT_THROW(backend.GetDevice(backend.GetDeviceCount() + 1), std::out_of_range);
    }
}

TEST(CudaBackendTest, GetName) {
    Context ctx;
    EXPECT_EQ("cuda", CudaBackend(ctx).GetName());
}

TEST(CudaBackendTest, SupportsTransfer) {
    Context ctx;
    CudaBackend backend{ctx};
    Device& device0 = backend.GetDevice(0);
    Device& device1 = backend.GetDevice(1);

    EXPECT_TRUE(backend.SupportsTransfer(device0, device0));
    EXPECT_TRUE(backend.SupportsTransfer(device0, device1));
}

// Data transfer test
class CudaBackendTransferTest : public ::testing::TestWithParam<::testing::tuple<int, int>> {};

TEST_P(CudaBackendTransferTest, TransferTo) {
    Context ctx;
    CudaBackend backend{ctx};
    Device& device0 = backend.GetDevice(::testing::get<0>(GetParam()));
    Device& device1 = backend.GetDevice(::testing::get<1>(GetParam()));

    size_t bytesize = 5;
    auto data = device1.Allocate(bytesize);

    // Transfer
    // TODO(niboshi): Offset is fixed to 0
    std::tuple<std::shared_ptr<void>, size_t> tuple = device0.TransferDataTo(device1, data, 0, bytesize);

    EXPECT_EQ(0, std::memcmp(data.get(), std::get<0>(tuple).get(), bytesize));
    // TODO(niboshi): Test offset

    cudaPointerAttributes attr = {};
    CheckError(cudaPointerGetAttributes(&attr, std::get<0>(tuple).get()));
    EXPECT_TRUE(attr.isManaged);
    EXPECT_EQ(device1.index(), attr.device);
}

TEST_P(CudaBackendTransferTest, TransferFrom) {
    Context ctx;
    CudaBackend backend{ctx};
    Device& device0 = backend.GetDevice(::testing::get<0>(GetParam()));
    Device& device1 = backend.GetDevice(::testing::get<1>(GetParam()));

    size_t bytesize = 5;
    auto data = device1.Allocate(bytesize);

    // Transfer
    // TODO(niboshi): Offset is fixed to 0
    std::tuple<std::shared_ptr<void>, size_t> tuple = device1.TransferDataFrom(device0, data, 0, bytesize);

    EXPECT_EQ(0, std::memcmp(data.get(), std::get<0>(tuple).get(), bytesize));
    // TODO(niboshi): Test offset

    cudaPointerAttributes attr = {};
    CheckError(cudaPointerGetAttributes(&attr, std::get<0>(tuple).get()));
    EXPECT_TRUE(attr.isManaged);
    EXPECT_EQ(device1.index(), attr.device);
}

INSTANTIATE_TEST_CASE_P(Device, CudaBackendTransferTest,
                        ::testing::Values(std::make_tuple(0, 0),  // transfer between same devices
                                          std::make_tuple(0, 1)   // transfer between dfferent CUDA devices
                                          ));

}  // namespace
}  // namespace cuda
}  // namespace xchainer
<commit_msg>clang-format<commit_after>#include "xchainer/cuda/cuda_backend.h"

#include <cuda_runtime.h>
#include <gtest/gtest.h>

#include "xchainer/context.h"
#include "xchainer/cuda/cuda_runtime.h"
#include "xchainer/device.h"

namespace xchainer {
namespace cuda {
namespace {

TEST(CudaBackendTest, GetDeviceCount) {
    Context ctx;
    int count = 0;
    CheckError(cudaGetDeviceCount(&count));
    EXPECT_EQ(count, CudaBackend(ctx).GetDeviceCount());
}

TEST(CudaBackendTest, GetDevice) {
    Context ctx;
    CudaBackend backend{ctx};
    {
        Device& device = backend.GetDevice(0);
        EXPECT_EQ(&backend, &device.backend());
        EXPECT_EQ(0, device.index());
    }
    {
        Device& device3 = backend.GetDevice(3);
        Device& device2 = backend.GetDevice(2);
        EXPECT_EQ(&backend, &device3.backend());
        EXPECT_EQ(3, device3.index());
        EXPECT_EQ(&backend, &device2.backend());
        EXPECT_EQ(2, device2.index());
    }
    {
        EXPECT_THROW(backend.GetDevice(-1), std::out_of_range);
        EXPECT_THROW(backend.GetDevice(backend.GetDeviceCount() + 1), std::out_of_range);
    }
}

TEST(CudaBackendTest, GetName) {
    Context ctx;
    EXPECT_EQ("cuda", CudaBackend(ctx).GetName());
}

TEST(CudaBackendTest, SupportsTransfer) {
    Context ctx;
    CudaBackend backend{ctx};
    Device& device0 = backend.GetDevice(0);
    Device& device1 = backend.GetDevice(1);

    EXPECT_TRUE(backend.SupportsTransfer(device0, device0));
    EXPECT_TRUE(backend.SupportsTransfer(device0, device1));
}

// Data transfer test
class CudaBackendTransferTest : public ::testing::TestWithParam<::testing::tuple<int, int>> {};

TEST_P(CudaBackendTransferTest, TransferTo) {
    Context ctx;
    CudaBackend backend{ctx};
    Device& device0 = backend.GetDevice(::testing::get<0>(GetParam()));
    Device& device1 = backend.GetDevice(::testing::get<1>(GetParam()));

    size_t bytesize = 5;
    auto data = device1.Allocate(bytesize);

    // Transfer
    // TODO(niboshi): Offset is fixed to 0
    std::tuple<std::shared_ptr<void>, size_t> tuple = device0.TransferDataTo(device1, data, 0, bytesize);

    EXPECT_EQ(0, std::memcmp(data.get(), std::get<0>(tuple).get(), bytesize));
    // TODO(niboshi): Test offset

    cudaPointerAttributes attr = {};
    CheckError(cudaPointerGetAttributes(&attr, std::get<0>(tuple).get()));
    EXPECT_TRUE(attr.isManaged);
    EXPECT_EQ(device1.index(), attr.device);
}

TEST_P(CudaBackendTransferTest, TransferFrom) {
    Context ctx;
    CudaBackend backend{ctx};
    Device& device0 = backend.GetDevice(::testing::get<0>(GetParam()));
    Device& device1 = backend.GetDevice(::testing::get<1>(GetParam()));

    size_t bytesize = 5;
    auto data = device1.Allocate(bytesize);

    // Transfer
    // TODO(niboshi): Offset is fixed to 0
    std::tuple<std::shared_ptr<void>, size_t> tuple = device1.TransferDataFrom(device0, data, 0, bytesize);

    EXPECT_EQ(0, std::memcmp(data.get(), std::get<0>(tuple).get(), bytesize));
    // TODO(niboshi): Test offset

    cudaPointerAttributes attr = {};
    CheckError(cudaPointerGetAttributes(&attr, std::get<0>(tuple).get()));
    EXPECT_TRUE(attr.isManaged);
    EXPECT_EQ(device1.index(), attr.device);
}

INSTANTIATE_TEST_CASE_P(Device, CudaBackendTransferTest, ::testing::Values(std::make_tuple(0, 0),  // transfer between same devices
                                                                           std::make_tuple(0, 1)   // transfer between dfferent CUDA devices
                                                                           ));

}  // namespace
}  // namespace cuda
}  // namespace xchainer
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: xformsapi.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: hr $ $Date: 2006-06-19 18:58:07 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include "xformsapi.hxx"

#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/xforms/XFormsSupplier.hpp>
#include <com/sun/star/xforms/XDataTypeRepository.hpp>
#include <com/sun/star/xforms/XModel.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/xsd/DataTypeClass.hpp>

#include <unotools/processfactory.hxx>
#include <tools/debug.hxx>

#include <xmltoken.hxx>
#include <nmspmap.hxx>
#include <xmlnmspe.hxx>
#include <xmltkmap.hxx>

using rtl::OUString;
using com::sun::star::uno::Reference;
using com::sun::star::uno::Sequence;
using com::sun::star::uno::UNO_QUERY;
using com::sun::star::uno::UNO_QUERY_THROW;
using com::sun::star::beans::XPropertySet;
using com::sun::star::container::XNameAccess;
using com::sun::star::lang::XMultiServiceFactory;
using com::sun::star::xforms::XFormsSupplier;
using com::sun::star::xforms::XDataTypeRepository;
using com::sun::star::container::XNameContainer;
using utl::getProcessServiceFactory;
using com::sun::star::uno::makeAny;
using com::sun::star::uno::Any;
using com::sun::star::uno::Exception;

using namespace com::sun::star;
using namespace xmloff::token;

Reference<XPropertySet> lcl_createPropertySet( const OUString& rServiceName )
{
    Reference<XMultiServiceFactory> xFactory = getProcessServiceFactory();
    DBG_ASSERT( xFactory.is(), "can't get service factory" );

    Reference<XPropertySet> xModel( xFactory->createInstance( rServiceName ),
                                    UNO_QUERY_THROW );
    DBG_ASSERT( xModel.is(), "can't create model" );

    return xModel;
}

Reference<XPropertySet> lcl_createXFormsModel()
{
    return lcl_createPropertySet( OUSTRING( "com.sun.star.xforms.Model" ) );
}

Reference<XPropertySet> lcl_createXFormsBinding()
{
    return lcl_createPropertySet( OUSTRING( "com.sun.star.xforms.Binding" ) );
}

void lcl_addXFormsModel(
    const Reference<frame::XModel>& xDocument,
    const Reference<XPropertySet>& xModel )
{
    bool bSuccess = false;
    try
    {
        Reference<XFormsSupplier> xSupplier( xDocument, UNO_QUERY );
        if( xSupplier.is() )
        {
            Reference<XNameContainer> xForms = xSupplier->getXForms();
            if( xForms.is() )
            {
                OUString sName;
                xModel->getPropertyValue( OUSTRING("ID")) >>= sName;
                xForms->insertByName( sName, makeAny( xModel ) );
                bSuccess = true;
            }
        }
    }
    catch( const Exception& )
    {
        ; // no success!
    }

    // TODO: implement proper error handling
    DBG_ASSERT( bSuccess, "can't import model" );
}

Reference<XPropertySet> lcl_findXFormsBindingOrSubmission(
    Reference<frame::XModel>& xDocument,
    const rtl::OUString& rBindingID,
    bool bBinding )
{
    // find binding by iterating over all models, and look for the
    // given binding ID

    Reference<XPropertySet> xRet;
    try
    {
        // get supplier
        Reference<XFormsSupplier> xSupplier( xDocument, UNO_QUERY );
        if( xSupplier.is() )
        {
            // get XForms models
            Reference<XNameContainer> xForms = xSupplier->getXForms();
            if( xForms.is() )
            {
                // iterate over all models
                Sequence<OUString> aNames = xForms->getElementNames();
                const OUString* pNames = aNames.getConstArray();
                sal_Int32 nNames = aNames.getLength();
                for( sal_Int32 n = 0; (n < nNames) && !xRet.is(); n++ )
                {
                    Reference<xforms::XModel> xModel(
                        xForms->getByName( pNames[n] ), UNO_QUERY );
                    if( xModel.is() )
                    {
                        // ask model for bindings
                        Reference<XNameAccess> xBindings(
                            bBinding
                                ? xModel->getBindings()
                                : xModel->getSubmissions(),
                            UNO_QUERY_THROW );

                        // finally, ask binding for name
                        if( xBindings->hasByName( rBindingID ) )
                            xRet.set( xBindings->getByName( rBindingID ),
                                      UNO_QUERY );
                    }
                }
            }
        }
    }
    catch( const Exception& )
    {
        ; // no success!
    }

    if( ! xRet.is() )
        ; // TODO: rImport.SetError(...);

    return xRet;
}

Reference<XPropertySet> lcl_findXFormsBinding(
    Reference<frame::XModel>& xDocument,
    const rtl::OUString& rBindingID )
{
    return lcl_findXFormsBindingOrSubmission( xDocument, rBindingID, true );
}

Reference<XPropertySet> lcl_findXFormsSubmission(
    Reference<frame::XModel>& xDocument,
    const rtl::OUString& rBindingID )
{
    return lcl_findXFormsBindingOrSubmission( xDocument, rBindingID, false );
}

void lcl_setValue( Reference<XPropertySet>& xPropertySet,
                   const OUString& rName,
                   const Any rAny )
{
    xPropertySet->setPropertyValue( rName, rAny );
}

Reference<XPropertySet> lcl_getXFormsModel( const Reference<frame::XModel>& xDoc )
{
    Reference<XPropertySet> xRet;
    try
    {
        Reference<XFormsSupplier> xSupplier( xDoc, UNO_QUERY );
        if( xSupplier.is() )
        {
            Reference<XNameContainer> xForms = xSupplier->getXForms();
            if( xForms.is() )
            {
                Sequence<OUString> aNames = xForms->getElementNames();
                if( aNames.getLength() > 0 )
                    xRet.set( xForms->getByName( aNames[0] ), UNO_QUERY );
            }
        }
    }
    catch( const Exception& )
    {
        ; // no success!
    }

    return xRet;
}

#define TOKEN_MAP_ENTRY(NAMESPACE,TOKEN) { XML_NAMESPACE_##NAMESPACE, xmloff::token::XML_##TOKEN, xmloff::token::XML_##TOKEN }
static SvXMLTokenMapEntry aTypes[] =
{
    TOKEN_MAP_ENTRY( XSD, STRING  ),
    TOKEN_MAP_ENTRY( XSD, DECIMAL ),
    TOKEN_MAP_ENTRY( XSD, DOUBLE ),
    TOKEN_MAP_ENTRY( XSD, FLOAT ),
    TOKEN_MAP_ENTRY( XSD, BOOLEAN ),
    TOKEN_MAP_ENTRY( XSD, ANYURI ),
    TOKEN_MAP_ENTRY( XSD, DATETIME_XSD ),
    TOKEN_MAP_ENTRY( XSD, DATE ),
    TOKEN_MAP_ENTRY( XSD, TIME ),
    TOKEN_MAP_ENTRY( XSD, YEAR ),
    TOKEN_MAP_ENTRY( XSD, MONTH ),
    TOKEN_MAP_ENTRY( XSD, DAY ),
    XML_TOKEN_MAP_END
};

sal_uInt16 lcl_getTypeClass(
    const Reference<XDataTypeRepository>&
    #ifdef DBG_UTIL
    xRepository
    #endif
    ,
    const SvXMLNamespaceMap& rNamespaceMap,
    const OUString& rXMLName )
{
    // translate name into token for local name
    OUString sLocalName;
    sal_uInt16 nPrefix = rNamespaceMap.GetKeyByAttrName(rXMLName, &sLocalName);
    SvXMLTokenMap aMap( aTypes );
    sal_uInt16 mnToken = aMap.Get( nPrefix, sLocalName );

    sal_uInt16 nTypeClass = com::sun::star::xsd::DataTypeClass::STRING;
    if( mnToken != XML_TOK_UNKNOWN )
    {
        // we found an XSD name: then get the proper API name for it
        DBG_ASSERT( xRepository.is(), "can't find type without repository");
        switch( mnToken )
        {
        case XML_STRING:
            nTypeClass = com::sun::star::xsd::DataTypeClass::STRING;
            break;
        case XML_ANYURI:
            nTypeClass = com::sun::star::xsd::DataTypeClass::anyURI;
            break;
        case XML_DECIMAL:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DECIMAL;
            break;
        case XML_DOUBLE:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DOUBLE;
            break;
        case XML_FLOAT:
            nTypeClass = com::sun::star::xsd::DataTypeClass::FLOAT;
            break;
        case XML_BOOLEAN:
            nTypeClass = com::sun::star::xsd::DataTypeClass::BOOLEAN;
            break;
        case XML_DATETIME_XSD:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DATETIME;
            break;
        case XML_DATE:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DATE;
            break;
        case XML_TIME:
            nTypeClass = com::sun::star::xsd::DataTypeClass::TIME;
            break;
        case XML_YEAR:
            nTypeClass = com::sun::star::xsd::DataTypeClass::gYear;
            break;
        case XML_DAY:
            nTypeClass = com::sun::star::xsd::DataTypeClass::gDay;
            break;
        case XML_MONTH:
            nTypeClass = com::sun::star::xsd::DataTypeClass::gMonth;
            break;

            /* data types not yet supported:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DURATION;
            nTypeClass = com::sun::star::xsd::DataTypeClass::gYearMonth;
            nTypeClass = com::sun::star::xsd::DataTypeClass::gMonthDay;
            nTypeClass = com::sun::star::xsd::DataTypeClass::hexBinary;
            nTypeClass = com::sun::star::xsd::DataTypeClass::base64Binary;
            nTypeClass = com::sun::star::xsd::DataTypeClass::QName;
            nTypeClass = com::sun::star::xsd::DataTypeClass::NOTATION;
            */
        }
    }

    return nTypeClass;
}

rtl::OUString lcl_getTypeName(
    const Reference<XDataTypeRepository>& xRepository,
    const SvXMLNamespaceMap& rNamespaceMap,
    const OUString& rXMLName )
{
    OUString sLocalName;
    sal_uInt16 nPrefix = rNamespaceMap.GetKeyByAttrName(rXMLName, &sLocalName);
    SvXMLTokenMap aMap( aTypes );
    sal_uInt16 mnToken = aMap.Get( nPrefix, sLocalName );
    return ( mnToken == XML_TOK_UNKNOWN )
        ? rXMLName
        : lcl_getBasicTypeName( xRepository, rNamespaceMap, rXMLName );
}

rtl::OUString lcl_getBasicTypeName(
    const Reference<XDataTypeRepository>& xRepository,
    const SvXMLNamespaceMap& rNamespaceMap,
    const OUString& rXMLName )
{
    OUString sTypeName = rXMLName;
    try
    {
        sTypeName =
            xRepository->getBasicDataType(
                lcl_getTypeClass( xRepository, rNamespaceMap, rXMLName ) )
            ->getName();
    }
    catch( const Exception& )
    {
        DBG_ERROR( "exception during type creation" );
    }
    return sTypeName;
}
<commit_msg>INTEGRATION: CWS pchfix02 (1.5.34); FILE MERGED 2006/09/01 18:00:22 kaib 1.5.34.1: #i68856# Added header markers and pch files<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: xformsapi.cxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-17 11:31:54 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_xmloff.hxx"

#include "xformsapi.hxx"

#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/xforms/XFormsSupplier.hpp>
#include <com/sun/star/xforms/XDataTypeRepository.hpp>
#include <com/sun/star/xforms/XModel.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/xsd/DataTypeClass.hpp>

#include <unotools/processfactory.hxx>
#include <tools/debug.hxx>

#include <xmltoken.hxx>
#include <nmspmap.hxx>
#include <xmlnmspe.hxx>
#include <xmltkmap.hxx>

using rtl::OUString;
using com::sun::star::uno::Reference;
using com::sun::star::uno::Sequence;
using com::sun::star::uno::UNO_QUERY;
using com::sun::star::uno::UNO_QUERY_THROW;
using com::sun::star::beans::XPropertySet;
using com::sun::star::container::XNameAccess;
using com::sun::star::lang::XMultiServiceFactory;
using com::sun::star::xforms::XFormsSupplier;
using com::sun::star::xforms::XDataTypeRepository;
using com::sun::star::container::XNameContainer;
using utl::getProcessServiceFactory;
using com::sun::star::uno::makeAny;
using com::sun::star::uno::Any;
using com::sun::star::uno::Exception;

using namespace com::sun::star;
using namespace xmloff::token;

Reference<XPropertySet> lcl_createPropertySet( const OUString& rServiceName )
{
    Reference<XMultiServiceFactory> xFactory = getProcessServiceFactory();
    DBG_ASSERT( xFactory.is(), "can't get service factory" );

    Reference<XPropertySet> xModel( xFactory->createInstance( rServiceName ),
                                    UNO_QUERY_THROW );
    DBG_ASSERT( xModel.is(), "can't create model" );

    return xModel;
}

Reference<XPropertySet> lcl_createXFormsModel()
{
    return lcl_createPropertySet( OUSTRING( "com.sun.star.xforms.Model" ) );
}

Reference<XPropertySet> lcl_createXFormsBinding()
{
    return lcl_createPropertySet( OUSTRING( "com.sun.star.xforms.Binding" ) );
}

void lcl_addXFormsModel(
    const Reference<frame::XModel>& xDocument,
    const Reference<XPropertySet>& xModel )
{
    bool bSuccess = false;
    try
    {
        Reference<XFormsSupplier> xSupplier( xDocument, UNO_QUERY );
        if( xSupplier.is() )
        {
            Reference<XNameContainer> xForms = xSupplier->getXForms();
            if( xForms.is() )
            {
                OUString sName;
                xModel->getPropertyValue( OUSTRING("ID")) >>= sName;
                xForms->insertByName( sName, makeAny( xModel ) );
                bSuccess = true;
            }
        }
    }
    catch( const Exception& )
    {
        ; // no success!
    }

    // TODO: implement proper error handling
    DBG_ASSERT( bSuccess, "can't import model" );
}

Reference<XPropertySet> lcl_findXFormsBindingOrSubmission(
    Reference<frame::XModel>& xDocument,
    const rtl::OUString& rBindingID,
    bool bBinding )
{
    // find binding by iterating over all models, and look for the
    // given binding ID

    Reference<XPropertySet> xRet;
    try
    {
        // get supplier
        Reference<XFormsSupplier> xSupplier( xDocument, UNO_QUERY );
        if( xSupplier.is() )
        {
            // get XForms models
            Reference<XNameContainer> xForms = xSupplier->getXForms();
            if( xForms.is() )
            {
                // iterate over all models
                Sequence<OUString> aNames = xForms->getElementNames();
                const OUString* pNames = aNames.getConstArray();
                sal_Int32 nNames = aNames.getLength();
                for( sal_Int32 n = 0; (n < nNames) && !xRet.is(); n++ )
                {
                    Reference<xforms::XModel> xModel(
                        xForms->getByName( pNames[n] ), UNO_QUERY );
                    if( xModel.is() )
                    {
                        // ask model for bindings
                        Reference<XNameAccess> xBindings(
                            bBinding
                                ? xModel->getBindings()
                                : xModel->getSubmissions(),
                            UNO_QUERY_THROW );

                        // finally, ask binding for name
                        if( xBindings->hasByName( rBindingID ) )
                            xRet.set( xBindings->getByName( rBindingID ),
                                      UNO_QUERY );
                    }
                }
            }
        }
    }
    catch( const Exception& )
    {
        ; // no success!
    }

    if( ! xRet.is() )
        ; // TODO: rImport.SetError(...);

    return xRet;
}

Reference<XPropertySet> lcl_findXFormsBinding(
    Reference<frame::XModel>& xDocument,
    const rtl::OUString& rBindingID )
{
    return lcl_findXFormsBindingOrSubmission( xDocument, rBindingID, true );
}

Reference<XPropertySet> lcl_findXFormsSubmission(
    Reference<frame::XModel>& xDocument,
    const rtl::OUString& rBindingID )
{
    return lcl_findXFormsBindingOrSubmission( xDocument, rBindingID, false );
}

void lcl_setValue( Reference<XPropertySet>& xPropertySet,
                   const OUString& rName,
                   const Any rAny )
{
    xPropertySet->setPropertyValue( rName, rAny );
}

Reference<XPropertySet> lcl_getXFormsModel( const Reference<frame::XModel>& xDoc )
{
    Reference<XPropertySet> xRet;
    try
    {
        Reference<XFormsSupplier> xSupplier( xDoc, UNO_QUERY );
        if( xSupplier.is() )
        {
            Reference<XNameContainer> xForms = xSupplier->getXForms();
            if( xForms.is() )
            {
                Sequence<OUString> aNames = xForms->getElementNames();
                if( aNames.getLength() > 0 )
                    xRet.set( xForms->getByName( aNames[0] ), UNO_QUERY );
            }
        }
    }
    catch( const Exception& )
    {
        ; // no success!
    }

    return xRet;
}

#define TOKEN_MAP_ENTRY(NAMESPACE,TOKEN) { XML_NAMESPACE_##NAMESPACE, xmloff::token::XML_##TOKEN, xmloff::token::XML_##TOKEN }
static SvXMLTokenMapEntry aTypes[] =
{
    TOKEN_MAP_ENTRY( XSD, STRING  ),
    TOKEN_MAP_ENTRY( XSD, DECIMAL ),
    TOKEN_MAP_ENTRY( XSD, DOUBLE ),
    TOKEN_MAP_ENTRY( XSD, FLOAT ),
    TOKEN_MAP_ENTRY( XSD, BOOLEAN ),
    TOKEN_MAP_ENTRY( XSD, ANYURI ),
    TOKEN_MAP_ENTRY( XSD, DATETIME_XSD ),
    TOKEN_MAP_ENTRY( XSD, DATE ),
    TOKEN_MAP_ENTRY( XSD, TIME ),
    TOKEN_MAP_ENTRY( XSD, YEAR ),
    TOKEN_MAP_ENTRY( XSD, MONTH ),
    TOKEN_MAP_ENTRY( XSD, DAY ),
    XML_TOKEN_MAP_END
};

sal_uInt16 lcl_getTypeClass(
    const Reference<XDataTypeRepository>&
    #ifdef DBG_UTIL
    xRepository
    #endif
    ,
    const SvXMLNamespaceMap& rNamespaceMap,
    const OUString& rXMLName )
{
    // translate name into token for local name
    OUString sLocalName;
    sal_uInt16 nPrefix = rNamespaceMap.GetKeyByAttrName(rXMLName, &sLocalName);
    SvXMLTokenMap aMap( aTypes );
    sal_uInt16 mnToken = aMap.Get( nPrefix, sLocalName );

    sal_uInt16 nTypeClass = com::sun::star::xsd::DataTypeClass::STRING;
    if( mnToken != XML_TOK_UNKNOWN )
    {
        // we found an XSD name: then get the proper API name for it
        DBG_ASSERT( xRepository.is(), "can't find type without repository");
        switch( mnToken )
        {
        case XML_STRING:
            nTypeClass = com::sun::star::xsd::DataTypeClass::STRING;
            break;
        case XML_ANYURI:
            nTypeClass = com::sun::star::xsd::DataTypeClass::anyURI;
            break;
        case XML_DECIMAL:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DECIMAL;
            break;
        case XML_DOUBLE:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DOUBLE;
            break;
        case XML_FLOAT:
            nTypeClass = com::sun::star::xsd::DataTypeClass::FLOAT;
            break;
        case XML_BOOLEAN:
            nTypeClass = com::sun::star::xsd::DataTypeClass::BOOLEAN;
            break;
        case XML_DATETIME_XSD:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DATETIME;
            break;
        case XML_DATE:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DATE;
            break;
        case XML_TIME:
            nTypeClass = com::sun::star::xsd::DataTypeClass::TIME;
            break;
        case XML_YEAR:
            nTypeClass = com::sun::star::xsd::DataTypeClass::gYear;
            break;
        case XML_DAY:
            nTypeClass = com::sun::star::xsd::DataTypeClass::gDay;
            break;
        case XML_MONTH:
            nTypeClass = com::sun::star::xsd::DataTypeClass::gMonth;
            break;

            /* data types not yet supported:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DURATION;
            nTypeClass = com::sun::star::xsd::DataTypeClass::gYearMonth;
            nTypeClass = com::sun::star::xsd::DataTypeClass::gMonthDay;
            nTypeClass = com::sun::star::xsd::DataTypeClass::hexBinary;
            nTypeClass = com::sun::star::xsd::DataTypeClass::base64Binary;
            nTypeClass = com::sun::star::xsd::DataTypeClass::QName;
            nTypeClass = com::sun::star::xsd::DataTypeClass::NOTATION;
            */
        }
    }

    return nTypeClass;
}

rtl::OUString lcl_getTypeName(
    const Reference<XDataTypeRepository>& xRepository,
    const SvXMLNamespaceMap& rNamespaceMap,
    const OUString& rXMLName )
{
    OUString sLocalName;
    sal_uInt16 nPrefix = rNamespaceMap.GetKeyByAttrName(rXMLName, &sLocalName);
    SvXMLTokenMap aMap( aTypes );
    sal_uInt16 mnToken = aMap.Get( nPrefix, sLocalName );
    return ( mnToken == XML_TOK_UNKNOWN )
        ? rXMLName
        : lcl_getBasicTypeName( xRepository, rNamespaceMap, rXMLName );
}

rtl::OUString lcl_getBasicTypeName(
    const Reference<XDataTypeRepository>& xRepository,
    const SvXMLNamespaceMap& rNamespaceMap,
    const OUString& rXMLName )
{
    OUString sTypeName = rXMLName;
    try
    {
        sTypeName =
            xRepository->getBasicDataType(
                lcl_getTypeClass( xRepository, rNamespaceMap, rXMLName ) )
            ->getName();
    }
    catch( const Exception& )
    {
        DBG_ERROR( "exception during type creation" );
    }
    return sTypeName;
}
<|endoftext|>


<commit_before>#include "BoundingBox.hpp"
#include "MotionPlanner.hpp"
#include <limits> // for numeric_limits

#include "boost/polygon/voronoi.hpp"
using boost::polygon::voronoi_builder;
using boost::polygon::voronoi_diagram;

namespace Slic3r {

MotionPlanner::MotionPlanner(const ExPolygons &islands)
    : islands(islands), initialized(false)
{}

MotionPlanner::~MotionPlanner()
{
    for (std::vector<MotionPlannerGraph*>::iterator graph = this->graphs.begin(); graph != this->graphs.end(); ++graph)
        delete *graph;
}

size_t
MotionPlanner::islands_count() const
{
    return this->islands.size();
}

void
MotionPlanner::initialize()
{
    if (this->initialized) return;
    if (this->islands.empty()) return;  // prevent initialization of empty BoundingBox

    ExPolygons expp;
    for (ExPolygons::const_iterator island = this->islands.begin(); island != this->islands.end(); ++island) {
        island->simplify(SCALED_EPSILON, expp);
    }
    this->islands = expp;

    // loop through islands in order to create inner expolygons and collect their contours
    this->inner.reserve(this->islands.size());
    Polygons outer_holes;
    for (ExPolygons::const_iterator island = this->islands.begin(); island != this->islands.end(); ++island) {
        this->inner.push_back(ExPolygonCollection());
        offset(*island, &this->inner.back().expolygons, -MP_INNER_MARGIN);

        outer_holes.push_back(island->contour);
    }

    // grow island contours in order to prepare holes of the outer environment
    // This is actually wrong because it might merge contours that are close,
    // thus confusing the island check in shortest_path() below
    //offset(outer_holes, &outer_holes, +MP_OUTER_MARGIN);

    // generate outer contour as bounding box of everything
    Points points;
    for (Polygons::const_iterator contour = outer_holes.begin(); contour != outer_holes.end(); ++contour)
        points.insert(points.end(), contour->points.begin(), contour->points.end());
    BoundingBox bb(points);

    // grow outer contour
    Polygons contour;
    offset(bb.polygon(), &contour, +MP_OUTER_MARGIN);
    assert(contour.size() == 1);

    // make expolygon for outer environment
    ExPolygons outer;
    diff(contour, outer_holes, &outer);
    assert(outer.size() == 1);
    this->outer = outer.front();

    this->graphs.resize(this->islands.size() + 1, NULL);
    this->initialized = true;
}

ExPolygonCollection
MotionPlanner::get_env(size_t island_idx) const
{
    if (island_idx == -1) {
        return ExPolygonCollection(this->outer);
    } else {
        return this->inner[island_idx];
    }
}

void
MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyline)
{
    // lazy generation of configuration space
    if (!this->initialized) this->initialize();

    // if we have an empty configuration space, return a straight move
    if (this->islands.empty()) {
        polyline->points.push_back(from);
        polyline->points.push_back(to);
        return;
    }

    // Are both points in the same island?
    int island_idx = -1;
    for (ExPolygons::const_iterator island = this->islands.begin(); island != this->islands.end(); ++island) {
        if (island->contains(from) && island->contains(to)) {
            // since both points are in the same island, is a direct move possible?
            // if so, we avoid generating the visibility environment
            if (island->contains(Line(from, to))) {
                polyline->points.push_back(from);
                polyline->points.push_back(to);
                return;
            }
            island_idx = island - this->islands.begin();
            break;
        }
    }

    // Now check whether points are inside the environment.
    Point inner_from    = from;
    Point inner_to      = to;
    bool from_is_inside, to_is_inside;

    ExPolygonCollection env = this->get_env(island_idx);
    if (!(from_is_inside = env.contains(from))) {
        // Find the closest inner point to start from.
        inner_from = this->nearest_env_point(env, from, to);
    }
    if (!(to_is_inside = env.contains(to))) {
        // Find the closest inner point to start from.
        inner_to = this->nearest_env_point(env, to, inner_from);
    }

    // perform actual path search
    MotionPlannerGraph* graph = this->init_graph(island_idx);
    graph->shortest_path(graph->find_node(inner_from), graph->find_node(inner_to), polyline);

    polyline->points.insert(polyline->points.begin(), from);
    polyline->points.push_back(to);

    {
        // grow our environment slightly in order for simplify_by_visibility()
        // to work best by considering moves on boundaries valid as well
        ExPolygonCollection grown_env;
        offset(env, &grown_env.expolygons, +SCALED_EPSILON);

        // remove unnecessary vertices
        polyline->simplify_by_visibility(grown_env);
    }

    /*
        SVG svg("shortest_path.svg");
        svg.draw(this->outer);
        svg.arrows = false;
        for (MotionPlannerGraph::adjacency_list_t::const_iterator it = graph->adjacency_list.begin(); it != graph->adjacency_list.end(); ++it) {
            Point a = graph->nodes[it - graph->adjacency_list.begin()];
            for (std::vector<MotionPlannerGraph::neighbor>::const_iterator n = it->begin(); n != it->end(); ++n) {
                Point b = graph->nodes[n->target];
                svg.draw(Line(a, b));
            }
        }
        svg.arrows = true;
        svg.draw(from);
        svg.draw(inner_from, "red");
        svg.draw(to);
        svg.draw(inner_to, "red");
        svg.draw(*polyline, "red");
        svg.Close();
    */
}

Point
MotionPlanner::nearest_env_point(const ExPolygonCollection &env, const Point &from, const Point &to) const
{
    /*  In order to ensure that the move between 'from' and the initial env point does
        not violate any of the configuration space boundaries, we limit our search to
        the points that satisfy this condition. */

    /*  Assume that this method is never called when 'env' contains 'from';
        so 'from' is either inside a hole or outside all contours */

    // get the points of the hole containing 'from', if any
    Points pp;
    for (ExPolygons::const_iterator ex = env.expolygons.begin(); ex != env.expolygons.end(); ++ex) {
        for (Polygons::const_iterator h = ex->holes.begin(); h != ex->holes.end(); ++h) {
            if (h->contains(from)) {
                pp = *h;
            }
        }
        if (!pp.empty()) break;
    }

    /*  If 'from' is not inside a hole, it's outside of all contours, so take all
        contours' points */
    if (pp.empty()) {
        for (ExPolygons::const_iterator ex = env.expolygons.begin(); ex != env.expolygons.end(); ++ex) {
            Points contour_pp = ex->contour;
            pp.insert(pp.end(), contour_pp.begin(), contour_pp.end());
        }
    }

    /*  Find the candidate result and check that it doesn't cross any boundary.
        (We could skip all of the above polygon finding logic and directly test all points
        in env, but this way we probably reduce complexity). */
    Polygons env_pp = env;
    while (pp.size() >= 2) {
        // find the point in pp that is closest to both 'from' and 'to'
        size_t result = from.nearest_waypoint_index(pp, to);

        if (intersects((Lines)Line(from, pp[result]), env_pp)) {
            // discard result
            pp.erase(pp.begin() + result);
        } else {
            return pp[result];
        }
    }

    // if we're here, return last point (better than nothing)
    return pp.front();
}

MotionPlannerGraph*
MotionPlanner::init_graph(int island_idx)
{
    if (this->graphs[island_idx + 1] == NULL) {
        // if this graph doesn't exist, initialize it
        MotionPlannerGraph* graph = this->graphs[island_idx + 1] = new MotionPlannerGraph();

        /*  We don't add polygon boundaries as graph edges, because we'd need to connect
            them to the Voronoi-generated edges by recognizing coinciding nodes. */

        typedef voronoi_diagram<double> VD;
        VD vd;

        // mapping between Voronoi vertices and graph nodes
        typedef std::map<const VD::vertex_type*,size_t> t_vd_vertices;
        t_vd_vertices vd_vertices;

        // get boundaries as lines
        ExPolygonCollection env = this->get_env(island_idx);
        Lines lines = env.lines();
        boost::polygon::construct_voronoi(lines.begin(), lines.end(), &vd);

        // traverse the Voronoi diagram and generate graph nodes and edges
        for (VD::const_edge_iterator edge = vd.edges().begin(); edge != vd.edges().end(); ++edge) {
            if (edge->is_infinite()) continue;

            const VD::vertex_type* v0 = edge->vertex0();
            const VD::vertex_type* v1 = edge->vertex1();
            Point p0 = Point(v0->x(), v0->y());
            Point p1 = Point(v1->x(), v1->y());

            // skip edge if any of its endpoints is outside our configuration space
            if (!env.contains_b(p0) || !env.contains_b(p1)) continue;

            t_vd_vertices::const_iterator i_v0 = vd_vertices.find(v0);
            size_t v0_idx;
            if (i_v0 == vd_vertices.end()) {
                graph->nodes.push_back(p0);
                vd_vertices[v0] = v0_idx = graph->nodes.size()-1;
            } else {
                v0_idx = i_v0->second;
            }

            t_vd_vertices::const_iterator i_v1 = vd_vertices.find(v1);
            size_t v1_idx;
            if (i_v1 == vd_vertices.end()) {
                graph->nodes.push_back(p1);
                vd_vertices[v1] = v1_idx = graph->nodes.size()-1;
            } else {
                v1_idx = i_v1->second;
            }

            // Euclidean distance is used as weight for the graph edge
            double dist = graph->nodes[v0_idx].distance_to(graph->nodes[v1_idx]);
            graph->add_edge(v0_idx, v1_idx, dist);
        }

        return graph;
    }
    return this->graphs[island_idx + 1];
}

void
MotionPlannerGraph::add_edge(size_t from, size_t to, double weight)
{
    // extend adjacency list until this start node
    if (this->adjacency_list.size() < from+1)
        this->adjacency_list.resize(from+1);

    this->adjacency_list[from].push_back(neighbor(to, weight));
}

size_t
MotionPlannerGraph::find_node(const Point &point) const
{
    /*
    for (Points::const_iterator p = this->nodes.begin(); p != this->nodes.end(); ++p) {
        if (p->coincides_with(point)) return p - this->nodes.begin();
    }
    */
    return point.nearest_point_index(this->nodes);
}

void
MotionPlannerGraph::shortest_path(size_t from, size_t to, Polyline* polyline)
{
    // this prevents a crash in case for some reason we got here with an empty adjacency list
    if (this->adjacency_list.empty()) return;

    const weight_t max_weight = std::numeric_limits<weight_t>::infinity();

    std::vector<weight_t> dist;
    std::vector<node_t> previous;
    {
        // number of nodes
        size_t n = this->adjacency_list.size();

        // initialize dist and previous
        dist.clear();
        dist.resize(n, max_weight);
        dist[from] = 0;  // distance from 'from' to itself
        previous.clear();
        previous.resize(n, -1);

        // initialize the Q with all nodes
        std::set<node_t> Q;
        for (node_t i = 0; i < n; ++i) Q.insert(i);

        while (!Q.empty())
        {
            // get node in Q having the minimum dist ('from' in the first loop)
            node_t u;
            {
                double min_dist = -1;
                for (std::set<node_t>::const_iterator n = Q.begin(); n != Q.end(); ++n) {
                    if (dist[*n] < min_dist || min_dist == -1) {
                        u = *n;
                        min_dist = dist[*n];
                    }
                }
            }
            Q.erase(u);

            // stop searching if we reached our destination
            if (u == to) break;

            // Visit each edge starting from node u
            const std::vector<neighbor> &neighbors = this->adjacency_list[u];
            for (std::vector<neighbor>::const_iterator neighbor_iter = neighbors.begin();
                 neighbor_iter != neighbors.end();
                 neighbor_iter++)
            {
                // neighbor node is v
                node_t v = neighbor_iter->target;

                // skip if we already visited this
                if (Q.find(v) == Q.end()) continue;

                // calculate total distance
                weight_t alt = dist[u] + neighbor_iter->weight;

                // if total distance through u is shorter than the previous
                // distance (if any) between 'from' and 'v', replace it
                if (alt < dist[v]) {
                    dist[v]     = alt;
                    previous[v] = u;
                }

            }
        }
    }

    for (node_t vertex = to; vertex != -1; vertex = previous[vertex])
        polyline->points.push_back(this->nodes[vertex]);
    polyline->points.push_back(this->nodes[from]);
    polyline->reverse();
}

#ifdef SLIC3RXS
REGISTER_CLASS(MotionPlanner, "MotionPlanner");
#endif

}
<commit_msg>Fixed segfault in new MotionPlanner code when environments were empty (small islands). #2511<commit_after>#include "BoundingBox.hpp"
#include "MotionPlanner.hpp"
#include <limits> // for numeric_limits

#include "boost/polygon/voronoi.hpp"
using boost::polygon::voronoi_builder;
using boost::polygon::voronoi_diagram;

namespace Slic3r {

MotionPlanner::MotionPlanner(const ExPolygons &islands)
    : islands(islands), initialized(false)
{}

MotionPlanner::~MotionPlanner()
{
    for (std::vector<MotionPlannerGraph*>::iterator graph = this->graphs.begin(); graph != this->graphs.end(); ++graph)
        delete *graph;
}

size_t
MotionPlanner::islands_count() const
{
    return this->islands.size();
}

void
MotionPlanner::initialize()
{
    if (this->initialized) return;
    if (this->islands.empty()) return;  // prevent initialization of empty BoundingBox

    ExPolygons expp;
    for (ExPolygons::const_iterator island = this->islands.begin(); island != this->islands.end(); ++island) {
        island->simplify(SCALED_EPSILON, expp);
    }
    this->islands = expp;

    // loop through islands in order to create inner expolygons and collect their contours
    this->inner.reserve(this->islands.size());
    Polygons outer_holes;
    for (ExPolygons::const_iterator island = this->islands.begin(); island != this->islands.end(); ++island) {
        this->inner.push_back(ExPolygonCollection());
        offset(*island, &this->inner.back().expolygons, -MP_INNER_MARGIN);

        outer_holes.push_back(island->contour);
    }

    // grow island contours in order to prepare holes of the outer environment
    // This is actually wrong because it might merge contours that are close,
    // thus confusing the island check in shortest_path() below
    //offset(outer_holes, &outer_holes, +MP_OUTER_MARGIN);

    // generate outer contour as bounding box of everything
    Points points;
    for (Polygons::const_iterator contour = outer_holes.begin(); contour != outer_holes.end(); ++contour)
        points.insert(points.end(), contour->points.begin(), contour->points.end());
    BoundingBox bb(points);

    // grow outer contour
    Polygons contour;
    offset(bb.polygon(), &contour, +MP_OUTER_MARGIN);
    assert(contour.size() == 1);

    // make expolygon for outer environment
    ExPolygons outer;
    diff(contour, outer_holes, &outer);
    assert(outer.size() == 1);
    this->outer = outer.front();

    this->graphs.resize(this->islands.size() + 1, NULL);
    this->initialized = true;
}

ExPolygonCollection
MotionPlanner::get_env(size_t island_idx) const
{
    if (island_idx == -1) {
        return ExPolygonCollection(this->outer);
    } else {
        return this->inner[island_idx];
    }
}

void
MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyline)
{
    // lazy generation of configuration space
    if (!this->initialized) this->initialize();

    // if we have an empty configuration space, return a straight move
    if (this->islands.empty()) {
        polyline->points.push_back(from);
        polyline->points.push_back(to);
        return;
    }

    // Are both points in the same island?
    int island_idx = -1;
    for (ExPolygons::const_iterator island = this->islands.begin(); island != this->islands.end(); ++island) {
        if (island->contains(from) && island->contains(to)) {
            // since both points are in the same island, is a direct move possible?
            // if so, we avoid generating the visibility environment
            if (island->contains(Line(from, to))) {
                polyline->points.push_back(from);
                polyline->points.push_back(to);
                return;
            }
            island_idx = island - this->islands.begin();
            break;
        }
    }

    // get environment
    ExPolygonCollection env = this->get_env(island_idx);
    if (env.expolygons.empty()) {
        // if this environment is empty (probably because it's too small), perform straight move
        // and avoid running the algorithms on empty dataset
        polyline->points.push_back(from);
        polyline->points.push_back(to);
        return; // bye bye
    }

    // Now check whether points are inside the environment.
    Point inner_from    = from;
    Point inner_to      = to;
    bool from_is_inside, to_is_inside;

    if (!(from_is_inside = env.contains(from))) {
        // Find the closest inner point to start from.
        inner_from = this->nearest_env_point(env, from, to);
    }
    if (!(to_is_inside = env.contains(to))) {
        // Find the closest inner point to start from.
        inner_to = this->nearest_env_point(env, to, inner_from);
    }

    // perform actual path search
    MotionPlannerGraph* graph = this->init_graph(island_idx);
    graph->shortest_path(graph->find_node(inner_from), graph->find_node(inner_to), polyline);

    polyline->points.insert(polyline->points.begin(), from);
    polyline->points.push_back(to);

    {
        // grow our environment slightly in order for simplify_by_visibility()
        // to work best by considering moves on boundaries valid as well
        ExPolygonCollection grown_env;
        offset(env, &grown_env.expolygons, +SCALED_EPSILON);

        // remove unnecessary vertices
        polyline->simplify_by_visibility(grown_env);
    }

    /*
        SVG svg("shortest_path.svg");
        svg.draw(this->outer);
        svg.arrows = false;
        for (MotionPlannerGraph::adjacency_list_t::const_iterator it = graph->adjacency_list.begin(); it != graph->adjacency_list.end(); ++it) {
            Point a = graph->nodes[it - graph->adjacency_list.begin()];
            for (std::vector<MotionPlannerGraph::neighbor>::const_iterator n = it->begin(); n != it->end(); ++n) {
                Point b = graph->nodes[n->target];
                svg.draw(Line(a, b));
            }
        }
        svg.arrows = true;
        svg.draw(from);
        svg.draw(inner_from, "red");
        svg.draw(to);
        svg.draw(inner_to, "red");
        svg.draw(*polyline, "red");
        svg.Close();
    */
}

Point
MotionPlanner::nearest_env_point(const ExPolygonCollection &env, const Point &from, const Point &to) const
{
    /*  In order to ensure that the move between 'from' and the initial env point does
        not violate any of the configuration space boundaries, we limit our search to
        the points that satisfy this condition. */

    /*  Assume that this method is never called when 'env' contains 'from';
        so 'from' is either inside a hole or outside all contours */

    // get the points of the hole containing 'from', if any
    Points pp;
    for (ExPolygons::const_iterator ex = env.expolygons.begin(); ex != env.expolygons.end(); ++ex) {
        for (Polygons::const_iterator h = ex->holes.begin(); h != ex->holes.end(); ++h) {
            if (h->contains(from)) {
                pp = *h;
            }
        }
        if (!pp.empty()) break;
    }

    /*  If 'from' is not inside a hole, it's outside of all contours, so take all
        contours' points */
    if (pp.empty()) {
        for (ExPolygons::const_iterator ex = env.expolygons.begin(); ex != env.expolygons.end(); ++ex) {
            Points contour_pp = ex->contour;
            pp.insert(pp.end(), contour_pp.begin(), contour_pp.end());
        }
    }

    /*  Find the candidate result and check that it doesn't cross any boundary.
        (We could skip all of the above polygon finding logic and directly test all points
        in env, but this way we probably reduce complexity). */
    Polygons env_pp = env;
    while (pp.size() >= 2) {
        // find the point in pp that is closest to both 'from' and 'to'
        size_t result = from.nearest_waypoint_index(pp, to);

        if (intersects((Lines)Line(from, pp[result]), env_pp)) {
            // discard result
            pp.erase(pp.begin() + result);
        } else {
            return pp[result];
        }
    }

    // if we're here, return last point if any (better than nothing)
    if (!pp.empty()) return pp.front();

    // if we have no points at all, then we have an empty environment and we
    // make this method behave as a no-op (we shouldn't get here by the way)
    return from;
}

MotionPlannerGraph*
MotionPlanner::init_graph(int island_idx)
{
    if (this->graphs[island_idx + 1] == NULL) {
        // if this graph doesn't exist, initialize it
        MotionPlannerGraph* graph = this->graphs[island_idx + 1] = new MotionPlannerGraph();

        /*  We don't add polygon boundaries as graph edges, because we'd need to connect
            them to the Voronoi-generated edges by recognizing coinciding nodes. */

        typedef voronoi_diagram<double> VD;
        VD vd;

        // mapping between Voronoi vertices and graph nodes
        typedef std::map<const VD::vertex_type*,size_t> t_vd_vertices;
        t_vd_vertices vd_vertices;

        // get boundaries as lines
        ExPolygonCollection env = this->get_env(island_idx);
        Lines lines = env.lines();
        boost::polygon::construct_voronoi(lines.begin(), lines.end(), &vd);

        // traverse the Voronoi diagram and generate graph nodes and edges
        for (VD::const_edge_iterator edge = vd.edges().begin(); edge != vd.edges().end(); ++edge) {
            if (edge->is_infinite()) continue;

            const VD::vertex_type* v0 = edge->vertex0();
            const VD::vertex_type* v1 = edge->vertex1();
            Point p0 = Point(v0->x(), v0->y());
            Point p1 = Point(v1->x(), v1->y());

            // skip edge if any of its endpoints is outside our configuration space
            if (!env.contains_b(p0) || !env.contains_b(p1)) continue;

            t_vd_vertices::const_iterator i_v0 = vd_vertices.find(v0);
            size_t v0_idx;
            if (i_v0 == vd_vertices.end()) {
                graph->nodes.push_back(p0);
                vd_vertices[v0] = v0_idx = graph->nodes.size()-1;
            } else {
                v0_idx = i_v0->second;
            }

            t_vd_vertices::const_iterator i_v1 = vd_vertices.find(v1);
            size_t v1_idx;
            if (i_v1 == vd_vertices.end()) {
                graph->nodes.push_back(p1);
                vd_vertices[v1] = v1_idx = graph->nodes.size()-1;
            } else {
                v1_idx = i_v1->second;
            }

            // Euclidean distance is used as weight for the graph edge
            double dist = graph->nodes[v0_idx].distance_to(graph->nodes[v1_idx]);
            graph->add_edge(v0_idx, v1_idx, dist);
        }

        return graph;
    }
    return this->graphs[island_idx + 1];
}

void
MotionPlannerGraph::add_edge(size_t from, size_t to, double weight)
{
    // extend adjacency list until this start node
    if (this->adjacency_list.size() < from+1)
        this->adjacency_list.resize(from+1);

    this->adjacency_list[from].push_back(neighbor(to, weight));
}

size_t
MotionPlannerGraph::find_node(const Point &point) const
{
    /*
    for (Points::const_iterator p = this->nodes.begin(); p != this->nodes.end(); ++p) {
        if (p->coincides_with(point)) return p - this->nodes.begin();
    }
    */
    return point.nearest_point_index(this->nodes);
}

void
MotionPlannerGraph::shortest_path(size_t from, size_t to, Polyline* polyline)
{
    // this prevents a crash in case for some reason we got here with an empty adjacency list
    if (this->adjacency_list.empty()) return;

    const weight_t max_weight = std::numeric_limits<weight_t>::infinity();

    std::vector<weight_t> dist;
    std::vector<node_t> previous;
    {
        // number of nodes
        size_t n = this->adjacency_list.size();

        // initialize dist and previous
        dist.clear();
        dist.resize(n, max_weight);
        dist[from] = 0;  // distance from 'from' to itself
        previous.clear();
        previous.resize(n, -1);

        // initialize the Q with all nodes
        std::set<node_t> Q;
        for (node_t i = 0; i < n; ++i) Q.insert(i);

        while (!Q.empty())
        {
            // get node in Q having the minimum dist ('from' in the first loop)
            node_t u;
            {
                double min_dist = -1;
                for (std::set<node_t>::const_iterator n = Q.begin(); n != Q.end(); ++n) {
                    if (dist[*n] < min_dist || min_dist == -1) {
                        u = *n;
                        min_dist = dist[*n];
                    }
                }
            }
            Q.erase(u);

            // stop searching if we reached our destination
            if (u == to) break;

            // Visit each edge starting from node u
            const std::vector<neighbor> &neighbors = this->adjacency_list[u];
            for (std::vector<neighbor>::const_iterator neighbor_iter = neighbors.begin();
                 neighbor_iter != neighbors.end();
                 neighbor_iter++)
            {
                // neighbor node is v
                node_t v = neighbor_iter->target;

                // skip if we already visited this
                if (Q.find(v) == Q.end()) continue;

                // calculate total distance
                weight_t alt = dist[u] + neighbor_iter->weight;

                // if total distance through u is shorter than the previous
                // distance (if any) between 'from' and 'v', replace it
                if (alt < dist[v]) {
                    dist[v]     = alt;
                    previous[v] = u;
                }

            }
        }
    }

    for (node_t vertex = to; vertex != -1; vertex = previous[vertex])
        polyline->points.push_back(this->nodes[vertex]);
    polyline->points.push_back(this->nodes[from]);
    polyline->reverse();
}

#ifdef SLIC3RXS
REGISTER_CLASS(MotionPlanner, "MotionPlanner");
#endif

}
<|endoftext|>


<commit_before>#include <QDebug>
#include "appcontroller.h"
#include "appwindow.h"

#include "widgets/loginwidget.h"
#include "widgets/librarywidget.h"

//TODO: Minimizing to tray and restoring the window multiple times, after moving it for the first time and while not maximized, cause the window to move if taskbars are present. Cause not known.

AppController::AppController(QObject *parent) :
    QObject(parent)
{
    api = new ItchioApi(this);

    setupSettings();

    showTrayIcon();
    showAppWindow();
}

void AppController::setupSettings()
{
    settingsFile = "settings.scratch";
    settings = new AppSettings(settingsFile, QSettings::IniFormat);

    api->login("hue", "hue", settings->loadSettings(API_KEY));
}

void AppController::hide()
{
    appWindow->showMinimized();

    appWindow->setWindowFlags(appWindow->windowFlags() ^ Qt::Tool);
}

void AppController::show()
{
    appWindow->setWindowFlags(appWindow->windowFlags() ^ Qt::Tool);

    appWindow->show();
    appWindow->setWindowState((appWindow->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
}

void AppController::quit()
{
    QCoreApplication::exit();
}

void AppController::trayIconDoubleClick(QSystemTrayIcon::ActivationReason reason)
{
    if(reason == QSystemTrayIcon::DoubleClick
            && !appWindow->isVisible()) {
        show();
    }
}

void AppController::showTrayIcon()
{
    trayIcon = new QSystemTrayIcon(this);
    trayIconMenu = new QMenu();

    actionQuit = new QAction("Quit", this);

    connect (actionQuit, SIGNAL(triggered()), this, SLOT(quit()));
    connect (trayIcon,SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
             this, SLOT(trayIconDoubleClick(QSystemTrayIcon::ActivationReason)));

    trayIconMenu->addAction (actionQuit);

    trayIcon->setIcon(QIcon(":/images/images/itchio-icon-16.png"));
    trayIcon->setContextMenu (trayIconMenu);
    trayIcon->show();
}

void AppController::showTrayIconNotification(TrayNotifications notification, int duration)
{
    if(trayIcon->supportsMessages()) {
        switch(notification)
        {
            case NOTIFICATION_TEST:
                trayIcon->showMessage("Title", "Test", QSystemTrayIcon::Information, duration);
                break;
        }
    }
}

void AppController::showAppWindow()
{
    appWindow = new AppWindow(this);
    appWindow->setWindowIcon(QIcon(":/images/images/itchio-icon-200.png"));

    appWindow->show();
}

void AppController::onLogin()
{
    settings->saveSettings(API_KEY, api->userKey);
    settings->saveSettings(USERNAME, api->userName);

    appWindow->loginWidget->hide();
    appWindow->loginWidget->deleteLater();
    appWindow->setupLibrary();
}
<commit_msg>Added conditional to api key login.<commit_after>#include <QDebug>
#include "appcontroller.h"
#include "appwindow.h"

#include "widgets/loginwidget.h"
#include "widgets/librarywidget.h"

//TODO: Minimizing to tray and restoring the window multiple times, after moving it for the first time and while not maximized, cause the window to move if taskbars are present. Cause not known.

AppController::AppController(QObject *parent) :
    QObject(parent)
{
    api = new ItchioApi(this);

    setupSettings();

    showTrayIcon();
    showAppWindow();
}

void AppController::setupSettings()
{
    settingsFile = "settings.scratch";
    settings = new AppSettings(settingsFile, QSettings::IniFormat);

    if(settings->loadSettings(API_KEY) != "") api->login("hue", "hue", settings->loadSettings(API_KEY));
}

void AppController::hide()
{
    appWindow->showMinimized();

    appWindow->setWindowFlags(appWindow->windowFlags() ^ Qt::Tool);
}

void AppController::show()
{
    appWindow->setWindowFlags(appWindow->windowFlags() ^ Qt::Tool);

    appWindow->show();
    appWindow->setWindowState((appWindow->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
}

void AppController::quit()
{
    QCoreApplication::exit();
}

void AppController::trayIconDoubleClick(QSystemTrayIcon::ActivationReason reason)
{
    if(reason == QSystemTrayIcon::DoubleClick
            && !appWindow->isVisible()) {
        show();
    }
}

void AppController::showTrayIcon()
{
    trayIcon = new QSystemTrayIcon(this);
    trayIconMenu = new QMenu();

    actionQuit = new QAction("Quit", this);

    connect (actionQuit, SIGNAL(triggered()), this, SLOT(quit()));
    connect (trayIcon,SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
             this, SLOT(trayIconDoubleClick(QSystemTrayIcon::ActivationReason)));

    trayIconMenu->addAction (actionQuit);

    trayIcon->setIcon(QIcon(":/images/images/itchio-icon-16.png"));
    trayIcon->setContextMenu (trayIconMenu);
    trayIcon->show();
}

void AppController::showTrayIconNotification(TrayNotifications notification, int duration)
{
    if(trayIcon->supportsMessages()) {
        switch(notification)
        {
            case NOTIFICATION_TEST:
                trayIcon->showMessage("Title", "Test", QSystemTrayIcon::Information, duration);
                break;
        }
    }
}

void AppController::showAppWindow()
{
    appWindow = new AppWindow(this);
    appWindow->setWindowIcon(QIcon(":/images/images/itchio-icon-200.png"));

    appWindow->show();
}

void AppController::onLogin()
{
    settings->saveSettings(API_KEY, api->userKey);
    settings->saveSettings(USERNAME, api->userName);

    appWindow->loginWidget->hide();
    appWindow->loginWidget->deleteLater();
    appWindow->setupLibrary();
}
<|endoftext|>


<commit_before>/*
 * Copyright 2014-present Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <folly/String.h>

#include <boost/algorithm/string.hpp>
#include <folly/Benchmark.h>
#include <folly/Random.h>
#include <random>

using namespace folly;
using namespace std;

BENCHMARK(libc_tolower, iters) {
  static const size_t kSize = 256;
  // This array is static to keep the compiler from optimizing the
  // entire function down to a no-op if it has an inlined impl of
  // tolower and thus is able to tell that there are no side-effects.
  // No side-effects + no writes to anything other than local variables
  // + no return value = no need to run any of the code in the function.
  // gcc, for example, makes that optimization with -O2.
  static char input[kSize];
  for (size_t i = 0; i < kSize; i++) {
    input[i] = (char)(i & 0xff);
  }
  for (auto i = iters; i > 0; i--) {
    for (size_t offset = 0; offset < kSize; offset++) {
      input[offset] = tolower(input[offset]);
    }
  }
}

BENCHMARK(folly_toLowerAscii, iters) {
  static const size_t kSize = 256;
  static char input[kSize];
  for (size_t i = 0; i < kSize; i++) {
    input[i] = (char)(i & 0xff);
  }
  for (auto i = iters; i > 0; i--) {
    folly::toLowerAscii(input, kSize);
  }
}

// A simple benchmark that tests various output sizes for a simple
// input; the goal is to measure the output buffer resize code cost.
void stringPrintfOutputSize(int iters, int param) {
  string buffer;
  BENCHMARK_SUSPEND {
    buffer.resize(param, 'x');
  }

  for (int64_t i = 0; i < iters; ++i) {
    string s = stringPrintf("msg: %d, %d, %s", 10, 20, buffer.c_str());
  }
}

// The first few of these tend to fit in the inline buffer, while the
// subsequent ones cross that limit, trigger a second vsnprintf, and
// exercise a different codepath.
BENCHMARK_PARAM(stringPrintfOutputSize, 1)
BENCHMARK_PARAM(stringPrintfOutputSize, 4)
BENCHMARK_PARAM(stringPrintfOutputSize, 16)
BENCHMARK_PARAM(stringPrintfOutputSize, 64)
BENCHMARK_PARAM(stringPrintfOutputSize, 256)
BENCHMARK_PARAM(stringPrintfOutputSize, 1024)

// Benchmark simple stringAppendf behavior to show a pathology Lovro
// reported (t5735468).
BENCHMARK(stringPrintfAppendfBenchmark, iters) {
  for (unsigned int i = 0; i < iters; ++i) {
    string s;
    BENCHMARK_SUSPEND {
      s.reserve(300000);
    }
    for (int j = 0; j < 300000; ++j) {
      stringAppendf(&s, "%d", 1);
    }
  }
}

namespace {
fbstring cbmString;
fbstring cbmEscapedString;
fbstring cEscapedString;
fbstring cUnescapedString;
const size_t kCBmStringLength = 64 << 10;
const uint32_t kCPrintablePercentage = 90;

fbstring uribmString;
fbstring uribmEscapedString;
fbstring uriEscapedString;
fbstring uriUnescapedString;
const size_t kURIBmStringLength = 256;
const uint32_t kURIPassThroughPercentage = 50;

fbstring hexlifyInput;
fbstring hexlifyOutput;
const size_t kHexlifyLength = 1024;

void initBenchmark() {
  std::mt19937 rnd;

  // C escape
  std::uniform_int_distribution<uint32_t> printable(32, 126);
  std::uniform_int_distribution<uint32_t> nonPrintable(0, 160);
  std::uniform_int_distribution<uint32_t> percentage(0, 99);

  cbmString.reserve(kCBmStringLength);
  for (size_t i = 0; i < kCBmStringLength; ++i) {
    unsigned char c;
    if (percentage(rnd) < kCPrintablePercentage) {
      c = printable(rnd);
    } else {
      c = nonPrintable(rnd);
      // Generate characters in both non-printable ranges:
      // 0..31 and 127..255
      if (c >= 32) {
        c += (126 - 32) + 1;
      }
    }
    cbmString.push_back(c);
  }

  cbmEscapedString = cEscape<fbstring>(cbmString);

  // URI escape
  std::uniform_int_distribution<uint32_t> passthrough('a', 'z');
  std::string encodeChars = " ?!\"',+[]";
  std::uniform_int_distribution<uint32_t> encode(0, encodeChars.size() - 1);

  uribmString.reserve(kURIBmStringLength);
  for (size_t i = 0; i < kURIBmStringLength; ++i) {
    unsigned char c;
    if (percentage(rnd) < kURIPassThroughPercentage) {
      c = passthrough(rnd);
    } else {
      c = encodeChars[encode(rnd)];
    }
    uribmString.push_back(c);
  }

  uribmEscapedString = uriEscape<fbstring>(uribmString);

  // hexlify
  hexlifyInput.resize(kHexlifyLength);
  Random::secureRandom(&hexlifyInput[0], kHexlifyLength);
  folly::hexlify(hexlifyInput, hexlifyOutput);
}

BENCHMARK(BM_cEscape, iters) {
  while (iters--) {
    cEscapedString = cEscape<fbstring>(cbmString);
    doNotOptimizeAway(cEscapedString.size());
  }
}

BENCHMARK(BM_cUnescape, iters) {
  while (iters--) {
    cUnescapedString = cUnescape<fbstring>(cbmEscapedString);
    doNotOptimizeAway(cUnescapedString.size());
  }
}

BENCHMARK(BM_uriEscape, iters) {
  while (iters--) {
    uriEscapedString = uriEscape<fbstring>(uribmString);
    doNotOptimizeAway(uriEscapedString.size());
  }
}

BENCHMARK(BM_uriUnescape, iters) {
  while (iters--) {
    uriUnescapedString = uriUnescape<fbstring>(uribmEscapedString);
    doNotOptimizeAway(uriUnescapedString.size());
  }
}

BENCHMARK(BM_unhexlify, iters) {
  // iters/sec = bytes output per sec
  std::string unhexed;
  folly::StringPiece hex = hexlifyOutput;
  for (; iters >= hex.size(); iters -= hex.size()) {
    folly::unhexlify(hex, unhexed);
  }
  iters -= iters % 2; // round down to an even number of chars
  hex = hex.subpiece(0, iters);
  folly::unhexlify(hex, unhexed);
}

} // namespace

//////////////////////////////////////////////////////////////////////

BENCHMARK(splitOnSingleChar, iters) {
  static const std::string line = "one:two:three:four";
  for (size_t i = 0; i < iters << 4; ++i) {
    std::vector<StringPiece> pieces;
    folly::split(':', line, pieces);
  }
}

BENCHMARK(splitOnSingleCharFixed, iters) {
  static const std::string line = "one:two:three:four";
  for (size_t i = 0; i < iters << 4; ++i) {
    StringPiece a, b, c, d;
    folly::split(':', line, a, b, c, d);
  }
}

BENCHMARK(splitOnSingleCharFixedAllowExtra, iters) {
  static const std::string line = "one:two:three:four";
  for (size_t i = 0; i < iters << 4; ++i) {
    StringPiece a, b, c, d;
    folly::split<false>(':', line, a, b, c, d);
  }
}

BENCHMARK(splitStr, iters) {
  static const std::string line = "one-*-two-*-three-*-four";
  for (size_t i = 0; i < iters << 4; ++i) {
    std::vector<StringPiece> pieces;
    folly::split("-*-", line, pieces);
  }
}

BENCHMARK(splitStrFixed, iters) {
  static const std::string line = "one-*-two-*-three-*-four";
  for (size_t i = 0; i < iters << 4; ++i) {
    StringPiece a, b, c, d;
    folly::split("-*-", line, a, b, c, d);
  }
}

BENCHMARK(boost_splitOnSingleChar, iters) {
  static const std::string line = "one:two:three:four";
  bool (*pred)(char) = [](char c) -> bool { return c == ':'; };
  for (size_t i = 0; i < iters << 4; ++i) {
    std::vector<boost::iterator_range<std::string::const_iterator>> pieces;
    boost::split(pieces, line, pred);
  }
}

BENCHMARK(joinCharStr, iters) {
  static const std::vector<std::string> input = {
      "one", "two", "three", "four", "five", "six", "seven"};
  for (size_t i = 0; i < iters << 4; ++i) {
    std::string output;
    folly::join(':', input, output);
  }
}

BENCHMARK(joinStrStr, iters) {
  static const std::vector<std::string> input = {
      "one", "two", "three", "four", "five", "six", "seven"};
  for (size_t i = 0; i < iters << 4; ++i) {
    std::string output;
    folly::join(":", input, output);
  }
}

BENCHMARK(joinInt, iters) {
  static const auto input = {123, 456, 78910, 1112, 1314, 151, 61718};
  for (size_t i = 0; i < iters << 4; ++i) {
    std::string output;
    folly::join(":", input, output);
  }
}

int main(int argc, char** argv) {
  gflags::ParseCommandLineFlags(&argc, &argv, true);
  initBenchmark();
  folly::runBenchmarks();
  return 0;
}

/*
Results on x86_64:
============================================================================
folly/test/StringBenchmark.cpp                  relative  time/iter  iters/s
============================================================================
libc_tolower                                               773.30ns    1.29M
folly_toLowerAscii                                          65.04ns   15.38M
stringPrintfOutputSize(1)                                  224.67ns    4.45M
stringPrintfOutputSize(4)                                  231.53ns    4.32M
stringPrintfOutputSize(16)                                 286.54ns    3.49M
stringPrintfOutputSize(64)                                 305.47ns    3.27M
stringPrintfOutputSize(256)                                  1.48us  674.45K
stringPrintfOutputSize(1024)                                 5.89us  169.72K
stringPrintfAppendfBenchmark                                34.43ms    29.04
BM_cEscape                                                 461.51us    2.17K
BM_cUnescape                                               328.19us    3.05K
BM_uriEscape                                                 4.36us  229.25K
BM_uriUnescape                                               2.22us  450.64K
splitOnSingleChar                                            1.46us  687.21K
splitOnSingleCharFixed                                     133.02ns    7.52M
splitOnSingleCharFixedAllowExtra                            74.35ns   13.45M
splitStr                                                     2.36us  424.00K
splitStrFixed                                              282.38ns    3.54M
boost_splitOnSingleChar                                      2.83us  353.12K
joinCharStr                                                  2.65us  376.93K
joinStrStr                                                   2.64us  378.09K
joinInt                                                      3.89us  257.37K
============================================================================
*/
<commit_msg>Add benchmarks similar to `stringPrintf` for `folly::Format` and `fmt`<commit_after>/*
 * Copyright 2014-present Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <fmt/format.h>
#include <folly/Format.h>
#include <folly/String.h>

#include <boost/algorithm/string.hpp>
#include <folly/Benchmark.h>
#include <folly/Random.h>
#include <random>

using namespace folly;
using namespace std;

BENCHMARK(libc_tolower, iters) {
  static const size_t kSize = 256;
  // This array is static to keep the compiler from optimizing the
  // entire function down to a no-op if it has an inlined impl of
  // tolower and thus is able to tell that there are no side-effects.
  // No side-effects + no writes to anything other than local variables
  // + no return value = no need to run any of the code in the function.
  // gcc, for example, makes that optimization with -O2.
  static char input[kSize];
  for (size_t i = 0; i < kSize; i++) {
    input[i] = (char)(i & 0xff);
  }
  for (auto i = iters; i > 0; i--) {
    for (size_t offset = 0; offset < kSize; offset++) {
      input[offset] = tolower(input[offset]);
    }
  }
}

BENCHMARK(folly_toLowerAscii, iters) {
  static const size_t kSize = 256;
  static char input[kSize];
  for (size_t i = 0; i < kSize; i++) {
    input[i] = (char)(i & 0xff);
  }
  for (auto i = iters; i > 0; i--) {
    folly::toLowerAscii(input, kSize);
  }
}

// A simple benchmark that tests various output sizes for a simple
// input; the goal is to measure the output buffer resize code cost.
const size_t kAppendBufSize = 300000;
void stringPrintfOutputSize(int iters, int param) {
  string buffer;
  BENCHMARK_SUSPEND {
    buffer.resize(param, 'x');
  }

  for (int64_t i = 0; i < iters; ++i) {
    string s = stringPrintf("msg: %d, %d, %s", 10, 20, buffer.c_str());
  }
}

// The first few of these tend to fit in the inline buffer, while the
// subsequent ones cross that limit, trigger a second vsnprintf, and
// exercise a different codepath.
BENCHMARK_PARAM(stringPrintfOutputSize, 1)
BENCHMARK_PARAM(stringPrintfOutputSize, 4)
BENCHMARK_PARAM(stringPrintfOutputSize, 16)
BENCHMARK_PARAM(stringPrintfOutputSize, 64)
BENCHMARK_PARAM(stringPrintfOutputSize, 256)
BENCHMARK_PARAM(stringPrintfOutputSize, 1024)

// Benchmark simple stringAppendf behavior to show a pathology Lovro
// reported (t5735468).
BENCHMARK(stringPrintfAppendfBenchmark, iters) {
  for (size_t i = 0; i < iters; ++i) {
    string s;
    BENCHMARK_SUSPEND {
      s.reserve(kAppendBufSize);
    }
    for (size_t j = 0; j < kAppendBufSize; ++j) {
      stringAppendf(&s, "%d", 1);
    }
    DCHECK_EQ(s.size(), kAppendBufSize);
  }
}

// A simple benchmark that tests various output sizes for a simple
// input; the goal is to measure the output buffer resize code cost.
// Intended for comparison with stringPrintf.
void fmtOutputSize(int iters, int param) {
  string buffer;
  BENCHMARK_SUSPEND {
    buffer.resize(param, 'x');
  }

  for (int64_t i = 0; i < iters; ++i) {
    string s = fmt::format("msg: {}, {}, {}", 10, 20, buffer);
  }
}

// The first few of these tend to fit in the inline buffer, while the
// subsequent ones cross that limit, trigger a second vsnprintf, and
// exercise a different codepath.
BENCHMARK_PARAM(fmtOutputSize, 1)
BENCHMARK_PARAM(fmtOutputSize, 4)
BENCHMARK_PARAM(fmtOutputSize, 16)
BENCHMARK_PARAM(fmtOutputSize, 64)
BENCHMARK_PARAM(fmtOutputSize, 256)
BENCHMARK_PARAM(fmtOutputSize, 1024)

// Benchmark simple fmt append behavior; intended as a comparison
// against stringAppendf.
BENCHMARK(fmtAppendfBenchmark, iters) {
  for (size_t i = 0; i < iters; ++i) {
    fmt::memory_buffer buf;
    for (size_t j = 0; j < kAppendBufSize; ++j) {
      fmt::format_to(buf, "{}", 1);
    }
    string s = fmt::to_string(buf);
    DCHECK_EQ(s.size(), kAppendBufSize);
  }
}

// A simple benchmark that tests various output sizes for a simple
// input; the goal is to measure the output buffer resize code cost.
// Intended for comparison with stringPrintf and fmt.
void follyFmtOutputSize(int iters, int param) {
  string buffer;
  BENCHMARK_SUSPEND {
    buffer.resize(param, 'x');
  }

  for (int64_t i = 0; i < iters; ++i) {
    string s = format("msg: {}, {}, {}", 10, 20, buffer).str();
  }
}

// The first few of these tend to fit in the inline buffer, while the
// subsequent ones cross that limit, trigger a second vsnprintf, and
// exercise a different codepath.
BENCHMARK_PARAM(follyFmtOutputSize, 1)
BENCHMARK_PARAM(follyFmtOutputSize, 4)
BENCHMARK_PARAM(follyFmtOutputSize, 16)
BENCHMARK_PARAM(follyFmtOutputSize, 64)
BENCHMARK_PARAM(follyFmtOutputSize, 256)
BENCHMARK_PARAM(follyFmtOutputSize, 1024)

// Benchmark simple fmt append behavior; intended as a comparison
// against stringAppendf.
BENCHMARK(follyFmtAppendfBenchmark, iters) {
  for (size_t i = 0; i < iters; ++i) {
    string s;
    BENCHMARK_SUSPEND {
      s.reserve(kAppendBufSize);
    }
    for (size_t j = 0; j < kAppendBufSize; ++j) {
      format("{}", 1).appendTo(s);
    }
    DCHECK_EQ(s.size(), kAppendBufSize);
  }
}

namespace {
fbstring cbmString;
fbstring cbmEscapedString;
fbstring cEscapedString;
fbstring cUnescapedString;
const size_t kCBmStringLength = 64 << 10;
const uint32_t kCPrintablePercentage = 90;

fbstring uribmString;
fbstring uribmEscapedString;
fbstring uriEscapedString;
fbstring uriUnescapedString;
const size_t kURIBmStringLength = 256;
const uint32_t kURIPassThroughPercentage = 50;

fbstring hexlifyInput;
fbstring hexlifyOutput;
const size_t kHexlifyLength = 1024;

void initBenchmark() {
  std::mt19937 rnd;

  // C escape
  std::uniform_int_distribution<uint32_t> printable(32, 126);
  std::uniform_int_distribution<uint32_t> nonPrintable(0, 160);
  std::uniform_int_distribution<uint32_t> percentage(0, 99);

  cbmString.reserve(kCBmStringLength);
  for (size_t i = 0; i < kCBmStringLength; ++i) {
    unsigned char c;
    if (percentage(rnd) < kCPrintablePercentage) {
      c = printable(rnd);
    } else {
      c = nonPrintable(rnd);
      // Generate characters in both non-printable ranges:
      // 0..31 and 127..255
      if (c >= 32) {
        c += (126 - 32) + 1;
      }
    }
    cbmString.push_back(c);
  }

  cbmEscapedString = cEscape<fbstring>(cbmString);

  // URI escape
  std::uniform_int_distribution<uint32_t> passthrough('a', 'z');
  std::string encodeChars = " ?!\"',+[]";
  std::uniform_int_distribution<uint32_t> encode(0, encodeChars.size() - 1);

  uribmString.reserve(kURIBmStringLength);
  for (size_t i = 0; i < kURIBmStringLength; ++i) {
    unsigned char c;
    if (percentage(rnd) < kURIPassThroughPercentage) {
      c = passthrough(rnd);
    } else {
      c = encodeChars[encode(rnd)];
    }
    uribmString.push_back(c);
  }

  uribmEscapedString = uriEscape<fbstring>(uribmString);

  // hexlify
  hexlifyInput.resize(kHexlifyLength);
  Random::secureRandom(&hexlifyInput[0], kHexlifyLength);
  folly::hexlify(hexlifyInput, hexlifyOutput);
}

BENCHMARK(BM_cEscape, iters) {
  while (iters--) {
    cEscapedString = cEscape<fbstring>(cbmString);
    doNotOptimizeAway(cEscapedString.size());
  }
}

BENCHMARK(BM_cUnescape, iters) {
  while (iters--) {
    cUnescapedString = cUnescape<fbstring>(cbmEscapedString);
    doNotOptimizeAway(cUnescapedString.size());
  }
}

BENCHMARK(BM_uriEscape, iters) {
  while (iters--) {
    uriEscapedString = uriEscape<fbstring>(uribmString);
    doNotOptimizeAway(uriEscapedString.size());
  }
}

BENCHMARK(BM_uriUnescape, iters) {
  while (iters--) {
    uriUnescapedString = uriUnescape<fbstring>(uribmEscapedString);
    doNotOptimizeAway(uriUnescapedString.size());
  }
}

BENCHMARK(BM_unhexlify, iters) {
  // iters/sec = bytes output per sec
  std::string unhexed;
  folly::StringPiece hex = hexlifyOutput;
  for (; iters >= hex.size(); iters -= hex.size()) {
    folly::unhexlify(hex, unhexed);
  }
  iters -= iters % 2; // round down to an even number of chars
  hex = hex.subpiece(0, iters);
  folly::unhexlify(hex, unhexed);
}

} // namespace

//////////////////////////////////////////////////////////////////////

BENCHMARK(splitOnSingleChar, iters) {
  static const std::string line = "one:two:three:four";
  for (size_t i = 0; i < iters << 4; ++i) {
    std::vector<StringPiece> pieces;
    folly::split(':', line, pieces);
  }
}

BENCHMARK(splitOnSingleCharFixed, iters) {
  static const std::string line = "one:two:three:four";
  for (size_t i = 0; i < iters << 4; ++i) {
    StringPiece a, b, c, d;
    folly::split(':', line, a, b, c, d);
  }
}

BENCHMARK(splitOnSingleCharFixedAllowExtra, iters) {
  static const std::string line = "one:two:three:four";
  for (size_t i = 0; i < iters << 4; ++i) {
    StringPiece a, b, c, d;
    folly::split<false>(':', line, a, b, c, d);
  }
}

BENCHMARK(splitStr, iters) {
  static const std::string line = "one-*-two-*-three-*-four";
  for (size_t i = 0; i < iters << 4; ++i) {
    std::vector<StringPiece> pieces;
    folly::split("-*-", line, pieces);
  }
}

BENCHMARK(splitStrFixed, iters) {
  static const std::string line = "one-*-two-*-three-*-four";
  for (size_t i = 0; i < iters << 4; ++i) {
    StringPiece a, b, c, d;
    folly::split("-*-", line, a, b, c, d);
  }
}

BENCHMARK(boost_splitOnSingleChar, iters) {
  static const std::string line = "one:two:three:four";
  bool (*pred)(char) = [](char c) -> bool { return c == ':'; };
  for (size_t i = 0; i < iters << 4; ++i) {
    std::vector<boost::iterator_range<std::string::const_iterator>> pieces;
    boost::split(pieces, line, pred);
  }
}

BENCHMARK(joinCharStr, iters) {
  static const std::vector<std::string> input = {
      "one", "two", "three", "four", "five", "six", "seven"};
  for (size_t i = 0; i < iters << 4; ++i) {
    std::string output;
    folly::join(':', input, output);
  }
}

BENCHMARK(joinStrStr, iters) {
  static const std::vector<std::string> input = {
      "one", "two", "three", "four", "five", "six", "seven"};
  for (size_t i = 0; i < iters << 4; ++i) {
    std::string output;
    folly::join(":", input, output);
  }
}

BENCHMARK(joinInt, iters) {
  static const auto input = {123, 456, 78910, 1112, 1314, 151, 61718};
  for (size_t i = 0; i < iters << 4; ++i) {
    std::string output;
    folly::join(":", input, output);
  }
}

int main(int argc, char** argv) {
  gflags::ParseCommandLineFlags(&argc, &argv, true);
  initBenchmark();
  folly::runBenchmarks();
  return 0;
}

/*
Results on x86_64:
============================================================================
folly/test/StringBenchmark.cpp                  relative  time/iter  iters/s
============================================================================
libc_tolower                                               773.30ns    1.29M
folly_toLowerAscii                                          65.04ns   15.38M
stringPrintfOutputSize(1)                                  224.67ns    4.45M
stringPrintfOutputSize(4)                                  231.53ns    4.32M
stringPrintfOutputSize(16)                                 286.54ns    3.49M
stringPrintfOutputSize(64)                                 305.47ns    3.27M
stringPrintfOutputSize(256)                                  1.48us  674.45K
stringPrintfOutputSize(1024)                                 5.89us  169.72K
stringPrintfAppendfBenchmark                                34.43ms    29.04
BM_cEscape                                                 461.51us    2.17K
BM_cUnescape                                               328.19us    3.05K
BM_uriEscape                                                 4.36us  229.25K
BM_uriUnescape                                               2.22us  450.64K
splitOnSingleChar                                            1.46us  687.21K
splitOnSingleCharFixed                                     133.02ns    7.52M
splitOnSingleCharFixedAllowExtra                            74.35ns   13.45M
splitStr                                                     2.36us  424.00K
splitStrFixed                                              282.38ns    3.54M
boost_splitOnSingleChar                                      2.83us  353.12K
joinCharStr                                                  2.65us  376.93K
joinStrStr                                                   2.64us  378.09K
joinInt                                                      3.89us  257.37K
============================================================================
*/
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2019.  All rights reserved
 */
#include "../../../StroikaPreComp.h"

#include "../../../../Foundation/Characters/Format.h"
#include "../../../../Foundation/Characters/String_Constant.h"
#include "../../../../Foundation/Containers/Sequence.h"
#include "../../../../Foundation/Debug/Trace.h"
#include "../../../../Foundation/Execution/Sleep.h"
#include "../../../../Foundation/Execution/Thread.h"
#include "../../../../Foundation/IO/Network/LinkMonitor.h"
#include "../../../../Foundation/IO/Network/Socket.h"
#include "../../../../Foundation/Streams/MemoryStream.h"

#include "../Advertisement.h"
#include "../Common.h"
#include "BasicServer.h"

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::Containers;
using namespace Stroika::Foundation::Execution;
using namespace Stroika::Foundation::IO;
using namespace Stroika::Foundation::IO::Network;

using namespace Stroika::Frameworks;
using namespace Stroika::Frameworks::UPnP;
using namespace Stroika::Frameworks::UPnP::SSDP;
using namespace Stroika::Frameworks::UPnP::SSDP::Server;

/*
********************************************************************************
******************************* BasicServer::Rep_ ******************************
********************************************************************************
*/
class BasicServer::Rep_ {
public:
    Sequence<Advertisement> fAdvertisements;
    FrequencyInfo           fFrequencyInfo;
    URI                     fLocation;
    Rep_ (const Device& d, const DeviceDescription& dd, const FrequencyInfo& fi)
        : fAdvertisements ()
        , fFrequencyInfo (fi)
        , fLocation (d.fLocation)
    {
        {
            SSDP::Advertisement dan;
            dan.fLocation = d.fLocation;
            dan.fServer   = d.fServer;
            {
                dan.fTarget = kTarget_UPNPRootDevice;
                dan.fUSN    = Format (L"uuid:%s::%s", d.fDeviceID.c_str (), kTarget_UPNPRootDevice.c_str ());
                fAdvertisements.Append (dan);
            }
            {
                dan.fUSN    = Format (L"uuid:%s", d.fDeviceID.c_str ());
                dan.fTarget = dan.fUSN;
                fAdvertisements.Append (dan);
            }
            if (not dd.fDeviceType.empty ()) {
                dan.fUSN    = Format (L"uuid:%s::%s", d.fDeviceID.c_str (), dd.fDeviceType.c_str ());
                dan.fTarget = dd.fDeviceType;
                fAdvertisements.Append (dan);
            }
        }

        StartNotifier_ ();
        StartResponder_ ();

        IO::Network::LinkMonitor lm;
        lm.AddCallback ([this] (IO::Network::LinkMonitor::LinkChange lc, String netName, String ipNum) {
            Debug::TraceContextBumper ctx{Stroika_Foundation_Debug_OptionalizeTraceArgs (L"Basic SSDP server - LinkMonitor callback", L"lc = %d, netName=%s, ipNum=%s", lc, netName.c_str (), ipNum.c_str ())};
            if (lc == IO::Network::LinkMonitor::LinkChange::eAdded) {
                this->Restart_ ();
            }
        });
        DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wpessimizing-move\"");
        fLinkMonitor_ = move (optional<IO::Network::LinkMonitor> (move (lm)));
        DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wpessimizing-move\"");
    }
    Sequence<Advertisement> GetAdjustedAdvertisements_ () const
    {
        // @todo EXPLAIN THIS LOGIC? What its doing is - if NO LOCATION HOST SPECIFIED - picka  good default (not so crazy) - but only
        // in that case do we patch/replace the location for the advertisements? That makes little sense
        if (fLocation.GetAuthority () and fLocation.GetAuthority ()->GetHost ()) {
            return fAdvertisements;
        }
        else {
            Sequence<Advertisement> revisedAdvertisements;
            URI                     useURL    = fLocation;
            URI::Authority          authority = useURL.GetAuthority ().value_or (URI::Authority{});
            authority.SetHost (URI::Host{IO::Network::GetPrimaryInternetAddress ()});
            useURL.SetAuthority (authority);
            for (auto ai : fAdvertisements) {
                ai.fLocation = useURL; // !@todo MAYBE this would make more sense replacing just the HOST part of each advertisement?
                revisedAdvertisements.Append (ai);
            }
            return revisedAdvertisements;
        }
    }
    void StartNotifier_ ()
    {
        fNotifierThread_ = Thread::New (
            [this] () {
                PeriodicNotifier l;
                l.Run (GetAdjustedAdvertisements_ (), PeriodicNotifier::FrequencyInfo ());
            },
            Thread::eAutoStart, String{L"SSDP Notifier"});
    }
    void StartResponder_ ()
    {
        fSearchResponderThread_ = Thread::New (
            [this] () {
                SearchResponder sr;
                sr.Run (GetAdjustedAdvertisements_ ());
            },
            Thread::eAutoStart, String{L"SSDP Search Responder"});
    }
    void Restart_ ()
    {
        Debug::TraceContextBumper ctx ("Restarting Basic SSDP server threads");
        {
            Thread::SuppressInterruptionInContext suppressInterruption; // critical to wait til done cuz captures this
            if (fNotifierThread_ != nullptr) {
                fNotifierThread_.AbortAndWaitForDone ();
            }
            if (fSearchResponderThread_ != nullptr) {
                fSearchResponderThread_.AbortAndWaitForDone ();
            }
        }
        StartNotifier_ ();
        StartResponder_ ();
    }
    Execution::Thread::CleanupPtr      fNotifierThread_{Execution::Thread::CleanupPtr::eAbortBeforeWaiting};
    Execution::Thread::CleanupPtr      fSearchResponderThread_{Execution::Thread::CleanupPtr::eAbortBeforeWaiting};
    optional<IO::Network::LinkMonitor> fLinkMonitor_; // optional so we can delete it first on shutdown (so no restart while stopping stuff)
};

/*
********************************************************************************
********************************** BasicServer *********************************
********************************************************************************
*/
BasicServer::BasicServer (const Device& d, const DeviceDescription& dd, const FrequencyInfo& fi)
    : fRep_ (make_shared<Rep_> (d, dd, fi))
{
}
<commit_msg>minor cleanup<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2019.  All rights reserved
 */
#include "../../../StroikaPreComp.h"

#include "../../../../Foundation/Characters/Format.h"
#include "../../../../Foundation/Characters/String_Constant.h"
#include "../../../../Foundation/Containers/Sequence.h"
#include "../../../../Foundation/Debug/Trace.h"
#include "../../../../Foundation/Execution/Sleep.h"
#include "../../../../Foundation/Execution/Thread.h"
#include "../../../../Foundation/IO/Network/LinkMonitor.h"
#include "../../../../Foundation/IO/Network/Socket.h"
#include "../../../../Foundation/Streams/MemoryStream.h"

#include "../Advertisement.h"
#include "../Common.h"
#include "BasicServer.h"

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::Containers;
using namespace Stroika::Foundation::Execution;
using namespace Stroika::Foundation::IO;
using namespace Stroika::Foundation::IO::Network;

using namespace Stroika::Frameworks;
using namespace Stroika::Frameworks::UPnP;
using namespace Stroika::Frameworks::UPnP::SSDP;
using namespace Stroika::Frameworks::UPnP::SSDP::Server;

/*
********************************************************************************
******************************* BasicServer::Rep_ ******************************
********************************************************************************
*/
class BasicServer::Rep_ {
public:
    Sequence<Advertisement> fAdvertisements;
    FrequencyInfo           fFrequencyInfo;
    URI                     fLocation;
    Rep_ (const Device& d, const DeviceDescription& dd, const FrequencyInfo& fi)
        : fAdvertisements ()
        , fFrequencyInfo (fi)
        , fLocation (d.fLocation)
    {
        {
            SSDP::Advertisement dan;
            dan.fLocation = d.fLocation;
            dan.fServer   = d.fServer;
            {
                dan.fTarget = kTarget_UPNPRootDevice;
                dan.fUSN    = Format (L"uuid:%s::%s", d.fDeviceID.c_str (), kTarget_UPNPRootDevice.c_str ());
                fAdvertisements.Append (dan);
            }
            {
                dan.fUSN    = Format (L"uuid:%s", d.fDeviceID.c_str ());
                dan.fTarget = dan.fUSN;
                fAdvertisements.Append (dan);
            }
            if (not dd.fDeviceType.empty ()) {
                dan.fUSN    = Format (L"uuid:%s::%s", d.fDeviceID.c_str (), dd.fDeviceType.c_str ());
                dan.fTarget = dd.fDeviceType;
                fAdvertisements.Append (dan);
            }
        }

        StartNotifier_ ();
        StartResponder_ ();

        IO::Network::LinkMonitor lm;
        lm.AddCallback ([this] (IO::Network::LinkMonitor::LinkChange lc, String netName, String ipNum) {
            Debug::TraceContextBumper ctx{Stroika_Foundation_Debug_OptionalizeTraceArgs (L"Basic SSDP server - LinkMonitor callback", L"lc = %d, netName=%s, ipNum=%s", lc, netName.c_str (), ipNum.c_str ())};
            if (lc == IO::Network::LinkMonitor::LinkChange::eAdded) {
                this->Restart_ ();
            }
        });
        DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wpessimizing-move\"");
        fLinkMonitor_ = move (optional<IO::Network::LinkMonitor> (move (lm)));
        DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wpessimizing-move\"");
    }
    Sequence<Advertisement> GetAdjustedAdvertisements_ () const
    {
        // @todo EXPLAIN THIS LOGIC? What its doing is - if NO LOCATION HOST SPECIFIED - picka  good default (not so crazy) - but only
        // in that case do we patch/replace the location for the advertisements? That makes little sense
        if (fLocation.GetAuthority () and fLocation.GetAuthority ()->GetHost ()) {
            return fAdvertisements;
        }
        else {
            Sequence<Advertisement> revisedAdvertisements;
            URI                     useURL    = fLocation;
            URI::Authority          authority = useURL.GetAuthority ().value_or (URI::Authority{});
            authority.SetHost (IO::Network::GetPrimaryInternetAddress ());
            useURL.SetAuthority (authority);
            for (auto ai : fAdvertisements) {
                ai.fLocation = useURL; // !@todo MAYBE this would make more sense replacing just the HOST part of each advertisement?
                revisedAdvertisements.Append (ai);
            }
            return revisedAdvertisements;
        }
    }
    void StartNotifier_ ()
    {
        fNotifierThread_ = Thread::New (
            [this] () {
                PeriodicNotifier l;
                l.Run (GetAdjustedAdvertisements_ (), PeriodicNotifier::FrequencyInfo ());
            },
            Thread::eAutoStart, String{L"SSDP Notifier"});
    }
    void StartResponder_ ()
    {
        fSearchResponderThread_ = Thread::New (
            [this] () {
                SearchResponder sr;
                sr.Run (GetAdjustedAdvertisements_ ());
            },
            Thread::eAutoStart, String{L"SSDP Search Responder"});
    }
    void Restart_ ()
    {
        Debug::TraceContextBumper ctx ("Restarting Basic SSDP server threads");
        {
            Thread::SuppressInterruptionInContext suppressInterruption; // critical to wait til done cuz captures this
            if (fNotifierThread_ != nullptr) {
                fNotifierThread_.AbortAndWaitForDone ();
            }
            if (fSearchResponderThread_ != nullptr) {
                fSearchResponderThread_.AbortAndWaitForDone ();
            }
        }
        StartNotifier_ ();
        StartResponder_ ();
    }
    Execution::Thread::CleanupPtr      fNotifierThread_{Execution::Thread::CleanupPtr::eAbortBeforeWaiting};
    Execution::Thread::CleanupPtr      fSearchResponderThread_{Execution::Thread::CleanupPtr::eAbortBeforeWaiting};
    optional<IO::Network::LinkMonitor> fLinkMonitor_; // optional so we can delete it first on shutdown (so no restart while stopping stuff)
};

/*
********************************************************************************
********************************** BasicServer *********************************
********************************************************************************
*/
BasicServer::BasicServer (const Device& d, const DeviceDescription& dd, const FrequencyInfo& fi)
    : fRep_ (make_shared<Rep_> (d, dd, fi))
{
}
<|endoftext|>