<commit_before>/*
    Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org>

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

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

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

#include "imapparser.h"

#include <QtCore/QDateTime>
#include <QtCore/QDebug>
#include <ctype.h>

using namespace Akonadi;

class ImapParser::Private {
  public:
    QByteArray tagBuffer;
    QByteArray dataBuffer;
    int parenthesesCount;
    int literalSize;
    bool continuation;

    // returns true if readBuffer contains a literal start and sets
    // parser state accordingly
    bool checkLiteralStart( const QByteArray &readBuffer, int pos = 0 )
    {
      if ( readBuffer.trimmed().endsWith( '}' ) ) {
        const int begin = readBuffer.lastIndexOf( '{' );
        const int end = readBuffer.lastIndexOf( '}' );

        // new literal in previous literal data block
        if ( begin < pos )
          return false;

        // TODO error handling
        literalSize = readBuffer.mid( begin + 1, end - begin - 1 ).toInt();

        // empty literal
        if ( literalSize == 0 )
          return false;

        continuation = true;
        return true;
      }
      return false;
    }
};

int ImapParser::parseParenthesizedList( const QByteArray & data, QList<QByteArray> &result, int start )
{
  result.clear();
  if ( start >= data.length() )
    return data.length();

  int begin = data.indexOf( '(', start );
  if ( begin < 0 )
    return start;

  int count = 0;
  int sublistbegin = start;
  for ( int i = begin + 1; i < data.length(); ++i ) {
    if ( data[i] == '(' ) {
      ++count;
      if ( count == 1 )
        sublistbegin = i;
      continue;
    }
    if ( data[i] == ')' ) {
      if ( count <= 0 )
        return i + 1;
      if ( count == 1 )
        result.append( data.mid( sublistbegin, i - sublistbegin + 1 ) );
      --count;
      continue;
    }
    if ( data[i] == ' ' )
      continue;
    if ( count == 0 ) {
      QByteArray ba;
      i = parseString( data, ba, i ) - 1; // compensate the increment
      result.append( ba );
    }
  }
  return data.length();
}

int ImapParser::parseString( const QByteArray & data, QByteArray & result, int start )
{
  int begin = stripLeadingSpaces( data, start );
  result.clear();
  if ( begin >= data.length() )
    return data.length();

  // literal string
  // TODO: error handling
  if ( data[begin] == '{' ) {
    int end = data.indexOf( '}', begin );
    Q_ASSERT( end > begin );
    int size = data.mid( begin + 1, end - begin - 1 ).toInt();

    // strip CRLF
    begin = end + 1;
    if ( begin < data.length() && data[begin] == '\r' )
      ++begin;
    if ( begin < data.length() && data[begin] == '\n' )
      ++begin;

    end = begin + size;
    result = data.mid( begin, end - begin );
    return end;
  }

  // quoted string
  return parseQuotedString( data, result, begin );
}

int ImapParser::parseQuotedString( const QByteArray & data, QByteArray &result, int start )
{
  int begin = stripLeadingSpaces( data, start );
  int end = begin;
  result.clear();
  if ( begin >= data.length() )
    return data.length();

  // quoted string
  if ( data[begin] == '"' ) {
    ++begin;
    for ( int i = begin; i < data.length(); ++i ) {
      if ( data[i] == '\\' ) {
        ++i;
        continue;
      }
      if ( data[i] == '"' ) {
        result = data.mid( begin, i - begin );
        end = i + 1; // skip the '"'
        break;
      }
    }
  }

  // unquoted string
  else {
    bool reachedInputEnd = true;
    for ( int i = begin; i < data.length(); ++i ) {
      if ( data[i] == ' ' || data[i] == '(' || data[i] == ')' || data[i] == '\n' || data[i] == '\r' ) {
        end = i;
        reachedInputEnd = false;
        break;
      }
    }
    if ( reachedInputEnd )
      end = data.length();
    result = data.mid( begin, end - begin );

    // transform unquoted NIL
    if ( result == "NIL" )
      result.clear();
  }

  // strip quotes
  // FIXME: this can be done more efficiently
  while ( result.contains( "\\\"" ) )
    result.replace( "\\\"", "\"" );
  while ( result.contains( "\\\\" ) )
    result.replace( "\\\\", "\\" );

  return end;
}

int ImapParser::stripLeadingSpaces( const QByteArray & data, int start )
{
  for ( int i = start; i < data.length(); ++i ) {
    if ( data[i] != ' ' )
      return i;
  }
  return data.length();
}

int ImapParser::parenthesesBalance( const QByteArray & data, int start )
{
  int count = 0;
  bool insideQuote = false;
  for ( int i = start; i < data.length(); ++i ) {
    if ( data[i] == '"' ) {
      insideQuote = !insideQuote;
      continue;
    }
    if ( data[i] == '\\' && insideQuote ) {
      ++i;
      continue;
    }
    if ( data[i] == '(' && !insideQuote ) {
      ++count;
      continue;
    }
    if ( data[i] == ')' && !insideQuote ) {
      --count;
      continue;
    }
  }
  return count;
}

QByteArray ImapParser::join(const QList< QByteArray > & list, const QByteArray & separator)
{
  if ( list.isEmpty() )
    return QByteArray();

  QByteArray result = list.first();
  QList<QByteArray>::ConstIterator it = list.constBegin();
  ++it;
  for ( ; it != list.constEnd(); ++it )
    result += separator + (*it);

  return result;
}

QByteArray ImapParser::join(const QSet< QByteArray > & set, const QByteArray & separator)
{
  QList< QByteArray > list = QList< QByteArray >::fromSet( set );

  return ImapParser::join( list, separator );
}

int ImapParser::parseString(const QByteArray & data, QString & result, int start)
{
  QByteArray tmp;
  int end = parseString( data, tmp, start );
  result = QString::fromUtf8( tmp );
  return end;
}

int ImapParser::parseNumber(const QByteArray & data, int & result, bool * ok, int start)
{
  if ( ok )
    *ok = false;
  int pos = stripLeadingSpaces( data, start );
  if ( pos >= data.length() )
    return data.length();
  QByteArray tmp;
  for (; pos < data.length(); ++pos ) {
    if ( !isdigit( data.at( pos ) ) )
      break;
    tmp += data.at( pos );
  }
  result = tmp.toInt( ok );
  return pos;
}

QByteArray ImapParser::quote(const QByteArray & data)
{
  QByteArray result( "\"" );
  result.reserve( data.length() + 2 );
  for ( int i = 0; i < data.length(); ++i ) {
    if ( data.at( i ) == '"' || data.at( i ) == '\\' )
      result += '\\';
    result += data.at( i );
  }
  result += '"';
  return result;
}

int ImapParser::parseSequenceSet(const QByteArray & data, ImapSet & result, int start)
{
  int begin = stripLeadingSpaces( data, start );
  int value = -1, lower = -1, upper = -1;
  for ( int i = begin; i < data.length(); ++i ) {
    if ( data[i] == '*' ) {
      value = 0;
    } else if ( data[i] == ':' ) {
      lower = value;
    } else if ( isdigit( data[i] ) ) {
      bool ok = false;
      i = parseNumber( data, value, &ok, i );
      Q_ASSERT( ok ); // TODO handle error
      --i;
    } else {
      upper = value;
      if ( lower < 0 )
        lower = value;
      result.add( ImapInterval( lower, upper ) );
      lower = -1;
      upper = -1;
      value = -1;
      if ( data[i] != ',' )
        return i;
    }
  }
  // take care of left-overs at input end
  upper = value;
  if ( lower < 0 )
    lower = value;
  if ( lower >= 0 && upper >= 0 )
    result.add( ImapInterval( lower, upper ) );
  return data.length();
}

int ImapParser::parseDateTime(const QByteArray & data, QDateTime & dateTime, int start)
{
  // Syntax:
  // date-time      = DQUOTE date-day-fixed "-" date-month "-" date-year
  //                  SP time SP zone DQUOTE
  // date-day-fixed = (SP DIGIT) / 2DIGIT
  //                    ; Fixed-format version of date-day
  // date-month     = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
  //                  "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
  // date-year      = 4DIGIT
  // time           = 2DIGIT ":" 2DIGIT ":" 2DIGIT
  //                    ; Hours minutes seconds
  // zone           = ("+" / "-") 4DIGIT
  //                    ; Signed four-digit value of hhmm representing
  //                    ; hours and minutes east of Greenwich (that is,
  //                    ; the amount that the given time differs from
  //                    ; Universal Time).  Subtracting the timezone
  //                    ; from the given time will give the UT form.
  //                    ; The Universal Time zone is "+0000".
  // Example : "28-May-2006 01:03:35 +0200"
  // Position: 0123456789012345678901234567
  //                     1         2

  int pos = stripLeadingSpaces( data, start );
  if ( data.length() <= pos )
    return pos;
  bool quoted = false;
  if ( data[pos] == '"' ) {
    quoted = true;
    ++pos;
  }
  if ( data.length() <= pos + 26 )
    return start;

  bool ok = true;
  const int day = ( data[pos] == ' ' ? data[pos + 1] - '0' // single digit day
                                     : data.mid( pos, 2 ).toInt( &ok ) );
  if ( !ok ) return start;
  pos += 3;
  const QByteArray shortMonthNames( "janfebmaraprmayjunjulaugsepoctnovdec" );
  int month = shortMonthNames.indexOf( data.mid( pos, 3 ).toLower() );
  if ( month == -1 ) return start;
  month = month / 3 + 1;
  pos += 4;
  const int year = data.mid( pos, 4 ).toInt( &ok );
  if ( !ok ) return start;
  pos += 5;
  const int hours = data.mid( pos, 2 ).toInt( &ok );
  if ( !ok ) return start;
  pos += 3;
  const int minutes = data.mid( pos, 2 ).toInt( &ok );
  if ( !ok ) return start;
  pos += 3;
  const int seconds = data.mid( pos, 2 ).toInt( &ok );
  if ( !ok ) return start;
  pos += 4;
  const int tzhh = data.mid( pos, 2 ).toInt( &ok );
  if ( !ok ) return start;
  pos += 2;
  const int tzmm = data.mid( pos, 2 ).toInt( &ok );
  if ( !ok ) return start;
  int tzsecs = tzhh*60*60 + tzmm*60;
  if ( data[pos - 3] == '-' ) tzsecs = -tzsecs;
  const QDate date( year, month, day );
  const QTime time( hours, minutes, seconds );
  dateTime = QDateTime( date, time, Qt::UTC );
  if ( !dateTime.isValid() ) return start;
  dateTime = dateTime.addSecs( -tzsecs );

  pos += 2;
  if ( data.length() <= pos || !quoted )
    return pos;
  if ( data[pos] == '"' )
    ++pos;
  return pos;
}

ImapParser::ImapParser() :
    d ( new Private )
{
  reset();
}

ImapParser::~ ImapParser()
{
  delete d;
}

bool ImapParser::parseNextLine(const QByteArray &readBuffer)
{
  d->continuation = false;

  // first line, get the tag
  if ( d->tagBuffer.isEmpty() ) {
    const int startOfData = ImapParser::parseString( readBuffer, d->tagBuffer );
    if ( startOfData < readBuffer.length() && startOfData >= 0 )
      d->dataBuffer = readBuffer.mid( startOfData + 1 );

  } else {
    d->dataBuffer += readBuffer;
  }

  // literal read in progress
  if ( d->literalSize > 0 ) {
    d->literalSize -= readBuffer.size();

    // still not everything read
    if ( d->literalSize > 0 )
      return false;

    // check the remaining (non-literal) part for parentheses
    if ( d->literalSize < 0 ) {
      // the following looks strange but works since literalSize can be negative here
      d->parenthesesCount = ImapParser::parenthesesBalance( readBuffer, readBuffer.length() + d->literalSize );

      // check if another literal read was started
      if ( d->checkLiteralStart( readBuffer, readBuffer.length() + d->literalSize ) )
        return false;
    }

    // literal string finished but still open parentheses
    if ( d->parenthesesCount > 0 )
        return false;

  } else {

    // open parentheses
    d->parenthesesCount += ImapParser::parenthesesBalance( readBuffer );

    // start new literal read
    if ( d->checkLiteralStart( readBuffer ) )
      return false;

    // still open parentheses
    if ( d->parenthesesCount > 0 )
      return false;

    // just a normal response, fall through
  }

  return true;
}

QByteArray ImapParser::tag() const
{
  return d->tagBuffer;
}

QByteArray ImapParser::data() const
{
  return d->dataBuffer;
}

void ImapParser::reset()
{
  d->dataBuffer.clear();
  d->tagBuffer.clear();
  d->parenthesesCount = 0;
  d->literalSize = 0;
  d->continuation = false;
}

bool ImapParser::continuationStarted() const
{
  return d->continuation;
}

int ImapParser::continuationSize() const
{
  return d->literalSize;
}<commit_msg>Don't serialize each item part three times in ItemAppendJob and a few other smaller optmizations pointed out by callgrind.<commit_after>/*
    Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org>

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

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

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

#include "imapparser.h"

#include <QtCore/QDateTime>
#include <QtCore/QDebug>
#include <ctype.h>

using namespace Akonadi;

class ImapParser::Private {
  public:
    QByteArray tagBuffer;
    QByteArray dataBuffer;
    int parenthesesCount;
    int literalSize;
    bool continuation;

    // returns true if readBuffer contains a literal start and sets
    // parser state accordingly
    bool checkLiteralStart( const QByteArray &readBuffer, int pos = 0 )
    {
      if ( readBuffer.trimmed().endsWith( '}' ) ) {
        const int begin = readBuffer.lastIndexOf( '{' );
        const int end = readBuffer.lastIndexOf( '}' );

        // new literal in previous literal data block
        if ( begin < pos )
          return false;

        // TODO error handling
        literalSize = readBuffer.mid( begin + 1, end - begin - 1 ).toInt();

        // empty literal
        if ( literalSize == 0 )
          return false;

        continuation = true;
        return true;
      }
      return false;
    }
};

int ImapParser::parseParenthesizedList( const QByteArray & data, QList<QByteArray> &result, int start )
{
  result.clear();
  if ( start >= data.length() )
    return data.length();

  int begin = data.indexOf( '(', start );
  if ( begin < 0 )
    return start;

  int count = 0;
  int sublistbegin = start;
  for ( int i = begin + 1; i < data.length(); ++i ) {
    if ( data[i] == '(' ) {
      ++count;
      if ( count == 1 )
        sublistbegin = i;
      continue;
    }
    if ( data[i] == ')' ) {
      if ( count <= 0 )
        return i + 1;
      if ( count == 1 )
        result.append( data.mid( sublistbegin, i - sublistbegin + 1 ) );
      --count;
      continue;
    }
    if ( data[i] == ' ' )
      continue;
    if ( count == 0 ) {
      QByteArray ba;
      i = parseString( data, ba, i ) - 1; // compensate the increment
      result.append( ba );
    }
  }
  return data.length();
}

int ImapParser::parseString( const QByteArray & data, QByteArray & result, int start )
{
  int begin = stripLeadingSpaces( data, start );
  result.clear();
  if ( begin >= data.length() )
    return data.length();

  // literal string
  // TODO: error handling
  if ( data[begin] == '{' ) {
    int end = data.indexOf( '}', begin );
    Q_ASSERT( end > begin );
    int size = data.mid( begin + 1, end - begin - 1 ).toInt();

    // strip CRLF
    begin = end + 1;
    if ( begin < data.length() && data[begin] == '\r' )
      ++begin;
    if ( begin < data.length() && data[begin] == '\n' )
      ++begin;

    end = begin + size;
    result = data.mid( begin, end - begin );
    return end;
  }

  // quoted string
  return parseQuotedString( data, result, begin );
}

int ImapParser::parseQuotedString( const QByteArray & data, QByteArray &result, int start )
{
  int begin = stripLeadingSpaces( data, start );
  int end = begin;
  result.clear();
  if ( begin >= data.length() )
    return data.length();

  // quoted string
  if ( data[begin] == '"' ) {
    ++begin;
    for ( int i = begin; i < data.length(); ++i ) {
      if ( data[i] == '\\' ) {
        ++i;
        continue;
      }
      if ( data[i] == '"' ) {
        result = data.mid( begin, i - begin );
        end = i + 1; // skip the '"'
        break;
      }
    }
  }

  // unquoted string
  else {
    bool reachedInputEnd = true;
    for ( int i = begin; i < data.length(); ++i ) {
      if ( data[i] == ' ' || data[i] == '(' || data[i] == ')' || data[i] == '\n' || data[i] == '\r' ) {
        end = i;
        reachedInputEnd = false;
        break;
      }
    }
    if ( reachedInputEnd )
      end = data.length();
    result = data.mid( begin, end - begin );

    // transform unquoted NIL
    if ( result == "NIL" )
      result.clear();
  }

  // strip quotes
  // FIXME: this can be done more efficiently
  while ( result.contains( "\\\"" ) )
    result.replace( "\\\"", "\"" );
  while ( result.contains( "\\\\" ) )
    result.replace( "\\\\", "\\" );

  return end;
}

int ImapParser::stripLeadingSpaces( const QByteArray & data, int start )
{
  for ( int i = start; i < data.length(); ++i ) {
    if ( data[i] != ' ' )
      return i;
  }
  return data.length();
}

int ImapParser::parenthesesBalance( const QByteArray & data, int start )
{
  int count = 0;
  bool insideQuote = false;
  for ( int i = start; i < data.length(); ++i ) {
    if ( data[i] == '"' ) {
      insideQuote = !insideQuote;
      continue;
    }
    if ( data[i] == '\\' && insideQuote ) {
      ++i;
      continue;
    }
    if ( data[i] == '(' && !insideQuote ) {
      ++count;
      continue;
    }
    if ( data[i] == ')' && !insideQuote ) {
      --count;
      continue;
    }
  }
  return count;
}

QByteArray ImapParser::join(const QList< QByteArray > & list, const QByteArray & separator)
{
  if ( list.isEmpty() )
    return QByteArray();

  QByteArray result = list.first();
  QList<QByteArray>::ConstIterator it = list.constBegin();
  ++it;
  for ( ; it != list.constEnd(); ++it )
    result += separator + (*it);

  return result;
}

QByteArray ImapParser::join(const QSet< QByteArray > & set, const QByteArray & separator)
{
  QList< QByteArray > list = QList< QByteArray >::fromSet( set );

  return ImapParser::join( list, separator );
}

int ImapParser::parseString(const QByteArray & data, QString & result, int start)
{
  QByteArray tmp;
  int end = parseString( data, tmp, start );
  result = QString::fromUtf8( tmp );
  return end;
}

int ImapParser::parseNumber(const QByteArray & data, int & result, bool * ok, int start)
{
  if ( ok )
    *ok = false;
  int pos = stripLeadingSpaces( data, start );
  if ( pos >= data.length() )
    return data.length();
  int begin = pos;
  for (; pos < data.length(); ++pos ) {
    if ( !isdigit( data.at( pos ) ) )
      break;
  }
  QByteArray tmp = data.mid( begin, pos - begin );
  result = tmp.toInt( ok );
  return pos;
}

QByteArray ImapParser::quote(const QByteArray & data)
{
  QByteArray result( "\"" );
  result.reserve( data.length() + 2 );
  for ( int i = 0; i < data.length(); ++i ) {
    if ( data.at( i ) == '"' || data.at( i ) == '\\' )
      result += '\\';
    result += data.at( i );
  }
  result += '"';
  return result;
}

int ImapParser::parseSequenceSet(const QByteArray & data, ImapSet & result, int start)
{
  int begin = stripLeadingSpaces( data, start );
  int value = -1, lower = -1, upper = -1;
  for ( int i = begin; i < data.length(); ++i ) {
    if ( data[i] == '*' ) {
      value = 0;
    } else if ( data[i] == ':' ) {
      lower = value;
    } else if ( isdigit( data[i] ) ) {
      bool ok = false;
      i = parseNumber( data, value, &ok, i );
      Q_ASSERT( ok ); // TODO handle error
      --i;
    } else {
      upper = value;
      if ( lower < 0 )
        lower = value;
      result.add( ImapInterval( lower, upper ) );
      lower = -1;
      upper = -1;
      value = -1;
      if ( data[i] != ',' )
        return i;
    }
  }
  // take care of left-overs at input end
  upper = value;
  if ( lower < 0 )
    lower = value;
  if ( lower >= 0 && upper >= 0 )
    result.add( ImapInterval( lower, upper ) );
  return data.length();
}

int ImapParser::parseDateTime(const QByteArray & data, QDateTime & dateTime, int start)
{
  // Syntax:
  // date-time      = DQUOTE date-day-fixed "-" date-month "-" date-year
  //                  SP time SP zone DQUOTE
  // date-day-fixed = (SP DIGIT) / 2DIGIT
  //                    ; Fixed-format version of date-day
  // date-month     = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
  //                  "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
  // date-year      = 4DIGIT
  // time           = 2DIGIT ":" 2DIGIT ":" 2DIGIT
  //                    ; Hours minutes seconds
  // zone           = ("+" / "-") 4DIGIT
  //                    ; Signed four-digit value of hhmm representing
  //                    ; hours and minutes east of Greenwich (that is,
  //                    ; the amount that the given time differs from
  //                    ; Universal Time).  Subtracting the timezone
  //                    ; from the given time will give the UT form.
  //                    ; The Universal Time zone is "+0000".
  // Example : "28-May-2006 01:03:35 +0200"
  // Position: 0123456789012345678901234567
  //                     1         2

  int pos = stripLeadingSpaces( data, start );
  if ( data.length() <= pos )
    return pos;
  bool quoted = false;
  if ( data[pos] == '"' ) {
    quoted = true;
    ++pos;
  }
  if ( data.length() <= pos + 26 )
    return start;

  bool ok = true;
  const int day = ( data[pos] == ' ' ? data[pos + 1] - '0' // single digit day
                                     : data.mid( pos, 2 ).toInt( &ok ) );
  if ( !ok ) return start;
  pos += 3;
  const QByteArray shortMonthNames( "janfebmaraprmayjunjulaugsepoctnovdec" );
  int month = shortMonthNames.indexOf( data.mid( pos, 3 ).toLower() );
  if ( month == -1 ) return start;
  month = month / 3 + 1;
  pos += 4;
  const int year = data.mid( pos, 4 ).toInt( &ok );
  if ( !ok ) return start;
  pos += 5;
  const int hours = data.mid( pos, 2 ).toInt( &ok );
  if ( !ok ) return start;
  pos += 3;
  const int minutes = data.mid( pos, 2 ).toInt( &ok );
  if ( !ok ) return start;
  pos += 3;
  const int seconds = data.mid( pos, 2 ).toInt( &ok );
  if ( !ok ) return start;
  pos += 4;
  const int tzhh = data.mid( pos, 2 ).toInt( &ok );
  if ( !ok ) return start;
  pos += 2;
  const int tzmm = data.mid( pos, 2 ).toInt( &ok );
  if ( !ok ) return start;
  int tzsecs = tzhh*60*60 + tzmm*60;
  if ( data[pos - 3] == '-' ) tzsecs = -tzsecs;
  const QDate date( year, month, day );
  const QTime time( hours, minutes, seconds );
  dateTime = QDateTime( date, time, Qt::UTC );
  if ( !dateTime.isValid() ) return start;
  dateTime = dateTime.addSecs( -tzsecs );

  pos += 2;
  if ( data.length() <= pos || !quoted )
    return pos;
  if ( data[pos] == '"' )
    ++pos;
  return pos;
}

ImapParser::ImapParser() :
    d ( new Private )
{
  reset();
}

ImapParser::~ ImapParser()
{
  delete d;
}

bool ImapParser::parseNextLine(const QByteArray &readBuffer)
{
  d->continuation = false;

  // first line, get the tag
  if ( d->tagBuffer.isEmpty() ) {
    const int startOfData = ImapParser::parseString( readBuffer, d->tagBuffer );
    if ( startOfData < readBuffer.length() && startOfData >= 0 )
      d->dataBuffer = readBuffer.mid( startOfData + 1 );

  } else {
    d->dataBuffer += readBuffer;
  }

  // literal read in progress
  if ( d->literalSize > 0 ) {
    d->literalSize -= readBuffer.size();

    // still not everything read
    if ( d->literalSize > 0 )
      return false;

    // check the remaining (non-literal) part for parentheses
    if ( d->literalSize < 0 ) {
      // the following looks strange but works since literalSize can be negative here
      d->parenthesesCount = ImapParser::parenthesesBalance( readBuffer, readBuffer.length() + d->literalSize );

      // check if another literal read was started
      if ( d->checkLiteralStart( readBuffer, readBuffer.length() + d->literalSize ) )
        return false;
    }

    // literal string finished but still open parentheses
    if ( d->parenthesesCount > 0 )
        return false;

  } else {

    // open parentheses
    d->parenthesesCount += ImapParser::parenthesesBalance( readBuffer );

    // start new literal read
    if ( d->checkLiteralStart( readBuffer ) )
      return false;

    // still open parentheses
    if ( d->parenthesesCount > 0 )
      return false;

    // just a normal response, fall through
  }

  return true;
}

QByteArray ImapParser::tag() const
{
  return d->tagBuffer;
}

QByteArray ImapParser::data() const
{
  return d->dataBuffer;
}

void ImapParser::reset()
{
  d->dataBuffer.clear();
  d->tagBuffer.clear();
  d->parenthesesCount = 0;
  d->literalSize = 0;
  d->continuation = false;
}

bool ImapParser::continuationStarted() const
{
  return d->continuation;
}

int ImapParser::continuationSize() const
{
  return d->literalSize;
}
<|endoftext|>


<commit_before>/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2011, Willow Garage, Inc.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of Willow Garage, Inc. nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 * Author: Nico Blodow (blodow@cs.tum.edu)
 */

