<commit_before>#include <sstream>
#include <math.h>
#include <boost/lexical_cast.hpp>
#include "labelconstructor.h"
#include "paraverlabels.h"
#include "histogram.h"
#include "paraverconfig.h"
#include "window.h"
#include "filter.h"

string LabelConstructor::objectLabel( TObjectOrder globalOrder,
                                      TWindowLevel level,
                                      Trace *whichTrace )
{
  string rowStr;
  stringstream label;

  rowStr = whichTrace->getRowLabel( level, globalOrder );
  if( rowStr != "" )
    return rowStr;

  if ( level == WORKLOAD )
    label << LEVEL_WORKLOAD;
  else if ( level == APPLICATION )
    label << LEVEL_APPLICATION << ' ' << globalOrder + 1;
  else if ( level == TASK )
  {
    TApplOrder appl;
    TTaskOrder task;
    whichTrace->getTaskLocation( globalOrder, appl, task );
    label << LEVEL_TASK << ' ' << appl + 1 << '.' << task + 1;
  }
  else if ( level == THREAD )
  {
    TApplOrder appl;
    TTaskOrder task;
    TThreadOrder thread;
    whichTrace->getThreadLocation( globalOrder, appl, task, thread );
    label << LEVEL_THREAD << ' ' << appl + 1 << '.' << task + 1 << '.' << thread + 1;
  }
  else if ( level == SYSTEM )
    label << LEVEL_SYSTEM;
  else if ( level == NODE )
    label << LEVEL_NODE << ' ' << globalOrder + 1;
  else if ( level == CPU )
  {
    TNodeOrder node;
    TCPUOrder cpu;
    whichTrace->getCPULocation( globalOrder, node, cpu );
    label << LEVEL_CPU << ' ' << node + 1 << '.' << cpu + 1;
  }

  return label.str();
}

string LabelConstructor::histoColumnLabel( THistogramColumn whichColumn,
    const Window *whichWindow,
    THistogramLimit min,
    THistogramLimit max,
    THistogramLimit delta )
{
  stringstream label;
  double tmp;

  if ( modf( min, &tmp ) != 0.0 || delta != 1.0 )
  {
    // Column range values
    label << '[' << ( whichColumn * delta ) + min << "..";
    if ( ( ( whichColumn * delta ) + min + delta ) > max )
    {
      label << max;
      label << ']';
    }
    else
    {
      label << ( whichColumn * delta ) + min + delta;
      label << ')';
    }
  }
  else
  {
    // Discrete integer value
    label << LabelConstructor::semanticLabel( whichWindow,
        ( whichColumn * delta ) + min, true );
  }

  return label.str();
}

inline string chomp( TSemanticValue& number )
{
  TSemanticValue origValue = number;
  number /= 1000.0;
  number = floor( number );
  TSemanticValue remainder = origValue - number * 1000.0;
  INT32 intRemainder = INT32( remainder );

  if ( number == 0 )
    return boost::lexical_cast<std::string>( intRemainder );
  else if ( remainder > 99 )
    return boost::lexical_cast<std::string>( intRemainder );
  else if ( remainder > 9 )
    return "0" + boost::lexical_cast<std::string>( intRemainder );
  else if ( remainder > 0 )
    return "00" + boost::lexical_cast<std::string>( intRemainder );
  return "000";
}

string LabelConstructor::histoCellLabel( const Histogram *whichHisto,
    TSemanticValue value, bool showUnits )
{
  stringstream label;

  if ( value == numeric_limits<double>::infinity() )
    return "inf";

  if ( whichHisto->getScientificNotation() )
    label << scientific;
  else
    label << fixed;

  label.precision( ParaverConfig::getInstance()->getPrecision() );

  if ( whichHisto->getThousandSeparator() &&
       !whichHisto->getScientificNotation() )
  {
    string strNum;
    TSemanticValue origValue = value;
    TSemanticValue intValue = floor( value );

    if ( origValue >= 1.0 )
    {
      while ( intValue > 0.0 )
        strNum = chomp( intValue ) + "," + strNum;
      strNum.erase( strNum.size() - 1, 1 );
      label << strNum;
    }

    stringstream tmp;
    if ( whichHisto->getScientificNotation() )
      tmp << scientific;
    else
      tmp << fixed;
    tmp.precision( ParaverConfig::getInstance()->getPrecision() );
    value -= INT64( origValue );
    if ( value > 0 )
    {
      tmp << value;
      strNum = tmp.str();
      if ( origValue >= 1.0 )
        strNum.erase( strNum.begin() );

      label << strNum;
    }
  }
  else
    label << value;

  if ( showUnits && whichHisto->getShowUnits() &&
       !whichHisto->itsCommunicationStat( whichHisto->getCurrentStat() ) )
    label << " " << whichHisto->getUnitsLabel( whichHisto->getCurrentStat() );

  return label.str();
}

string LabelConstructor::histoTotalLabel( THistoTotals whichTotal )
{
  switch ( whichTotal )
  {
    case TOTAL:
      return "Total";
    case AVERAGE:
      return "Average";
    case MAXIMUM:
      return "Maximum";
    case MINIMUM:
      return "Minimum";
    case STDEV:
      return "StDev";
    case AVGDIVMAX:
      return "Avg/Max";
    case NUMTOTALS:
      return "";
  }

  return "";
}

string LabelConstructor::timeLabel( TTime value, TTimeUnit unit )
{
  stringstream label;

  label << fixed;
  label.precision( ParaverConfig::getInstance()->getPrecision() );

  label << value;
  label << " " << LABEL_TIMEUNIT[ unit ];

  return label.str();
}

string LabelConstructor::semanticLabel( const Window * whichWindow,
                                        TSemanticValue value,
                                        bool text )
{
  stringstream label;
  SemanticInfoType infoType = whichWindow->getSemanticInfoType();

  label << fixed;
  if ( ( value - INT64( value ) ) > 0.0 )
    label.precision( ParaverConfig::getInstance()->getPrecision() );
  else
    label.precision( 0 );

  if ( infoType == NO_TYPE || !text )
  {
    label << value;
  }
  else
  {
    if ( infoType == OBJECT_TYPE )
      label << LabelConstructor::objectLabel( value, whichWindow->getLevel(),
                                              whichWindow->getTrace() );
    else if ( infoType == APPL_TYPE )
      label << LabelConstructor::objectLabel( value, APPLICATION,
                                              whichWindow->getTrace() );
    else if ( infoType == TASK_TYPE )
      label << LabelConstructor::objectLabel( value, TASK,
                                              whichWindow->getTrace() );
    else if ( infoType == THREAD_TYPE )
      label << LabelConstructor::objectLabel( value, THREAD,
                                              whichWindow->getTrace() );
    else if ( infoType == NODE_TYPE )
      label << LabelConstructor::objectLabel( value, NODE,
                                              whichWindow->getTrace() );
    else if ( infoType == CPU_TYPE )
      label << LabelConstructor::objectLabel( value, CPU,
                                              whichWindow->getTrace() );
    else if ( infoType == TIME_TYPE )
      label << LabelConstructor::timeLabel( value, whichWindow->getTimeUnit() );
    else if ( infoType == STATE_TYPE )
    {
      string tmpstr;
      if ( !whichWindow->getTrace()->getStateLabels().getStateLabel( value, tmpstr ) )
        label << tmpstr << " state " << value;
      else
        label << tmpstr;
    }
    else if ( infoType == EVENTTYPE_TYPE )
    {
      string tmpstr;
      if ( !whichWindow->getTrace()->getEventLabels().getEventTypeLabel( value, tmpstr ) )
        label << tmpstr << " type " << value;
      else
        label << tmpstr;
    }
    else if ( infoType == EVENTVALUE_TYPE )
    {
      string tmpstr;
      vector<TEventType> types;
      bool found = false;
      whichWindow->getFilter()->getEventType( types );
      for( vector<TEventType>::iterator it = types.begin(); it != types.end(); ++it )
      {
        if( whichWindow->getTrace()->getEventLabels().getEventValueLabel(
          (*it), value, tmpstr ) )
        {
          found = true;
          label << tmpstr;
        }
      }
      if( !found )
      {
        if ( !whichWindow->getTrace()->getEventLabels().getEventValueLabel( value, tmpstr ) )
          label << tmpstr << " value " << value;
        else
          label << tmpstr;
      }
    }
    else if ( infoType == COMMSIZE_TYPE )
      label << value << " bytes";
    else if ( infoType == COMMTAG_TYPE )
      label << value;
    else if ( infoType == BANDWIDTH_TYPE )
      label << value << " bytes/sec";
    else
      label << "unknown " << value;
  }

  return label.str();
}
<commit_msg>*** empty log message ***<commit_after>#include <sstream>
#include <math.h>
#include <boost/lexical_cast.hpp>
#include "labelconstructor.h"
#include "paraverlabels.h"
#include "histogram.h"
#include "paraverconfig.h"
#include "window.h"
#include "filter.h"

string LabelConstructor::objectLabel( TObjectOrder globalOrder,
                                      TWindowLevel level,
                                      Trace *whichTrace )
{
  string rowStr;
  stringstream label;

  rowStr = whichTrace->getRowLabel( level, globalOrder );
  if ( rowStr != "" )
    return rowStr;

  if ( level == WORKLOAD )
    label << LEVEL_WORKLOAD;
  else if ( level == APPLICATION )
    label << LEVEL_APPLICATION << ' ' << globalOrder + 1;
  else if ( level == TASK )
  {
    TApplOrder appl;
    TTaskOrder task;
    whichTrace->getTaskLocation( globalOrder, appl, task );
    label << LEVEL_TASK << ' ' << appl + 1 << '.' << task + 1;
  }
  else if ( level == THREAD )
  {
    TApplOrder appl;
    TTaskOrder task;
    TThreadOrder thread;
    whichTrace->getThreadLocation( globalOrder, appl, task, thread );
    label << LEVEL_THREAD << ' ' << appl + 1 << '.' << task + 1 << '.' << thread + 1;
  }
  else if ( level == SYSTEM )
    label << LEVEL_SYSTEM;
  else if ( level == NODE )
    label << LEVEL_NODE << ' ' << globalOrder + 1;
  else if ( level == CPU )
  {
    TNodeOrder node;
    TCPUOrder cpu;
    whichTrace->getCPULocation( globalOrder, node, cpu );
    label << LEVEL_CPU << ' ' << node + 1 << '.' << cpu + 1;
  }

  return label.str();
}

string LabelConstructor::histoColumnLabel( THistogramColumn whichColumn,
    const Window *whichWindow,
    THistogramLimit min,
    THistogramLimit max,
    THistogramLimit delta )
{
  stringstream label;
  double tmp;

  if ( modf( min, &tmp ) != 0.0 || delta != 1.0 )
  {
    // Column range values
    label << '[' << ( whichColumn * delta ) + min << "..";
    if ( ( ( whichColumn * delta ) + min + delta ) > max )
    {
      label << max;
      label << ']';
    }
    else
    {
      label << ( whichColumn * delta ) + min + delta;
      label << ')';
    }
  }
  else
  {
    // Discrete integer value
    label << LabelConstructor::semanticLabel( whichWindow,
        ( whichColumn * delta ) + min, true );
  }

  return label.str();
}

inline string chomp( TSemanticValue& number )
{
  TSemanticValue origValue = number;
  number /= 1000.0;
  number = floor( number );
  TSemanticValue remainder = origValue - number * 1000.0;
  INT32 intRemainder = INT32( remainder );

  if ( number == 0 )
    return boost::lexical_cast<std::string>( intRemainder );
  else if ( remainder > 99 )
    return boost::lexical_cast<std::string>( intRemainder );
  else if ( remainder > 9 )
    return "0" + boost::lexical_cast<std::string>( intRemainder );
  else if ( remainder > 0 )
    return "00" + boost::lexical_cast<std::string>( intRemainder );
  return "000";
}

string LabelConstructor::histoCellLabel( const Histogram *whichHisto,
    TSemanticValue value, bool showUnits )
{
  stringstream label;

  if ( value == numeric_limits<double>::infinity() )
    return "inf";

  if ( whichHisto->getScientificNotation() )
    label << scientific;
  else
    label << fixed;

  label.precision( ParaverConfig::getInstance()->getPrecision() );

  if ( whichHisto->getThousandSeparator() &&
       !whichHisto->getScientificNotation() )
  {
    string strNum;
    TSemanticValue origValue = value;
    TSemanticValue intValue = floor( value );

    if ( origValue >= 1.0 )
    {
      while ( intValue > 0.0 )
        strNum = chomp( intValue ) + "," + strNum;
      strNum.erase( strNum.size() - 1, 1 );
      label << strNum;
    }

    stringstream tmp;
    if ( whichHisto->getScientificNotation() )
      tmp << scientific;
    else
      tmp << fixed;
    tmp.precision( ParaverConfig::getInstance()->getPrecision() );
    value -= INT64( origValue );
    if ( value > 0 )
    {
      tmp << value;
      strNum = tmp.str();
      if ( origValue >= 1.0 )
        strNum.erase( strNum.begin() );

      label << strNum;
    }
  }
  else
    label << value;

  if ( showUnits && whichHisto->getShowUnits() &&
       !whichHisto->itsCommunicationStat( whichHisto->getCurrentStat() ) )
    label << " " << whichHisto->getUnitsLabel( whichHisto->getCurrentStat() );

  return label.str();
}

