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

class qfile_vs_qnetworkaccessmanager : public QObject
{
    Q_OBJECT
    // do not use on symbian.. 100 MB is too large..
    // but.. this is a manual test anyway, so :)
protected:
    void qnamFileRead_iteration(QNetworkAccessManager &manager, QNetworkRequest &request);
    void qnamImmediateFileRead_iteration(QNetworkAccessManager &manager, QNetworkRequest &request);
    void qfileFileRead_iteration();
    static const int iterations = 10;

private slots:
    void qnamFileRead();
    void qnamImmediateFileRead();
    void qfileFileRead();

    void initTestCase();
    void cleanupTestCase();

public:
    qint64 size;
    QTemporaryFile testFile;

    qfile_vs_qnetworkaccessmanager() : QObject(), size(0) {};
};

void qfile_vs_qnetworkaccessmanager::initTestCase()
{
    testFile.open();
    QByteArray qba(1*1024*1024, 'x'); // 1 MB
    for (int i = 0; i < 100; i++) {
        testFile.write(qba);
        testFile.flush();
        size += qba.size();
    } // 100 MB
    testFile.reset();
}

void qfile_vs_qnetworkaccessmanager::cleanupTestCase()
{

}

void qfile_vs_qnetworkaccessmanager::qnamFileRead_iteration(QNetworkAccessManager &manager, QNetworkRequest &request)
{
    QNetworkReply* reply = manager.get(request);
    connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
    QTestEventLoop::instance().enterLoop(10);
    QVERIFY(!QTestEventLoop::instance().timeout());
    QByteArray qba = reply->readAll();
    delete reply;
}

void qfile_vs_qnetworkaccessmanager::qnamFileRead()
{
    QNetworkAccessManager manager;
    QTime t;
    QNetworkRequest request(QUrl(testFile.fileName()));

    // do 3 dry runs for cache warmup
    qnamFileRead_iteration(manager, request);
    qnamFileRead_iteration(manager, request);
    qnamFileRead_iteration(manager, request);

    t.start();
    // 10 real runs
    QBENCHMARK_ONCE {
        for (int i = 0; i < iterations; i++) {
            qnamFileRead_iteration(manager, request);
        }
    }

    qint64 elapsed = t.elapsed();
    qDebug() << endl << "Finished!";
    qDebug() << "Bytes:" << size;
    qDebug() << "Speed:" <<  (qreal(size*iterations) / 1024.0) / (qreal(elapsed) / 1000.0) << "KB/sec";
}

void qfile_vs_qnetworkaccessmanager::qnamImmediateFileRead_iteration(QNetworkAccessManager &manager, QNetworkRequest &request)
{
    QNetworkReply* reply = manager.get(request);
    QVERIFY(reply->isFinished()); // should be like that!
    QByteArray qba = reply->readAll();
    delete reply;
}

void qfile_vs_qnetworkaccessmanager::qnamImmediateFileRead()
{
    QNetworkAccessManager manager;
    QTime t;
    QNetworkRequest request(QUrl(testFile.fileName()));

    // do 3 dry runs for cache warmup
    qnamImmediateFileRead_iteration(manager, request);
    qnamImmediateFileRead_iteration(manager, request);
    qnamImmediateFileRead_iteration(manager, request);

    t.start();
    // 10 real runs
    QBENCHMARK_ONCE {
        for (int i = 0; i < iterations; i++) {
            qnamImmediateFileRead_iteration(manager, request);
        }
    }

    qint64 elapsed = t.elapsed();
    qDebug() << endl << "Finished!";
    qDebug() << "Bytes:" << size;
    qDebug() << "Speed:" <<  (qreal(size*iterations) / 1024.0) / (qreal(elapsed) / 1000.0) << "KB/sec";
}

void qfile_vs_qnetworkaccessmanager::qfileFileRead_iteration()
{
    testFile.reset();
    QByteArray qba = testFile.readAll();
}

void qfile_vs_qnetworkaccessmanager::qfileFileRead()
{
    QTime t;

    // do 3 dry runs for cache warmup
    qfileFileRead_iteration();
    qfileFileRead_iteration();
    qfileFileRead_iteration();

    t.start();
    // 10 real runs
    QBENCHMARK_ONCE {
        for (int i = 0; i < iterations; i++) {
            qfileFileRead_iteration();
        }
    }

    qint64 elapsed = t.elapsed();
    qDebug() << endl << "Finished!";
    qDebug() << "Bytes:" << size;
    qDebug() << "Speed:" <<  (qreal(size*iterations) / 1024.0) / (qreal(elapsed) / 1000.0) << "KB/sec";
}

QTEST_MAIN(qfile_vs_qnetworkaccessmanager)

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

class qfile_vs_qnetworkaccessmanager : public QObject
{
    Q_OBJECT
    // do not use on symbian.. 100 MB is too large..
    // but.. this is a manual test anyway, so :)
protected:
    void qnamFileRead_iteration(QNetworkAccessManager &manager, QNetworkRequest &request);
    void qnamImmediateFileRead_iteration(QNetworkAccessManager &manager, QNetworkRequest &request);
    void qfileFileRead_iteration();
    static const int iterations = 10;

private slots:
    void qnamFileRead();
    void qnamImmediateFileRead();
    void qfileFileRead();

    void initTestCase();
    void cleanupTestCase();

public:
    qint64 size;
    QTemporaryFile testFile;

    qfile_vs_qnetworkaccessmanager() : QObject(), size(0) {};
};