#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/io/openni_grabber.h>
#include <pcl/io/pcd_io.h>
#include <pcl/common/time.h>

class OpenNIGrabFrame
{
  public:
    OpenNIGrabFrame () : no_frame(true) {}

    void
    cloud_cb_ (const pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr &cloud)
    {
      if (no_frame)
      {

        std::string output_dir;
        if (output_dir.empty ())
          output_dir = ".";

        std::stringstream ss;
        ss << output_dir << "/frame_" << boost::posix_time::to_iso_string(boost::posix_time::microsec_clock::local_time());
        std::string file = ss.str ();

        //w.writeASCII<pcl::PointXYZRGB> (file + "_ascii.pcd", *cloud);
        //w.writeBinary<pcl::PointXYZRGB> (file + "_binary.pcd", *cloud);
        w.writeBinaryCompressed<pcl::PointXYZRGB> (file + "_binary_compressed.pcd", *cloud);

        std::cerr << "Data saved to " << ss.str () << "_{ascii,binary,binary_compresed}.pcd" << std::endl;
        //no_frame = false;
      }
    }

    void
    run ()
    {
      // create a new grabber for OpenNI devices
      pcl::Grabber* interface = new pcl::OpenNIGrabber();

      // make callback function from member function
      boost::function<void (const pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr&)> f =
        boost::bind (&OpenNIGrabFrame::cloud_cb_, this, _1);

      // connect callback function for desired signal. In this case its a point cloud with color values
      boost::signals2::connection c = interface->registerCallback (f);

      // start receiving point clouds
      interface->start ();

      // wait until user quits program with Ctrl-C, but no busy-waiting -> sleep (1);
      while (no_frame)
        boost::this_thread::sleep (boost::posix_time::seconds (1));

      // stop the grabber
      interface->stop ();
    }