string LabelConstructor::histoTotalLabel( THistoTotals whichTotal )
{
  switch ( whichTotal )
  {
    case TOTAL:
      return "Total";
    case AVERAGE:
      return "Average";
    case MAXIMUM:
      return "Maximum";
    case MINIMUM:
      return "Minimum";
    case STDEV:
      return "StDev";
    case AVGDIVMAX:
      return "Avg/Max";
    case NUMTOTALS:
      return "";
  }

  return "";
}

string LabelConstructor::timeLabel( TTime value, TTimeUnit unit )
{
  stringstream label;

  label << fixed;
  label.precision( ParaverConfig::getInstance()->getPrecision() );

  label << value;
  label << " " << LABEL_TIMEUNIT[ unit ];

  return label.str();
}

string LabelConstructor::semanticLabel( const Window * whichWindow,
                                        TSemanticValue value,
                                        bool text )
{
  stringstream label;
  SemanticInfoType infoType = whichWindow->getSemanticInfoType();

  label << fixed;
  if ( ( value - INT64( value ) ) > 0.0 )
    label.precision( ParaverConfig::getInstance()->getPrecision() );
  else
    label.precision( 0 );

  if ( infoType == NO_TYPE || !text )
  {
    label << value;
  }
  else
  {
    if ( infoType == OBJECT_TYPE )
      label << LabelConstructor::objectLabel( value, whichWindow->getLevel(),
                                              whichWindow->getTrace() );
    else if ( infoType == APPL_TYPE )
      label << LabelConstructor::objectLabel( value, APPLICATION,
                                              whichWindow->getTrace() );
    else if ( infoType == TASK_TYPE )
      label << LabelConstructor::objectLabel( value, TASK,
                                              whichWindow->getTrace() );
    else if ( infoType == THREAD_TYPE )
      label << LabelConstructor::objectLabel( value, THREAD,
                                              whichWindow->getTrace() );
    else if ( infoType == NODE_TYPE )
      label << LabelConstructor::objectLabel( value, NODE,
                                              whichWindow->getTrace() );
    else if ( infoType == CPU_TYPE )
      label << LabelConstructor::objectLabel( value, CPU,
                                              whichWindow->getTrace() );
    else if ( infoType == TIME_TYPE )
      label << LabelConstructor::timeLabel( value, whichWindow->getTimeUnit() );
    else if ( infoType == STATE_TYPE )
    {
      string tmpstr;
      if ( !whichWindow->getTrace()->getStateLabels().getStateLabel( value, tmpstr ) )
        label << tmpstr << " state " << value;
      else
        label << tmpstr;
    }
    else if ( infoType == EVENTTYPE_TYPE )
    {
      string tmpstr;
      if ( !whichWindow->getTrace()->getEventLabels().getEventTypeLabel( value, tmpstr ) )
        label << tmpstr << " type " << value;
      else
        label << tmpstr;
    }
    else if ( infoType == EVENTVALUE_TYPE )
    {
      string tmpstr;
      vector<TEventType> types;
      bool found = false;
      whichWindow->getFilter()->getEventType( types );
      for ( vector<TEventType>::iterator it = types.begin(); it != types.end(); ++it )
      {
        if ( whichWindow->getTrace()->getEventLabels().getEventValueLabel(
               ( *it ), value, tmpstr ) )
        {
          found = true;
          label << tmpstr;
          break;
        }
      }
      if ( !found )
      {
        if ( !whichWindow->getTrace()->getEventLabels().getEventValueLabel( value, tmpstr ) )
          label << tmpstr << " value " << value;
        else
          label << tmpstr;
      }
    }
    else if ( infoType == COMMSIZE_TYPE )
      label << value << " bytes";
    else if ( infoType == COMMTAG_TYPE )
      label << value;
    else if ( infoType == BANDWIDTH_TYPE )
      label << value << " bytes/sec";
    else
      label << "unknown " << value;
  }

  return label.str();
}
<|endoftext|>


<commit_before>/*
This file is part of Bohrium and copyright (c) 2012 the Bohrium
team <http://www.bh107.org>.

Bohrium 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, either version 3
of the License, or (at your option) any later version.

Bohrium 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 General Public License for more details.

You should have received a copy of the
GNU Lesser General Public License along with Bohrium.

If not, see <http://www.gnu.org/licenses/>.
*/

#include <bh.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

/* Number of non-broadcasted elements in a given view
 *
 * @view    The view in question.
 * @return  Number of elements.
 */
bh_index bh_nelements_nbcast(const bh_view *view)
{
    bh_index res = 1;
    for (int i = 0; i < view->ndim; ++i)
    {
        if(view->stride[i] > 0)
            res *= view->shape[i];
    }
    return res;
}

/* Number of element in a given shape
 *
 * @ndim     Number of dimentions
 * @shape[]  Number of elements in each dimention.
 * @return   Number of element operations
 */
bh_index bh_nelements(bh_intp ndim,
                      const bh_index shape[])
{
    bh_index res = 1;
    for (int i = 0; i < ndim; ++i)
    {
        res *= shape[i];
    }
    return res;
}

/* Size of the base array in bytes
 *
 * @base    The base in question
 * @return  The size of the base array in bytes
 */
bh_index bh_base_size(const bh_base *base)
{
    return base->nelem * bh_type_size(base->type);
}

/* Set the view stride to contiguous row-major
 *
 * @view    The view in question
 * @return  The total number of elements in view
 */
bh_intp bh_set_contiguous_stride(bh_view *view)
{
    bh_intp s = 1;
    for(bh_intp i=view->ndim-1; i >= 0; --i)
    {
        view->stride[i] = s;
        s *= view->shape[i];
    }
    return s;
}

/* Updates the view with the complete base
 *
 * @view    The view to update (in-/out-put)
 * @base    The base assign to the view
 * @return  The total number of elements in view
 */
void bh_assign_complete_base(bh_view *view, bh_base *base)
{
    view->base = base;
    view->ndim = 1;
    view->start = 0;
    view->shape[0] = view->base->nelem;
    view->stride[0] = 1;
}

/* Set the data pointer for the view.
 * Can only set to non-NULL if the data ptr is already NULL
 *
 * @view   The view in question
 * @data   The new data pointer
 * @return Error code (BH_SUCCESS, BH_ERROR)
 */
bh_error bh_data_set(bh_view* view, bh_data_ptr data)
{
    bh_base* base;

    if(view == NULL)
    {
        fprintf(stderr, "Attempt to set data pointer for a null view\n");
        return BH_ERROR;
    }

    base = bh_base_array(view);

    if(base->data != NULL && data != NULL)
    {
        fprintf(stderr, "Attempt to set data pointer an array with existing data pointer\n");
        return BH_ERROR;
    }

    base->data = data;

    return BH_SUCCESS;
}

/* Get the data pointer for the view.
 *
 * @view    The view in question
 * @result  Output data pointer
 * @return  Error code (BH_SUCCESS, BH_ERROR)
 */
bh_error bh_data_get(bh_view* view, bh_data_ptr* result)
{
    bh_base* base;

    if(view == NULL)
    {
        fprintf(stderr, "Attempt to get data pointer for a null view\n");
        return BH_ERROR;
    }

    base = bh_base_array(view);

    *result = base->data;

    return BH_SUCCESS;
}

/* Allocate data memory for the given base if not already allocated.
 * For convenience, the base is allowed to be NULL.
 *
 * @base    The base in question
 * @return  Error code (BH_SUCCESS, BH_ERROR, BH_OUT_OF_MEMORY)
 */
bh_error bh_data_malloc(bh_base* base)
{
    bh_intp bytes;

    if(base == NULL)
        return BH_SUCCESS;

    if(base->data != NULL)
        return BH_SUCCESS;

    bytes = bh_base_size(base);
    if(bytes == 0)//We allow zero sized arrays.
        return BH_SUCCESS;

    if(bytes < 0)
        return BH_ERROR;

    base->data = bh_memory_malloc(bytes);
    if(base->data == NULL)
    {
        int errsv = errno;//mmap() sets the errno.
        printf("bh_data_malloc() could not allocate a data region. "
               "Returned error code: %s.\n", strerror(errsv));
        return BH_OUT_OF_MEMORY;
    }

    return BH_SUCCESS;
}

/* Frees data memory for the given view.
 * For convenience, the view is allowed to be NULL.
 *
 * @base    The base in question
 * @return  Error code (BH_SUCCESS, BH_ERROR)
 */
bh_error bh_data_free(bh_base* base)
{
    bh_intp bytes;

    if(base == NULL)
        return BH_SUCCESS;

    if(base->data == NULL)
        return BH_SUCCESS;

    bytes = bh_base_size(base);

    if(bh_memory_free(base->data, bytes) != 0)
    {
        int errsv = errno;//munmmap() sets the errno.
        printf("bh_data_free() could not free a data region. "
               "Returned error code: %s.\n", strerror(errsv));
        return BH_ERROR;
    }
    base->data = NULL;
    return BH_SUCCESS;
}

/* Retrive the operands of a instruction.
 *
 * @instruction  The instruction in question
 * @return The operand list
 */
bh_view *bh_inst_operands(bh_instruction *instruction)
{
    return (bh_view *) &instruction->operand;
}

/* Determines whether the base array is a scalar.
 *
 * @view The view
 * @return The boolean answer
 */
bool bh_is_scalar(const bh_view* view)
{
    return bh_base_array(view)->nelem == 1;
}

/* Determines whether the operand is a constant
 *
 * @o The operand
 * @return The boolean answer
 */
bool bh_is_constant(const bh_view* o)
{
    return (o->base == NULL);
}

/* Flag operand as a constant
 *
 * @o      The operand
 */
void bh_flag_constant(bh_view* o)
{
    o->base = NULL;
}

inline int gcd(int a, int b)
{
    if (b==0)
        return a;
    int c = a % b;
    while(c != 0)
    {
        a = b;
        b = c;
        c = a % b;
    }
    return b;
}

/* Determines whether two views access some of the same data points
 * NB: This functions may return True on non-overlapping views.
 *     But will always return False on overlapping views.
 *
 * @a The first view
 * @b The second view
 * @return The boolean answer
 */
bool bh_view_disjoint(const bh_view *a, const bh_view *b)
{
    if (bh_is_constant(a) || bh_is_constant(b)) // One is a constant
        return true;
    if(bh_base_array(a) != bh_base_array(b)) //different base
        return true;
    if(a->ndim != b->ndim) // we dont handle views of differenr dimensions yet
        return false;

    int astart = a->start;
    int bstart = b->start;
    int stride = 1;
    for (int i = 0; i < a->ndim; ++i)
    {
        stride = gcd(a->stride[i], b->stride[i]);
        int as = astart / stride;
        int bs = bstart / stride;
        int ae = as + a->shape[i] * (a->stride[i]/stride);
        int be = bs + b->shape[i] * (b->stride[i]/stride);
        if (ae <= bs || be <= as)
            return true;
        astart %= stride;
        bstart %= stride;
    }
    if (stride > 1 && a->start % stride != b->start % stride)
        return true;
    return false;
}

/* Determines whether two views are identical and points
 * to the same base array.
 *
 * @a The first view
 * @b The second view
 * @return The boolean answer
 */
bool bh_view_identical(const bh_view *a, const bh_view *b)
{
    int i;
    if(bh_is_constant(a) || bh_is_constant(b))
        return false;
    if(a->base != b->base)
        return false;
    if(a->ndim != b->ndim)
        return false;
    if(a->start != b->start)
        return false;
    for(i=0; i<a->ndim; ++i)
    {
        if(a->shape[i] != b->shape[i])
            return false;
        if(a->stride[i] != b->stride[i])
            return false;
    }
    return true;
}
<commit_msg>Prevent divide by zero in special case in bh_view_disjoint<commit_after>/*
This file is part of Bohrium and copyright (c) 2012 the Bohrium
team <http://www.bh107.org>.

Bohrium 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, either version 3
of the License, or (at your option) any later version.

Bohrium 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 General Public License for more details.

You should have received a copy of the
GNU Lesser General Public License along with Bohrium.

If not, see <http://www.gnu.org/licenses/>.
*/

#include <bh.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

/* Number of non-broadcasted elements in a given view
 *
 * @view    The view in question.
 * @return  Number of elements.
 */
bh_index bh_nelements_nbcast(const bh_view *view)
{
    bh_index res = 1;
    for (int i = 0; i < view->ndim; ++i)
    {
        if(view->stride[i] > 0)
            res *= view->shape[i];
    }
    return res;
}

/* Number of element in a given shape
 *
 * @ndim     Number of dimentions
 * @shape[]  Number of elements in each dimention.
 * @return   Number of element operations
 */
bh_index bh_nelements(bh_intp ndim,
                      const bh_index shape[])
{
    bh_index res = 1;
    for (int i = 0; i < ndim; ++i)
    {
        res *= shape[i];
    }
    return res;
}

/* Size of the base array in bytes
 *
 * @base    The base in question
 * @return  The size of the base array in bytes
 */
bh_index bh_base_size(const bh_base *base)
{
    return base->nelem * bh_type_size(base->type);
}

/* Set the view stride to contiguous row-major
 *
 * @view    The view in question
 * @return  The total number of elements in view
 */