void qfile_vs_qnetworkaccessmanager::initTestCase()
{
    testFile.open();
    QByteArray qba(1*1024*1024, 'x'); // 1 MB
#ifdef Q_OS_SYMBIAN
    for (int i = 0; i < 10; i++) { // for Symbian only 10 MB
#else
    for (int i = 0; i < 100; i++) {
#endif
        testFile.write(qba);
        testFile.flush();
        size += qba.size();
    } // 100 MB or 10 MB
    testFile.reset();
}

void qfile_vs_qnetworkaccessmanager::cleanupTestCase()
{

}

void qfile_vs_qnetworkaccessmanager::qnamFileRead_iteration(QNetworkAccessManager &manager, QNetworkRequest &request)
{
    QNetworkReply* reply = manager.get(request);
    connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
    QTestEventLoop::instance().enterLoop(10);
    QVERIFY(!QTestEventLoop::instance().timeout());
    QByteArray qba = reply->readAll();
    delete reply;
}

void qfile_vs_qnetworkaccessmanager::qnamFileRead()
{
    QNetworkAccessManager manager;
    QTime t;
    QNetworkRequest request(QUrl::fromLocalFile(testFile.fileName()));

    // do 3 dry runs for cache warmup
    qnamFileRead_iteration(manager, request);
    qnamFileRead_iteration(manager, request);
    qnamFileRead_iteration(manager, request);

    t.start();
    // 10 real runs
    QBENCHMARK_ONCE {
        for (int i = 0; i < iterations; i++) {
            qnamFileRead_iteration(manager, request);
        }
    }

    qint64 elapsed = t.elapsed();
    qDebug() << endl << "Finished!";
    qDebug() << "Bytes:" << size;
    qDebug() << "Speed:" <<  (qreal(size*iterations) / 1024.0) / (qreal(elapsed) / 1000.0) << "KB/sec";
}

void qfile_vs_qnetworkaccessmanager::qnamImmediateFileRead_iteration(QNetworkAccessManager &manager, QNetworkRequest &request)
{
    QNetworkReply* reply = manager.get(request);
    QVERIFY(reply->isFinished()); // should be like that!
    QByteArray qba = reply->readAll();
    delete reply;
}

void qfile_vs_qnetworkaccessmanager::qnamImmediateFileRead()
{
    QNetworkAccessManager manager;
    QTime t;
    QNetworkRequest request(QUrl(testFile.fileName()));

    // do 3 dry runs for cache warmup
    qnamImmediateFileRead_iteration(manager, request);
    qnamImmediateFileRead_iteration(manager, request);
    qnamImmediateFileRead_iteration(manager, request);

    t.start();
    // 10 real runs
    QBENCHMARK_ONCE {
        for (int i = 0; i < iterations; i++) {
            qnamImmediateFileRead_iteration(manager, request);
        }
    }

    qint64 elapsed = t.elapsed();
    qDebug() << endl << "Finished!";
    qDebug() << "Bytes:" << size;
    qDebug() << "Speed:" <<  (qreal(size*iterations) / 1024.0) / (qreal(elapsed) / 1000.0) << "KB/sec";
}

void qfile_vs_qnetworkaccessmanager::qfileFileRead_iteration()
{
    testFile.reset();
    QByteArray qba = testFile.readAll();
}

void qfile_vs_qnetworkaccessmanager::qfileFileRead()
{
    QTime t;

    // do 3 dry runs for cache warmup
    qfileFileRead_iteration();
    qfileFileRead_iteration();
    qfileFileRead_iteration();

    t.start();
    // 10 real runs
    QBENCHMARK_ONCE {
        for (int i = 0; i < iterations; i++) {
            qfileFileRead_iteration();
        }
    }

    qint64 elapsed = t.elapsed();
    qDebug() << endl << "Finished!";
    qDebug() << "Bytes:" << size;
    qDebug() << "Speed:" <<  (qreal(size*iterations) / 1024.0) / (qreal(elapsed) / 1000.0) << "KB/sec";
}

QTEST_MAIN(qfile_vs_qnetworkaccessmanager)

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


<commit_before>#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>

#include <arpa/inet.h>

#include <glog/logging.h>

#include <iostream>
#include <string>

#include <boost/unordered_map.hpp>

#include "config.hpp"
#include "pid.hpp"
#include "process.hpp"

using std::istream;
using std::ostream;
using std::size_t;
using std::string;

namespace process {

UPID::UPID(const char* s)
{
  std::istringstream in(s);
  in >> *this;
}

UPID::UPID(const std::string& s)
{
  std::istringstream in(s);
  in >> *this;
}

// TODO(benh): Make this inline-able (cyclic dependency issues).
UPID::UPID(const ProcessBase& process)
{
  id = process.self().id;
  ip = process.self().ip;
  port = process.self().port;
}

UPID::operator std::string() const
{
  std::ostringstream out;
  out << *this;
  return out.str();
}

ostream& operator << (ostream& stream, const UPID& pid)
{
  // Call inet_ntop since inet_ntoa is not thread-safe!
  char ip[INET_ADDRSTRLEN];
  if (inet_ntop(AF_INET, (in_addr *) &pid.ip, ip, INET_ADDRSTRLEN) == NULL)
    memset(ip, 0, INET_ADDRSTRLEN);

  stream << pid.id << "@" << ip << ":" << pid.port;
  return stream;
}

istream& operator >> (istream& stream, UPID& pid)
{
  pid.id = "";
  pid.ip = 0;
  pid.port = 0;

  string str;
  if (!(stream >> str)) {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  VLOG(1) << "Attempting to parse '" << str << "' into a PID";

  if (str.size() == 0) {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  string id;
  string host;
  uint32_t ip;
  uint16_t port;

  size_t index = str.find('@');

  if (index != string::npos) {
    id = str.substr(0, index);
  } else {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  str = str.substr(index + 1);

  index = str.find(':');

  if (index != string::npos) {
    host = str.substr(0, index);
  } else {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  hostent he, *hep;
  char* temp;
  size_t length;
  int result;
  int herrno;

  // Allocate temporary buffer for gethostbyname2_r.
  length = 1024;
  temp = new char[length];

  while ((result = gethostbyname2_r(host.c_str(), AF_INET, &he,
				    temp, length, &hep, &herrno)) == ERANGE) {
    // Enlarge the buffer.
    delete temp;
    length *= 2;
    temp = new char[length];
  }

  if (result != 0 || hep == NULL) {
    VLOG(1) << "Failed to parse host '" << host
	    << "' because " << hstrerror(herrno);
    stream.setstate(std::ios_base::badbit);
    delete temp;
    return stream;
  }

  if (hep->h_addr_list[0] == NULL) {
    VLOG(1) << "Got no addresses for '" << host << "'";
    stream.setstate(std::ios_base::badbit);
    delete temp;
    return stream;
  }

  ip = *((uint32_t*) hep->h_addr_list[0]);

  delete temp;

  str = str.substr(index + 1);

  if (sscanf(str.c_str(), "%hu", &port) != 1) {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  pid.id = id;
  pid.ip = ip;
  pid.port = port;

  return stream;
}

size_t hash_value(const UPID& pid)
{
  size_t seed = 0;
  boost::hash_combine(seed, pid.id);
  boost::hash_combine(seed, pid.ip);
  boost::hash_combine(seed, pid.port);
  return seed;
}

} // namespace process {
<commit_msg>Made PID parsing log output require a verbosity setting of 2 instead of 1.<commit_after>#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>

#include <arpa/inet.h>

#include <glog/logging.h>

#include <iostream>
#include <string>

#include <boost/unordered_map.hpp>

#include "config.hpp"
#include "pid.hpp"
#include "process.hpp"

using std::istream;
using std::ostream;
using std::size_t;
using std::string;

namespace process {

UPID::UPID(const char* s)
{
  std::istringstream in(s);
  in >> *this;
}

UPID::UPID(const std::string& s)
{
  std::istringstream in(s);
  in >> *this;
}

// TODO(benh): Make this inline-able (cyclic dependency issues).
UPID::UPID(const ProcessBase& process)
{
  id = process.self().id;
  ip = process.self().ip;
  port = process.self().port;
}

UPID::operator std::string() const
{
  std::ostringstream out;
  out << *this;
  return out.str();
}

ostream& operator << (ostream& stream, const UPID& pid)
{
  // Call inet_ntop since inet_ntoa is not thread-safe!
  char ip[INET_ADDRSTRLEN];
  if (inet_ntop(AF_INET, (in_addr *) &pid.ip, ip, INET_ADDRSTRLEN) == NULL)
    memset(ip, 0, INET_ADDRSTRLEN);

  stream << pid.id << "@" << ip << ":" << pid.port;
  return stream;
}

istream& operator >> (istream& stream, UPID& pid)
{
  pid.id = "";
  pid.ip = 0;
  pid.port = 0;

  string str;
  if (!(stream >> str)) {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  VLOG(2) << "Attempting to parse '" << str << "' into a PID";

  if (str.size() == 0) {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  string id;
  string host;
  uint32_t ip;
  uint16_t port;

  size_t index = str.find('@');

  if (index != string::npos) {
    id = str.substr(0, index);
  } else {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  str = str.substr(index + 1);

  index = str.find(':');

  if (index != string::npos) {
    host = str.substr(0, index);
  } else {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  hostent he, *hep;
  char* temp;
  size_t length;
  int result;
  int herrno;

  // Allocate temporary buffer for gethostbyname2_r.
  length = 1024;
  temp = new char[length];

  while ((result = gethostbyname2_r(host.c_str(), AF_INET, &he,
				    temp, length, &hep, &herrno)) == ERANGE) {
    // Enlarge the buffer.
    delete temp;
    length *= 2;
    temp = new char[length];
  }

  if (result != 0 || hep == NULL) {
    VLOG(2) << "Failed to parse host '" << host
	    << "' because " << hstrerror(herrno);
    stream.setstate(std::ios_base::badbit);
    delete temp;
    return stream;
  }

  if (hep->h_addr_list[0] == NULL) {
    VLOG(2) << "Got no addresses for '" << host << "'";
    stream.setstate(std::ios_base::badbit);
    delete temp;
    return stream;
  }

  ip = *((uint32_t*) hep->h_addr_list[0]);

  delete temp;

  str = str.substr(index + 1);

  if (sscanf(str.c_str(), "%hu", &port) != 1) {
    stream.setstate(std::ios_base::badbit);
    return stream;
  }

  pid.id = id;
  pid.ip = ip;
  pid.port = port;

  return stream;
}

size_t hash_value(const UPID& pid)
{
  size_t seed = 0;
  boost::hash_combine(seed, pid.id);
  boost::hash_combine(seed, pid.ip);
  boost::hash_combine(seed, pid.port);
  return seed;
}

} // namespace process {
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: animationbasenode.cxx,v $
 *
 *  $Revision: 1.10 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-17 08:33:15 $
 *
 *  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_slideshow.hxx"

// must be first
#include "canvas/debug.hxx"
#include "canvas/verbosetrace.hxx"
#include "cppuhelper/exc_hlp.hxx"
#include "comphelper/anytostring.hxx"
#include "com/sun/star/presentation/ParagraphTarget.hpp"
#include "com/sun/star/animations/Timing.hpp"
#include "com/sun/star/animations/AnimationAdditiveMode.hpp"
#include "com/sun/star/presentation/ShapeAnimationSubType.hpp"
#include "nodetools.hxx"
#include "doctreenode.hxx"
#include "animationbasenode.hxx"
#include "delayevent.hxx"
#include "boost/bind.hpp"
#include "boost/optional.hpp"
#include <vector>
#include <algorithm>

namespace css = com::sun::star;
using namespace css;

namespace presentation {
namespace internal {

AnimationBaseNode::AnimationBaseNode(
    const uno::Reference< animations::XAnimationNode >&   xNode,
    const BaseContainerNodeSharedPtr&                     rParent,
    const NodeContext&                                    rContext )
    : BaseNode( xNode, rParent, rContext ),
      mxAnimateNode( xNode, uno::UNO_QUERY_THROW ),
      maAttributeLayerHolder(),
      mpActivity(),
      mpShape(),
      mpShapeSubset(),
      mbIsIndependentSubset( rContext.mbIsIndependentSubset )
{
    // extract native node targets
    // ===========================

    // plain shape target
    uno::Reference< drawing::XShape > xShape( mxAnimateNode->getTarget(),
                                              uno::UNO_QUERY );

    // distinguish 5 cases:
    //
    //  - plain shape target
    //  (NodeContext.mpMasterShapeSubset full set)
    //
    //  - parent-generated subset (generate an
    //  independent subset)
    //
    //  - parent-generated subset from iteration
    //  (generate a dependent subset)
    //
    //  - XShape target at the XAnimatioNode (generate
    //  a plain shape target)
    //
    //  - ParagraphTarget target at the XAnimationNode
    //  (generate an independent shape subset)
    if( rContext.mpMasterShapeSubset.get() )
    {
        if( rContext.mpMasterShapeSubset->isFullSet() )
        {
            // case 1: plain shape target from parent
            mpShape = rContext.mpMasterShapeSubset->getSubsetShape();
        }
        else
        {
            // cases 2 & 3: subset shape
            mpShapeSubset = rContext.mpMasterShapeSubset;
        }
    }
    else
    {
        // no parent-provided shape, try to extract
        // from XAnimationNode - cases 4 and 5

        // try to extract Shape from parent node's target attribute
        uno::Reference< drawing::XShape > xShape( mxAnimateNode->getTarget(),
                                                  uno::UNO_QUERY );

        if( xShape.is() )
        {
            mpShape = lookupAttributableShape( getContext().mpLayerManager,
                                               xShape );
        }
        else
        {
            // no shape provided. Maybe a ParagraphTarget?
            css::presentation::ParagraphTarget aTarget;

            if( !(mxAnimateNode->getTarget() >>= aTarget) )
                ENSURE_AND_THROW(
                    false, "could not extract any target information" );

            xShape = aTarget.Shape;

            ENSURE_AND_THROW( xShape.is(), "invalid shape in ParagraphTarget" );

            mpShape = lookupAttributableShape( getContext().mpLayerManager,
                                               xShape );

            // NOTE: For shapes with ParagraphTarget, we ignore
            // the SubItem property. We implicitely assume that it
            // is set to ONLY_TEXT.
            OSL_ENSURE(
                mxAnimateNode->getSubItem() ==
                css::presentation::ShapeAnimationSubType::ONLY_TEXT ||
                mxAnimateNode->getSubItem() ==
                css::presentation::ShapeAnimationSubType::AS_WHOLE,
                "ParagraphTarget given, but subitem not AS_TEXT or AS_WHOLE? "
                "Make up your mind, I'll ignore the subitem." );

            // okay, found a ParagraphTarget with a valid XShape. Does the shape
            // provide the given paragraph?
            const DocTreeNode& rTreeNode(
                mpShape->getTreeNodeSupplier().getTreeNode(
                    aTarget.Paragraph,
                    DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH ) );

            // CAUTION: the creation of the subset shape
            // _must_ stay in the node constructor, since
            // Slide::prefetchShow() initializes shape
            // attributes right after animation import (or
            // the Slide class must be changed).
            mpShapeSubset.reset(
                new ShapeSubset( mpShape,
                                 rTreeNode,
                                 getContext().mpLayerManager ));

            // Override NodeContext, and flag this node as
            // a special independent subset one. This is
            // important when applying initial attributes:
            // independent shape subsets must be setup
            // when the slide starts, since they, as their
            // name suggest, can have state independent to
            // the master shape. The following example
            // might illustrate that: a master shape has
            // no effect, one of the text paragraphs
            // within it has an appear effect. Now, the
            // respective paragraph must be invisible when
            // the slide is initially shown, and become
            // visible only when the effect starts.
            mbIsIndependentSubset = true;

            // already enable subset right here, the
            // setup of initial shape attributes of
            // course needs the subset shape
            // generated, to apply e.g. visibility
            // changes.
            mpShapeSubset->enableSubsetShape();
        }
    }
}

void AnimationBaseNode::dispose()
{
    if (mpActivity) {
        mpActivity->dispose();
        mpActivity.reset();
    }

    maAttributeLayerHolder.reset();
    mxAnimateNode.clear();
    mpShape.reset();
    mpShapeSubset.reset();

    BaseNode::dispose();
}

bool AnimationBaseNode::init_st()
{
    // if we've still got an old activity lying around, dispose it:
    if (mpActivity) {
        mpActivity->dispose();
        mpActivity.reset();
    }

    // note: actually disposing the activity too early might cause problems,
    // because on dequeued() it calls endAnimation(pAnim->end()), thus ending
    // animation _after_ last screen update.
    // review that end() is properly called (which calls endAnimation(), too).

    try {
        // TODO(F2): For restart functionality, we must regenerate activities,
        // since they are not able to reset their state (or implement _that_)
        mpActivity = createActivity();
    }
    catch (uno::Exception const&) {
        OSL_ENSURE( false, rtl::OUStringToOString(
                        comphelper::anyToString(cppu::getCaughtException()),
                        RTL_TEXTENCODING_UTF8 ) );
        // catch and ignore. We later handle empty activities, but for
        // other nodes to function properly, the core functionality of
        // this node must remain up and running.
    }
    return true;
}

bool AnimationBaseNode::resolve_st()
{
    // enable shape subset for automatically generated
    // subsets. Independent subsets are already setup
    // during construction time. Doing it only here
    // saves us a lot of sprites and shapes lying
    // around. This is especially important for
    // character-wise iterations, since the shape
    // content (e.g. thousands of characters) would
    // otherwise be painted character-by-character.
    if (isDependentSubsettedShape() && mpShapeSubset) {
        mpShapeSubset->enableSubsetShape();
    }
    return true;
}

void AnimationBaseNode::activate_st()
{
    // create new attribute layer
    maAttributeLayerHolder.createAttributeLayer( getShape() );

    ENSURE_AND_THROW( maAttributeLayerHolder.get(),
                      "Could not generate shape attribute layer" );

    // TODO(Q2): This affects the way mpActivity
    // works, but is performed here because of
    // locality (we're fiddling with the additive mode
    // here, anyway, and it's the only place where we
    // do). OTOH, maybe the complete additive mode
    // setup should be moved to the activities.

    // for simple by-animations, the SMIL spec
    // requires us to emulate "0,by-value" value list
    // behaviour, with additive mode forced to "sum",
    // no matter what the input is
    // (http://www.w3.org/TR/smil20/animation.html#adef-by).
    if( mxAnimateNode->getBy().hasValue() &&
        !mxAnimateNode->getTo().hasValue() &&
        !mxAnimateNode->getFrom().hasValue() )
    {
        // force attribute mode to REPLACE (note the
        // subtle discrepancy to the paragraph above,
        // where SMIL requires SUM. This is internally
        // handled by the FromToByActivity, and is
        // because otherwise DOM values would not be
        // handled correctly: the activity cannot
        // determine whether an
        // Activity::getUnderlyingValue() yields the
        // DOM value, or already a summed-up conglomerate)
        //
        // Note that this poses problems with our
        // hybrid activity duration (time or min number of frames),
        // since if activities
        // exceed their duration, wrong 'by' start
        // values might arise ('Laser effect')
        maAttributeLayerHolder.get()->setAdditiveMode(
            animations::AnimationAdditiveMode::REPLACE );
    }
    else
    {
        // apply additive mode to newly created Attribute layer
        maAttributeLayerHolder.get()->setAdditiveMode(
            mxAnimateNode->getAdditive() );
    }

    // fake normal animation behaviour, even if we
    // show nothing.  This is the appropriate way to
    // handle errors on Activity generation, because
    // maybe all other effects on the slide are
    // correctly initialized (but won't run, if we
    // signal an error here)
    if (mpActivity) {
        // supply Activity (and the underlying Animation) with
        // it's AttributeLayer, to perform the animation on
        mpActivity->setTargets( getShape(), maAttributeLayerHolder.get() );

        // add to activities queue
        getContext().mrActivitiesQueue.addActivity( mpActivity );
    }
    else {
        // Actually, DO generate the event for empty activity,
        // to keep the chain of animations running
        BaseNode::scheduleDeactivationEvent();
    }
}

void AnimationBaseNode::deactivate_st( NodeState eDestState )
{
    if (eDestState == FROZEN) {
        if (mpActivity)
            mpActivity->end();
    }

    if (isDependentSubsettedShape()) {
        // for dependent subsets, remove subset shape
        // from layer, re-integrate subsetted part
        // back into original shape. For independent
        // subsets, we cannot make any assumptions
        // about subset attribute state relative to
        // master shape, thus, have to keep it. This
        // will effectively re-integrate the subsetted
        // part into the original shape (whose
        // animation will hopefully have ended, too)

        // this statement will save a whole lot of
        // sprites for iterated text effects, since
        // those sprites will only exist during the
        // actual lifetime of the effects
        if (mpShapeSubset) {
            mpShapeSubset->disableSubsetShape();
        }
    }

    if (eDestState == ENDED) {

        // no shape anymore, no layer needed:
        maAttributeLayerHolder.reset();

        if (! isDependentSubsettedShape()) {

            // for all other shapes, removing the
            // attribute layer quite possibly changes
            // shape display. Thus, force update
            AttributableShapeSharedPtr const pShape( getShape() );

            // don't anybody dare to check against
            // pShape->isVisible() here, removing the
            // attribute layer might actually make the
            // shape invisible!
            getContext().mpLayerManager->notifyShapeUpdate( pShape );
        }

        if (mpActivity) {
            // kill activity, if still running
            mpActivity->dispose();
            mpActivity.reset();
        }
    }
}

bool AnimationBaseNode::hasPendingAnimation() const
{
    // TODO(F1): This might not always be true. Are there 'inactive'
    // animation nodes?
    return true;
}

#if defined(VERBOSE) && defined(DBG_UTIL)
void AnimationBaseNode::showState() const
{
    BaseNode::showState();

    VERBOSE_TRACE( "AnimationBaseNode info: independent subset=%s",
                   mbIsIndependentSubset ? "y" : "n" );
}
#endif

ActivitiesFactory::CommonParameters
AnimationBaseNode::fillCommonParameters() const
{
    double nDuration = 0.0;

    // TODO(F3): Duration/End handling is barely there
    if( !(mxAnimateNode->getDuration() >>= nDuration) ) {
        mxAnimateNode->getEnd() >>= nDuration; // Wah.
    }

    // minimal duration we fallback to (avoid 0 here!)
    nDuration = ::std::max( 0.001, nDuration );

    const bool bAutoReverse( mxAnimateNode->getAutoReverse() );

    boost::optional<double> aRepeats;
    double nRepeats;
    if( (mxAnimateNode->getRepeatCount() >>= nRepeats) ) {
        aRepeats.reset( nRepeats );
    }
    else {
        if( (mxAnimateNode->getRepeatDuration() >>= nRepeats) ) {
            // when repeatDuration is given,
            // autoreverse does _not_ modify the
            // active duration. Thus, calc repeat
            // count with already adapted simple
            // duration (twice the specified duration)

            // convert duration back to repeat counts
            if( bAutoReverse )
                aRepeats.reset( nRepeats / (2.0 * nDuration) );
            else
                aRepeats.reset( nRepeats / nDuration );
        }
        else {
            // no double value for both values - Timing::INDEFINITE?
            animations::Timing eTiming;

            if( !(mxAnimateNode->getRepeatDuration() >>= eTiming) ||
                eTiming != animations::Timing_INDEFINITE )
            {
                if( !(mxAnimateNode->getRepeatCount() >>= eTiming) ||
                    eTiming != animations::Timing_INDEFINITE )
                {
                    // no indefinite timing, no other values given -
                    // use simple run, i.e. repeat of 1.0
                    aRepeats.reset( 1.0 );
                }
            }
        }
    }

    // calc accel/decel:
    double nAcceleration = 0.0;
    double nDeceleration = 0.0;
    BaseNodeSharedPtr const pSelf( getSelf() );
    for ( boost::shared_ptr<BaseNode> pNode( pSelf );
          pNode; pNode = pNode->getParentNode() )
    {
        uno::Reference<animations::XAnimationNode> const xAnimationNode(
            pNode->getXAnimationNode() );
        nAcceleration = std::max( nAcceleration,
                                  xAnimationNode->getAcceleration() );
        nDeceleration = std::max( nDeceleration,
                                  xAnimationNode->getDecelerate() );
    }

    EventSharedPtr pEndEvent;
    if (pSelf) {
        pEndEvent = makeEvent(
            boost::bind( &AnimationNode::deactivate, pSelf ) );
    }

    return ActivitiesFactory::CommonParameters(
        pEndEvent,
        getContext().mrEventQueue,
        getContext().mrActivitiesQueue,
        nDuration,
        10, // always display at least 10 frames
        bAutoReverse,
        aRepeats,
        nAcceleration,
        nDeceleration,
        getShape(),
        getContext().mpLayerManager );
}

AttributableShapeSharedPtr AnimationBaseNode::getShape() const
{
    // any subsetting at all?
    if (mpShapeSubset)
        return mpShapeSubset->getSubsetShape();
    else
        return mpShape; // nope, plain shape always
}

} // namespace internal
} // namespace presentation

<commit_msg>INTEGRATION: CWS sb59 (1.8.40); FILE MERGED 2006/08/18 18:29:50 sb 1.8.40.2: RESYNC: (1.8-1.9); FILE MERGED 2006/08/11 20:35:46 thb 1.8.40.1: #i68336# Made slideshow warning free<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: animationbasenode.cxx,v $
 *
 *  $Revision: 1.11 $
 *
 *  last change: $Author: obo $ $Date: 2006-10-12 13:58:45 $
 *
 *  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_slideshow.hxx"

// must be first
#include "canvas/debug.hxx"
#include "canvas/verbosetrace.hxx"
#include "cppuhelper/exc_hlp.hxx"
#include "comphelper/anytostring.hxx"
#include "com/sun/star/presentation/ParagraphTarget.hpp"
#include "com/sun/star/animations/Timing.hpp"
#include "com/sun/star/animations/AnimationAdditiveMode.hpp"
#include "com/sun/star/presentation/ShapeAnimationSubType.hpp"
#include "nodetools.hxx"
#include "doctreenode.hxx"
#include "animationbasenode.hxx"
#include "delayevent.hxx"
#include "boost/bind.hpp"
#include "boost/optional.hpp"
#include <vector>
#include <algorithm>

namespace css = com::sun::star;
using namespace css;

namespace presentation {
namespace internal {

AnimationBaseNode::AnimationBaseNode(
    const uno::Reference< animations::XAnimationNode >&   xNode,
    const BaseContainerNodeSharedPtr&                     rParent,
    const NodeContext&                                    rContext )
    : BaseNode( xNode, rParent, rContext ),
      mxAnimateNode( xNode, uno::UNO_QUERY_THROW ),
      maAttributeLayerHolder(),
      mpActivity(),
      mpShape(),
      mpShapeSubset(),
      mbIsIndependentSubset( rContext.mbIsIndependentSubset )
{
    // extract native node targets
    // ===========================

    // plain shape target
    uno::Reference< drawing::XShape > xShape( mxAnimateNode->getTarget(),
                                              uno::UNO_QUERY );

    // distinguish 5 cases:
    //
    //  - plain shape target
    //  (NodeContext.mpMasterShapeSubset full set)
    //
    //  - parent-generated subset (generate an
    //  independent subset)
    //
    //  - parent-generated subset from iteration
    //  (generate a dependent subset)
    //
    //  - XShape target at the XAnimatioNode (generate
    //  a plain shape target)
    //
    //  - ParagraphTarget target at the XAnimationNode
    //  (generate an independent shape subset)
    if( rContext.mpMasterShapeSubset.get() )
    {
        if( rContext.mpMasterShapeSubset->isFullSet() )
        {
            // case 1: plain shape target from parent
            mpShape = rContext.mpMasterShapeSubset->getSubsetShape();
        }
        else
        {
            // cases 2 & 3: subset shape
            mpShapeSubset = rContext.mpMasterShapeSubset;
        }
    }
    else
    {
        // no parent-provided shape, try to extract
        // from XAnimationNode - cases 4 and 5

        if( xShape.is() )
        {
            mpShape = lookupAttributableShape( getContext().mpLayerManager,
                                               xShape );
        }
        else
        {
            // no shape provided. Maybe a ParagraphTarget?
            css::presentation::ParagraphTarget aTarget;

            if( !(mxAnimateNode->getTarget() >>= aTarget) )
                ENSURE_AND_THROW(
                    false, "could not extract any target information" );

            xShape = aTarget.Shape;

            ENSURE_AND_THROW( xShape.is(), "invalid shape in ParagraphTarget" );

            mpShape = lookupAttributableShape( getContext().mpLayerManager,
                                               xShape );

            // NOTE: For shapes with ParagraphTarget, we ignore
            // the SubItem property. We implicitely assume that it
            // is set to ONLY_TEXT.
            OSL_ENSURE(
                mxAnimateNode->getSubItem() ==
                css::presentation::ShapeAnimationSubType::ONLY_TEXT ||
                mxAnimateNode->getSubItem() ==
                css::presentation::ShapeAnimationSubType::AS_WHOLE,
                "ParagraphTarget given, but subitem not AS_TEXT or AS_WHOLE? "
                "Make up your mind, I'll ignore the subitem." );

            // okay, found a ParagraphTarget with a valid XShape. Does the shape
            // provide the given paragraph?
            const DocTreeNode& rTreeNode(
                mpShape->getTreeNodeSupplier().getTreeNode(
                    aTarget.Paragraph,
                    DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH ) );

            // CAUTION: the creation of the subset shape
            // _must_ stay in the node constructor, since
            // Slide::prefetchShow() initializes shape
            // attributes right after animation import (or
            // the Slide class must be changed).
            mpShapeSubset.reset(
                new ShapeSubset( mpShape,
                                 rTreeNode,
                                 getContext().mpLayerManager ));

            // Override NodeContext, and flag this node as
            // a special independent subset one. This is
            // important when applying initial attributes:
            // independent shape subsets must be setup
            // when the slide starts, since they, as their
            // name suggest, can have state independent to
            // the master shape. The following example
            // might illustrate that: a master shape has
            // no effect, one of the text paragraphs
            // within it has an appear effect. Now, the
            // respective paragraph must be invisible when
            // the slide is initially shown, and become
            // visible only when the effect starts.
            mbIsIndependentSubset = true;

            // already enable subset right here, the
            // setup of initial shape attributes of
            // course needs the subset shape
            // generated, to apply e.g. visibility
            // changes.
            mpShapeSubset->enableSubsetShape();
        }
    }
}

void AnimationBaseNode::dispose()
{
    if (mpActivity) {
        mpActivity->dispose();
        mpActivity.reset();
    }

    maAttributeLayerHolder.reset();
    mxAnimateNode.clear();
    mpShape.reset();
    mpShapeSubset.reset();

    BaseNode::dispose();
}

bool AnimationBaseNode::init_st()
{
    // if we've still got an old activity lying around, dispose it:
    if (mpActivity) {
        mpActivity->dispose();
        mpActivity.reset();
    }

    // note: actually disposing the activity too early might cause problems,
    // because on dequeued() it calls endAnimation(pAnim->end()), thus ending
    // animation _after_ last screen update.
    // review that end() is properly called (which calls endAnimation(), too).

    try {
        // TODO(F2): For restart functionality, we must regenerate activities,
        // since they are not able to reset their state (or implement _that_)
        mpActivity = createActivity();
    }
    catch (uno::Exception const&) {
        OSL_ENSURE( false, rtl::OUStringToOString(
                        comphelper::anyToString(cppu::getCaughtException()),
                        RTL_TEXTENCODING_UTF8 ) );
        // catch and ignore. We later handle empty activities, but for
        // other nodes to function properly, the core functionality of
        // this node must remain up and running.
    }
    return true;
}

bool AnimationBaseNode::resolve_st()
{
    // enable shape subset for automatically generated
    // subsets. Independent subsets are already setup
    // during construction time. Doing it only here
    // saves us a lot of sprites and shapes lying
    // around. This is especially important for
    // character-wise iterations, since the shape
    // content (e.g. thousands of characters) would
    // otherwise be painted character-by-character.
    if (isDependentSubsettedShape() && mpShapeSubset) {
        mpShapeSubset->enableSubsetShape();
    }
    return true;
}

void AnimationBaseNode::activate_st()
{
    // create new attribute layer
    maAttributeLayerHolder.createAttributeLayer( getShape() );

    ENSURE_AND_THROW( maAttributeLayerHolder.get(),
                      "Could not generate shape attribute layer" );

    // TODO(Q2): This affects the way mpActivity
    // works, but is performed here because of
    // locality (we're fiddling with the additive mode
    // here, anyway, and it's the only place where we
    // do). OTOH, maybe the complete additive mode
    // setup should be moved to the activities.

    // for simple by-animations, the SMIL spec
    // requires us to emulate "0,by-value" value list
    // behaviour, with additive mode forced to "sum",
    // no matter what the input is
    // (http://www.w3.org/TR/smil20/animation.html#adef-by).
    if( mxAnimateNode->getBy().hasValue() &&
        !mxAnimateNode->getTo().hasValue() &&
        !mxAnimateNode->getFrom().hasValue() )
    {
        // force attribute mode to REPLACE (note the
        // subtle discrepancy to the paragraph above,
        // where SMIL requires SUM. This is internally
        // handled by the FromToByActivity, and is
        // because otherwise DOM values would not be
        // handled correctly: the activity cannot
        // determine whether an
        // Activity::getUnderlyingValue() yields the
        // DOM value, or already a summed-up conglomerate)
        //
        // Note that this poses problems with our
        // hybrid activity duration (time or min number of frames),
        // since if activities
        // exceed their duration, wrong 'by' start
        // values might arise ('Laser effect')
        maAttributeLayerHolder.get()->setAdditiveMode(
            animations::AnimationAdditiveMode::REPLACE );
    }
    else
    {
        // apply additive mode to newly created Attribute layer
        maAttributeLayerHolder.get()->setAdditiveMode(
            mxAnimateNode->getAdditive() );
    }

    // fake normal animation behaviour, even if we
    // show nothing.  This is the appropriate way to
    // handle errors on Activity generation, because
    // maybe all other effects on the slide are
    // correctly initialized (but won't run, if we
    // signal an error here)
    if (mpActivity) {
        // supply Activity (and the underlying Animation) with
        // it's AttributeLayer, to perform the animation on
        mpActivity->setTargets( getShape(), maAttributeLayerHolder.get() );

        // add to activities queue
        getContext().mrActivitiesQueue.addActivity( mpActivity );
    }
    else {
        // Actually, DO generate the event for empty activity,
        // to keep the chain of animations running
        BaseNode::scheduleDeactivationEvent();
    }
}

void AnimationBaseNode::deactivate_st( NodeState eDestState )
{
    if (eDestState == FROZEN) {
        if (mpActivity)
            mpActivity->end();
    }

    if (isDependentSubsettedShape()) {
        // for dependent subsets, remove subset shape
        // from layer, re-integrate subsetted part
        // back into original shape. For independent
        // subsets, we cannot make any assumptions
        // about subset attribute state relative to
        // master shape, thus, have to keep it. This
        // will effectively re-integrate the subsetted
        // part into the original shape (whose
        // animation will hopefully have ended, too)

        // this statement will save a whole lot of
        // sprites for iterated text effects, since
        // those sprites will only exist during the
        // actual lifetime of the effects
        if (mpShapeSubset) {
            mpShapeSubset->disableSubsetShape();
        }
    }

    if (eDestState == ENDED) {

        // no shape anymore, no layer needed:
        maAttributeLayerHolder.reset();

        if (! isDependentSubsettedShape()) {

            // for all other shapes, removing the
            // attribute layer quite possibly changes
            // shape display. Thus, force update
            AttributableShapeSharedPtr const pShape( getShape() );

            // don't anybody dare to check against
            // pShape->isVisible() here, removing the
            // attribute layer might actually make the
            // shape invisible!
            getContext().mpLayerManager->notifyShapeUpdate( pShape );
        }

        if (mpActivity) {
            // kill activity, if still running
            mpActivity->dispose();
            mpActivity.reset();
        }
    }
}

bool AnimationBaseNode::hasPendingAnimation() const
{
    // TODO(F1): This might not always be true. Are there 'inactive'
    // animation nodes?
    return true;
}

#if defined(VERBOSE) && defined(DBG_UTIL)
void AnimationBaseNode::showState() const
{
    BaseNode::showState();

    VERBOSE_TRACE( "AnimationBaseNode info: independent subset=%s",
                   mbIsIndependentSubset ? "y" : "n" );
}
#endif

ActivitiesFactory::CommonParameters
AnimationBaseNode::fillCommonParameters() const
{
    double nDuration = 0.0;

    // TODO(F3): Duration/End handling is barely there
    if( !(mxAnimateNode->getDuration() >>= nDuration) ) {
        mxAnimateNode->getEnd() >>= nDuration; // Wah.
    }

    // minimal duration we fallback to (avoid 0 here!)
    nDuration = ::std::max( 0.001, nDuration );

    const bool bAutoReverse( mxAnimateNode->getAutoReverse() );

    boost::optional<double> aRepeats;
    double nRepeats;
    if( (mxAnimateNode->getRepeatCount() >>= nRepeats) ) {
        aRepeats.reset( nRepeats );
    }
    else {
        if( (mxAnimateNode->getRepeatDuration() >>= nRepeats) ) {
            // when repeatDuration is given,
            // autoreverse does _not_ modify the
            // active duration. Thus, calc repeat
            // count with already adapted simple
            // duration (twice the specified duration)

            // convert duration back to repeat counts
            if( bAutoReverse )
                aRepeats.reset( nRepeats / (2.0 * nDuration) );
            else
                aRepeats.reset( nRepeats / nDuration );
        }
        else {
            // no double value for both values - Timing::INDEFINITE?
            animations::Timing eTiming;

            if( !(mxAnimateNode->getRepeatDuration() >>= eTiming) ||
                eTiming != animations::Timing_INDEFINITE )
            {
                if( !(mxAnimateNode->getRepeatCount() >>= eTiming) ||
                    eTiming != animations::Timing_INDEFINITE )
                {
                    // no indefinite timing, no other values given -
                    // use simple run, i.e. repeat of 1.0
                    aRepeats.reset( 1.0 );
                }
            }
        }
    }

    // calc accel/decel:
    double nAcceleration = 0.0;
    double nDeceleration = 0.0;
    BaseNodeSharedPtr const pSelf( getSelf() );
    for ( boost::shared_ptr<BaseNode> pNode( pSelf );
          pNode; pNode = pNode->getParentNode() )
    {
        uno::Reference<animations::XAnimationNode> const xAnimationNode(
            pNode->getXAnimationNode() );
        nAcceleration = std::max( nAcceleration,
                                  xAnimationNode->getAcceleration() );
        nDeceleration = std::max( nDeceleration,
                                  xAnimationNode->getDecelerate() );
    }

    EventSharedPtr pEndEvent;
    if (pSelf) {
        pEndEvent = makeEvent(
            boost::bind( &AnimationNode::deactivate, pSelf ) );
    }

    return ActivitiesFactory::CommonParameters(
        pEndEvent,
        getContext().mrEventQueue,
        getContext().mrActivitiesQueue,
        nDuration,
        10, // always display at least 10 frames
        bAutoReverse,
        aRepeats,
        nAcceleration,
        nDeceleration,
        getShape(),
        getContext().mpLayerManager );
}

AttributableShapeSharedPtr AnimationBaseNode::getShape() const
{
    // any subsetting at all?
    if (mpShapeSubset)
        return mpShapeSubset->getSubsetShape();
    else
        return mpShape; // nope, plain shape always
}

} // namespace internal
} // namespace presentation

<|endoftext|>


<commit_before>// This file is part of the "x0" project, http://github.com/christianparpart/x0>
//   (c) 2009-2017 Christian Parpart <christian@parpart.family>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

#include <xzero/net/TcpEndPoint.h>
#include <xzero/net/TcpConnector.h>
#include <xzero/net/TcpUtil.h>
#include <xzero/net/Connection.h>
#include <xzero/util/BinaryReader.h>
#include <xzero/io/FileUtil.h>
#include <xzero/executor/Executor.h>
#include <xzero/logging.h>
#include <xzero/RuntimeError.h>
#include <xzero/Buffer.h>
#include <xzero/sysconfig.h>
#include <xzero/RefPtr.h>
#include <stdexcept>
#include <netinet/tcp.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

namespace xzero {

#if !defined(NDEBUG)
#define TRACE(msg...) logTrace("TcpEndPoint", msg)
#define DEBUG(msg...) logDebug("TcpEndPoint", msg)
#else
#define TRACE(msg...) do {} while (0)
#define DEBUG(msg...) do {} while (0)
#endif

TcpEndPoint::TcpEndPoint(FileDescriptor&& socket,
                         int addressFamily,
                         Duration readTimeout,
                         Duration writeTimeout,
                         Executor* executor,
                         std::function<void(TcpEndPoint*)> onEndPointClosed)
    : io_(),
      executor_(executor),
      readTimeout_(readTimeout),
      writeTimeout_(writeTimeout),
      inputBuffer_(),
      inputOffset_(0),
      handle_(std::move(socket)),
      addressFamily_(addressFamily),
      isCorking_(false),
      onEndPointClosed_(onEndPointClosed),
      connection_() {
  TRACE("$0 ctor", this);
}

void TcpEndPoint::onTimeout() {
  if (connection()) {
    if (connection()->onReadTimeout()) {
      close();
    }
  }
}

TcpEndPoint::~TcpEndPoint() {
  TRACE("$0 dtor", this);
  if (isOpen()) {
    close();
  }
}

Option<InetAddress> TcpEndPoint::remoteAddress() const {
  Result<InetAddress> addr = TcpUtil::getRemoteAddress(handle_, addressFamily());
  if (addr.isSuccess())
    return Some(*addr);
  else {
    logError("TcpEndPoint", "remoteAddress: ($0) $1",
        addr.error().category().name(),
        addr.error().message().c_str());
    return None();
  }
}

Option<InetAddress> TcpEndPoint::localAddress() const {
  Result<InetAddress> addr = TcpUtil::getLocalAddress(handle_, addressFamily());
  if (addr.isSuccess())
    return Some(*addr);
  else {
    logError("TcpEndPoint", "localAddress: ($0) $1",
        addr.error().category().name(),
        addr.error().message().c_str());
    return None();
  }
}

bool TcpEndPoint::isOpen() const XZERO_NOEXCEPT {
  return handle_ >= 0;
}

void TcpEndPoint::close() {
  if (isOpen()) {
    TRACE("close() fd=$0", handle_.get());

    if (onEndPointClosed_) {
      onEndPointClosed_(this);
    }

    handle_.close();
  } else {
    TRACE("close(fd=$0) invoked, but we're closed already", handle_.get());
  }
}

void TcpEndPoint::setConnection(std::unique_ptr<Connection>&& c) {
  connection_ = std::move(c);
}

bool TcpEndPoint::isBlocking() const {
  return !(fcntl(handle_, F_GETFL) & O_NONBLOCK);
}

void TcpEndPoint::setBlocking(bool enable) {
  FileUtil::setBlocking(handle_, enable);
}

bool TcpEndPoint::isCorking() const {
  return isCorking_;
}

void TcpEndPoint::setCorking(bool enable) {
  if (isCorking_ != enable) {
    TcpUtil::setCorking(handle_, enable);
    isCorking_ = enable;
  }
}

bool TcpEndPoint::isTcpNoDelay() const {
  return TcpUtil::isTcpNoDelay(handle_);
}

void TcpEndPoint::setTcpNoDelay(bool enable) {
  TcpUtil::setTcpNoDelay(handle_, enable);
}

std::string TcpEndPoint::toString() const {
  char buf[32];
  snprintf(buf, sizeof(buf), "TcpEndPoint(%d)@%p", handle(), this);
  return buf;
}

void TcpEndPoint::startDetectProtocol(bool dataReady,
                                      ProtocolCallback createConnection) {
  inputBuffer_.reserve(256);

  if (dataReady) {
    onDetectProtocol(createConnection);
  } else {
    executor_->executeOnReadable(
        handle(),
        std::bind(&TcpEndPoint::onDetectProtocol, this, createConnection));
  }
}

void TcpEndPoint::onDetectProtocol(ProtocolCallback createConnection) {
  size_t n = fill(&inputBuffer_);

  if (n == 0) {
    close();
    return;
  }

  // XXX detect magic byte (0x01) to protocol detection
  if (inputBuffer_[0] == TcpConnector::MagicProtocolSwitchByte) {
    BinaryReader reader(inputBuffer_);
    reader.parseVarUInt(); // skip magic
    std::string protocol = reader.parseString();
    inputOffset_ = inputBuffer_.size() - reader.pending();
    createConnection(protocol, this);
  } else {
    // create Connection object for given endpoint
    TRACE("$0 protocol-switch not detected.", this);
    createConnection("", this);
  }

  if (connection_) {
    connection_->onOpen(true);
  } else {
    close();
  }
}

size_t TcpEndPoint::prefill(size_t maxBytes) {
  const size_t nprefilled = prefilled();
  if (nprefilled > 0)
    return nprefilled;

  inputBuffer_.reserve(maxBytes);
  return fill(&inputBuffer_);
}

size_t TcpEndPoint::fill(Buffer* sink) {
  int space = sink->capacity() - sink->size();
  if (space < 4 * 1024) {
    sink->reserve(sink->capacity() + 8 * 1024);
    space = sink->capacity() - sink->size();
  }

  return fill(sink, space);
}

size_t TcpEndPoint::fill(Buffer* result, size_t count) {
  assert(count <= result->capacity() - result->size());

  if (inputOffset_ < inputBuffer_.size()) {
    TRACE("$0 fill: with inputBuffer ($1, $2)", this, inputOffset_, inputBuffer_.size());
    count = std::min(count, inputBuffer_.size() - inputOffset_);
    result->push_back(inputBuffer_.ref(inputOffset_, count));
    TRACE("$0 fill: \"$1\"", this, inputBuffer_.ref(inputOffset_, count));
    inputOffset_ += count;
    if (inputOffset_ == inputBuffer_.size()) {
      inputBuffer_.clear();
      inputOffset_ = 0;
    }
    return count;
  }

  ssize_t n = read(handle(), result->end(), count);
  TRACE("read($0 bytes) -> $1", result->capacity() - result->size(), n);

  if (n < 0) {
    // don't raise on soft errors, such as there is simply no more data to read.
    switch (errno) {
      case EBUSY:
      case EAGAIN:
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
      case EWOULDBLOCK:
#endif
        break;
      default:
        RAISE_ERRNO(errno);
    }
  } else {
    result->resize(result->size() + n);
  }

  return n;
}

size_t TcpEndPoint::flush(const BufferRef& source) {
  ssize_t rv = write(handle(), source.data(), source.size());

  TRACE("flush($0 bytes) -> $1", source.size(), rv);

  if (rv < 0)
    RAISE_ERRNO(errno);

  // EOF exception?

  return rv;
}

size_t TcpEndPoint::flush(const FileView& view) {
  return TcpUtil::sendfile(handle(), view);
}

void TcpEndPoint::wantFill() {
  TRACE("$0 wantFill()", this);
  // TODO: abstract away the logic of TCP_DEFER_ACCEPT

  if (!io_) {
    io_ = executor_->executeOnReadable(
        handle(),
        std::bind(&TcpEndPoint::fillable, this),
        readTimeout(),
        std::bind(&TcpEndPoint::onTimeout, this));
  }
}

void TcpEndPoint::fillable() {
  RefPtr<TcpEndPoint> _guard(this);

  try {
    io_.reset();
    connection()->onFillable();
  } catch (const std::exception& e) {
    connection()->onInterestFailure(e);
  } catch (...) {
    connection()->onInterestFailure(
        EXCEPTION(RuntimeError, (int) Status::CaughtUnknownExceptionError,
                  StatusCategory::get()));
  }
}

void TcpEndPoint::wantFlush() {
  TRACE("$0 wantFlush() $1", this, io_.get() ? "again" : "first time");

  if (!io_) {
    io_ = executor_->executeOnWritable(
        handle(),
        std::bind(&TcpEndPoint::flushable, this),
        writeTimeout(),
        std::bind(&TcpEndPoint::onTimeout, this));
  }
}

void TcpEndPoint::flushable() {
  TRACE("$0 flushable()", this);
  RefPtr<TcpEndPoint> _guard(this);
  try {
    io_.reset();
    connection()->onFlushable();
  } catch (const std::exception& e) {
    connection()->onInterestFailure(e);
  } catch (...) {
    connection()->onInterestFailure(
        EXCEPTION(RuntimeError, (int) Status::CaughtUnknownExceptionError,
                  StatusCategory::get()));
  }
}

Duration TcpEndPoint::readTimeout() const noexcept {
  return readTimeout_;
}

Duration TcpEndPoint::writeTimeout() const noexcept {
  return writeTimeout_;
}

class TcpConnectState {
 public:
  InetAddress address;
  int fd;
  Duration readTimeout;
  Duration writeTimeout;
  Executor* executor;
  Promise<RefPtr<TcpEndPoint>> promise;

  void onTimeout() {
    TRACE("$0 onTimeout: connecting timed out", this);
    promise.failure(std::errc::timed_out);
    delete this;
  }

  void onConnectComplete() {
    int val = 0;
    socklen_t vlen = sizeof(val);
    if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &vlen) == 0) {
      if (val == 0) {
        TRACE("$0 onConnectComplete: connected $1. $2", this, val, strerror(val));
        promise.success(make_ref<TcpEndPoint>(FileDescriptor{fd},
                                              address.family(),
                                              readTimeout,
                                              writeTimeout,
                                              executor,
                                              nullptr));
      } else {
        DEBUG("Connecting to $0 failed. $1", address, strerror(val));
        promise.failure(std::make_error_code(static_cast<std::errc>(val)));
      }
    } else {
      DEBUG("Connecting to $0 failed. $1", address, strerror(val));
      promise.failure(std::make_error_code(static_cast<std::errc>(val)));
    }
    delete this;
  }
};

Future<RefPtr<TcpEndPoint>> TcpEndPoint::connect(const InetAddress& address,
                                                 Duration connectTimeout,
                                                 Duration readTimeout,
                                                 Duration writeTimeout,
                                                 Executor* executor) {
  int flags = 0;
#if defined(SOCK_CLOEXEC)
  flags |= SOCK_CLOEXEC;
#endif
#if defined(SOCK_NONBLOCK)
  flags |= SOCK_NONBLOCK;
#endif

  Promise<RefPtr<TcpEndPoint>> promise;

  int fd = socket(address.family(), SOCK_STREAM | flags, IPPROTO_TCP);
  if (fd < 0) {
    promise.failure(std::make_error_code(static_cast<std::errc>(errno)));
    return promise.future();
  }

#if !defined(SOCK_NONBLOCK)
  FileUtil::setBlocking(false);
#endif

  std::error_code ec = TcpUtil::connect(fd, address);
  if (!ec) {
    TRACE("connect: connected instantly");
    promise.success(make_ref<TcpEndPoint>(FileDescriptor{fd},
                                          address.family(),
                                          readTimeout,
                                          writeTimeout,
                                          executor,
                                          nullptr));
  } else if (ec == std::errc::operation_in_progress) {
    TRACE("connect: backgrounding");
    auto state = new TcpConnectState{address, fd, readTimeout, writeTimeout,
                                     executor, promise};
    executor->executeOnWritable(fd,
        std::bind(&TcpConnectState::onConnectComplete, state),
        connectTimeout,
        std::bind(&TcpConnectState::onTimeout, state));
  } else {
    TRACE("connect: connect() error. $0", strerror(errno));
    promise.failure(std::make_error_code(static_cast<std::errc>(errno)));
  }
  return promise.future();
}

} // namespace xzero
<commit_msg>[xzero] compile fix<commit_after>// This file is part of the "x0" project, http://github.com/christianparpart/x0>
//   (c) 2009-2017 Christian Parpart <christian@parpart.family>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

#include <xzero/net/TcpEndPoint.h>
#include <xzero/net/TcpConnector.h>
#include <xzero/net/TcpUtil.h>
#include <xzero/net/Connection.h>
#include <xzero/util/BinaryReader.h>
#include <xzero/io/FileUtil.h>
#include <xzero/executor/Executor.h>
#include <xzero/logging.h>
#include <xzero/RuntimeError.h>
#include <xzero/Buffer.h>
#include <xzero/sysconfig.h>
#include <xzero/RefPtr.h>
#include <stdexcept>
#include <netinet/tcp.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

namespace xzero {

#if !defined(NDEBUG)
#define TRACE(msg...) logTrace("TcpEndPoint", msg)
#define DEBUG(msg...) logDebug("TcpEndPoint", msg)
#else
#define TRACE(msg...) do {} while (0)
#define DEBUG(msg...) do {} while (0)
#endif

TcpEndPoint::TcpEndPoint(FileDescriptor&& socket,
                         int addressFamily,
                         Duration readTimeout,
                         Duration writeTimeout,
                         Executor* executor,
                         std::function<void(TcpEndPoint*)> onEndPointClosed)
    : io_(),
      executor_(executor),
      readTimeout_(readTimeout),
      writeTimeout_(writeTimeout),
      inputBuffer_(),
      inputOffset_(0),
      handle_(std::move(socket)),
      addressFamily_(addressFamily),
      isCorking_(false),
      onEndPointClosed_(onEndPointClosed),
      connection_() {
  TRACE("$0 ctor", this);
}

void TcpEndPoint::onTimeout() {
  if (connection()) {
    if (connection()->onReadTimeout()) {
      close();
    }
  }
}

TcpEndPoint::~TcpEndPoint() {
  TRACE("$0 dtor", this);
  if (isOpen()) {
    close();
  }
}

Option<InetAddress> TcpEndPoint::remoteAddress() const {
  Result<InetAddress> addr = TcpUtil::getRemoteAddress(handle_, addressFamily());
  if (addr.isSuccess())
    return Some(*addr);
  else {
    logError("TcpEndPoint", "remoteAddress: ($0) $1",
        addr.error().category().name(),
        addr.error().message().c_str());
    return None();
  }
}

Option<InetAddress> TcpEndPoint::localAddress() const {
  Result<InetAddress> addr = TcpUtil::getLocalAddress(handle_, addressFamily());
  if (addr.isSuccess())
    return Some(*addr);
  else {
    logError("TcpEndPoint", "localAddress: ($0) $1",
        addr.error().category().name(),
        addr.error().message().c_str());
    return None();
  }
}

bool TcpEndPoint::isOpen() const XZERO_NOEXCEPT {
  return handle_ >= 0;
}

void TcpEndPoint::close() {
  if (isOpen()) {
    TRACE("close() fd=$0", handle_.get());

    if (onEndPointClosed_) {
      onEndPointClosed_(this);
    }

    handle_.close();
  } else {
    TRACE("close(fd=$0) invoked, but we're closed already", handle_.get());
  }
}

void TcpEndPoint::setConnection(std::unique_ptr<Connection>&& c) {
  connection_ = std::move(c);
}

bool TcpEndPoint::isBlocking() const {
  return !(fcntl(handle_, F_GETFL) & O_NONBLOCK);
}

void TcpEndPoint::setBlocking(bool enable) {
  FileUtil::setBlocking(handle_, enable);
}

bool TcpEndPoint::isCorking() const {
  return isCorking_;
}

void TcpEndPoint::setCorking(bool enable) {
  if (isCorking_ != enable) {
    TcpUtil::setCorking(handle_, enable);
    isCorking_ = enable;
  }
}

bool TcpEndPoint::isTcpNoDelay() const {
  return TcpUtil::isTcpNoDelay(handle_);
}

void TcpEndPoint::setTcpNoDelay(bool enable) {
  TcpUtil::setTcpNoDelay(handle_, enable);
}

std::string TcpEndPoint::toString() const {
  char buf[32];
  snprintf(buf, sizeof(buf), "TcpEndPoint(%d)@%p", handle(), this);
  return buf;
}

void TcpEndPoint::startDetectProtocol(bool dataReady,
                                      ProtocolCallback createConnection) {
  inputBuffer_.reserve(256);

  if (dataReady) {
    onDetectProtocol(createConnection);
  } else {
    executor_->executeOnReadable(
        handle(),
        std::bind(&TcpEndPoint::onDetectProtocol, this, createConnection));
  }
}

void TcpEndPoint::onDetectProtocol(ProtocolCallback createConnection) {
  size_t n = fill(&inputBuffer_);

  if (n == 0) {
    close();
    return;
  }

  // XXX detect magic byte (0x01) to protocol detection
  if (inputBuffer_[0] == TcpConnector::MagicProtocolSwitchByte) {
    BinaryReader reader(inputBuffer_);
    reader.parseVarUInt(); // skip magic
    std::string protocol = reader.parseString();
    inputOffset_ = inputBuffer_.size() - reader.pending();
    createConnection(protocol, this);
  } else {
    // create Connection object for given endpoint
    TRACE("$0 protocol-switch not detected.", this);
    createConnection("", this);
  }

  if (connection_) {
    connection_->onOpen(true);
  } else {
    close();
  }
}

size_t TcpEndPoint::prefill(size_t maxBytes) {
  const size_t nprefilled = prefilled();
  if (nprefilled > 0)
    return nprefilled;

  inputBuffer_.reserve(maxBytes);
  return fill(&inputBuffer_);
}

size_t TcpEndPoint::fill(Buffer* sink) {
  int space = sink->capacity() - sink->size();
  if (space < 4 * 1024) {
    sink->reserve(sink->capacity() + 8 * 1024);
    space = sink->capacity() - sink->size();
  }

  return fill(sink, space);
}

size_t TcpEndPoint::fill(Buffer* result, size_t count) {
  assert(count <= result->capacity() - result->size());

  if (inputOffset_ < inputBuffer_.size()) {
    TRACE("$0 fill: with inputBuffer ($1, $2)", this, inputOffset_, inputBuffer_.size());
    count = std::min(count, inputBuffer_.size() - inputOffset_);
    result->push_back(inputBuffer_.ref(inputOffset_, count));
    TRACE("$0 fill: \"$1\"", this, inputBuffer_.ref(inputOffset_, count));
    inputOffset_ += count;
    if (inputOffset_ == inputBuffer_.size()) {
      inputBuffer_.clear();
      inputOffset_ = 0;
    }
    return count;
  }

  ssize_t n = read(handle(), result->end(), count);
  TRACE("read($0 bytes) -> $1", result->capacity() - result->size(), n);

  if (n < 0) {
    // don't raise on soft errors, such as there is simply no more data to read.
    switch (errno) {
      case EBUSY:
      case EAGAIN:
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
      case EWOULDBLOCK:
#endif
        break;
      default:
        RAISE_ERRNO(errno);
    }
  } else {
    result->resize(result->size() + n);
  }

  return n;
}

size_t TcpEndPoint::flush(const BufferRef& source) {
  ssize_t rv = write(handle(), source.data(), source.size());

  TRACE("flush($0 bytes) -> $1", source.size(), rv);

  if (rv < 0)
    RAISE_ERRNO(errno);

  // EOF exception?

  return rv;
}

size_t TcpEndPoint::flush(const FileView& view) {
  return TcpUtil::sendfile(handle(), view);
}

void TcpEndPoint::wantFill() {
  TRACE("$0 wantFill()", this);
  // TODO: abstract away the logic of TCP_DEFER_ACCEPT

  if (!io_) {
    io_ = executor_->executeOnReadable(
        handle(),
        std::bind(&TcpEndPoint::fillable, this),
        readTimeout(),
        std::bind(&TcpEndPoint::onTimeout, this));
  }
}

void TcpEndPoint::fillable() {
  RefPtr<TcpEndPoint> _guard(this);

  try {
    io_.reset();
    connection()->onFillable();
  } catch (const std::exception& e) {
    connection()->onInterestFailure(e);
  } catch (...) {
    connection()->onInterestFailure(
        EXCEPTION(RuntimeError, (int) Status::CaughtUnknownExceptionError,
                  StatusCategory::get()));
  }
}

void TcpEndPoint::wantFlush() {
  TRACE("$0 wantFlush() $1", this, io_.get() ? "again" : "first time");

  if (!io_) {
    io_ = executor_->executeOnWritable(
        handle(),
        std::bind(&TcpEndPoint::flushable, this),
        writeTimeout(),
        std::bind(&TcpEndPoint::onTimeout, this));
  }
}

void TcpEndPoint::flushable() {
  TRACE("$0 flushable()", this);
  RefPtr<TcpEndPoint> _guard(this);
  try {
    io_.reset();
    connection()->onFlushable();
  } catch (const std::exception& e) {
    connection()->onInterestFailure(e);
  } catch (...) {
    connection()->onInterestFailure(
        EXCEPTION(RuntimeError, (int) Status::CaughtUnknownExceptionError,
                  StatusCategory::get()));
  }
}

Duration TcpEndPoint::readTimeout() const noexcept {
  return readTimeout_;
}

Duration TcpEndPoint::writeTimeout() const noexcept {
  return writeTimeout_;
}

class TcpConnectState {
 public:
  InetAddress address;
  int fd;
  Duration readTimeout;
  Duration writeTimeout;
  Executor* executor;
  Promise<RefPtr<TcpEndPoint>> promise;

  void onTimeout() {
    TRACE("$0 onTimeout: connecting timed out", this);
    promise.failure(std::errc::timed_out);
    delete this;
  }

  void onConnectComplete() {
    int val = 0;
    socklen_t vlen = sizeof(val);
    if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &vlen) == 0) {
      if (val == 0) {
        TRACE("$0 onConnectComplete: connected $1. $2", this, val, strerror(val));
        promise.success(make_ref<TcpEndPoint>(FileDescriptor{fd},
                                              address.family(),
                                              readTimeout,
                                              writeTimeout,
                                              executor,
                                              nullptr));
      } else {
        DEBUG("Connecting to $0 failed. $1", address, strerror(val));
        promise.failure(std::make_error_code(static_cast<std::errc>(val)));
      }
    } else {
      DEBUG("Connecting to $0 failed. $1", address, strerror(val));
      promise.failure(std::make_error_code(static_cast<std::errc>(val)));
    }
    delete this;
  }
};

Future<RefPtr<TcpEndPoint>> TcpEndPoint::connect(const InetAddress& address,
                                                 Duration connectTimeout,
                                                 Duration readTimeout,
                                                 Duration writeTimeout,
                                                 Executor* executor) {
  int flags = 0;
#if defined(SOCK_CLOEXEC)
  flags |= SOCK_CLOEXEC;
#endif
#if defined(SOCK_NONBLOCK)
  flags |= SOCK_NONBLOCK;
#endif

  Promise<RefPtr<TcpEndPoint>> promise;

  int fd = socket(address.family(), SOCK_STREAM | flags, IPPROTO_TCP);
  if (fd < 0) {
    promise.failure(std::make_error_code(static_cast<std::errc>(errno)));
    return promise.future();
  }

#if !defined(SOCK_NONBLOCK)
  FileUtil::setBlocking(fd, false);
#endif

  std::error_code ec = TcpUtil::connect(fd, address);
  if (!ec) {
    TRACE("connect: connected instantly");
    promise.success(make_ref<TcpEndPoint>(FileDescriptor{fd},
                                          address.family(),
                                          readTimeout,
                                          writeTimeout,
                                          executor,
                                          nullptr));
  } else if (ec == std::errc::operation_in_progress) {
    TRACE("connect: backgrounding");
    auto state = new TcpConnectState{address, fd, readTimeout, writeTimeout,
                                     executor, promise};
    executor->executeOnWritable(fd,
        std::bind(&TcpConnectState::onConnectComplete, state),
        connectTimeout,
        std::bind(&TcpConnectState::onTimeout, state));
  } else {
    TRACE("connect: connect() error. $0", strerror(errno));
    promise.failure(std::make_error_code(static_cast<std::errc>(errno)));
  }
  return promise.future();
}

} // namespace xzero
<|endoftext|>