    pcl::PCDWriter w;
    bool no_frame;
};

int
main ()
{
  OpenNIGrabFrame v;
  v.run ();
  return 0;
}

<commit_msg>* added functionality for openni_grab_frame * added optional command line options * optinal output filename  * chose output format<commit_after>/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2011, Willow Garage, Inc.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of Willow Garage, Inc. nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 * Author: Nico Blodow (blodow@cs.tum.edu)
 *         Christian Potthast (potthast@usc.edu)
 */

#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/io/openni_grabber.h>
#include <pcl/io/pcd_io.h>
#include <pcl/common/time.h>
#include <pcl/console/print.h>
#include <pcl/console/parse.h>

using namespace pcl::console;

std::string default_format = "bc";
bool default_noend = false;
bool set_file_name = false;

class OpenNIGrabFrame
{
  public:
    OpenNIGrabFrame () : no_frame(true) {}

    void
    cloud_cb_ (const pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr &cloud)
    {
      if (no_frame)
      {
        std::string output_dir;
        if (output_dir.empty ())
          output_dir = ".";

	std::stringstream ss;
	if (noend || !set_file_name)
	{
          ss << output_dir << "/frame_" << boost::posix_time::to_iso_string(boost::posix_time::microsec_clock::local_time());
          file = ss.str ();
	}

	if (strcmp (format.c_str(), "ascii") == 0)
	{
          w.writeASCII<pcl::PointXYZRGB> (file + ".pcd", *cloud);
	  std::cerr << "Data saved in ASCII format to " << file << ".pcd" << std::endl;
	}
	else if (strcmp (format.c_str(), "b") == 0)
	{
	  w.writeBinary<pcl::PointXYZRGB> (file + ".pcd", *cloud);
	  std::cerr << "Data saved in BINARY format to " << file << ".pcd" << std::endl;
	}
	else if (strcmp (format.c_str(), "bc") == 0)
	{
	  w.writeBinaryCompressed<pcl::PointXYZRGB> (file + ".pcd", *cloud);
	  std::cerr << "Data saved in BINARY COMPRESSED format to " << file << ".pcd" << std::endl;
	}
	  else if (strcmp (format.c_str(), "all") == 0)
	{
	  w.writeASCII<pcl::PointXYZRGB> (file + "_ascii.pcd", *cloud);
	  w.writeBinary<pcl::PointXYZRGB> (file + "_binary.pcd", *cloud);
	  w.writeBinaryCompressed<pcl::PointXYZRGB> (file + "_binary_compressed.pcd", *cloud);
	  std::cerr << "Data saved as ASCII, BINARY and BINARY COMPRESSED to " << file << "_{ascii,binary,binary_compresed}.pcd" << std::endl;
	}

	if (!noend)
	  no_frame = false;
      }
    }

    void
    run ()
    {
      // create a new grabber for OpenNI devices
      pcl::Grabber* interface = new pcl::OpenNIGrabber();

      // make callback function from member function
      boost::function<void (const pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr&)> f =
        boost::bind (&OpenNIGrabFrame::cloud_cb_, this, _1);

      // connect callback function for desired signal. In this case its a point cloud with color values
      boost::signals2::connection c = interface->registerCallback (f);

      // start receiving point clouds
      interface->start ();

      // wait until user quits program with Ctrl-C, but no busy-waiting -> sleep (1);
      while (no_frame)
        boost::this_thread::sleep (boost::posix_time::seconds (1));

      // stop the grabber
      interface->stop ();
    }

    void
    setOptions (std::string filename, std::string pcd_format, bool loop)
    {
      if (strcmp (filename.c_str(), "") != 0)
      {
	set_file_name = true;

	size_t index = filename.find(".");
	file = filename.substr(0, index);
      }
      noend = loop;
      format = pcd_format;
    }

    pcl::PCDWriter w;
    bool no_frame;
    bool noend;
    std::string file;
    std::string format;
};

void
usage (char ** argv)
{
  std::cout << "usage: " << argv[0] << " <filename> <options>\n\n";

  print_info ("  filename: if no filename is provided a generic timestamp will be set as filename\n\n");
  print_info ("  where options are:\n");
  print_info ("                    -format = PCD file format (b=binary; bc=binary compressed; ascii=acsii; all=all) (default: ");
  print_value ("%s", default_format.c_str()); print_info(")\n");
  print_info ("                    -noend  = Store files to disk until the user quits the program with Ctrl-C (default: ") ;
  print_value ("%d", default_noend); print_info(")\n");
}

int
main (int argc, char** argv)
{
  OpenNIGrabFrame v;

  std::string arg;
  if (argc > 1)
    arg = std::string (argv[1]);

  if (arg == "--help" || arg == "-h")
  {
    usage (argv);
    return 1;
  }

  std::string format = default_format;
  bool noend = default_noend;
  std::string filename;
  if (argc > 1)
  {
    // Parse the command line arguments for .pcd file
    std::vector<int> p_file_indices;
    p_file_indices = parse_file_extension_argument (argc, argv, ".pcd");
    if (p_file_indices.size () > 0)
      filename = argv[p_file_indices[0]];

    // Command line parsing
    parse_argument (argc, argv, "-format", format);
    if (find_argument(argc, argv, "-noend") != -1)
      noend = true;
  }

  v.setOptions (filename, format, noend);
  v.run ();
  return 0;
}

<|endoftext|>


<commit_before>// Copyright (C) 2010 and 2011 Marcin Arkadiusz Skrobiranda.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.