bh_intp bh_set_contiguous_stride(bh_view *view)
{
    bh_intp s = 1;
    for(bh_intp i=view->ndim-1; i >= 0; --i)
    {
        view->stride[i] = s;
        s *= view->shape[i];
    }
    return s;
}

/* Updates the view with the complete base
 *
 * @view    The view to update (in-/out-put)
 * @base    The base assign to the view
 * @return  The total number of elements in view
 */
void bh_assign_complete_base(bh_view *view, bh_base *base)
{
    view->base = base;
    view->ndim = 1;
    view->start = 0;
    view->shape[0] = view->base->nelem;
    view->stride[0] = 1;
}

/* Set the data pointer for the view.
 * Can only set to non-NULL if the data ptr is already NULL
 *
 * @view   The view in question
 * @data   The new data pointer
 * @return Error code (BH_SUCCESS, BH_ERROR)
 */
bh_error bh_data_set(bh_view* view, bh_data_ptr data)
{
    bh_base* base;

    if(view == NULL)
    {
        fprintf(stderr, "Attempt to set data pointer for a null view\n");
        return BH_ERROR;
    }

    base = bh_base_array(view);

    if(base->data != NULL && data != NULL)
    {
        fprintf(stderr, "Attempt to set data pointer an array with existing data pointer\n");
        return BH_ERROR;
    }

    base->data = data;

    return BH_SUCCESS;
}

/* Get the data pointer for the view.
 *
 * @view    The view in question
 * @result  Output data pointer
 * @return  Error code (BH_SUCCESS, BH_ERROR)
 */
bh_error bh_data_get(bh_view* view, bh_data_ptr* result)
{
    bh_base* base;

    if(view == NULL)
    {
        fprintf(stderr, "Attempt to get data pointer for a null view\n");
        return BH_ERROR;
    }

    base = bh_base_array(view);

    *result = base->data;

    return BH_SUCCESS;
}

/* Allocate data memory for the given base if not already allocated.
 * For convenience, the base is allowed to be NULL.
 *
 * @base    The base in question
 * @return  Error code (BH_SUCCESS, BH_ERROR, BH_OUT_OF_MEMORY)
 */
bh_error bh_data_malloc(bh_base* base)
{
    bh_intp bytes;

    if(base == NULL)
        return BH_SUCCESS;

    if(base->data != NULL)
        return BH_SUCCESS;

    bytes = bh_base_size(base);
    if(bytes == 0)//We allow zero sized arrays.
        return BH_SUCCESS;

    if(bytes < 0)
        return BH_ERROR;

    base->data = bh_memory_malloc(bytes);
    if(base->data == NULL)
    {
        int errsv = errno;//mmap() sets the errno.
        printf("bh_data_malloc() could not allocate a data region. "
               "Returned error code: %s.\n", strerror(errsv));
        return BH_OUT_OF_MEMORY;
    }

    return BH_SUCCESS;
}

/* Frees data memory for the given view.
 * For convenience, the view is allowed to be NULL.
 *
 * @base    The base in question
 * @return  Error code (BH_SUCCESS, BH_ERROR)
 */
bh_error bh_data_free(bh_base* base)
{
    bh_intp bytes;

    if(base == NULL)
        return BH_SUCCESS;

    if(base->data == NULL)
        return BH_SUCCESS;

    bytes = bh_base_size(base);

    if(bh_memory_free(base->data, bytes) != 0)
    {
        int errsv = errno;//munmmap() sets the errno.
        printf("bh_data_free() could not free a data region. "
               "Returned error code: %s.\n", strerror(errsv));
        return BH_ERROR;
    }
    base->data = NULL;
    return BH_SUCCESS;
}

/* Retrive the operands of a instruction.
 *
 * @instruction  The instruction in question
 * @return The operand list
 */
bh_view *bh_inst_operands(bh_instruction *instruction)
{
    return (bh_view *) &instruction->operand;
}

/* Determines whether the base array is a scalar.
 *
 * @view The view
 * @return The boolean answer
 */
bool bh_is_scalar(const bh_view* view)
{
    return bh_base_array(view)->nelem == 1;
}

/* Determines whether the operand is a constant
 *
 * @o The operand
 * @return The boolean answer
 */
bool bh_is_constant(const bh_view* o)
{
    return (o->base == NULL);
}

/* Flag operand as a constant
 *
 * @o      The operand
 */
void bh_flag_constant(bh_view* o)
{
    o->base = NULL;
}

inline int gcd(int a, int b)
{
    if (b==0)
        return a;
    int c = a % b;
    while(c != 0)
    {
        a = b;
        b = c;
        c = a % b;
    }
    return b;
}

/* Determines whether two views access some of the same data points
 * NB: This functions may return True on non-overlapping views.
 *     But will always return False on overlapping views.
 *
 * @a The first view
 * @b The second view
 * @return The boolean answer
 */
bool bh_view_disjoint(const bh_view *a, const bh_view *b)
{
    if (bh_is_constant(a) || bh_is_constant(b)) // One is a constant
        return true;
    if(bh_base_array(a) != bh_base_array(b)) //different base
        return true;
    if(a->ndim != b->ndim) // we dont handle views of differenr dimensions yet
        return false;

    int astart = a->start;
    int bstart = b->start;
    int stride = 1;
    for (int i = 0; i < a->ndim; ++i)
    {
        stride = gcd(a->stride[i], b->stride[i]);
        if (stride == 0) // stride is 0 in both views: dimension is virtual
            continue;
        int as = astart / stride;
        int bs = bstart / stride;
        int ae = as + a->shape[i] * (a->stride[i]/stride);
        int be = bs + b->shape[i] * (b->stride[i]/stride);
        if (ae <= bs || be <= as)
            return true;
        astart %= stride;
        bstart %= stride;
    }
    if (stride > 1 && a->start % stride != b->start % stride)
        return true;
    return false;
}

/* Determines whether two views are identical and points
 * to the same base array.
 *
 * @a The first view
 * @b The second view
 * @return The boolean answer
 */
bool bh_view_identical(const bh_view *a, const bh_view *b)
{
    int i;
    if(bh_is_constant(a) || bh_is_constant(b))
        return false;
    if(a->base != b->base)
        return false;
    if(a->ndim != b->ndim)
        return false;
    if(a->start != b->start)
        return false;
    for(i=0; i<a->ndim; ++i)
    {
        if(a->shape[i] != b->shape[i])
            return false;
        if(a->stride[i] != b->stride[i])
            return false;
    }
    return true;
}
<|endoftext|>


<commit_before>/*
This file is part of Bohrium and copyright (c) 2012 the Bohrium
team <http://www.bh107.org>.

Bohrium 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, either version 3
of the License, or (at your option) any later version.

Bohrium 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 General Public License for more details.

You should have received a copy of the
GNU Lesser General Public License along with Bohrium.

If not, see <http://www.gnu.org/licenses/>.
*/
#include <bh.h>
#include <bh_dag.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <boost/foreach.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
#include <vector>
#include <map>
#include <iterator>

using namespace std;
using namespace boost;

typedef adjacency_list<setS, vecS, bidirectionalS, bh_ir_kernel> Graph;
typedef graph_traits<Graph>::vertex_descriptor Vertex;
typedef graph_traits<Graph>::edge_descriptor Edge;

bool fuse_mask(const Graph &dag, const vector<Edge> &edges2explore,
               const vector<bool> &mask, Graph &new_dag)
{
    vector<Edge> edges2merge;
    unsigned int i=0;
    BOOST_FOREACH(const Edge &e, edges2explore)
    {
        if(mask[i++])
        {
            edges2merge.push_back(e);
        }
    }
    try
    {
        bh_dag_merge_vertices(dag, edges2merge, new_dag, true);
    }
    catch (const runtime_error &e)
    {
        return false;
    }

    if(bh_dag_cycles(new_dag))
        return false;

    return true;
}

int fuser_count=0;
uint64_t best_cost = numeric_limits<uint64_t>().max();
Graph best_dag;
void fuse(const Graph &dag, const vector<Edge> &edges2explore,
          vector<bool> mask, unsigned int offset, bool merge_next)
{
    if(not merge_next)
    {
        Graph new_dag;
        mask[offset] = merge_next;
        if(fuse_mask(dag, edges2explore, mask, new_dag))
        {
            const uint64_t c = bh_dag_cost(new_dag);
            if(best_cost > c)
            {
                best_cost = c;
                best_dag = new_dag;
#ifdef VERBOSE
                std::stringstream ss;
                ss << "new_best_dag-" << fuser_count << "-" << bh_dag_cost(new_dag) << ".dot";
                printf("write file: %s\n", ss.str().c_str());
                bh_dag_pprint(new_dag, ss.str().c_str());
#endif
            }
        }
    }
    if(offset+1 < mask.size())
    {
        fuse(dag, edges2explore, mask, offset+1, true);
        fuse(dag, edges2explore, mask, offset+1, false);
    }
}

void fuser(bh_ir &bhir)
{
    Graph dag;
    bh_dag_from_bhir(bhir, dag);
    bh_dag_transitive_reduction(dag);
    bh_dag_fuse_gentle(dag);

    //The list of edges that we should try to merge
    vector<Edge> edges2explore;
    BOOST_FOREACH(const Edge &e, edges(dag))
    {
        if(dag[target(e,dag)].fusible(dag[source(e,dag)]))
            edges2explore.push_back(e);
    }

    vector<bool> mask(edges2explore.size(), true);
    if(mask.size() == 0)
        return;

    //First we check the trivial case where all kernels are merged
    {
        Graph new_dag;
        if(fuse_mask(dag, edges2explore, mask, new_dag))
        {
            bh_dag_fill_kernels(new_dag, bhir.kernel_list);
            return;
        }
    }

    if(mask.size() > 20)
    {
        cout << "FUSER-OPTIMAL: ABORT the size of the search space is too large: 2^";
        cout << mask.size() << "!" << endl;
        bh_dag_fill_kernels(dag, bhir.kernel_list);
        return;
    }
    else if(mask.size() > 10)
    {
        cout << "FUSER-OPTIMAL: the size of the search space is 2^" << mask.size() << "!" << endl;
    }

    fuse(dag, edges2explore, mask, 0, true);
    fuse(dag, edges2explore, mask, 0, false);

    if(best_cost < numeric_limits<uint64_t>().max())
    {
        bh_dag_fill_kernels(best_dag, bhir.kernel_list);
    }
}

<commit_msg>optimal-fuser: now using branch and bound<commit_after>/*
This file is part of Bohrium and copyright (c) 2012 the Bohrium
team <http://www.bh107.org>.

Bohrium 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, either version 3
of the License, or (at your option) any later version.

Bohrium 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 General Public License for more details.

You should have received a copy of the
GNU Lesser General Public License along with Bohrium.

If not, see <http://www.gnu.org/licenses/>.
*/
#include <bh.h>
#include <bh_dag.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <boost/foreach.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
#include <vector>
#include <map>
#include <iterator>

using namespace std;
using namespace boost;

typedef adjacency_list<setS, vecS, bidirectionalS, bh_ir_kernel> Graph;
typedef graph_traits<Graph>::vertex_descriptor Vertex;
typedef graph_traits<Graph>::edge_descriptor Edge;

bool fuse_mask(const Graph &dag, const vector<Edge> &edges2explore,
               const vector<bool> &mask, Graph &new_dag)
{
    vector<Edge> edges2merge;
    unsigned int i=0;
    BOOST_FOREACH(const Edge &e, edges2explore)
    {
        if(mask[i++])
        {
            edges2merge.push_back(e);
        }
    }
    try
    {
        bh_dag_merge_vertices(dag, edges2merge, new_dag, true);
    }
    catch (const runtime_error &e)
    {
        return false;
    }

    if(bh_dag_cycles(new_dag))
        return false;

    return true;
}

int fuser_count=0;
uint64_t best_cost;
Graph best_dag;
void fuse(const Graph &dag, const vector<Edge> &edges2explore,
          vector<bool> mask, unsigned int offset, bool merge_next)
{
    if(not merge_next)
    {
        Graph new_dag;
        mask[offset] = merge_next;
        const bool fusible = fuse_mask(dag, edges2explore, mask, new_dag);
        const uint64_t cost = bh_dag_cost(new_dag);
        if(cost >= best_cost)
            return;
        if(fusible)
        {
            best_cost = cost;
            best_dag = new_dag;
            #ifdef VERBOSE
                std::stringstream ss;
                ss << "new_best_dag-" << fuser_count << "-" << bh_dag_cost(new_dag) << ".dot";
                printf("write file: %s\n", ss.str().c_str());
                bh_dag_pprint(new_dag, ss.str().c_str());
            #endif
            return;
        }
    }
    if(offset+1 < mask.size())
    {
        fuse(dag, edges2explore, mask, offset+1, true);
        fuse(dag, edges2explore, mask, offset+1, false);
    }
}