#include "../../../Building/BuildingToHumanTranslator.hpp"
#include "../../HumanToBuildingTranslator.hpp"
#include "EngageHumanOperator.hpp"
#include <GameServer/Configuration/Configurator/Human/ConfiguratorHuman.hpp>

using namespace GameServer::Building;
using namespace GameServer::Common;
using namespace GameServer::Configuration;
using namespace GameServer::Persistence;
using namespace GameServer::Resource;

namespace GameServer
{
namespace Human
{

EngageHumanOperator::EngageHumanOperator(
    IContextShrPtr                   const a_context,
    IBuildingPersistenceFacadeShrPtr       a_building_persistence_facade,
    IHumanPersistenceFacadeShrPtr          a_human_persistence_facade,
    IResourcePersistenceFacadeShrPtr       a_resource_persistence_facade
)
    : m_context(a_context),
      m_building_persistence_facade(a_building_persistence_facade),
      m_human_persistence_facade(a_human_persistence_facade),
      m_resource_persistence_facade(a_resource_persistence_facade)
{
}

/**
 * TODO: Check if holder exists.
 */
EngageHumanOperatorExitCode EngageHumanOperator::engageHuman(
    ITransactionShrPtr         a_transaction,
    IDHolder           const & a_id_holder,
    IHumanKey          const & a_key,
    Volume             const & a_volume
) const
{
    try
    {
        // Trying to engage zero humans.
        if (a_volume == 0)
        {
            return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_TRYING_TO_ENGAGE_ZERO_HUMANS);
        }

        // Human is not engageable.
        if (!verifyEngageable(a_transaction, a_key))
        {
            return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_HUMAN_IS_NOT_ENGAGEABLE);
        }

        // There are not enough jobless.
        if (!verifyJobless(a_transaction, a_id_holder, a_volume))
        {
            return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_NOT_ENOUGH_JOBLESS);
        }

        // Get available resources.
        ResourceSet resource_set = m_resource_persistence_facade->getResources(a_transaction, a_id_holder);

        // Get total cost.
        ResourceSet cost = CONFIGURATOR_HUMAN.getHuman(a_key)->getCostsToEngage();

        // Multiply total cost.
        cost *= a_volume;

        // Check if there is enough resources.
        if (!(resource_set >= cost))
        {
            return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_NOT_ENOUGH_RESOURCES);
        }

        // Verifies if all possible dependencies on buildings are fulfilled.
        if (!verifyDependencyOfEngagementOnBuilding(a_transaction, a_id_holder, a_key, a_volume))
        {
            return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_NOT_ENOUGH_BUILDINGS);
        }

        // Subtract the resources.
        bool const result_subtract_resource =
            m_resource_persistence_facade->subtractResourceSet(a_transaction, a_id_holder, cost);

        // There is a possible situation (in multithreaded application) of a race condition between checking if
        // there is enough resources and trying to subtract it.
        // As it is not an error (just a result of another action) the appropriate exit code is returned.
        if (!result_subtract_resource)
        {
            return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_RESOURCES_MISSING_IN_THE_MEANTIME);
        }

        // Subtract the jobless.
        bool const result_subtract_human =
            m_human_persistence_facade->subtractHuman(a_transaction, a_id_holder, KEY_WORKER_JOBLESS_NOVICE, a_volume);

        // There is a possible situation (in multithreaded application) of a race condition between checking if
        // there is enough jobless and trying to subtract them.
        // As it is not an error (just a result of another action) the appropriate exit code is returned.
        if (!result_subtract_human)
        {
            return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_JOBLESS_MISSING_IN_THE_MEANTIME);
        }

        // Add the humans.
        m_human_persistence_facade->addHuman(a_transaction, a_id_holder, a_key, a_volume);

        // Everything went fine.
        return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_HUMAN_HAS_BEEN_ENGAGED);
    }
    catch (...)
    {
        return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_UNEXPECTED_ERROR);
    }
}

bool EngageHumanOperator::verifyDependencyOfEngagementOnBuilding(
    ITransactionShrPtr         a_transaction,
    IDHolder           const & a_id_holder,
    IHumanKey          const & a_key,
    Volume             const & a_volume
) const
{
    // Verify space in buildings.
    HumanToBuildingTranslator translator;
    IBuildingShrPtr building = translator.getPlaceOfWork(m_context, a_key);
    if (building)
    {
        // Get available buildings.
        BuildingWithVolumeShrPtr building_with_volume =
            m_building_persistence_facade->getBuilding(a_transaction, a_id_holder, building->getKey());

        // Building does not exist.
        if (!building_with_volume)
        {
            return false;
        }

        // Get a vector of identifiers of a human to check if building is a place of work for some humans.
        KeyVec humans = BuildingToHumanTranslator::getHumansHostedForWork(building->getKey());

        // The building is a place of work for at least one human.
        BOOST_ASSERT(!humans.empty());

        // A sum of humans.
        Volume sum(0);

        // Get humans engaged in buildings.
        for (KeyVec::const_iterator it = humans.begin(); it != humans.end(); ++it)
        {
            // Get a human by identifier of a human.
            HumanWithVolumeShrPtr human = m_human_persistence_facade->getHuman(a_transaction, a_id_holder, *it);

            if (human)
            {
                sum += human->getVolume();
            }
        }

        // Check if there is enough place.
        {
            // Check if there is enough capacity.
            if (building_with_volume->getVolume() * building_with_volume->getBuilding()->getCapacity() - sum < a_volume)
            {
                return false;
            }
        }
    }

    return true;
}

bool EngageHumanOperator::verifyEngageable(
    ITransactionShrPtr         a_transaction,
    IHumanKey          const & a_key
) const
{
    IHumanShrPtr human = CONFIGURATOR_HUMAN.getHuman(a_key);

    BOOST_ASSERT(human);

    return human->isEngageable();
}

bool EngageHumanOperator::verifyJobless(
    ITransactionShrPtr         a_transaction,
    IDHolder           const & a_id_holder,
    Volume             const & a_volume
) const
{
    // Get the jobless.
    HumanWithVolumeShrPtr jobless =
        m_human_persistence_facade->getHuman(a_transaction, a_id_holder, KEY_WORKER_JOBLESS_NOVICE);

    // There are no jobless.
    if (!jobless)
    {
        return false;
    }

    return (jobless->getVolume() >= a_volume);
}

} // namespace Human
} // namespace GameServer
<commit_msg>EngageHumanOperator uses ConfiguratorHuman got from Context.<commit_after>// Copyright (C) 2010 and 2011 Marcin Arkadiusz Skrobiranda.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.

#include "../../../Building/BuildingToHumanTranslator.hpp"
#include "../../HumanToBuildingTranslator.hpp"
#include "EngageHumanOperator.hpp"

using namespace GameServer::Building;
using namespace GameServer::Common;
using namespace GameServer::Configuration;
using namespace GameServer::Persistence;
using namespace GameServer::Resource;

namespace GameServer
{
namespace Human
{

EngageHumanOperator::EngageHumanOperator(
    IContextShrPtr                   const a_context,
    IBuildingPersistenceFacadeShrPtr       a_building_persistence_facade,
    IHumanPersistenceFacadeShrPtr          a_human_persistence_facade,
    IResourcePersistenceFacadeShrPtr       a_resource_persistence_facade
)
    : m_context(a_context),
      m_building_persistence_facade(a_building_persistence_facade),
      m_human_persistence_facade(a_human_persistence_facade),
      m_resource_persistence_facade(a_resource_persistence_facade)
{
}

/**
 * TODO: Check if holder exists.
 */
EngageHumanOperatorExitCode EngageHumanOperator::engageHuman(
    ITransactionShrPtr         a_transaction,
    IDHolder           const & a_id_holder,
    IHumanKey          const & a_key,
    Volume             const & a_volume
) const
{
    try
    {
        // Trying to engage zero humans.
        if (a_volume == 0)
        {
            return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_TRYING_TO_ENGAGE_ZERO_HUMANS);
        }

        // Human is not engageable.
        if (!verifyEngageable(a_transaction, a_key))
        {
            return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_HUMAN_IS_NOT_ENGAGEABLE);
        }

        // There are not enough jobless.
        if (!verifyJobless(a_transaction, a_id_holder, a_volume))
        {
            return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_NOT_ENOUGH_JOBLESS);
        }

        // Get available resources.
        ResourceSet resource_set = m_resource_persistence_facade->getResources(a_transaction, a_id_holder);

        // Get total cost.
        ResourceSet cost = m_context->getConfiguratorHuman()->getHuman(a_key)->getCostsToEngage();

        // Multiply total cost.
        cost *= a_volume;

        // Check if there is enough resources.
        if (!(resource_set >= cost))
        {
            return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_NOT_ENOUGH_RESOURCES);
        }

        // Verifies if all possible dependencies on buildings are fulfilled.
        if (!verifyDependencyOfEngagementOnBuilding(a_transaction, a_id_holder, a_key, a_volume))
        {
            return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_NOT_ENOUGH_BUILDINGS);
        }

        // Subtract the resources.
        bool const result_subtract_resource =
            m_resource_persistence_facade->subtractResourceSet(a_transaction, a_id_holder, cost);

        // There is a possible situation (in multithreaded application) of a race condition between checking if
        // there is enough resources and trying to subtract it.
        // As it is not an error (just a result of another action) the appropriate exit code is returned.
        if (!result_subtract_resource)
        {
            return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_RESOURCES_MISSING_IN_THE_MEANTIME);
        }

        // Subtract the jobless.
        bool const result_subtract_human =
            m_human_persistence_facade->subtractHuman(a_transaction, a_id_holder, KEY_WORKER_JOBLESS_NOVICE, a_volume);

        // There is a possible situation (in multithreaded application) of a race condition between checking if
        // there is enough jobless and trying to subtract them.
        // As it is not an error (just a result of another action) the appropriate exit code is returned.
        if (!result_subtract_human)
        {
            return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_JOBLESS_MISSING_IN_THE_MEANTIME);
        }

        // Add the humans.
        m_human_persistence_facade->addHuman(a_transaction, a_id_holder, a_key, a_volume);

        // Everything went fine.
        return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_HUMAN_HAS_BEEN_ENGAGED);
    }
    catch (...)
    {
        return EngageHumanOperatorExitCode(ENGAGE_HUMAN_OPERATOR_EXIT_CODE_UNEXPECTED_ERROR);
    }
}

bool EngageHumanOperator::verifyDependencyOfEngagementOnBuilding(
    ITransactionShrPtr         a_transaction,
    IDHolder           const & a_id_holder,
    IHumanKey          const & a_key,
    Volume             const & a_volume
) const
{
    // Verify space in buildings.
    HumanToBuildingTranslator translator;
    IBuildingShrPtr building = translator.getPlaceOfWork(m_context, a_key);
    if (building)
    {
        // Get available buildings.
        BuildingWithVolumeShrPtr building_with_volume =
            m_building_persistence_facade->getBuilding(a_transaction, a_id_holder, building->getKey());

        // Building does not exist.
        if (!building_with_volume)
        {
            return false;
        }

        // Get a vector of identifiers of a human to check if building is a place of work for some humans.
        KeyVec humans = BuildingToHumanTranslator::getHumansHostedForWork(building->getKey());

        // The building is a place of work for at least one human.
        BOOST_ASSERT(!humans.empty());

        // A sum of humans.
        Volume sum(0);

        // Get humans engaged in buildings.
        for (KeyVec::const_iterator it = humans.begin(); it != humans.end(); ++it)
        {
            // Get a human by identifier of a human.
            HumanWithVolumeShrPtr human = m_human_persistence_facade->getHuman(a_transaction, a_id_holder, *it);

            if (human)
            {
                sum += human->getVolume();
            }
        }

        // Check if there is enough place.
        {
            // Check if there is enough capacity.
            if (building_with_volume->getVolume() * building_with_volume->getBuilding()->getCapacity() - sum < a_volume)
            {
                return false;
            }
        }
    }

    return true;
}