void fuser(bh_ir &bhir)
{
    Graph dag;
    bh_dag_from_bhir(bhir, dag);
    bh_dag_transitive_reduction(dag);
    bh_dag_fuse_gentle(dag);

    //The list of edges that we should try to merge
    vector<Edge> edges2explore;
    BOOST_FOREACH(const Edge &e, edges(dag))
    {
        if(dag[target(e,dag)].fusible(dag[source(e,dag)]))
            edges2explore.push_back(e);
    }

    if(edges2explore.size() == 0)
    {
        bh_dag_fill_kernels(dag, bhir.kernel_list);
        return;
    }

    //First we check the trivial case where all kernels are merged
    vector<bool> mask(edges2explore.size(), true);
    {
        Graph new_dag;
        if(fuse_mask(dag, edges2explore, mask, new_dag))
        {
            bh_dag_fill_kernels(new_dag, bhir.kernel_list);
            return;
        }
    }

    //Then we use the greedy algorithm to find a good initial guess
    best_dag = dag;
    bh_dag_fuse_greedy(best_dag);
    best_cost = bh_dag_cost(best_dag);

    if(mask.size() > 100)
    {
        cout << "FUSER-OPTIMAL: ABORT the size of the search space is too large: 2^";
        cout << mask.size() << "!" << endl;
        bh_dag_fill_kernels(best_dag, bhir.kernel_list);
        return;
    }
    else if(mask.size() > 10)
    {
        cout << "FUSER-OPTIMAL: the size of the search space is 2^" << mask.size() << "!" << endl;
    }

    fuse(dag, edges2explore, mask, 0, true);
    fuse(dag, edges2explore, mask, 0, false);
    bh_dag_fill_kernels(best_dag, bhir.kernel_list);
}

<|endoftext|>


<commit_before>/*
 * Copyright 2017, Andrej Kislovskij
 *
 * This is PUBLIC DOMAIN software so use at your own risk as it comes
 * with no warranties. This code is yours to share, use and modify without
 * any restrictions or obligations.
 *
 * For more information see conwrap/LICENSE or refer refer to http://unlicense.org
 *
 * Author: gimesketvirtadieni at gmail dot com (Andrej Kislovskij)
 */

#pragma once

#include <functional>
#include <iostream>
#include <string>

#include "slim/log/log.hpp"
#include "slim/util/OutputStreamCallback.hpp"
#include "slim/wave/WAVEStream.hpp"

namespace slim
{
	namespace proto
	{
		template<typename ConnectionType>
		class StreamingSession
		{
			using OutputStreamCallback = util::OutputStreamCallback<std::function<std::streamsize(const char*, std::streamsize)>>;

			public:
				StreamingSession(ConnectionType& co, std::string cl, unsigned int channels, unsigned int sr, unsigned int bitePerSample)
				: connection{co}
				, clientID{cl}
				, outputStreamCallback{[&](auto* buffer, auto size) mutable
				{
					return connection.send(buffer, size);
				}}
				, samplingRate{sr}
				, waveStream{std::make_unique<std::ostream>(&outputStreamCallback), channels, samplingRate, bitePerSample}
				{
					LOG(INFO) << "HTTP session created";

					// sending HTTP response with the headers
					waveStream.write("HTTP/1.1 200 OK\r\n");
					waveStream.write("Server: SlimStreamer (0.0.1)\r\n");
					waveStream.write("Connection: close\r\n");
					waveStream.write("Content-Type: audio/x-wave\r\n");
					waveStream.write("\r\n");
				}

				~StreamingSession()
				{
					LOG(INFO) << "HTTP session deleted";
				}

				StreamingSession(const StreamingSession&) = delete;             // non-copyable
				StreamingSession& operator=(const StreamingSession&) = delete;  // non-assignable
				StreamingSession(StreamingSession&& rhs) = delete;              // non-movable
				StreamingSession& operator=(StreamingSession&& rhs) = delete;   // non-movable-assignable

				inline void onChunk(Chunk& chunk, unsigned int sr)
				{
					if (samplingRate == sr)
					{
						waveStream.write(chunk.getBuffer(), chunk.getDataSize());
					}
					else
					{
						// TODO: implement differing chunks processing
						LOG(WARNING) << "Skipping chunk transmition due to client and PCM sampling rate";
					}
				}

				inline auto getClientID()
				{
					return clientID;
				}

				inline auto getSamplingRate()
				{
					return samplingRate;
				}

				void onRequest(unsigned char* buffer, std::size_t receivedSize)
				{
					LOG(DEBUG) << "HTTP onData";

					//for (unsigned int ii = 0; ii < receivedSize; ii++)
					//{
					//	LOG(DEBUG) << buffer[ii];
					//}
				}

				static auto parseClientID(std::string header)
				{
					std::string result{};
					std::string separator{"="};

					auto index = header.find(separator);
					if ( index != std::string::npos)
					{
						result = std::string{header.c_str() + index + separator.length(), header.length() - index - separator.length()};
					}

					return result;
				}

			private:
				ConnectionType&      connection;
				std::string          clientID;
				OutputStreamCallback outputStreamCallback;
				unsigned int         samplingRate;
				wave::WAVEStream     waveStream;
		};
	}
}
<commit_msg>Using std::optional for storing last ping time point<commit_after>/*
 * Copyright 2017, Andrej Kislovskij
 *
 * This is PUBLIC DOMAIN software so use at your own risk as it comes
 * with no warranties. This code is yours to share, use and modify without
 * any restrictions or obligations.
 *
 * For more information see conwrap/LICENSE or refer refer to http://unlicense.org
 *
 * Author: gimesketvirtadieni at gmail dot com (Andrej Kislovskij)
 */

#pragma once

#include <functional>
#include <iostream>
#include <optional>
#include <string>

#include "slim/log/log.hpp"
#include "slim/util/OutputStreamCallback.hpp"
#include "slim/wave/WAVEStream.hpp"

namespace slim
{
	namespace proto
	{
		template<typename ConnectionType>
		class StreamingSession
		{
			using OutputStreamCallback = util::OutputStreamCallback<std::function<std::streamsize(const char*, std::streamsize)>>;

			public:
				StreamingSession(ConnectionType& co, std::string cl, unsigned int channels, unsigned int sr, unsigned int bitePerSample)
				: connection{co}
				, clientID{cl}
				, outputStreamCallback{[&](auto* buffer, auto size) mutable
				{
					return connection.send(buffer, size);
				}}
				, samplingRate{sr}
				, waveStream{std::make_unique<std::ostream>(&outputStreamCallback), channels, samplingRate, bitePerSample}
				{
					LOG(INFO) << "HTTP session created";

					// sending HTTP response with the headers
					waveStream.write("HTTP/1.1 200 OK\r\n");
					waveStream.write("Server: SlimStreamer (0.0.1)\r\n");
					waveStream.write("Connection: close\r\n");
					waveStream.write("Content-Type: audio/x-wave\r\n");
					waveStream.write("\r\n");
				}

				~StreamingSession()
				{
					LOG(INFO) << "HTTP session deleted";
				}

				StreamingSession(const StreamingSession&) = delete;             // non-copyable
				StreamingSession& operator=(const StreamingSession&) = delete;  // non-assignable
				StreamingSession(StreamingSession&& rhs) = delete;              // non-movable
				StreamingSession& operator=(StreamingSession&& rhs) = delete;   // non-movable-assignable

				inline void onChunk(Chunk& chunk, unsigned int sr)
				{
					if (samplingRate == sr)
					{
						waveStream.write(chunk.getBuffer(), chunk.getDataSize());
					}
					else
					{
						// TODO: implement differing chunks processing
						LOG(WARNING) << "Skipping chunk transmition due to client and PCM sampling rate";
					}
				}

				inline auto getClientID()
				{
					return clientID;
				}

				inline auto getSamplingRate()
				{
					return samplingRate;
				}

				void onRequest(unsigned char* buffer, std::size_t receivedSize)
				{
					LOG(DEBUG) << "HTTP onData";

					//for (unsigned int ii = 0; ii < receivedSize; ii++)
					//{
					//	LOG(DEBUG) << buffer[ii];
					//}
				}

				static auto parseClientID(std::string header)
				{
					auto result{std::optional<std::string>{std::nullopt}};
					auto separator{std::string{"="}};

					auto index = header.find(separator);
					if ( index != std::string::npos)
					{
						result = std::string{header.c_str() + index + separator.length(), header.length() - index - separator.length()};
					}

					return result;
				}

			private:
				ConnectionType&      connection;
				std::string          clientID;
				OutputStreamCallback outputStreamCallback;
				unsigned int         samplingRate;
				wave::WAVEStream     waveStream;
		};
	}
}
<|endoftext|>


<commit_before>/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2011-2014 Paul Asmuth, Google Inc.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <fnordmetric/sql_extensions/domainconfig.h>
#include <fnordmetric/sql_extensions/drawstatement.h>
#include <fnordmetric/sql_extensions/barchartbuilder.h>
#include <fnordmetric/sql_extensions/linechartbuilder.h>

namespace fnordmetric {
namespace query {

DrawStatement::DrawStatement(ASTNode* ast) : ast_(ast->deepCopy()) {}

void DrawStatement::execute(ui::Canvas* canvas) const {
  ui::Drawable* chart = nullptr;

  switch (ast_->getToken()->getType()) {
    case Token::T_BARCHART:
      chart = mkChart<BarChartBuilder>(canvas);
      break;
    case Token::T_LINECHART:
      chart = mkChart<LineChartBuilder>(canvas);
      break;
    default:
      RAISE(
          util::RuntimeException,
          "invalid chart type: %s",
          Token::getTypeName(ast_->getToken()->getType()));
  }

  applyDomainDefinitions(chart);
  applyTitle(chart);
  applyAxisDefinitions(chart);
  applyGrid(chart);
}

ASTNode const* DrawStatement::getProperty(Token::kTokenType key) const {
  for (const auto& child : ast_->getChildren()) {
    if (child->getType() != ASTNode::T_PROPERTY) {
      continue;
    }

    if (child->getToken()->getType() != key) {
      continue;
    }

    const auto& values = child->getChildren();
    if (values.size() != 1) {
      RAISE(util::RuntimeException, "corrupt AST: T_PROPERTY has != 1 child");
    }

    return values[0];
  }

  return nullptr;
}

void DrawStatement::applyAxisDefinitions(ui::Drawable* chart) const {
  for (const auto& child : ast_->getChildren()) {
    if (child->getType() != ASTNode::T_AXIS ||
        child->getChildren().size() < 1 ||
        child->getChildren()[0]->getType() != ASTNode::T_AXIS_POSITION) {
      continue;
    }

    ui::AxisDefinition* axis = nullptr;

    if (child->getChildren().size() < 1) {
      RAISE(util::RuntimeException, "corrupt AST: AXIS has < 1 child");
    }

    switch (child->getChildren()[0]->getToken()->getType()) {
      case Token::T_TOP:
        axis = chart->addAxis(ui::AxisDefinition::TOP);
        break;

      case Token::T_RIGHT:
        axis = chart->addAxis(ui::AxisDefinition::RIGHT);
        break;

      case Token::T_BOTTOM:
        axis = chart->addAxis(ui::AxisDefinition::BOTTOM);
        break;

      case Token::T_LEFT:
        axis = chart->addAxis(ui::AxisDefinition::LEFT);
        break;

      default:
        RAISE(util::RuntimeException, "corrupt AST: invalid axis position");
    }
  }
}

void DrawStatement::applyDomainDefinitions(ui::Drawable* chart) const {
  for (const auto& child : ast_->getChildren()) {
    bool invert = false;
    bool logarithmic = false;
    ASTNode* min_expr = nullptr;
    ASTNode* max_expr = nullptr;

    if (child->getType() != ASTNode::T_DOMAIN) {
      continue;
    }

    if (child->getToken() == nullptr) {
      RAISE(util::RuntimeException, "corrupt AST: DOMAIN has no token");
    }

    ui::AnyDomain::kDimension dim;
    switch (child->getToken()->getType()) {
      case Token::T_XDOMAIN:
        dim = ui::AnyDomain::DIM_X;
        break;
      case Token::T_YDOMAIN:
        dim = ui::AnyDomain::DIM_Y;
        break;
      case Token::T_ZDOMAIN:
        dim = ui::AnyDomain::DIM_Z;
        break;
      default:
        RAISE(util::RuntimeException, "corrupt AST: DOMAIN has invalid token");
    }

    for (const auto& domain_prop : child->getChildren()) {
      switch (domain_prop->getType()) {
        case ASTNode::T_DOMAIN_SCALE: {
          auto min_max_expr = domain_prop->getChildren();
          if (min_max_expr.size() != 2 ) {
            RAISE(util::RuntimeException, "corrupt AST: invalid DOMAIN SCALE");
          }
          min_expr = min_max_expr[0];
          max_expr = min_max_expr[1];
          break;
        }

        case ASTNode::T_PROPERTY: {
          if (domain_prop->getToken() != nullptr) {
            switch (domain_prop->getToken()->getType()) {
              case Token::T_INVERT:
                invert = true;
                continue;
              case Token::T_LOGARITHMIC:
                logarithmic = true;
                continue;
              default:
                break;
            }
          }

          RAISE(util::RuntimeException, "corrupt AST: invalid DOMAIN property");
          break;
        }

        default:
          RAISE(util::RuntimeException, "corrupt AST: unexpected DOMAIN child");

      }
    }

    DomainConfig domain_config(chart, dim);
    domain_config.setInvert(invert);
    domain_config.setLogarithmic(logarithmic);
    if (min_expr != nullptr && max_expr != nullptr) {
      domain_config.setMin(executeSimpleConstExpression(min_expr));
      domain_config.setMax(executeSimpleConstExpression(max_expr));
    }
  }
}

void DrawStatement::applyTitle(ui::Drawable* chart) const {
  for (const auto& child : ast_->getChildren()) {
    if (child->getType() != ASTNode::T_PROPERTY ||
        child->getToken() == nullptr || !(
        child->getToken()->getType() == Token::T_TITLE ||
        child->getToken()->getType() == Token::T_SUBTITLE)) {
      continue;
    }

    if (child->getChildren().size() != 1) {
      RAISE(util::RuntimeException, "corrupt AST: [SUB]TITLE has != 1 child");
    }

    auto title_eval = executeSimpleConstExpression(child->getChildren()[0]);
    auto title_str = title_eval.toString();

    switch (child->getToken()->getType()) {
      case Token::T_TITLE:
        chart->setTitle(title_str);
        break;

      case Token::T_SUBTITLE:
        chart->setSubtitle(title_str);
        break;

      default:
        break;
    }
  }
}

void DrawStatement::applyGrid(ui::Drawable* chart) const {
  ASTNode* grid = nullptr;

  for (const auto& child : ast_->getChildren()) {
    if (child->getType() == ASTNode::T_GRID) {
      grid = child;
      break;
    }
  }

  if (!grid) {
    return;
  }

  bool horizontal = false;
  bool vertical = false;

  for (const auto& prop : grid->getChildren()) {
    if (prop->getType() == ASTNode::T_PROPERTY && prop->getToken() != nullptr) {
      switch (prop->getToken()->getType()) {
        case Token::T_HORIZONTAL:
          horizontal = true;
          break;

        case Token::T_VERTICAL:
          vertical = true;
          break;

        default:
          RAISE(
              util::RuntimeException,
              "corrupt AST: GRID has invalid property");
      }
    }
  }

  if (horizontal || vertical) {
    chart->addGrid(horizontal, vertical);
  }
}

}
}
<commit_msg>pipe through axis titles<commit_after>/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2011-2014 Paul Asmuth, Google Inc.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <fnordmetric/sql_extensions/domainconfig.h>
#include <fnordmetric/sql_extensions/drawstatement.h>
#include <fnordmetric/sql_extensions/barchartbuilder.h>
#include <fnordmetric/sql_extensions/linechartbuilder.h>

namespace fnordmetric {
namespace query {

DrawStatement::DrawStatement(ASTNode* ast) : ast_(ast->deepCopy()) {}

void DrawStatement::execute(ui::Canvas* canvas) const {
  ui::Drawable* chart = nullptr;

  switch (ast_->getToken()->getType()) {
    case Token::T_BARCHART:
      chart = mkChart<BarChartBuilder>(canvas);
      break;
    case Token::T_LINECHART:
      chart = mkChart<LineChartBuilder>(canvas);
      break;
    default:
      RAISE(
          util::RuntimeException,
          "invalid chart type: %s",
          Token::getTypeName(ast_->getToken()->getType()));
  }

  applyDomainDefinitions(chart);
  applyTitle(chart);
  applyAxisDefinitions(chart);
  applyGrid(chart);
}

ASTNode const* DrawStatement::getProperty(Token::kTokenType key) const {
  for (const auto& child : ast_->getChildren()) {
    if (child->getType() != ASTNode::T_PROPERTY) {
      continue;
    }

    if (child->getToken()->getType() != key) {
      continue;
    }

    const auto& values = child->getChildren();
    if (values.size() != 1) {
      RAISE(util::RuntimeException, "corrupt AST: T_PROPERTY has != 1 child");
    }

    return values[0];
  }

  return nullptr;
}

void DrawStatement::applyAxisDefinitions(ui::Drawable* chart) const {
  for (const auto& child : ast_->getChildren()) {
    if (child->getType() != ASTNode::T_AXIS ||
        child->getChildren().size() < 1 ||
        child->getChildren()[0]->getType() != ASTNode::T_AXIS_POSITION) {
      continue;
    }

    ui::AxisDefinition* axis = nullptr;

    if (child->getChildren().size() < 1) {
      RAISE(util::RuntimeException, "corrupt AST: AXIS has < 1 child");
    }

    switch (child->getChildren()[0]->getToken()->getType()) {
      case Token::T_TOP:
        axis = chart->addAxis(ui::AxisDefinition::TOP);
        break;

      case Token::T_RIGHT:
        axis = chart->addAxis(ui::AxisDefinition::RIGHT);
        break;

      case Token::T_BOTTOM:
        axis = chart->addAxis(ui::AxisDefinition::BOTTOM);
        break;

      case Token::T_LEFT:
        axis = chart->addAxis(ui::AxisDefinition::LEFT);
        break;

      default:
        RAISE(util::RuntimeException, "corrupt AST: invalid axis position");
    }

    for (int i = 1; i < child->getChildren().size(); ++i) {
      auto prop = child->getChildren()[i];

      if (prop->getType() == ASTNode::T_PROPERTY &&
          prop->getToken() != nullptr &&
          *prop->getToken() == Token::T_TITLE &&
          prop->getChildren().size() == 1) {
        auto axis_title = executeSimpleConstExpression(prop->getChildren()[0]);
        axis->setTitle(axis_title.toString());
        continue;
      }

      if (prop->getType() == ASTNode::T_AXIS_LABELS) {
        //applyAxisLabels(
      }
    }
  }
}

void DrawStatement::applyDomainDefinitions(ui::Drawable* chart) const {
  for (const auto& child : ast_->getChildren()) {
    bool invert = false;
    bool logarithmic = false;
    ASTNode* min_expr = nullptr;
    ASTNode* max_expr = nullptr;

    if (child->getType() != ASTNode::T_DOMAIN) {
      continue;
    }

    if (child->getToken() == nullptr) {
      RAISE(util::RuntimeException, "corrupt AST: DOMAIN has no token");
    }

    ui::AnyDomain::kDimension dim;
    switch (child->getToken()->getType()) {
      case Token::T_XDOMAIN:
        dim = ui::AnyDomain::DIM_X;
        break;
      case Token::T_YDOMAIN:
        dim = ui::AnyDomain::DIM_Y;
        break;
      case Token::T_ZDOMAIN:
        dim = ui::AnyDomain::DIM_Z;
        break;
      default:
        RAISE(util::RuntimeException, "corrupt AST: DOMAIN has invalid token");
    }

    for (const auto& domain_prop : child->getChildren()) {
      switch (domain_prop->getType()) {
        case ASTNode::T_DOMAIN_SCALE: {
          auto min_max_expr = domain_prop->getChildren();
          if (min_max_expr.size() != 2 ) {
            RAISE(util::RuntimeException, "corrupt AST: invalid DOMAIN SCALE");
          }
          min_expr = min_max_expr[0];
          max_expr = min_max_expr[1];
          break;
        }

        case ASTNode::T_PROPERTY: {
          if (domain_prop->getToken() != nullptr) {
            switch (domain_prop->getToken()->getType()) {
              case Token::T_INVERT:
                invert = true;
                continue;
              case Token::T_LOGARITHMIC:
                logarithmic = true;
                continue;
              default:
                break;
            }
          }

          RAISE(util::RuntimeException, "corrupt AST: invalid DOMAIN property");
          break;
        }

        default:
          RAISE(util::RuntimeException, "corrupt AST: unexpected DOMAIN child");

      }
    }

    DomainConfig domain_config(chart, dim);
    domain_config.setInvert(invert);
    domain_config.setLogarithmic(logarithmic);
    if (min_expr != nullptr && max_expr != nullptr) {
      domain_config.setMin(executeSimpleConstExpression(min_expr));
      domain_config.setMax(executeSimpleConstExpression(max_expr));
    }
  }
}

void DrawStatement::applyTitle(ui::Drawable* chart) const {
  for (const auto& child : ast_->getChildren()) {
    if (child->getType() != ASTNode::T_PROPERTY ||
        child->getToken() == nullptr || !(
        child->getToken()->getType() == Token::T_TITLE ||
        child->getToken()->getType() == Token::T_SUBTITLE)) {
      continue;
    }

    if (child->getChildren().size() != 1) {
      RAISE(util::RuntimeException, "corrupt AST: [SUB]TITLE has != 1 child");
    }

    auto title_eval = executeSimpleConstExpression(child->getChildren()[0]);
    auto title_str = title_eval.toString();

    switch (child->getToken()->getType()) {
      case Token::T_TITLE:
        chart->setTitle(title_str);
        break;

      case Token::T_SUBTITLE:
        chart->setSubtitle(title_str);
        break;

      default:
        break;
    }
  }
}

void DrawStatement::applyGrid(ui::Drawable* chart) const {
  ASTNode* grid = nullptr;

  for (const auto& child : ast_->getChildren()) {
    if (child->getType() == ASTNode::T_GRID) {
      grid = child;
      break;
    }
  }

  if (!grid) {
    return;
  }

  bool horizontal = false;
  bool vertical = false;

  for (const auto& prop : grid->getChildren()) {
    if (prop->getType() == ASTNode::T_PROPERTY && prop->getToken() != nullptr) {
      switch (prop->getToken()->getType()) {
        case Token::T_HORIZONTAL:
          horizontal = true;
          break;

        case Token::T_VERTICAL:
          vertical = true;
          break;

        default:
          RAISE(
              util::RuntimeException,
              "corrupt AST: GRID has invalid property");
      }
    }
  }

  if (horizontal || vertical) {
    chart->addGrid(horizontal, vertical);
  }
}

}
}
<|endoftext|>


<commit_before>/*
    Copyright (C) 1998 by Jorrit Tyberghein

    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; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "cssysdef.h"
#include "qint.h"
#include "csgeom/math2d.h"
#include "csutil/util.h"
#include "soft_g3d.h"
#include "scan.h"

// The structure that stays behind csHaloHandle
class csSoftHalo : public iHalo
{
  // Halo R,G,B components
  float R, G, B;
  // Halo alpha map
  unsigned char *Alpha;
  // Halo size
  int Width, Height;

  // Software 3D rasterizer
  csGraphics3DSoftwareCommon *G3D;

public:

  DECLARE_IBASE;

  /// Create the halo object
  csSoftHalo (float iR, float iG, float iB, unsigned char *iAlpha,
    int iWidth, int iHeight, csGraphics3DSoftwareCommon *iG3D);

  /// Destroy the halo object
  virtual ~csSoftHalo ();

  //---------------------------------// iHalo interface implementation //-----//
  /// Query halo width
  virtual int GetWidth () { return Width; }

  /// Query halo height
  virtual int GetHeight () { return Height; }

  /// Change halo color
  virtual void SetColor (float &iR, float &iG, float &iB)
  { R = iR; G = iG; B = iB; }

  /// Query halo color
  virtual void GetColor (float &oR, float &oG, float &oB)
  { oR = R; oG = G; oB = B; }

  /**
   * Draw the halo given a center point and an intensity.
   * If either w and/or h is negative, the native width and/or height
   * is used instead. If the halo should be clipped against some
   * polygon, that polygon should be given, otherwise if a NULL pointer
   * is passed, the halo is clipped just against screen bounds.
   */
  virtual void Draw (float x, float y, float w, float h, float iIntensity,
    csVector2 *iVertices, int iVertCount);
};

//----------------// Halo scanline rasterizing functions for all modes //-----//

void halo_dscan_8 (void *src, void *dest, int count, int delta)
{
  unsigned char *A = (unsigned char *)src;
  unsigned char *D = (unsigned char *)dest;
  unsigned char *E = D + count;
  if (delta == 0x10000)
    while (D < E)
    {
      unsigned int a = (*A++ * Scan.FogDensity) & 0x1f00;
      if (a) *D = Scan.Fog8 [(a ^ 0x1f00) | *D];
      D++;
    }
  else
  {
    unsigned int ax = 0;
    while (D < E)
    {
      unsigned int a = (A [ax >> 16] * Scan.FogDensity) & 0x1f00;
      if (a) *D = Scan.Fog8 [(a ^ 0x1f00) | *D];
      D++;
      ax += delta;
    }
  }
}

#define HALO_NAME	halo_dscan_16_555
#define HALO_BPP	16
#define HALO_RM		0x7c00
#define HALO_GM		0x03e0
#define HALO_BM		0x001f
#include "haloscan.inc"

#define HALO_NAME	halo_dscan_16_555_c
#define HALO_BPP	16
#define HALO_RM		0x7c00
#define HALO_GM		0x03e0
#define HALO_BM		0x001f
#define HALO_CLAMP
#include "haloscan.inc"

#define HALO_NAME	halo_dscan_16_565
#define HALO_BPP	16
#define HALO_RM		0xf800
#define HALO_GM		0x07e0
#define HALO_BM		0x001f
#include "haloscan.inc"

#define HALO_NAME	halo_dscan_16_565_c
#define HALO_BPP	16
#define HALO_RM		0xf800
#define HALO_GM		0x07e0
#define HALO_BM		0x001f
#define HALO_CLAMP
#include "haloscan.inc"

#define HALO_NAME	halo_dscan_32
#define HALO_BPP	32
#define HALO_RM		0xff0000
#define HALO_GM		0x00ff00
#define HALO_BM		0x0000ff
#include "haloscan.inc"

#define HALO_NAME	halo_dscan_32_c
#define HALO_BPP	32
#define HALO_RM		0xff0000
#define HALO_GM		0x00ff00
#define HALO_BM		0x0000ff
#define HALO_CLAMP
#include "haloscan.inc"

//----------------------------------------------// Software halo class //-----//