bool EngageHumanOperator::verifyEngageable(
    ITransactionShrPtr         a_transaction,
    IHumanKey          const & a_key
) const
{
    IHumanShrPtr human = m_context->getConfiguratorHuman()->getHuman(a_key);

    BOOST_ASSERT(human);

    return human->isEngageable();
}

bool EngageHumanOperator::verifyJobless(
    ITransactionShrPtr         a_transaction,
    IDHolder           const & a_id_holder,
    Volume             const & a_volume
) const
{
    // Get the jobless.
    HumanWithVolumeShrPtr jobless =
        m_human_persistence_facade->getHuman(a_transaction, a_id_holder, KEY_WORKER_JOBLESS_NOVICE);

    // There are no jobless.
    if (!jobless)
    {
        return false;
    }

    return (jobless->getVolume() >= a_volume);
}

} // namespace Human
} // namespace GameServer
<|endoftext|>


<commit_before>/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "Test.h"
#include "SkPath.h"
#include "SkCanvas.h"

static void appendStr(SkString* str, const SkPaint& paint) {
    str->appendf(" style[%d] cap[%d] join[%d] antialias[%d]",
                 paint.getStyle(), paint.getStrokeCap(),
                 paint.getStrokeJoin(), paint.isAntiAlias());
}

static void appendStr(SkString* str, const SkPath& path) {
    str->appendf(" filltype[%d] ptcount[%d]",
                 path.getFillType(), path.countPoints());
}

#define DIMENSION   32

static void drawAndTest(skiatest::Reporter* reporter, const SkPath& path,
                        const SkPaint& paint, bool shouldDraw) {
    SkBitmap bm;
    // explicitly specify a trim rowbytes, so we have no padding on each row
    bm.setConfig(SkBitmap::kARGB_8888_Config, DIMENSION, DIMENSION, DIMENSION*4);
    bm.allocPixels();
    bm.eraseColor(0);

    SkCanvas canvas(bm);
    SkPaint p(paint);
    p.setColor(SK_ColorWHITE);

    canvas.drawPath(path, p);

    size_t count = DIMENSION * DIMENSION;
    const SkPMColor* ptr = bm.getAddr32(0, 0);

    SkPMColor andValue = ~0;
    SkPMColor orValue = 0;
    for (size_t i = 0; i < count; ++i) {
        SkPMColor c = ptr[i];
        andValue &= c;
        orValue |= c;
    }

    // success means we drew everywhere or nowhere (depending on shouldDraw)
    bool success = shouldDraw ? (~0 == andValue) : (0 == orValue);

    if (!success) {
        SkString str;
        if (shouldDraw) {
            str.set("Path expected to draw everywhere, but didn't. ");
        } else {
            str.set("Path expected to draw nowhere, but did. ");
        }
        appendStr(&str, paint);
        appendStr(&str, path);
        reporter->report(str.c_str(), skiatest::Reporter::kFailed);

// uncomment this if you want to step in to see the failure
//        canvas.drawPath(path, p);
    }
}

static void iter_paint(skiatest::Reporter* reporter, const SkPath& path, bool shouldDraw) {
    static const SkPaint::Cap gCaps[] = {
        SkPaint::kButt_Cap,
        SkPaint::kRound_Cap,
        SkPaint::kSquare_Cap
    };
    static const SkPaint::Join gJoins[] = {
        SkPaint::kMiter_Join,
        SkPaint::kRound_Join,
        SkPaint::kBevel_Join
    };
    static const SkPaint::Style gStyles[] = {
        SkPaint::kFill_Style,
        SkPaint::kStroke_Style,
        SkPaint::kStrokeAndFill_Style
    };
    for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
        for (size_t join = 0; join < SK_ARRAY_COUNT(gJoins); ++join) {
            for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
                SkPaint paint;
                paint.setStrokeWidth(SkIntToScalar(10));

                paint.setStrokeCap(gCaps[cap]);
                paint.setStrokeJoin(gJoins[join]);
                paint.setStyle(gStyles[style]);

                paint.setAntiAlias(false);
                drawAndTest(reporter, path, paint, shouldDraw);
                paint.setAntiAlias(true);
                drawAndTest(reporter, path, paint, shouldDraw);
            }
        }
    }
}

#define CX  (SkIntToScalar(DIMENSION) / 2)
#define CY  (SkIntToScalar(DIMENSION) / 2)

static void make_empty(SkPath* path) {}
static void make_move(SkPath* path) { path->moveTo(CX, CY); }
static void make_line(SkPath* path) { path->moveTo(CX, CY); path->lineTo(CX, CY); }
static void make_quad(SkPath* path) { path->moveTo(CX, CY); path->quadTo(CX, CY, CX, CY); }
static void make_cubic(SkPath* path) { path->moveTo(CX, CY); path->cubicTo(CX, CY, CX, CY, CX, CY); }

/*  Two invariants are tested: How does an empty/degenerate path draw?
 *  - if the path is drawn inverse, it should draw everywhere
 *  - if the path is drawn non-inverse, it should draw nowhere
 *
 *  Things to iterate on:
 *  - path (empty, degenerate line/quad/cubic w/ and w/o close
 *  - paint style
 *  - path filltype
 *  - path stroke variants (e.g. caps, joins, width)
 */
static void test_emptydrawing(skiatest::Reporter* reporter) {
    static void (*gMakeProc[])(SkPath*) = {
        make_empty, make_move, make_line, make_quad, make_cubic
    };
    static SkPath::FillType gFills[] = {
        SkPath::kWinding_FillType,
        SkPath::kEvenOdd_FillType,
        SkPath::kInverseWinding_FillType,
        SkPath::kInverseEvenOdd_FillType
    };
    for (int doClose = 0; doClose < 2; ++doClose) {
        for  (size_t i = 0; i < SK_ARRAY_COUNT(gMakeProc); ++i) {
            SkPath path;
            gMakeProc[i](&path);
            if (doClose) {
                path.close();
            }
            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
                path.setFillType(gFills[fill]);
                bool shouldDraw = path.isInverseFillType();
                iter_paint(reporter, path, shouldDraw);
            }
        }
    }
}

static void TestEmptyPath(skiatest::Reporter* reporter) {
    test_emptydrawing(reporter);
}

#include "TestClassDef.h"
DEFINE_TESTCLASS("EmptyPath", TestEmptyPathClass, TestEmptyPath)
<commit_msg>add move-close-move and move-close-move-close to the "empty" paths to test<commit_after>/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "Test.h"
#include "SkPath.h"
#include "SkCanvas.h"

static void appendStr(SkString* str, const SkPaint& paint) {
    str->appendf(" style[%d] cap[%d] join[%d] antialias[%d]",
                 paint.getStyle(), paint.getStrokeCap(),
                 paint.getStrokeJoin(), paint.isAntiAlias());
}

static void appendStr(SkString* str, const SkPath& path) {
    str->appendf(" filltype[%d] ptcount[%d]",
                 path.getFillType(), path.countPoints());
}

#define DIMENSION   32

static void drawAndTest(skiatest::Reporter* reporter, const SkPath& path,
                        const SkPaint& paint, bool shouldDraw) {
    SkBitmap bm;
    // explicitly specify a trim rowbytes, so we have no padding on each row
    bm.setConfig(SkBitmap::kARGB_8888_Config, DIMENSION, DIMENSION, DIMENSION*4);
    bm.allocPixels();
    bm.eraseColor(0);

    SkCanvas canvas(bm);
    SkPaint p(paint);
    p.setColor(SK_ColorWHITE);

    canvas.drawPath(path, p);

    size_t count = DIMENSION * DIMENSION;
    const SkPMColor* ptr = bm.getAddr32(0, 0);

    SkPMColor andValue = ~0;
    SkPMColor orValue = 0;
    for (size_t i = 0; i < count; ++i) {
        SkPMColor c = ptr[i];
        andValue &= c;
        orValue |= c;
    }

    // success means we drew everywhere or nowhere (depending on shouldDraw)
    bool success = shouldDraw ? (~0 == andValue) : (0 == orValue);

    if (!success) {
        SkString str;
        if (shouldDraw) {
            str.set("Path expected to draw everywhere, but didn't. ");
        } else {
            str.set("Path expected to draw nowhere, but did. ");
        }
        appendStr(&str, paint);
        appendStr(&str, path);
        reporter->report(str.c_str(), skiatest::Reporter::kFailed);

// uncomment this if you want to step in to see the failure
//        canvas.drawPath(path, p);
    }
}

static void iter_paint(skiatest::Reporter* reporter, const SkPath& path, bool shouldDraw) {
    static const SkPaint::Cap gCaps[] = {
        SkPaint::kButt_Cap,
        SkPaint::kRound_Cap,
        SkPaint::kSquare_Cap
    };
    static const SkPaint::Join gJoins[] = {
        SkPaint::kMiter_Join,
        SkPaint::kRound_Join,
        SkPaint::kBevel_Join
    };
    static const SkPaint::Style gStyles[] = {
        SkPaint::kFill_Style,
        SkPaint::kStroke_Style,
        SkPaint::kStrokeAndFill_Style
    };
    for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
        for (size_t join = 0; join < SK_ARRAY_COUNT(gJoins); ++join) {
            for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
                SkPaint paint;
                paint.setStrokeWidth(SkIntToScalar(10));

                paint.setStrokeCap(gCaps[cap]);
                paint.setStrokeJoin(gJoins[join]);
                paint.setStyle(gStyles[style]);

                paint.setAntiAlias(false);
                drawAndTest(reporter, path, paint, shouldDraw);
                paint.setAntiAlias(true);
                drawAndTest(reporter, path, paint, shouldDraw);
            }
        }
    }
}

#define CX  (SkIntToScalar(DIMENSION) / 2)
#define CY  (SkIntToScalar(DIMENSION) / 2)

static void make_empty(SkPath* path) {}
static void make_M(SkPath* path) { path->moveTo(CX, CY); }
static void make_MM(SkPath* path) { path->moveTo(CX, CY); path->moveTo(CX, CY); }
static void make_MZM(SkPath* path) { path->moveTo(CX, CY); path->close(); path->moveTo(CX, CY); }
static void make_L(SkPath* path) { path->moveTo(CX, CY); path->lineTo(CX, CY); }
static void make_Q(SkPath* path) { path->moveTo(CX, CY); path->quadTo(CX, CY, CX, CY); }
static void make_C(SkPath* path) { path->moveTo(CX, CY); path->cubicTo(CX, CY, CX, CY, CX, CY); }

/*  Two invariants are tested: How does an empty/degenerate path draw?
 *  - if the path is drawn inverse, it should draw everywhere
 *  - if the path is drawn non-inverse, it should draw nowhere
 *
 *  Things to iterate on:
 *  - path (empty, degenerate line/quad/cubic w/ and w/o close
 *  - paint style
 *  - path filltype
 *  - path stroke variants (e.g. caps, joins, width)
 */
static void test_emptydrawing(skiatest::Reporter* reporter) {
    static void (*gMakeProc[])(SkPath*) = {
        make_empty, make_M, make_MM, make_MZM, make_L, make_Q, make_C
    };
    static SkPath::FillType gFills[] = {
        SkPath::kWinding_FillType,
        SkPath::kEvenOdd_FillType,
        SkPath::kInverseWinding_FillType,
        SkPath::kInverseEvenOdd_FillType
    };
    for (int doClose = 0; doClose < 2; ++doClose) {
        for  (size_t i = 0; i < SK_ARRAY_COUNT(gMakeProc); ++i) {
            SkPath path;
            gMakeProc[i](&path);
            if (doClose) {
                path.close();
            }
            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
                path.setFillType(gFills[fill]);
                bool shouldDraw = path.isInverseFillType();
                iter_paint(reporter, path, shouldDraw);
            }
        }
    }
}

static void TestEmptyPath(skiatest::Reporter* reporter) {
    test_emptydrawing(reporter);
}

#include "TestClassDef.h"
DEFINE_TESTCLASS("EmptyPath", TestEmptyPathClass, TestEmptyPath)
<|endoftext|>


<commit_before>// Time:  O(n)
// Space: O(n)

/**
 * Definition of TreeNode:
 * class TreeNode {
 * public:
 *     int val;
 *     TreeNode *left, *right;
 *     TreeNode(int val) {
 *         this->val = val;
 *         this->left = this->right = NULL;
 *     }
 * }
 */
class Solution {
 public:
    /**
     * @param A: Given an integer array with no duplicates.
     * @return: The root of max tree.
     */
    TreeNode* maxTree(vector<int> A) {
        vector<TreeNode *> nodeStack;
        for (int i = 0; i < A.size(); ++i) {
            auto tmp = new TreeNode(A[i]);
            while (!nodeStack.empty() && A[i] > nodeStack.back()->val) {
                tmp->left = nodeStack.back();
                nodeStack.pop_back();
            }
            if (!nodeStack.empty()) {
                nodeStack.back()->right = tmp;
            }
            nodeStack.emplace_back(tmp);
        }
        return nodeStack.front();
    }
};
<commit_msg>Update max-tree.cpp<commit_after>// Time:  O(n)
// Space: O(n)

/**
 * Definition of TreeNode:
 * class TreeNode {
 * public:
 *     int val;
 *     TreeNode *left, *right;
 *     TreeNode(int val) {
 *         this->val = val;
 *         this->left = this->right = NULL;
 *     }
 * }
 */
class Solution {
 public:
    /**
     * @param A: Given an integer array with no duplicates.
     * @return: The root of max tree.
     */
    TreeNode* maxTree(vector<int> A) {
        vector<TreeNode *> nodeStack;
        for (int i = 0; i < A.size(); ++i) {
            auto node = new TreeNode(A[i]);
            while (!nodeStack.empty() && A[i] > nodeStack.back()->val) {
                node->left = nodeStack.back();
                nodeStack.pop_back();
            }
            if (!nodeStack.empty()) {
                nodeStack.back()->right = node;
            }
            nodeStack.emplace_back(node);
        }
        return nodeStack.front();
    }
};
<|endoftext|>


<commit_before>/*

 Generic replacement for memalign in terms of xxmalloc and xxfree.

 LIMITATIONS:

 - This code assumes that the underlying allocator will do "the right
   thing" when xxfree() is called with a pointer internal to an
   allocated object. Header-based allocators, for example, need not
   apply.

*/

extern "C" {
  void * xxmalloc(size_t);
  void xxfree(void *);
}

extern "C"
void * generic_xxmemalign(size_t alignment, size_t size) {
  // Check for non power-of-two alignment.
  if ((alignment == 0) || (alignment & (alignment - 1)))
    {
      return nullptr;
    }

  if (alignment <= alignof(max_align_t)) {
    // Already aligned by default.
    return xxmalloc(size);
  } else {
    // Try to just allocate an object of the requested size.
    // If it happens to be aligned properly, just return it.
    void * ptr = xxmalloc(size);
    if (((size_t) ptr & ~(alignment - 1)) == (size_t) ptr) {
      // It is already aligned just fine; return it.
      return ptr;
    }
    // It was not aligned as requested: free the object and allocate a big one,
    // and align within.
    xxfree(ptr);
    ptr = xxmalloc (size + 2 * alignment);
    void * alignedPtr = (void *) (((size_t) ptr + alignment - 1) & ~(alignment - 1));
    return alignedPtr;
  }
}
<commit_msg>Added include for size_t and alignment types.<commit_after>/*

 Generic replacement for memalign in terms of xxmalloc and xxfree.

 LIMITATIONS:

 - This code assumes that the underlying allocator will do "the right
   thing" when xxfree() is called with a pointer internal to an
   allocated object. Header-based allocators, for example, need not
   apply.

*/

#include <stddef.h>

extern "C" {
  void * xxmalloc(size_t);
  void xxfree(void *);
}

extern "C"
void * generic_xxmemalign(size_t alignment, size_t size) {
  // Check for non power-of-two alignment.
  if ((alignment == 0) || (alignment & (alignment - 1)))
    {
      return nullptr;
    }

  if (alignment <= alignof(max_align_t)) {
    // Already aligned by default.
    return xxmalloc(size);
  } else {
    // Try to just allocate an object of the requested size.
    // If it happens to be aligned properly, just return it.
    void * ptr = xxmalloc(size);
    if (((size_t) ptr & ~(alignment - 1)) == (size_t) ptr) {
      // It is already aligned just fine; return it.
      return ptr;
    }
    // It was not aligned as requested: free the object and allocate a big one,
    // and align within.
    xxfree(ptr);
    ptr = xxmalloc (size + 2 * alignment);
    void * alignedPtr = (void *) (((size_t) ptr + alignment - 1) & ~(alignment - 1));
    return alignedPtr;
  }
}
<|endoftext|>


<commit_before>#include "caffeine/layer.hpp"

namespace caffeine {

INSTANTIATE_CLASS(Layer);

}  // namespace caffeine
<commit_msg>layer implementation now purely in layer.hpp<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.

/*
  Approach 1:
    - Updated Init function to take initial contion as input
    - Setting options must occur after initialization
    - Addition of LinSysSetup functions to setup linear systems
    - Addition of SUNLinSolEmpty() and SUNMatEmpty() functions to make
      creating wrappers to linear solver and matrix easier. Also protects
      against the addition of new optional operations to the APIs.
    - Simplified user-supplied methods for custom linear solvers.
    - Need to add ReInit and ReSize methods.
*/

#ifndef MFEM_SUNDIALS
#define MFEM_SUNDIALS

#include "../config/config.hpp"

#ifdef MFEM_USE_SUNDIALS

#ifdef MFEM_USE_MPI
#include <mpi.h>
#endif

#include "ode.hpp"
#include "solvers.hpp"

#include <cvode/cvode.h>
#include <arkode/arkode_arkstep.h>

namespace mfem
{
  // ---------------------------------------------------------------------------
  // Base class for interfacing with SUNMatrix and SUNLinearSolver API
  // ---------------------------------------------------------------------------

  /** Abstract base class for providing custom linear solvers to SUNDIALS ODE
      packages, CVODE and ARKODE. For a given ODE system

      dy/dt = f(y,t) or M dy/dt = f(y,t)

      the purpose of this class is to facilitate the (approximate) solution of
      linear systems of the form

      (I - gamma J) y = b or (M - gamma J) y = b,   J = J(y,t) = df/dy

      and mass matrix systems of the form

      M y = b,   M = M(t)

      for given b, y, t and gamma, where gamma is a scaled time step. */
  class SundialsODELinearSolver
  {
  private:
    TimeDependentOperator *oper;

  protected:
    SundialsODELinearSolver() : oper(NULL) { }
    virtual ~SundialsODELinearSolver();

  public:
    /** Setup the ODE linear system A(y,t) = (I - gamma J) or A = (M - gamma J)
        @param[in]  t     The time at which A(y,t)  should be evaluated
        @param[in]  y     The state at which A(y,t) should be evaluated
        @param[in]  fy    The current value of the ODE Rhs function, f(y,t)
        @param[in]  jok   Flag indicating if the Jacobian should be updated
        @param[out] jcur  Flag to signal if the Jacobian was updated
        @param[in]  gamma The scaled time step value */
    virtual int ODELinSys(double t, Vector y, Vector fy, int jok, int *jcur,
                          double gamma)
    {
      mfem_error("SundialsODELinearSolver::ODELinSys() is not overridden!");
      return(1);
    }

    /** Setup the ODE Mass matrix system M
        @param[in] t The time at which M(t) should be evaluated*/
    virtual int ODEMassSys(double t)
    {
      mfem_error("SundialsODELinearSolver::ODEMassSys() is not overridden!");
      return(1);
    }

    /** Initialize the linear solver (optional) */
    virtual int LSInit() { return(0); };

    /** Setup the linear solver (optional) */
    virtual int LSSetup() { return(0); };

    /** Solve the linear system A x = b
        @param[in/out]  x  On input, the initial guess. On output, the solution
        @param[in]      b  The linear system right-hand side */
    virtual int LSSolve(Vector &x, Vector b) = 0;
  };

  // ---------------------------------------------------------------------------
  // Base class for interfacing with SUNDIALS packages
  // ---------------------------------------------------------------------------

  class SundialsODESolver
  {
  protected:
    void *sundials_mem; /// SUNDIALS mem structure
    mutable int flag;   /// Last flag returned from a call to SUNDIALS
    int step_mode;      /// SUNDIALS step mode (NORMAL or ONE_STEP)

    N_Vector           y;   /// State vector
    SUNMatrix          A;   /// Linear system A = (I - gamma J) or (M - gamma J)
    SUNMatrix          M;   /// Mass matrix M
    SUNLinearSolver    LSA; /// Linear solver for A
    SUNLinearSolver    LSM; /// Linear solver for M
    SUNNonlinearSolver NLS; /// Nonlinear solver

#ifdef MFEM_USE_MPI
    bool Parallel() const
    { return (N_VGetVectorID(y) != SUNDIALS_NVEC_SERIAL); }
#else
    bool Parallel() const { return false; }
#endif

    /// Wrapper to compute the ODE Rhs function
    static int ODERhs(realtype t, const N_Vector y, N_Vector ydot,
                      void *user_data);

    /// Default scalar tolerances
    static constexpr double default_rel_tol = 1e-4;
    static constexpr double default_abs_tol = 1e-9;