IMPLEMENT_IBASE (csSoftHalo)
  IMPLEMENTS_INTERFACE (iHalo)
IMPLEMENT_IBASE_END

// Create the halo object
csSoftHalo::csSoftHalo (float iR, float iG, float iB, unsigned char *iAlpha,
  int iWidth, int iHeight, csGraphics3DSoftwareCommon *iG3D)
{
  CONSTRUCT_IBASE (NULL);
  R = iR; G = iG; B = iB;
  Alpha = new unsigned char [(Width = iWidth) * (Height = iHeight)];
  memcpy (Alpha, iAlpha, Width * Height);
  (G3D = iG3D)->IncRef ();
}

// Destroy the halo object
csSoftHalo::~csSoftHalo ()
{
  delete [] Alpha;
  G3D->DecRef ();
}

void csSoftHalo::Draw (float x, float y, float w, float h, float iIntensity,
  csVector2 *iVertices, int iVertCount)
{
  // Check if halo is visible
  if (iIntensity <= 0)
    return;
  if (iIntensity > 1)
    iIntensity = 1;

  if (w < 0) w = Width;
  if (h < 0) h = Height;

  // Draw a single scanline of halo
  void (*dscan)(void *src, void *dest, int count, int delta) = 0;
  bool clamp = false;
  const csPixelFormat& pfmt = G3D->pfmt;

  if (pfmt.PixelBytes == 1)
  {
    Scan.FogR = QRound (R * 255);
    Scan.FogG = QRound (G * 255);
    Scan.FogB = QRound (B * 255);
    Scan.Fog8 = G3D->BuildIndexedFogTable ();
    Scan.FogPix = G3D->texman->find_rgb (Scan.FogR, Scan.FogG, Scan.FogB);
    // halo intensity (0..31)
    Scan.FogDensity = QRound (iIntensity * 32);
  }
  else
  {
    Scan.FogR = QRound (R * ((1 << pfmt.RedBits  ) - 1))
      << R8G8B8_SHIFT_ADJUST(pfmt.RedShift);
    Scan.FogG = QRound (G * ((1 << pfmt.GreenBits) - 1))
      << R8G8B8_SHIFT_ADJUST(pfmt.GreenShift);
    Scan.FogB = QRound (B * ((1 << pfmt.BlueBits ) - 1))
      << R8G8B8_SHIFT_ADJUST(pfmt.BlueShift);

    // halo intensity (0..64)
    Scan.FogDensity = QRound (iIntensity * 64);
    // Detect when the halo will possibly overflow
    clamp = (Scan.FogR > R8G8B8_PIXEL_PREPROC(pfmt.RedMask  ))
         || (Scan.FogG > R8G8B8_PIXEL_PREPROC(pfmt.GreenMask))
         || (Scan.FogB > R8G8B8_PIXEL_PREPROC(pfmt.BlueMask ));
  }

  switch (pfmt.PixelBytes)
  {
    case 1:
      dscan = halo_dscan_8;
      break;
    case 2:
      if (pfmt.GreenBits == 6)
      {
        if (clamp)
	  dscan = halo_dscan_16_565_c;
	else
	  dscan = halo_dscan_16_565;
      }
      else
      {
        if (clamp)
	  dscan = halo_dscan_16_555_c;
	else
	  dscan = halo_dscan_16_555;
      }
      break;
    case 4:
    {
      unsigned int rs = R8G8B8_SHIFT_ADJUST(pfmt.RedShift);
      unsigned int gs = R8G8B8_SHIFT_ADJUST(pfmt.GreenShift);
      ULong r = Scan.FogR, g = Scan.FogG, b = Scan.FogB;
      Scan.FogR = (rs == 16) ? r : (gs == 16) ? g : b;
      Scan.FogG = (rs ==  8) ? r : (gs ==  8) ? g : b;
      Scan.FogB = (rs ==  0) ? r : (gs ==  0) ? g : b;
      if (clamp)
        dscan = halo_dscan_32_c;
      else
        dscan = halo_dscan_32;
      break;
    }
    default:
      return;
  }

  csVector2 HaloPoly [4];
  if (!iVertices)
  {
    iVertCount = 4;
    iVertices = HaloPoly;

    float x1 = x, y1 = y, x2 = x + w, y2 = y + h;
    if (x1 < 0) x1 = 0; if (x2 > G3D->width ) x2 = G3D->width ;
    if (y1 < 0) y1 = 0; if (y2 > G3D->height) y2 = G3D->height;
    if ((x1 >= x2) || (y1 >= y2))
      return;

    HaloPoly [0].Set (x1, y1);
    HaloPoly [1].Set (x1, y2);
    HaloPoly [2].Set (x2, y2);
    HaloPoly [3].Set (x2, y1);
  };

  // Draw the halo polygon
  int i, min_i, max_i;
  float min_y, max_y;
  min_i = max_i = 0;
  min_y = max_y = iVertices [0].y;
  for (i = 1; i < iVertCount; i++)
  {
    if (min_y > iVertices [i].y)
    { min_i = i; min_y = iVertices [i].y; }
    if (max_y < iVertices [i].y)
    { max_i = i; max_y = iVertices [i].y; }
  }

  int scanL1, scanL2, scanR1, scanR2;   // scan vertex left/right start/final
  float sxL, sxR, dxL, dxR;             // scanline X left/right and deltas
  int sy, fyL, fyR;                     // scanline Y, final Y left, final Y right

  sxL = sxR = dxL = dxR = 0;
  scanL2 = scanR2 = min_i;
  sy = fyL = fyR = QRound (iVertices [scanL2].y);

  // Shift amount to get pixel address
  int pixel_shift = csLog2 (pfmt.PixelBytes);
  // Compute halo X and Y scale
  float scaleX = w / Width;
  float scaleY = h / Height;
  // Compute rounded top-left halo corner coordinate
  int xTL = QRound (x);
  int yTL = QRound (y);
  // Compute horizontal motion delta
  int delta = QInt16 (scaleX);

  // The halo polygon is counterclockwise (since the clipping polygon is
  // counterclockwise). Now we should draw it from top to bottom.

  for ( ; ; )
  {
    //-----
    // We have reached the next segment. Recalculate the slopes.
    //-----
    bool leave;
    do
    {
      leave = true;
      if (sy >= fyR)
      {
        // Check first if polygon has been finished
        if (scanR2 == max_i)
          return;
        scanR1 = scanR2;
	if (--scanR2 < 0)
	  scanR2 = iVertCount - 1;

        leave = false;
        fyR = QRound (iVertices [scanR2].y);
        if (sy >= fyR)
          continue;

        float dyR = (iVertices [scanR2].y - iVertices [scanR1].y);
        if (dyR)
        {
          sxR = iVertices [scanR1].x;
          dxR = (iVertices [scanR2].x - sxR) / dyR;
          // horizontal pixel correction
          sxR += dxR * (iVertices [scanR1].y - (float (sy) - 0.5));
        } /* endif */
      } /* endif */
      if (sy >= fyL)
      {
        scanL1 = scanL2;
	if (++scanL2 >= iVertCount)
	  scanL2 = 0;

        leave = false;
        fyL = QRound (iVertices [scanL2].y);
        if (sy >= fyL)
          continue;

        float dyL = (iVertices [scanL2].y - iVertices [scanL1].y);
        if (dyL)
        {
          sxL = iVertices [scanL1].x;
          dxL = (iVertices [scanL2].x - sxL) / dyL;
          // horizontal pixel correction
          sxL += dxL * (iVertices [scanL1].y - (float (sy) - 0.5));
        } /* endif */
      } /* endif */
    } while (!leave); /* enddo */

    // Find the trapezoid top
    int fin_y;
    if (fyL < fyR)
      fin_y = fyL;
    else
      fin_y = fyR;

    while (sy < fin_y)
    {
      if ((sy & 1) != G3D->do_interlaced)
      {
        // Compute the rounded screen coordinates of horizontal strip
        int xL = QRound (sxL);
        int xR = QRound (sxR);
        unsigned char *d = G3D->line_table [sy] + (xL << pixel_shift);
        unsigned char *s = Alpha + QRound (scaleY * (sy - yTL)) * Width +
          QRound (scaleX * (xL - xTL));
        dscan (s, d, xR - xL, delta);
      }

      sxL += dxL;
      sxR += dxR;
      sy++;
    } /* endwhile */
  } /* endfor */
}

iHalo *csGraphics3DSoftwareCommon::CreateHalo (float iR, float iG, float iB,
  unsigned char *iAlpha, int iWidth, int iHeight)
{
  // Allow R,G,B in range 0..2
  if (iR > 2.0) iR = 2.0;
  if (iG > 2.0) iG = 2.0;
  if (iB > 2.0) iB = 2.0;
  csSoftHalo *h = new csSoftHalo (iR, iG, iB, iAlpha, iWidth, iHeight, this);
  return h;
}
<commit_msg>Fixed a bug in the halo drawing code of the software renderer. Due to Qrounding the scan could go on for one more line, below the actual height of the halo. I changed those relevant QRound calls to QInt, in order to round down. This skips this line, preventing the appearance of a line with small random dots below a halo.<commit_after>/*
    Copyright (C) 1998 by Jorrit Tyberghein

    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; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "cssysdef.h"
#include "qint.h"
#include "csgeom/math2d.h"
#include "csutil/util.h"
#include "soft_g3d.h"
#include "scan.h"

// The structure that stays behind csHaloHandle
class csSoftHalo : public iHalo
{
  // Halo R,G,B components
  float R, G, B;
  // Halo alpha map
  unsigned char *Alpha;
  // Halo size
  int Width, Height;

  // Software 3D rasterizer
  csGraphics3DSoftwareCommon *G3D;

public:

  DECLARE_IBASE;

  /// Create the halo object
  csSoftHalo (float iR, float iG, float iB, unsigned char *iAlpha,
    int iWidth, int iHeight, csGraphics3DSoftwareCommon *iG3D);

  /// Destroy the halo object
  virtual ~csSoftHalo ();

  //---------------------------------// iHalo interface implementation //-----//
  /// Query halo width
  virtual int GetWidth () { return Width; }

  /// Query halo height
  virtual int GetHeight () { return Height; }

  /// Change halo color
  virtual void SetColor (float &iR, float &iG, float &iB)
  { R = iR; G = iG; B = iB; }

  /// Query halo color
  virtual void GetColor (float &oR, float &oG, float &oB)
  { oR = R; oG = G; oB = B; }

  /**
   * Draw the halo given a center point and an intensity.
   * If either w and/or h is negative, the native width and/or height
   * is used instead. If the halo should be clipped against some
   * polygon, that polygon should be given, otherwise if a NULL pointer
   * is passed, the halo is clipped just against screen bounds.
   */
  virtual void Draw (float x, float y, float w, float h, float iIntensity,
    csVector2 *iVertices, int iVertCount);
};

//----------------// Halo scanline rasterizing functions for all modes //-----//

void halo_dscan_8 (void *src, void *dest, int count, int delta)
{
  unsigned char *A = (unsigned char *)src;
  unsigned char *D = (unsigned char *)dest;
  unsigned char *E = D + count;
  if (delta == 0x10000)
    while (D < E)
    {
      unsigned int a = (*A++ * Scan.FogDensity) & 0x1f00;
      if (a) *D = Scan.Fog8 [(a ^ 0x1f00) | *D];
      D++;
    }
  else
  {
    unsigned int ax = 0;
    while (D < E)
    {
      unsigned int a = (A [ax >> 16] * Scan.FogDensity) & 0x1f00;
      if (a) *D = Scan.Fog8 [(a ^ 0x1f00) | *D];
      D++;
      ax += delta;
    }
  }
}

#define HALO_NAME	halo_dscan_16_555
#define HALO_BPP	16
#define HALO_RM		0x7c00
#define HALO_GM		0x03e0
#define HALO_BM		0x001f
#include "haloscan.inc"

#define HALO_NAME	halo_dscan_16_555_c
#define HALO_BPP	16
#define HALO_RM		0x7c00
#define HALO_GM		0x03e0
#define HALO_BM		0x001f
#define HALO_CLAMP
#include "haloscan.inc"

#define HALO_NAME	halo_dscan_16_565
#define HALO_BPP	16
#define HALO_RM		0xf800
#define HALO_GM		0x07e0
#define HALO_BM		0x001f
#include "haloscan.inc"

#define HALO_NAME	halo_dscan_16_565_c
#define HALO_BPP	16
#define HALO_RM		0xf800
#define HALO_GM		0x07e0
#define HALO_BM		0x001f
#define HALO_CLAMP
#include "haloscan.inc"

#define HALO_NAME	halo_dscan_32
#define HALO_BPP	32
#define HALO_RM		0xff0000
#define HALO_GM		0x00ff00
#define HALO_BM		0x0000ff
#include "haloscan.inc"

#define HALO_NAME	halo_dscan_32_c
#define HALO_BPP	32
#define HALO_RM		0xff0000
#define HALO_GM		0x00ff00
#define HALO_BM		0x0000ff
#define HALO_CLAMP
#include "haloscan.inc"

//----------------------------------------------// Software halo class //-----//

IMPLEMENT_IBASE (csSoftHalo)
  IMPLEMENTS_INTERFACE (iHalo)
IMPLEMENT_IBASE_END