    /// Constructors
    SundialsODESolver() : sundials_mem(NULL), flag(0), step_mode(1),
                          y(NULL), A(NULL), M(NULL), LSA(NULL), LSM(NULL),
                          NLS(NULL) { }

    SundialsODESolver(void *mem) : sundials_mem(mem) { }

  public:
    /// Access the SUNDIALS memory structure
    void *GetMem() const { return sundials_mem; }

    /// Returns the last flag retured a call to a SUNDIALS function
    int GetFlag() const { return flag; }
  };

  // ---------------------------------------------------------------------------
  // Interface to the CVODE library -- linear multi-step methods
  // ---------------------------------------------------------------------------

  class CVODESolver : public ODESolver, public SundialsODESolver
  {
  public:
    /** Construct a serial wrapper to SUNDIALS' CVODE integrator
        @param[in] lmm Specifies the linear multistep method, the options are:
                       CV_ADAMS - implicit methods for non-stiff systems
                       CV_BDF   - implicit methods for stiff systems */
    CVODESolver(int lmm = CV_BDF);

#ifdef MFEM_USE_MPI
    /** Construct a parallel wrapper to SUNDIALS' CVODE integrator
        @param[in] comm The MPI communicator used to partition the ODE system
        @param[in] lmm  Specifies the linear multistep method, the options are:
                        CV_ADAMS - implicit methods for non-stiff systems
                        CV_BDF   - implicit methods for stiff systems */
    CVODESolver(MPI_Comm comm, int lmm = CV_BDF);
#endif

    /// Base class Init -- DO NOT CALL, use the below initialization function
    /// that takes the initial t and x as inputs.
    virtual void Init(TimeDependentOperator &f_);

    /** Initialize CVODE: Calls CVodeInit() and sets some defaults.
        @param[in] f_ the TimeDependentOperator that defines the ODE system
        @param[in] t  the initial time
        @param[in] x  the initial condition

        @note All other methods must be called after Init(). */
    void Init(TimeDependentOperator &f_, double &t, Vector &x);

    /** Integrate the ODE with CVODE using the specified step mode.

        @param[out]    x  Solution vector at the requested output timem x=x(t).
        @param[in/out] t  On output, the output time reached.
        @param[in/out] dt On output, the last time step taken.

        @note On input, the values of t and dt are used to compute desired
        output time for the integration, tout = t + dt.
    */
    virtual void Step(Vector &x, double &t, double &dt);

    /** Attach a custom linear solver solver to CVODE
        @param[in] ls_spec A SundialsODELinearSolver object defining the custom
                           linear solver */
    void SetLinearSolver(SundialsODELinearSolver &ls_spec);

    /** Select the CVODE step mode: CV_NORMAL (default) or CV_ONE_STEP
        @param[in] itask  The desired step mode */
    void SetStepMode(int itask);

    /** Print various CVODE statistics. */
    void PrintInfo() const;

    /// Destroy the associated CVODE memory and SUNDIALS objects
    virtual ~CVODESolver();
  };

  // ---------------------------------------------------------------------------
  // Interface to ARKode's ARKStep module -- Additive Runge-Kutta methods
  // ---------------------------------------------------------------------------

  class ARKStepSolver : public ODESolver, public SundialsODESolver
  {
  protected:
    bool use_implicit;
    int  irk_table, erk_table;

  public:
    /// Types of ARKODE solvers.
    enum Type { EXPLICIT, IMPLICIT };

    /** Construct a serial wrapper to SUNDIALS' ARKode integrator
        @param[in] type Specifies the RK method type
                        EXPLICIT - explicit RK method
                        IMPLICIT - implicit RK method */
    ARKStepSolver(Type type = EXPLICIT);

#ifdef MFEM_USE_MPI
    /** Construct a parallel wrapper to SUNDIALS' ARKode integrator
        @param[in] comm The MPI communicator used to partition the ODE system
        @param[in] type Specifies the RK method type
                        EXPLICIT - explicit RK method
                        IMPLICIT - implicit RK method */
    ARKStepSolver(MPI_Comm comm, Type type = EXPLICIT);
#endif

    /// Base class Init -- DO NOT CALL, use the below initialization function
    /// that takes the initial t and x as inputs.
    virtual void Init(TimeDependentOperator &f_);

    /** Initialize ARKode: Calls ARKStepInit() and sets some defaults.
        @param[in] f_ the TimeDependentOperator that defines the ODE system
        @param[in] t  the initial time
        @param[in] x  the initial condition

        @note All other methods must be called after Init(). */
    void Init(TimeDependentOperator &f_, double &t, Vector &x);

    /** Integrate the ODE with ARKode using the specified step mode.

        @param[out]    x  Solution vector at the requested output timem x=x(t).
        @param[in/out] t  On output, the output time reached.
        @param[in/out] dt On output, the last time step taken.

        @note On input, the values of t and dt are used to compute desired
        output time for the integration, tout = t + dt.
    */
    virtual void Step(Vector &x, double &t, double &dt);

    /** Attach a custom linear solver solver to ARKode
        @param[in] ls_spec A SundialsODELinearSolver object defining the custom
                           linear solver */
    void SetLinearSolver(SundialsODELinearSolver &ls_spec);

    /** Attach a custom mass matrix linear solver solver to ARKode
        @param[in] ls_spec A SundialsODELinearSolver object defining the custom
                           linear solver
        @param[in] tdep    A integer flag indicating if the mass matrix is time
                           dependent (1) or time independent (0). */
    void SetMassLinearSolver(SundialsODELinearSolver &ls_spec, int tdep);

    /** Select the ARKode step mode: ARK_NORMAL (default) or ARK_ONE_STEP
        @param[in] itask  The desired step mode */
    void SetStepMode(int itask);

    /** Print various ARKStep statistics. */
    void PrintInfo() const;

    /// Destroy the associated ARKode memory and SUNDIALS objects
    virtual ~ARKStepSolver();
  };

}  // namespace mfem

#endif // MFEM_USE_SUNDIALS

#endif // MFEM_SUNDIALS
<commit_msg>remove operator from LS base class<commit_after>// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.

/*
  Approach 1:
    - Updated Init function to take initial contion as input
    - Setting options must occur after initialization
    - Addition of LinSysSetup functions to setup linear systems
    - Addition of SUNLinSolEmpty() and SUNMatEmpty() functions to make
      creating wrappers to linear solver and matrix easier. Also protects
      against the addition of new optional operations to the APIs.
    - Simplified user-supplied methods for custom linear solvers.
    - Need to add ReInit and ReSize methods.
*/

#ifndef MFEM_SUNDIALS
#define MFEM_SUNDIALS

#include "../config/config.hpp"

#ifdef MFEM_USE_SUNDIALS

#ifdef MFEM_USE_MPI
#include <mpi.h>
#endif

#include "ode.hpp"
#include "solvers.hpp"

#include <cvode/cvode.h>
#include <arkode/arkode_arkstep.h>

namespace mfem
{
  // ---------------------------------------------------------------------------
  // Base class for interfacing with SUNMatrix and SUNLinearSolver API
  // ---------------------------------------------------------------------------

  /** Abstract base class for providing custom linear solvers to SUNDIALS ODE
      packages, CVODE and ARKODE. For a given ODE system

      dy/dt = f(y,t) or M dy/dt = f(y,t)

      the purpose of this class is to facilitate the (approximate) solution of
      linear systems of the form

      (I - gamma J) y = b or (M - gamma J) y = b,   J = J(y,t) = df/dy

      and mass matrix systems of the form

      M y = b,   M = M(t)

      for given b, y, t and gamma, where gamma is a scaled time step. */
  class SundialsODELinearSolver
  {
  public:
    /** Setup the ODE linear system A(y,t) = (I - gamma J) or A = (M - gamma J)
        @param[in]  t     The time at which A(y,t)  should be evaluated
        @param[in]  y     The state at which A(y,t) should be evaluated
        @param[in]  fy    The current value of the ODE Rhs function, f(y,t)
        @param[in]  jok   Flag indicating if the Jacobian should be updated
        @param[out] jcur  Flag to signal if the Jacobian was updated
        @param[in]  gamma The scaled time step value */
    virtual int ODELinSys(double t, Vector y, Vector fy, int jok, int *jcur,
                          double gamma)
    {
      mfem_error("SundialsODELinearSolver::ODELinSys() is not overridden!");
      return(-1);
    }

    /** Setup the ODE Mass matrix system M
        @param[in] t The time at which M(t) should be evaluated*/
    virtual int ODEMassSys(double t)
    {
      mfem_error("SundialsODELinearSolver::ODEMassSys() is not overridden!");
      return(-1);
    }

    /** Initialize the linear solver (optional) */
    virtual int LSInit() { return(0); };

    /** Setup the linear solver (optional) */
    virtual int LSSetup() { return(0); };

    /** Solve the linear system A x = b
        @param[in/out]  x  On input, the initial guess. On output, the solution
        @param[in]      b  The linear system right-hand side */
    virtual int LSSolve(Vector &x, Vector b) = 0;
  };

  // ---------------------------------------------------------------------------
  // Base class for interfacing with SUNDIALS packages
  // ---------------------------------------------------------------------------

  class SundialsODESolver
  {
  protected:
    void *sundials_mem; /// SUNDIALS mem structure
    mutable int flag;   /// Last flag returned from a call to SUNDIALS
    int step_mode;      /// SUNDIALS step mode (NORMAL or ONE_STEP)

    N_Vector           y;   /// State vector
    SUNMatrix          A;   /// Linear system A = (I - gamma J) or (M - gamma J)
    SUNMatrix          M;   /// Mass matrix M
    SUNLinearSolver    LSA; /// Linear solver for A
    SUNLinearSolver    LSM; /// Linear solver for M
    SUNNonlinearSolver NLS; /// Nonlinear solver

#ifdef MFEM_USE_MPI
    bool Parallel() const
    { return (N_VGetVectorID(y) != SUNDIALS_NVEC_SERIAL); }
#else
    bool Parallel() const { return false; }
#endif

    /// Wrapper to compute the ODE Rhs function
    static int ODERhs(realtype t, const N_Vector y, N_Vector ydot,
                      void *user_data);

    /// Default scalar tolerances
    static constexpr double default_rel_tol = 1e-4;
    static constexpr double default_abs_tol = 1e-9;

    /// Constructors
    SundialsODESolver() : sundials_mem(NULL), flag(0), step_mode(1),
                          y(NULL), A(NULL), M(NULL), LSA(NULL), LSM(NULL),
                          NLS(NULL) { }

    SundialsODESolver(void *mem) : sundials_mem(mem) { }

  public:
    /// Access the SUNDIALS memory structure
    void *GetMem() const { return sundials_mem; }

    /// Returns the last flag retured a call to a SUNDIALS function
    int GetFlag() const { return flag; }
  };

  // ---------------------------------------------------------------------------
  // Interface to the CVODE library -- linear multi-step methods
  // ---------------------------------------------------------------------------

  class CVODESolver : public ODESolver, public SundialsODESolver
  {
  public:
    /** Construct a serial wrapper to SUNDIALS' CVODE integrator
        @param[in] lmm Specifies the linear multistep method, the options are:
                       CV_ADAMS - implicit methods for non-stiff systems
                       CV_BDF   - implicit methods for stiff systems */
    CVODESolver(int lmm = CV_BDF);

#ifdef MFEM_USE_MPI
    /** Construct a parallel wrapper to SUNDIALS' CVODE integrator
        @param[in] comm The MPI communicator used to partition the ODE system
        @param[in] lmm  Specifies the linear multistep method, the options are:
                        CV_ADAMS - implicit methods for non-stiff systems
                        CV_BDF   - implicit methods for stiff systems */
    CVODESolver(MPI_Comm comm, int lmm = CV_BDF);
#endif

    /// Base class Init -- DO NOT CALL, use the below initialization function
    /// that takes the initial t and x as inputs.
    virtual void Init(TimeDependentOperator &f_);

    /** Initialize CVODE: Calls CVodeInit() and sets some defaults.
        @param[in] f_ the TimeDependentOperator that defines the ODE system
        @param[in] t  the initial time
        @param[in] x  the initial condition

        @note All other methods must be called after Init(). */
    void Init(TimeDependentOperator &f_, double &t, Vector &x);

    /** Integrate the ODE with CVODE using the specified step mode.

        @param[out]    x  Solution vector at the requested output timem x=x(t).
        @param[in/out] t  On output, the output time reached.
        @param[in/out] dt On output, the last time step taken.

        @note On input, the values of t and dt are used to compute desired
        output time for the integration, tout = t + dt.
    */
    virtual void Step(Vector &x, double &t, double &dt);

    /** Attach a custom linear solver solver to CVODE
        @param[in] ls_spec A SundialsODELinearSolver object defining the custom
                           linear solver */
    void SetLinearSolver(SundialsODELinearSolver &ls_spec);

    /** Select the CVODE step mode: CV_NORMAL (default) or CV_ONE_STEP
        @param[in] itask  The desired step mode */
    void SetStepMode(int itask);

    /** Print various CVODE statistics. */
    void PrintInfo() const;

    /// Destroy the associated CVODE memory and SUNDIALS objects
    virtual ~CVODESolver();
  };

  // ---------------------------------------------------------------------------
  // Interface to ARKode's ARKStep module -- Additive Runge-Kutta methods
  // ---------------------------------------------------------------------------

  class ARKStepSolver : public ODESolver, public SundialsODESolver
  {
  protected:
    bool use_implicit;
    int  irk_table, erk_table;

  public:
    /// Types of ARKODE solvers.
    enum Type { EXPLICIT, IMPLICIT };

    /** Construct a serial wrapper to SUNDIALS' ARKode integrator
        @param[in] type Specifies the RK method type
                        EXPLICIT - explicit RK method
                        IMPLICIT - implicit RK method */
    ARKStepSolver(Type type = EXPLICIT);

#ifdef MFEM_USE_MPI
    /** Construct a parallel wrapper to SUNDIALS' ARKode integrator
        @param[in] comm The MPI communicator used to partition the ODE system
        @param[in] type Specifies the RK method type
                        EXPLICIT - explicit RK method
                        IMPLICIT - implicit RK method */
    ARKStepSolver(MPI_Comm comm, Type type = EXPLICIT);
#endif

    /// Base class Init -- DO NOT CALL, use the below initialization function
    /// that takes the initial t and x as inputs.
    virtual void Init(TimeDependentOperator &f_);

    /** Initialize ARKode: Calls ARKStepInit() and sets some defaults.
        @param[in] f_ the TimeDependentOperator that defines the ODE system
        @param[in] t  the initial time
        @param[in] x  the initial condition

        @note All other methods must be called after Init(). */
    void Init(TimeDependentOperator &f_, double &t, Vector &x);

    /** Integrate the ODE with ARKode using the specified step mode.

        @param[out]    x  Solution vector at the requested output timem x=x(t).
        @param[in/out] t  On output, the output time reached.
        @param[in/out] dt On output, the last time step taken.

        @note On input, the values of t and dt are used to compute desired
        output time for the integration, tout = t + dt.
    */
    virtual void Step(Vector &x, double &t, double &dt);

    /** Attach a custom linear solver solver to ARKode
        @param[in] ls_spec A SundialsODELinearSolver object defining the custom
                           linear solver */
    void SetLinearSolver(SundialsODELinearSolver &ls_spec);

    /** Attach a custom mass matrix linear solver solver to ARKode
        @param[in] ls_spec A SundialsODELinearSolver object defining the custom
                           linear solver
        @param[in] tdep    A integer flag indicating if the mass matrix is time
                           dependent (1) or time independent (0). */
    void SetMassLinearSolver(SundialsODELinearSolver &ls_spec, int tdep);

    /** Select the ARKode step mode: ARK_NORMAL (default) or ARK_ONE_STEP
        @param[in] itask  The desired step mode */
    void SetStepMode(int itask);

    /** Print various ARKStep statistics. */
    void PrintInfo() const;

    /// Destroy the associated ARKode memory and SUNDIALS objects
    virtual ~ARKStepSolver();
  };

}  // namespace mfem

#endif // MFEM_USE_SUNDIALS

#endif // MFEM_SUNDIALS
<|endoftext|>


<commit_before>/* This is a simple HTTP(S) web server much like Python's SimpleHTTPServer */

#include <App.h>

/* Helpers for this example */
#include "helpers/AsyncFileReader.h"
#include "helpers/AsyncFileStreamer.h"
#include "helpers/Middleware.h"

/* optparse */
#define OPTPARSE_IMPLEMENTATION
#include "helpers/optparse.h"

int main(int argc, char **argv) {

    int option;
    struct optparse options;
    optparse_init(&options, argv);

    struct optparse_long longopts[] = {
        {"port", 'p', OPTPARSE_REQUIRED},
        {"help", 'h', OPTPARSE_NONE},
        {"passphrase", 'a', OPTPARSE_REQUIRED},
        {"key", 'k', OPTPARSE_REQUIRED},
        {"cert", 'c', OPTPARSE_REQUIRED},
        {"dh_params", 'd', OPTPARSE_REQUIRED},
        {0}
    };

    int port = 3000;
    struct us_ssl_socket_context_options ssl_options = {};

    while ((option = optparse_long(&options, longopts, nullptr)) != -1) {
        switch (option) {
        case 'p':
            port = atoi(options.optarg);
            break;
        case 'a':
            ssl_options.passphrase = options.optarg;
            break;
        case 'c':
            ssl_options.cert_file_name = options.optarg;
            break;
        case 'k':
            ssl_options.key_file_name = options.optarg;
            break;
        case 'd':
            ssl_options.dh_params_file_name = options.optarg;
            break;
        case 'h':
        case '?':
            fail:
            std::cout << "Usage: " << argv[0] << " [--help] [--port <port>] [--key <ssl key>] [--cert <ssl cert>] [--passphrase <ssl key passphrase>] [--dh_params <ssl dh params file>] <public root>" << std::endl;
            return 0;
        }
    }

    char *root = optparse_arg(&options);
    if (!root) {
        goto fail;
    }

    AsyncFileStreamer asyncFileStreamer(root);

    /* Either serve over HTTP or HTTPS */
    struct us_ssl_socket_context_options empty_ssl_options = {};
    if (memcmp(&ssl_options, &empty_ssl_options, sizeof(empty_ssl_options))) {
        /* HTTPS */
        uWS::SSLApp(ssl_options).get("/*", [&asyncFileStreamer](auto *res, auto *req) {
            asyncFileStreamer.streamFile(res, req->getUrl());
        }).listen(port, [port, root](auto *token) {
            if (token) {
                std::cout << "Serving " << root << " over HTTPS a " << port << std::endl;
            }
        }).run();
    } else {
        /* HTTP */
        uWS::App().get("/*", [&asyncFileStreamer](auto *res, auto *req) {
            asyncFileStreamer.streamFile(res, req->getUrl());
        }).listen(port, [port, root](auto *token) {
            if (token) {
                std::cout << "Serving " << root << " over HTTP a " << port << std::endl;
            }
        }).run();
    }

    std::cout << "Failed to listen to port " << port << std::endl;
}
<commit_msg>Add mime types to files served<commit_after>/* This is a simple HTTP(S) web server much like Python's SimpleHTTPServer */

#include <App.h>

/* Helpers for this example */
#include "helpers/AsyncFileReader.h"
#include "helpers/AsyncFileStreamer.h"
#include "helpers/Middleware.h"

/* optparse */
#define OPTPARSE_IMPLEMENTATION
#include "helpers/optparse.h"

int main(int argc, char **argv) {

    int option;
    struct optparse options;
    optparse_init(&options, argv);

    struct optparse_long longopts[] = {
        {"port", 'p', OPTPARSE_REQUIRED},
        {"help", 'h', OPTPARSE_NONE},
        {"passphrase", 'a', OPTPARSE_REQUIRED},
        {"key", 'k', OPTPARSE_REQUIRED},
        {"cert", 'c', OPTPARSE_REQUIRED},
        {"dh_params", 'd', OPTPARSE_REQUIRED},
        {0}
    };

    int port = 3000;
    struct us_ssl_socket_context_options ssl_options = {};

    while ((option = optparse_long(&options, longopts, nullptr)) != -1) {
        switch (option) {
        case 'p':
            port = atoi(options.optarg);
            break;
        case 'a':
            ssl_options.passphrase = options.optarg;
            break;
        case 'c':
            ssl_options.cert_file_name = options.optarg;
            break;
        case 'k':
            ssl_options.key_file_name = options.optarg;
            break;
        case 'd':
            ssl_options.dh_params_file_name = options.optarg;
            break;
        case 'h':
        case '?':
            fail:
            std::cout << "Usage: " << argv[0] << " [--help] [--port <port>] [--key <ssl key>] [--cert <ssl cert>] [--passphrase <ssl key passphrase>] [--dh_params <ssl dh params file>] <public root>" << std::endl;
            return 0;
        }
    }

    char *root = optparse_arg(&options);
    if (!root) {
        goto fail;
    }

    AsyncFileStreamer asyncFileStreamer(root);

    /* Either serve over HTTP or HTTPS */
    struct us_ssl_socket_context_options empty_ssl_options = {};
    if (memcmp(&ssl_options, &empty_ssl_options, sizeof(empty_ssl_options))) {
        /* HTTPS */
        uWS::SSLApp(ssl_options).get("/*", [&asyncFileStreamer](auto *res, auto *req) {
            serveFile(res, req);
            asyncFileStreamer.streamFile(res, req->getUrl());
        }).listen(port, [port, root](auto *token) {
            if (token) {
                std::cout << "Serving " << root << " over HTTPS a " << port << std::endl;
            }
        }).run();
    } else {
        /* HTTP */
        uWS::App().get("/*", [&asyncFileStreamer](auto *res, auto *req) {
            serveFile(res, req);
            asyncFileStreamer.streamFile(res, req->getUrl());
        }).listen(port, [port, root](auto *token) {
            if (token) {
                std::cout << "Serving " << root << " over HTTP a " << port << std::endl;
            }
        }).run();
    }

    std::cout << "Failed to listen to port " << port << std::endl;
}
<|endoftext|>