// Create the halo object
csSoftHalo::csSoftHalo (float iR, float iG, float iB, unsigned char *iAlpha,
  int iWidth, int iHeight, csGraphics3DSoftwareCommon *iG3D)
{
  CONSTRUCT_IBASE (NULL);
  R = iR; G = iG; B = iB;
  Alpha = new unsigned char [(Width = iWidth) * (Height = iHeight)];
  memcpy (Alpha, iAlpha, Width * Height);
  (G3D = iG3D)->IncRef ();
}

// Destroy the halo object
csSoftHalo::~csSoftHalo ()
{
  delete [] Alpha;
  G3D->DecRef ();
}

void csSoftHalo::Draw (float x, float y, float w, float h, float iIntensity,
  csVector2 *iVertices, int iVertCount)
{
  // Check if halo is visible
  if (iIntensity <= 0)
    return;
  if (iIntensity > 1)
    iIntensity = 1;

  if (w < 0) w = Width;
  if (h < 0) h = Height;

  // Draw a single scanline of halo
  void (*dscan)(void *src, void *dest, int count, int delta) = 0;
  bool clamp = false;
  const csPixelFormat& pfmt = G3D->pfmt;

  if (pfmt.PixelBytes == 1)
  {
    Scan.FogR = QRound (R * 255);
    Scan.FogG = QRound (G * 255);
    Scan.FogB = QRound (B * 255);
    Scan.Fog8 = G3D->BuildIndexedFogTable ();
    Scan.FogPix = G3D->texman->find_rgb (Scan.FogR, Scan.FogG, Scan.FogB);
    // halo intensity (0..31)
    Scan.FogDensity = QRound (iIntensity * 32);
  }
  else
  {
    Scan.FogR = QRound (R * ((1 << pfmt.RedBits  ) - 1))
      << R8G8B8_SHIFT_ADJUST(pfmt.RedShift);
    Scan.FogG = QRound (G * ((1 << pfmt.GreenBits) - 1))
      << R8G8B8_SHIFT_ADJUST(pfmt.GreenShift);
    Scan.FogB = QRound (B * ((1 << pfmt.BlueBits ) - 1))
      << R8G8B8_SHIFT_ADJUST(pfmt.BlueShift);

    // halo intensity (0..64)
    Scan.FogDensity = QRound (iIntensity * 64);
    // Detect when the halo will possibly overflow
    clamp = (Scan.FogR > R8G8B8_PIXEL_PREPROC(pfmt.RedMask  ))
         || (Scan.FogG > R8G8B8_PIXEL_PREPROC(pfmt.GreenMask))
         || (Scan.FogB > R8G8B8_PIXEL_PREPROC(pfmt.BlueMask ));
  }

  switch (pfmt.PixelBytes)
  {
    case 1:
      dscan = halo_dscan_8;
      break;
    case 2:
      if (pfmt.GreenBits == 6)
      {
        if (clamp)
	  dscan = halo_dscan_16_565_c;
	else
	  dscan = halo_dscan_16_565;
      }
      else
      {
        if (clamp)
	  dscan = halo_dscan_16_555_c;
	else
	  dscan = halo_dscan_16_555;
      }
      break;
    case 4:
    {
      unsigned int rs = R8G8B8_SHIFT_ADJUST(pfmt.RedShift);
      unsigned int gs = R8G8B8_SHIFT_ADJUST(pfmt.GreenShift);
      ULong r = Scan.FogR, g = Scan.FogG, b = Scan.FogB;
      Scan.FogR = (rs == 16) ? r : (gs == 16) ? g : b;
      Scan.FogG = (rs ==  8) ? r : (gs ==  8) ? g : b;
      Scan.FogB = (rs ==  0) ? r : (gs ==  0) ? g : b;
      if (clamp)
        dscan = halo_dscan_32_c;
      else
        dscan = halo_dscan_32;
      break;
    }
    default:
      return;
  }

  csVector2 HaloPoly [4];
  if (!iVertices)
  {
    iVertCount = 4;
    iVertices = HaloPoly;

    float x1 = x, y1 = y, x2 = x + w, y2 = y + h;
    if (x1 < 0) x1 = 0; if (x2 > G3D->width ) x2 = G3D->width ;
    if (y1 < 0) y1 = 0; if (y2 > G3D->height) y2 = G3D->height;
    if ((x1 >= x2) || (y1 >= y2))
      return;

    HaloPoly [0].Set (x1, y1);
    HaloPoly [1].Set (x1, y2);
    HaloPoly [2].Set (x2, y2);
    HaloPoly [3].Set (x2, y1);
  };

  // Draw the halo polygon
  int i, min_i, max_i;
  float min_y, max_y;
  min_i = max_i = 0;
  min_y = max_y = iVertices [0].y;
  for (i = 1; i < iVertCount; i++)
  {
    if (min_y > iVertices [i].y)
    { min_i = i; min_y = iVertices [i].y; }
    if (max_y < iVertices [i].y)
    { max_i = i; max_y = iVertices [i].y; }
  }

  int scanL1, scanL2, scanR1, scanR2;   // scan vertex left/right start/final
  float sxL, sxR, dxL, dxR;             // scanline X left/right and deltas
  int sy, fyL, fyR;                     // scanline Y, final Y left, final Y right

  sxL = sxR = dxL = dxR = 0;
  scanL2 = scanR2 = min_i;
  sy = fyL = fyR = QInt (iVertices [scanL2].y); // round down

  // Shift amount to get pixel address
  int pixel_shift = csLog2 (pfmt.PixelBytes);
  // Compute halo X and Y scale
  float scaleX = w / Width;
  float scaleY = h / Height;
  // Compute rounded top-left halo corner coordinate
  int xTL = QRound (x);
  int yTL = QRound (y);
  // Compute horizontal motion delta
  int delta = QInt16 (scaleX);

  // The halo polygon is counterclockwise (since the clipping polygon is
  // counterclockwise). Now we should draw it from top to bottom.

  for ( ; ; )
  {
    //-----
    // We have reached the next segment. Recalculate the slopes.
    //-----
    bool leave;
    do
    {
      leave = true;
      if (sy >= fyR)
      {
        // Check first if polygon has been finished
        if (scanR2 == max_i)
          return;
        scanR1 = scanR2;
	if (--scanR2 < 0)
	  scanR2 = iVertCount - 1;

        leave = false;
        fyR = QInt (iVertices [scanR2].y);
        if (sy >= fyR)
          continue;

        float dyR = (iVertices [scanR2].y - iVertices [scanR1].y);
        if (dyR)
        {
          sxR = iVertices [scanR1].x;
          dxR = (iVertices [scanR2].x - sxR) / dyR;
          // horizontal pixel correction
          sxR += dxR * (iVertices [scanR1].y - (float (sy) - 0.5));
        } /* endif */
      } /* endif */
      if (sy >= fyL)
      {
        scanL1 = scanL2;
	if (++scanL2 >= iVertCount)
	  scanL2 = 0;

        leave = false;
        fyL = QInt (iVertices [scanL2].y);
        if (sy >= fyL)
          continue;

        float dyL = (iVertices [scanL2].y - iVertices [scanL1].y);
        if (dyL)
        {
          sxL = iVertices [scanL1].x;
          dxL = (iVertices [scanL2].x - sxL) / dyL;
          // horizontal pixel correction
          sxL += dxL * (iVertices [scanL1].y - (float (sy) - 0.5));
        } /* endif */
      } /* endif */
    } while (!leave); /* enddo */

    // Find the trapezoid top
    int fin_y;
    if (fyL < fyR)
      fin_y = fyL;
    else
      fin_y = fyR;

    while (sy < fin_y)
    {
      if ((sy & 1) != G3D->do_interlaced)
      {
        // Compute the rounded screen coordinates of horizontal strip
        int xL = QRound (sxL);
        int xR = QRound (sxR);
        unsigned char *d = G3D->line_table [sy] + (xL << pixel_shift);
        unsigned char *s = Alpha + QRound (scaleY * (sy - yTL)) * Width +
          QRound (scaleX * (xL - xTL));
        dscan (s, d, xR - xL, delta);
      }

      sxL += dxL;
      sxR += dxR;
      sy++;
    } /* endwhile */
  } /* endfor */
}

iHalo *csGraphics3DSoftwareCommon::CreateHalo (float iR, float iG, float iB,
  unsigned char *iAlpha, int iWidth, int iHeight)
{
  // Allow R,G,B in range 0..2
  if (iR > 2.0) iR = 2.0;
  if (iG > 2.0) iG = 2.0;
  if (iB > 2.0) iB = 2.0;
  csSoftHalo *h = new csSoftHalo (iR, iG, iB, iAlpha, iWidth, iHeight, this);
  return h;
}
<|endoftext|>


<commit_before>//
//  pollset_epoll.cc
//

#if defined(__linux__)

#include "plat_poll.h"

#include <cassert>
#include <cstring>
#include <cerrno>
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
#include <unordered_map>

#include <sys/resource.h>

#include "io.h"
#include "log.h"
#include "socket.h"
#include "pollset.h"
#include "pollset_epoll.h"

const int pollset_epoll::max_events = 48;

pollset_epoll::pollset_epoll()
{
    epoll_fd = epoll_create(max_events);
    eevents.resize(max_events);

    struct rlimit rlimit_nofile;
    if (getrlimit(RLIMIT_NOFILE, &rlimit_nofile) < 0) {
        log_fatal_exit("getrlimit(RLIMIT_NOFILE): %s", strerror(errno));
    }

    pollobjects_map.resize(rlimit_nofile.rlim_max);
}

pollset_epoll::~pollset_epoll()
{
    close(epoll_fd);
}

const std::vector<poll_object>& pollset_epoll::get_objects()
{
    pollobjects.resize(0);
    for (auto &ent : pollobjects_map) {
        if (ent.fd != -1) pollobjects.push_back(ent);
    }
    return pollobjects;
}

bool pollset_epoll::add_object(poll_object obj, int events)
{
    int epoll_op;
    auto &ent = pollobjects_map[obj.fd];
    if (ent.fd != -1) {
        epoll_op = EPOLL_CTL_MOD;
    } else {
        epoll_op = EPOLL_CTL_ADD;
        pollobjects_map[obj.fd] = obj;
    }

    struct epoll_event eevt;
    memset(&eevt, 0, sizeof(eevt));
    eevt.events = events;
    eevt.data.fd = obj.fd;

    if (epoll_ctl(epoll_fd, epoll_op, obj.fd, &eevt) < 0) {
        log_error("pollset_epoll:::add_object: obj=%p: %s", obj.ptr, strerror(errno));
        return false;
    }
    return true;
}

bool pollset_epoll::remove_object(poll_object obj)
{
    auto &ent = pollobjects_map[obj.fd];
    if (ent.fd == -1) {
        log_error("pollset_epoll:::remove_object: object not found obj=%p", obj.ptr);
        return false;
    }
    if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, obj.fd, nullptr) < 0) {
        log_error("pollset_epoll:::add_object: obj=%p: %s", obj.ptr, strerror(errno));
        return false;
    }
    pollobjects_map[obj.fd] = poll_object();
    return true;
}

const std::vector<poll_object>& pollset_epoll::do_poll(int timeout)
{
    int nevents = epoll_wait(epoll_fd, &eevents[0], (int)eevents.size(), timeout * 1000);

    if (nevents < 0 && errno != EAGAIN) {
        log_error("pollset_epoll:::do_poll: epoll_wait: %s", strerror(errno));
        return events;
    }

    events.resize(nevents);
    for (int i = 0; i < nevents; i++) {
        struct epoll_event *eevt = &eevents[i];
        events[i] = poll_object(pollobjects_map[eevt->data.fd], eevt->events);
    }
    return events;
}

#endif
<commit_msg>pollset_epoll: return empty events on error<commit_after>//
//  pollset_epoll.cc
//

#if defined(__linux__)

#include "plat_poll.h"

#include <cassert>
#include <cstring>
#include <cerrno>
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
#include <unordered_map>

#include <sys/resource.h>

#include "io.h"
#include "log.h"
#include "socket.h"
#include "pollset.h"
#include "pollset_epoll.h"

const int pollset_epoll::max_events = 48;

pollset_epoll::pollset_epoll()
{
    epoll_fd = epoll_create(max_events);
    eevents.resize(max_events);

    struct rlimit rlimit_nofile;
    if (getrlimit(RLIMIT_NOFILE, &rlimit_nofile) < 0) {
        log_fatal_exit("getrlimit(RLIMIT_NOFILE): %s", strerror(errno));
    }

    pollobjects_map.resize(rlimit_nofile.rlim_max);
}

pollset_epoll::~pollset_epoll()
{
    close(epoll_fd);
}

const std::vector<poll_object>& pollset_epoll::get_objects()
{
    pollobjects.resize(0);
    for (auto &ent : pollobjects_map) {
        if (ent.fd != -1) pollobjects.push_back(ent);
    }
    return pollobjects;
}

bool pollset_epoll::add_object(poll_object obj, int events)
{
    int epoll_op;
    auto &ent = pollobjects_map[obj.fd];
    if (ent.fd != -1) {
        epoll_op = EPOLL_CTL_MOD;
    } else {
        epoll_op = EPOLL_CTL_ADD;
        pollobjects_map[obj.fd] = obj;
    }

    struct epoll_event eevt;
    memset(&eevt, 0, sizeof(eevt));
    eevt.events = events;
    eevt.data.fd = obj.fd;

    if (epoll_ctl(epoll_fd, epoll_op, obj.fd, &eevt) < 0) {
        log_error("pollset_epoll:::add_object: obj=%p: %s", obj.ptr, strerror(errno));
        return false;
    }
    return true;
}

bool pollset_epoll::remove_object(poll_object obj)
{
    auto &ent = pollobjects_map[obj.fd];
    if (ent.fd == -1) {
        log_error("pollset_epoll:::remove_object: object not found obj=%p", obj.ptr);
        return false;
    }
    if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, obj.fd, nullptr) < 0) {
        log_error("pollset_epoll:::add_object: obj=%p: %s", obj.ptr, strerror(errno));
        return false;
    }
    pollobjects_map[obj.fd] = poll_object();
    return true;
}

const std::vector<poll_object>& pollset_epoll::do_poll(int timeout)
{
    int nevents = epoll_wait(epoll_fd, &eevents[0], (int)eevents.size(), timeout * 1000);

    if (nevents < 0 && errno != EAGAIN) {
        log_error("pollset_epoll:::do_poll: epoll_wait: %s", strerror(errno));
        events.resize(0);
        return events;
    }

    events.resize(nevents);
    for (int i = 0; i < nevents; i++) {
        struct epoll_event *eevt = &eevents[i];
        events[i] = poll_object(pollobjects_map[eevt->data.fd], eevt->events);
    }
    return events;
}

#endif
<|endoftext|>


<commit_before>/**
 * @file posix/wait.cpp
 * @brief POSIX event/timeout handling
 *
 * (c) 2013-2014 by Mega Limited, Auckland, New Zealand
 *
 * This file is part of the MEGA SDK - Client Access Engine.
 *
 * Applications using the MEGA API must present a valid application key
 * and comply with the the rules set forth in the Terms of Service.
 *
 * The MEGA SDK 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.
 *
 * @copyright Simplified (2-clause) BSD License.
 *
 * You should have received a copy of the license along with this
 * program.
 */

#include "mega.h"

#ifdef USE_POLL
    #include <poll.h> //poll
#endif

namespace mega {
dstime Waiter::ds;

PosixWaiter::PosixWaiter()
{
    // pipe to be able to leave the select() call
    if (pipe(m_pipe) < 0)
    {
        LOG_fatal << "Error creating pipe";
        throw std::runtime_error("Error creating pipe");
    }

    if (fcntl(m_pipe[0], F_SETFL, O_NONBLOCK) < 0)
    {
        LOG_err << "fcntl error";
    }

    maxfd = -1;
}

PosixWaiter::~PosixWaiter()
{
    close(m_pipe[0]);
    close(m_pipe[1]);
}

void PosixWaiter::init(dstime ds)
{
    Waiter::init(ds);

    maxfd = -1;

    MEGA_FD_ZERO(&rfds);
    MEGA_FD_ZERO(&wfds);
    MEGA_FD_ZERO(&efds);
    MEGA_FD_ZERO(&ignorefds);
}

// update monotonously increasing timestamp in deciseconds
void Waiter::bumpds()
{
    timespec ts;

    m_clock_getmonotonictime(&ts);

    ds = static_cast<dstime>(ts.tv_sec * 10 + ts.tv_nsec / 100000000);
}

// update maxfd for select()
void PosixWaiter::bumpmaxfd(int fd)
{
    if (fd > maxfd)
    {
        maxfd = fd;
    }
}

// checks if an unfiltered fd is set
// FIXME: use bitwise & instead of scanning
bool PosixWaiter::fd_filter(int nfds, mega_fd_set_t* fds, mega_fd_set_t* ignorefds) const
{
    while (nfds--)
    {
        if (MEGA_FD_ISSET(nfds, fds) && !MEGA_FD_ISSET(nfds, ignorefds)) return true;
    }

    return false;
}

// wait for supplied events (sockets, filesystem changes), plus timeout + application events
// maxds specifies the maximum amount of time to wait in deciseconds (or ~0 if no timeout scheduled)
// returns application-specific bitmask. bit 0 set indicates that exec() needs to be called.
int PosixWaiter::wait()
{
    int numfd = 0;
    timeval tv;

    //Pipe added to rfds to be able to leave select() when needed
    MEGA_FD_SET(m_pipe[0], &rfds);

    bumpmaxfd(m_pipe[0]);

    if (maxds + 1)
    {
        dstime us = 1000000 / 10 * maxds;

        tv.tv_sec = us / 1000000;
        tv.tv_usec = us - tv.tv_sec * 1000000;
    }

    bumpds();
    auto ds_before = ds;

#ifdef USE_POLL
    dstime ms = 1000 / 10 * maxds;

    auto total = rfds.size() +  wfds.size() +  efds.size();
    struct pollfd fds[total];

    int polli = 0;
    for (auto & fd : rfds)
    {
        fds[polli].fd = fd;
        fds[polli].events = POLLIN_SET;
        polli++;
    }

    for (auto & fd : wfds)
    {
        fds[polli].fd = fd;
        fds[polli].events = POLLOUT_SET;
        polli++;
    }

    for (auto & fd : efds)
    {
        fds[polli].fd = fd;
        fds[polli].events = POLLEX_SET;
        polli++;
    }

    numfd = poll(fds, total,  ms);
#else
    numfd = select(maxfd + 1, &rfds, &wfds, &efds, maxds + 1 ? &tv : NULL);
#endif

    // empty pipe
    uint8_t buf;
    bool external = false;

    {
        std::lock_guard<std::mutex> g(mMutex);
        while (read(m_pipe[0], &buf, sizeof buf) > 0)
        {
            external = true;
        }
        alreadyNotified = false;
    }

    // timeout or error
    if (external || numfd <= 0)
    {
        retval = NEEDEXEC;
    }
    else
    {
    // request exec() to be run only if a non-ignored fd was triggered
#ifdef USE_POLL
    for (unsigned int i = 0 ; i < total ; i++)
    {
        if  ((fds[i].revents & (POLLIN_SET | POLLOUT_SET | POLLEX_SET) )  && !MEGA_FD_ISSET(fds[i].fd, &ignorefds) )
        {
            return NEEDEXEC;
        }
    }
    int retval = 0;
#else
    int retval = (fd_filter(maxfd + 1, &rfds, &ignorefds)
         || fd_filter(maxfd + 1, &wfds, &ignorefds)
         || fd_filter(maxfd + 1, &efds, &ignorefds)) ? NEEDEXEC : 0;

#endif
    }

    if (ds_before > last_exec_ds + 5)
    {
        // report what the waiter is doing, if it's been more than 0.5 second since exec()
        bumpds();
        LOG_debug << "Waiter waited " << (ds - ds_before) << "ds, for maxds: " << maxds << ", will return " << retval << ". maxfd: " << maxfd << " numfd: " << numfd << " external: " << external << " now: " << ds;

        int nfds = maxfd + 1;
        while (nfds--)
        {
            if (MEGA_FD_ISSET(nfds, ignorefds)) LOG_debug << " ignored socket " << nfds;
        }

        // don't report more often than that once per 0.5 second though
        last_exec_ds = ds;
    }

    return retval;
}

void PosixWaiter::notify()
{
    std::lock_guard<std::mutex> g(mMutex);
    if (!alreadyNotified)
    {
        write(m_pipe[1], "0", 1);
        alreadyNotified = true;
    }
}
} // namespace
<commit_msg>Adjust for waiter to report retval periodically during exec gaps<commit_after>/**
 * @file posix/wait.cpp
 * @brief POSIX event/timeout handling
 *
 * (c) 2013-2014 by Mega Limited, Auckland, New Zealand
 *
 * This file is part of the MEGA SDK - Client Access Engine.
 *
 * Applications using the MEGA API must present a valid application key
 * and comply with the the rules set forth in the Terms of Service.
 *
 * The MEGA SDK 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.
 *
 * @copyright Simplified (2-clause) BSD License.
 *
 * You should have received a copy of the license along with this
 * program.
 */

#include "mega.h"

#ifdef USE_POLL
    #include <poll.h> //poll
#endif

namespace mega {
dstime Waiter::ds;

PosixWaiter::PosixWaiter()
{
    // pipe to be able to leave the select() call
    if (pipe(m_pipe) < 0)
    {
        LOG_fatal << "Error creating pipe";
        throw std::runtime_error("Error creating pipe");
    }

    if (fcntl(m_pipe[0], F_SETFL, O_NONBLOCK) < 0)
    {
        LOG_err << "fcntl error";
    }

    maxfd = -1;
}

PosixWaiter::~PosixWaiter()
{
    close(m_pipe[0]);
    close(m_pipe[1]);
}

void PosixWaiter::init(dstime ds)
{
    Waiter::init(ds);

    maxfd = -1;

    MEGA_FD_ZERO(&rfds);
    MEGA_FD_ZERO(&wfds);
    MEGA_FD_ZERO(&efds);
    MEGA_FD_ZERO(&ignorefds);
}

// update monotonously increasing timestamp in deciseconds
void Waiter::bumpds()
{
    timespec ts;

    m_clock_getmonotonictime(&ts);

    ds = static_cast<dstime>(ts.tv_sec * 10 + ts.tv_nsec / 100000000);
}

// update maxfd for select()
void PosixWaiter::bumpmaxfd(int fd)
{
    if (fd > maxfd)
    {
        maxfd = fd;
    }
}

// checks if an unfiltered fd is set
// FIXME: use bitwise & instead of scanning
bool PosixWaiter::fd_filter(int nfds, mega_fd_set_t* fds, mega_fd_set_t* ignorefds) const
{
    while (nfds--)
    {
        if (MEGA_FD_ISSET(nfds, fds) && !MEGA_FD_ISSET(nfds, ignorefds)) return true;
    }

    return false;
}

// wait for supplied events (sockets, filesystem changes), plus timeout + application events
// maxds specifies the maximum amount of time to wait in deciseconds (or ~0 if no timeout scheduled)
// returns application-specific bitmask. bit 0 set indicates that exec() needs to be called.
int PosixWaiter::wait()
{
    int numfd = 0;
    timeval tv;

    //Pipe added to rfds to be able to leave select() when needed
    MEGA_FD_SET(m_pipe[0], &rfds);

    bumpmaxfd(m_pipe[0]);

    if (maxds + 1)
    {
        dstime us = 1000000 / 10 * maxds;

        tv.tv_sec = us / 1000000;
        tv.tv_usec = us - tv.tv_sec * 1000000;
    }

    bumpds();
    auto ds_before = ds;

#ifdef USE_POLL
    dstime ms = 1000 / 10 * maxds;

    auto total = rfds.size() +  wfds.size() +  efds.size();
    struct pollfd fds[total];

    int polli = 0;
    for (auto & fd : rfds)
    {
        fds[polli].fd = fd;
        fds[polli].events = POLLIN_SET;
        polli++;
    }

    for (auto & fd : wfds)
    {
        fds[polli].fd = fd;
        fds[polli].events = POLLOUT_SET;
        polli++;
    }

    for (auto & fd : efds)
    {
        fds[polli].fd = fd;
        fds[polli].events = POLLEX_SET;
        polli++;
    }

    numfd = poll(fds, total,  ms);
#else
    numfd = select(maxfd + 1, &rfds, &wfds, &efds, maxds + 1 ? &tv : NULL);
#endif

    // empty pipe
    uint8_t buf;
    bool external = false;

    {
        std::lock_guard<std::mutex> g(mMutex);
        while (read(m_pipe[0], &buf, sizeof buf) > 0)
        {
            external = true;
        }
        alreadyNotified = false;
    }

    // timeout or error
    int retval;
    if (external || numfd <= 0)
    {
        retval = NEEDEXEC;
    }
    else
    {
    // request exec() to be run only if a non-ignored fd was triggered
#ifdef USE_POLL
    for (unsigned int i = 0 ; i < total ; i++)
    {
        if  ((fds[i].revents & (POLLIN_SET | POLLOUT_SET | POLLEX_SET) )  && !MEGA_FD_ISSET(fds[i].fd, &ignorefds) )
        {
            return NEEDEXEC;
        }
    }
    retval = 0;
#else
    retval = (fd_filter(maxfd + 1, &rfds, &ignorefds)
         || fd_filter(maxfd + 1, &wfds, &ignorefds)
         || fd_filter(maxfd + 1, &efds, &ignorefds)) ? NEEDEXEC : 0;

#endif
    }

    if (ds_before > last_exec_ds + 5)
    {
        // report what the waiter is doing, if it's been more than 0.5 second since exec()
        bumpds();
        LOG_debug << "Waiter waited " << (ds - ds_before) << "ds, for maxds: " << maxds << ", will return " << retval << ". maxfd: " << maxfd << " numfd: " << numfd << " external: " << external << " now: " << ds;

        int nfds = maxfd + 1;
        while (nfds--)
        {
            if (MEGA_FD_ISSET(nfds, ignorefds)) LOG_debug << " ignored socket " << nfds;
        }

        // don't report more often than that once per 0.5 second though
        last_exec_ds = ds;
    }

    return retval;
}

void PosixWaiter::notify()
{
    std::lock_guard<std::mutex> g(mMutex);
    if (!alreadyNotified)
    {
        write(m_pipe[1], "0", 1);
        alreadyNotified = true;
    }
}
} // namespace
<|endoftext|>