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

  Program:   Visualization Toolkit
  Module:    vtkTimerLog.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 1993-1998 Ken Martin, Will Schroeder, Bill Lorensen.

This software is copyrighted by Ken Martin, Will Schroeder and Bill Lorensen.
The following terms apply to all files associated with the software unless
explicitly disclaimed in individual files. This copyright specifically does
not apply to the related textbook "The Visualization Toolkit" ISBN
013199837-4 published by Prentice Hall which is covered by its own copyright.

The authors hereby grant permission to use, copy, and distribute this
software and its documentation for any purpose, provided that existing
copyright notices are retained in all copies and that this notice is included
verbatim in any distributions. Additionally, the authors grant permission to
modify this software and its documentation for any purpose, provided that
such modifications are not distributed without the explicit consent of the
authors and that existing copyright notices are retained in all copies. Some
of the algorithms implemented by this software are patented, observe all
applicable patent law.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=========================================================================*/
// .NAME vtkTimerLog - Maintains timing table for performance analysis
// .SECTION Description
// vtkTimerLog contains walltime and cputime measurements associated
// with a given event.  These results can be later analyzed when
// "dumping out" the table.
//
// In addition, vtkTimerLog allows the user to simply get the current
// time, and to start/stop a simple timer separate from the timing
// table logging.

#include "vtkTimerLog.h"
#ifndef _WIN32
#include <limits.h>     // for CLK_TCK
#endif

// initialze the class variables
int vtkTimerLog::MaxEntries = 100;
int vtkTimerLog::NextEntry = 0;
int vtkTimerLog::WrapFlag = 0;
vtkTimerLogEntry *vtkTimerLog::TimerLog = NULL;
#ifdef CLK_TCK
int vtkTimerLog::TicksPerSecond = CLK_TCK;
#else
int vtkTimerLog::TicksPerSecond = 60;
#endif

#ifdef _WIN32
timeb vtkTimerLog::FirstWallTime;
timeb vtkTimerLog::CurrentWallTime;
#else
timeval vtkTimerLog::FirstWallTime;
timeval vtkTimerLog::CurrentWallTime;
tms     vtkTimerLog::FirstCpuTicks;
tms     vtkTimerLog::CurrentCpuTicks;
#endif

// Description:
// Allocate timing table with MaxEntries elements.
void vtkTimerLog::AllocateLog()
{
  if (vtkTimerLog::TimerLog != NULL) delete [] vtkTimerLog::TimerLog;
  vtkTimerLog::TimerLog = new vtkTimerLogEntry[vtkTimerLog::MaxEntries];
}

// Description:
// Clear the timing table.  walltime and cputime will also be set
// to zero when the first new event is recorded.
void vtkTimerLog::ResetLog()
{
  vtkTimerLog::WrapFlag = 0;
  vtkTimerLog::NextEntry = 0;
  // may want to free TimerLog to force realloc so
  // that user can resize the table by changing MaxEntries.
}

// Description:
// Record a timing event.  The event is represented by a formatted
// string.
void vtkTimerLog::FormatAndMarkEvent(char *format, ...)
{
static  char event[4096];

  va_list var_args;
  va_start(var_args, format);
  vsprintf(event, format, var_args);
  va_end(var_args);

  vtkTimerLog::MarkEvent(event);
}

// Description:
// Record a timing event and capture walltime and cputicks.
void vtkTimerLog::MarkEvent(char *event)
{
  int strsize;
  double time_diff;
  int ticks_diff;

  strsize = (strlen(event)) > VTK_LOG_EVENT_LENGTH - 1 ? VTK_LOG_EVENT_LENGTH-1 : strlen(event);

  // If this the first event we're recording, allocate the
  // internal timing table and initialize WallTime and CpuTicks
  // for this first event to zero.
  if (vtkTimerLog::NextEntry == 0 && ! vtkTimerLog::WrapFlag)
    {
    if (vtkTimerLog::TimerLog == NULL)
      {
      vtkTimerLog::AllocateLog();
      }

#ifdef _WIN32
    ftime( &(vtkTimerLog::FirstWallTime) );
#else
    gettimeofday( &(vtkTimerLog::FirstWallTime), NULL );
    times(&FirstCpuTicks);
#endif

    TimerLog[0].WallTime = 0.0;
    TimerLog[0].CpuTicks = 0;
    strncpy(TimerLog[0].Event, event, strsize);
    TimerLog[0].Event[strsize] = '\0';
    NextEntry = 1;
    return;
    }

#ifdef _WIN32
  static double scale = 1.0/1000.0;
  ftime( &(vtkTimerLog::CurrentWallTime) );
  time_diff  =  vtkTimerLog::CurrentWallTime.time - vtkTimerLog::FirstWallTime.time;
  time_diff +=
    (vtkTimerLog::CurrentWallTime.millitm - vtkTimerLog::FirstWallTime.millitm) * scale;
  ticks_diff = 0;
#else
  static double scale = 1.0/1000000.0;
  gettimeofday( &(vtkTimerLog::CurrentWallTime), NULL );
  time_diff  =  vtkTimerLog::CurrentWallTime.tv_sec - vtkTimerLog::FirstWallTime.tv_sec;
  time_diff +=
    (vtkTimerLog::CurrentWallTime.tv_usec - vtkTimerLog::FirstWallTime.tv_usec) * scale;

  times(&CurrentCpuTicks);
  ticks_diff = (CurrentCpuTicks.tms_utime + CurrentCpuTicks.tms_stime) -
                (FirstCpuTicks.tms_utime + FirstCpuTicks.tms_stime);
#endif

  TimerLog[NextEntry].WallTime = (float)time_diff;
  TimerLog[NextEntry].CpuTicks = ticks_diff;
  strncpy(TimerLog[NextEntry].Event, event, strsize);
  TimerLog[NextEntry].Event[strsize] = '\0';

  NextEntry++;
  if (NextEntry == MaxEntries)
    {
    NextEntry = 0;
    WrapFlag = 1;
    }
}

// Description:
// Write the timing table out to a file.  Calculate some helpful
// statistics (deltas and  percentages) in the process.
void vtkTimerLog::DumpLog(char *filename)
{
  ofstream os(filename);
  int i;

  os << " Entry   Wall Time (sec)  Delta   CPU Time (sec)  Delta  %CPU   Event\n";
  os << "----------------------------------------------------------------------\n";

  if ( WrapFlag )
    {
    DumpEntry(os, 0, TimerLog[NextEntry].WallTime, 0,
              TimerLog[NextEntry].CpuTicks, 0, TimerLog[NextEntry].Event);
    for (i=NextEntry+1; i<MaxEntries; i++)
      {
      DumpEntry(os, i-NextEntry, TimerLog[i].WallTime,
                TimerLog[i].WallTime - TimerLog[i-1].WallTime,
                TimerLog[i].CpuTicks,
                TimerLog[i].CpuTicks - TimerLog[i-1].CpuTicks,
                TimerLog[i].Event);
      }
    DumpEntry(os, MaxEntries-NextEntry, TimerLog[0].WallTime,
              TimerLog[0].WallTime - TimerLog[MaxEntries-1].WallTime,
              TimerLog[0].CpuTicks,
              TimerLog[0].CpuTicks - TimerLog[MaxEntries-1].CpuTicks,
              TimerLog[0].Event);
    for (i=1; i<NextEntry; i++)
      {
      DumpEntry(os, MaxEntries-NextEntry+i, TimerLog[i].WallTime,
                TimerLog[i].WallTime - TimerLog[i-1].WallTime,
                TimerLog[i].CpuTicks,
                TimerLog[i].CpuTicks - TimerLog[i-1].CpuTicks,
                TimerLog[i].Event);
      }
    }
  else
    {
    DumpEntry(os, 0, TimerLog[0].WallTime, 0,
              TimerLog[0].CpuTicks, 0, TimerLog[0].Event);
    for (i=1; i<NextEntry; i++)
      {
      DumpEntry(os, i, TimerLog[i].WallTime,
                TimerLog[i].WallTime - TimerLog[i-1].WallTime,
                TimerLog[i].CpuTicks,
                TimerLog[i].CpuTicks - TimerLog[i-1].CpuTicks,
                TimerLog[i].Event);
      }
    }

  os.close();
}

// Description:
// Print method for vtkTimerLog.
void vtkTimerLog::PrintSelf(ostream& os, vtkIndent indent)
{
  int i;

  vtkObject::PrintSelf(os, indent);

  os << indent << "MaxEntries: " << vtkTimerLog::MaxEntries << "\n";
  os << indent << "NextEntry: " << vtkTimerLog::NextEntry << "\n";
  os << indent << "WrapFlag: " << vtkTimerLog::WrapFlag << "\n";
  os << indent << "TicksPerSecond: " << vtkTimerLog::TicksPerSecond << "\n";
  os << "\n";

  os << indent << "Entry \tWall Time\tCpuTicks\tEvent\n";
  os << indent << "----------------------------------------------\n";

  if ( WrapFlag )
    {
    for (i=NextEntry; i<MaxEntries; i++)
      {
      os << indent << i << "\t\t" << TimerLog[i].WallTime << "\t\t" <<
	TimerLog[i].CpuTicks << "\t\t" << TimerLog[i].Event << "\n";
      }
    }

  for (i=0; i<NextEntry; i++)
    {
    os << indent << i << "\t\t" << TimerLog[i].WallTime << "\t\t" <<
      TimerLog[i].CpuTicks << "\t\t" << TimerLog[i].Event << "\n";
    }

  os << "\n" << indent << "StartTime: " << this->StartTime << "\n";
  os << indent << "WrapFlag: " << vtkTimerLog::WrapFlag << "\n";
}

// Methods to support simple timer functionality, separate from
// timer table logging.

// Description:
// Returns the elapsed number of seconds since January 1, 1970. This
// is also called Universal Coordinated Time.
double vtkTimerLog::GetCurrentTime()
{
  double currentTimeInSeconds;

#ifdef _WIN32
  timeb CurrentTime;
  static double scale = 1.0/1000.0;
  ftime( &CurrentTime );
  currentTimeInSeconds = CurrentTime.time + scale * CurrentTime.millitm;
#else
  timeval CurrentTime;
  static double scale = 1.0/1000000.0;
  gettimeofday( &CurrentTime, NULL );
  currentTimeInSeconds = CurrentTime.tv_sec + scale * CurrentTime.tv_usec;
#endif

  return (currentTimeInSeconds);
}

// Description:
// Set the StartTime to the current time. Used with GetElapsedTime().
void vtkTimerLog::StartTimer()
{
  this->StartTime = vtkTimerLog::GetCurrentTime();
}

// Description:
// Sets EndTime to the current time. Used with GetElapsedTime().
void vtkTimerLog::StopTimer()
{
  this->EndTime = vtkTimerLog::GetCurrentTime();
}

// Description:
// Returns the difference between StartTime and EndTime as
// a floating point value indicating the elapsed time in seconds.
double vtkTimerLog::GetElapsedTime()
{
  return (this->EndTime - this->StartTime);
}

void vtkTimerLog::DumpEntry(ostream& os, int index, float time,
			    float deltatime,
			    int tick, int deltatick, char *event)
{
  os << index << "   "
     << time << "  "
     << deltatime << "   "
     << (float)tick/TicksPerSecond << "  "
     << (float)deltatick/TicksPerSecond << "  ";
  if (deltatime == 0.0)
    os << "0.0   ";
  else
    os << 100.0*deltatick/TicksPerSecond/deltatime << "   ";
  os << event << "\n";
}

void vtkTimerLog::SetMaxEntries(int a)
{
  vtkTimerLog::MaxEntries = a;
}

int vtkTimerLog::GetMaxEntries()
{
  return vtkTimerLog::MaxEntries;
}
<commit_msg>ENH: added :: scope to ftime calls<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkTimerLog.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 1993-1998 Ken Martin, Will Schroeder, Bill Lorensen.

This software is copyrighted by Ken Martin, Will Schroeder and Bill Lorensen.
The following terms apply to all files associated with the software unless
explicitly disclaimed in individual files. This copyright specifically does
not apply to the related textbook "The Visualization Toolkit" ISBN
013199837-4 published by Prentice Hall which is covered by its own copyright.

The authors hereby grant permission to use, copy, and distribute this
software and its documentation for any purpose, provided that existing
copyright notices are retained in all copies and that this notice is included
verbatim in any distributions. Additionally, the authors grant permission to
modify this software and its documentation for any purpose, provided that
such modifications are not distributed without the explicit consent of the
authors and that existing copyright notices are retained in all copies. Some
of the algorithms implemented by this software are patented, observe all
applicable patent law.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=========================================================================*/
// .NAME vtkTimerLog - Maintains timing table for performance analysis
// .SECTION Description
// vtkTimerLog contains walltime and cputime measurements associated
// with a given event.  These results can be later analyzed when
// "dumping out" the table.
//
// In addition, vtkTimerLog allows the user to simply get the current
// time, and to start/stop a simple timer separate from the timing
// table logging.

#include "vtkTimerLog.h"
#ifndef _WIN32
#include <limits.h>     // for CLK_TCK
#endif

// initialze the class variables
int vtkTimerLog::MaxEntries = 100;
int vtkTimerLog::NextEntry = 0;
int vtkTimerLog::WrapFlag = 0;
vtkTimerLogEntry *vtkTimerLog::TimerLog = NULL;
#ifdef CLK_TCK
int vtkTimerLog::TicksPerSecond = CLK_TCK;
#else
int vtkTimerLog::TicksPerSecond = 60;
#endif

#ifdef _WIN32
timeb vtkTimerLog::FirstWallTime;
timeb vtkTimerLog::CurrentWallTime;
#else
timeval vtkTimerLog::FirstWallTime;
timeval vtkTimerLog::CurrentWallTime;
tms     vtkTimerLog::FirstCpuTicks;
tms     vtkTimerLog::CurrentCpuTicks;
#endif

// Description:
// Allocate timing table with MaxEntries elements.
void vtkTimerLog::AllocateLog()
{
  if (vtkTimerLog::TimerLog != NULL) delete [] vtkTimerLog::TimerLog;
  vtkTimerLog::TimerLog = new vtkTimerLogEntry[vtkTimerLog::MaxEntries];
}

// Description:
// Clear the timing table.  walltime and cputime will also be set
// to zero when the first new event is recorded.
void vtkTimerLog::ResetLog()
{
  vtkTimerLog::WrapFlag = 0;
  vtkTimerLog::NextEntry = 0;
  // may want to free TimerLog to force realloc so
  // that user can resize the table by changing MaxEntries.
}

// Description:
// Record a timing event.  The event is represented by a formatted
// string.
void vtkTimerLog::FormatAndMarkEvent(char *format, ...)
{
static  char event[4096];

  va_list var_args;
  va_start(var_args, format);
  vsprintf(event, format, var_args);
  va_end(var_args);

  vtkTimerLog::MarkEvent(event);
}

// Description:
// Record a timing event and capture walltime and cputicks.
void vtkTimerLog::MarkEvent(char *event)
{
  int strsize;
  double time_diff;
  int ticks_diff;

  strsize = (strlen(event)) > VTK_LOG_EVENT_LENGTH - 1 ? VTK_LOG_EVENT_LENGTH-1 : strlen(event);

  // If this the first event we're recording, allocate the
  // internal timing table and initialize WallTime and CpuTicks
  // for this first event to zero.
  if (vtkTimerLog::NextEntry == 0 && ! vtkTimerLog::WrapFlag)
    {
    if (vtkTimerLog::TimerLog == NULL)
      {
      vtkTimerLog::AllocateLog();
      }

#ifdef _WIN32
    ::ftime( &(vtkTimerLog::FirstWallTime) );
#else
    gettimeofday( &(vtkTimerLog::FirstWallTime), NULL );
    times(&FirstCpuTicks);
#endif

    TimerLog[0].WallTime = 0.0;
    TimerLog[0].CpuTicks = 0;
    strncpy(TimerLog[0].Event, event, strsize);
    TimerLog[0].Event[strsize] = '\0';
    NextEntry = 1;
    return;
    }

#ifdef _WIN32
  static double scale = 1.0/1000.0;
  ::ftime( &(vtkTimerLog::CurrentWallTime) );
  time_diff  =  vtkTimerLog::CurrentWallTime.time - vtkTimerLog::FirstWallTime.time;
  time_diff +=
    (vtkTimerLog::CurrentWallTime.millitm - vtkTimerLog::FirstWallTime.millitm) * scale;
  ticks_diff = 0;
#else
  static double scale = 1.0/1000000.0;
  gettimeofday( &(vtkTimerLog::CurrentWallTime), NULL );
  time_diff  =  vtkTimerLog::CurrentWallTime.tv_sec - vtkTimerLog::FirstWallTime.tv_sec;
  time_diff +=
    (vtkTimerLog::CurrentWallTime.tv_usec - vtkTimerLog::FirstWallTime.tv_usec) * scale;

  times(&CurrentCpuTicks);
  ticks_diff = (CurrentCpuTicks.tms_utime + CurrentCpuTicks.tms_stime) -
                (FirstCpuTicks.tms_utime + FirstCpuTicks.tms_stime);
#endif

  TimerLog[NextEntry].WallTime = (float)time_diff;
  TimerLog[NextEntry].CpuTicks = ticks_diff;
  strncpy(TimerLog[NextEntry].Event, event, strsize);
  TimerLog[NextEntry].Event[strsize] = '\0';

  NextEntry++;
  if (NextEntry == MaxEntries)
    {
    NextEntry = 0;
    WrapFlag = 1;
    }
}

// Description:
// Write the timing table out to a file.  Calculate some helpful
// statistics (deltas and  percentages) in the process.
void vtkTimerLog::DumpLog(char *filename)
{
  ofstream os(filename);
  int i;

  os << " Entry   Wall Time (sec)  Delta   CPU Time (sec)  Delta  %CPU   Event\n";
  os << "----------------------------------------------------------------------\n";

  if ( WrapFlag )
    {
    DumpEntry(os, 0, TimerLog[NextEntry].WallTime, 0,
              TimerLog[NextEntry].CpuTicks, 0, TimerLog[NextEntry].Event);
    for (i=NextEntry+1; i<MaxEntries; i++)
      {
      DumpEntry(os, i-NextEntry, TimerLog[i].WallTime,
                TimerLog[i].WallTime - TimerLog[i-1].WallTime,
                TimerLog[i].CpuTicks,
                TimerLog[i].CpuTicks - TimerLog[i-1].CpuTicks,
                TimerLog[i].Event);
      }
    DumpEntry(os, MaxEntries-NextEntry, TimerLog[0].WallTime,
              TimerLog[0].WallTime - TimerLog[MaxEntries-1].WallTime,
              TimerLog[0].CpuTicks,
              TimerLog[0].CpuTicks - TimerLog[MaxEntries-1].CpuTicks,
              TimerLog[0].Event);
    for (i=1; i<NextEntry; i++)
      {
      DumpEntry(os, MaxEntries-NextEntry+i, TimerLog[i].WallTime,
                TimerLog[i].WallTime - TimerLog[i-1].WallTime,
                TimerLog[i].CpuTicks,
                TimerLog[i].CpuTicks - TimerLog[i-1].CpuTicks,
                TimerLog[i].Event);
      }
    }
  else
    {
    DumpEntry(os, 0, TimerLog[0].WallTime, 0,
              TimerLog[0].CpuTicks, 0, TimerLog[0].Event);
    for (i=1; i<NextEntry; i++)
      {
      DumpEntry(os, i, TimerLog[i].WallTime,
                TimerLog[i].WallTime - TimerLog[i-1].WallTime,
                TimerLog[i].CpuTicks,
                TimerLog[i].CpuTicks - TimerLog[i-1].CpuTicks,
                TimerLog[i].Event);
      }
    }

  os.close();
}

// Description:
// Print method for vtkTimerLog.
void vtkTimerLog::PrintSelf(ostream& os, vtkIndent indent)
{
  int i;

  vtkObject::PrintSelf(os, indent);

  os << indent << "MaxEntries: " << vtkTimerLog::MaxEntries << "\n";
  os << indent << "NextEntry: " << vtkTimerLog::NextEntry << "\n";
  os << indent << "WrapFlag: " << vtkTimerLog::WrapFlag << "\n";
  os << indent << "TicksPerSecond: " << vtkTimerLog::TicksPerSecond << "\n";
  os << "\n";

  os << indent << "Entry \tWall Time\tCpuTicks\tEvent\n";
  os << indent << "----------------------------------------------\n";

  if ( WrapFlag )
    {
    for (i=NextEntry; i<MaxEntries; i++)
      {
      os << indent << i << "\t\t" << TimerLog[i].WallTime << "\t\t" <<
	TimerLog[i].CpuTicks << "\t\t" << TimerLog[i].Event << "\n";
      }
    }

  for (i=0; i<NextEntry; i++)
    {
    os << indent << i << "\t\t" << TimerLog[i].WallTime << "\t\t" <<
      TimerLog[i].CpuTicks << "\t\t" << TimerLog[i].Event << "\n";
    }

  os << "\n" << indent << "StartTime: " << this->StartTime << "\n";
  os << indent << "WrapFlag: " << vtkTimerLog::WrapFlag << "\n";
}

// Methods to support simple timer functionality, separate from
// timer table logging.

// Description:
// Returns the elapsed number of seconds since January 1, 1970. This
// is also called Universal Coordinated Time.
double vtkTimerLog::GetCurrentTime()
{
  double currentTimeInSeconds;

#ifdef _WIN32
  timeb CurrentTime;
  static double scale = 1.0/1000.0;
  ::ftime( &CurrentTime );
  currentTimeInSeconds = CurrentTime.time + scale * CurrentTime.millitm;
#else
  timeval CurrentTime;
  static double scale = 1.0/1000000.0;
  gettimeofday( &CurrentTime, NULL );
  currentTimeInSeconds = CurrentTime.tv_sec + scale * CurrentTime.tv_usec;
#endif

  return (currentTimeInSeconds);
}

// Description:
// Set the StartTime to the current time. Used with GetElapsedTime().
void vtkTimerLog::StartTimer()
{
  this->StartTime = vtkTimerLog::GetCurrentTime();
}

// Description:
// Sets EndTime to the current time. Used with GetElapsedTime().
void vtkTimerLog::StopTimer()
{
  this->EndTime = vtkTimerLog::GetCurrentTime();
}

// Description:
// Returns the difference between StartTime and EndTime as
// a floating point value indicating the elapsed time in seconds.
double vtkTimerLog::GetElapsedTime()
{
  return (this->EndTime - this->StartTime);
}

void vtkTimerLog::DumpEntry(ostream& os, int index, float time,
			    float deltatime,
			    int tick, int deltatick, char *event)
{
  os << index << "   "
     << time << "  "
     << deltatime << "   "
     << (float)tick/TicksPerSecond << "  "
     << (float)deltatick/TicksPerSecond << "  ";
  if (deltatime == 0.0)
    os << "0.0   ";
  else
    os << 100.0*deltatick/TicksPerSecond/deltatime << "   ";
  os << event << "\n";
}

void vtkTimerLog::SetMaxEntries(int a)
{
  vtkTimerLog::MaxEntries = a;
}

int vtkTimerLog::GetMaxEntries()
{
  return vtkTimerLog::MaxEntries;
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#ifndef SPIRIT_LEXER_H
#define SPIRIT_LEXER_H

#include <fstream>
#include <string>
#include <utility>
#include <stack>

#include <boost/spirit/include/classic_position_iterator.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_functor_parser.hpp>
#include <boost/spirit/include/classic_attribute.hpp>
#include <boost/spirit/include/classic_symbols.hpp>

namespace eddic {

namespace lexer {

namespace spirit = boost::spirit;
namespace lex = boost::spirit::lex;

/*!
 * \class SimpleLexer
 * \brief The EDDI lexer.
 *
 * This class is used to do lexical analysis on an EDDI source file. This file is based on a Boost Spirit Lexer. It's
 * used by the parser to parse a source file.
 */
template<typename L>
class SpiritLexer : public lex::lexer<L> {
    public:
        SpiritLexer() {
            //Define keywords
            for_ = "for";
            while_ = "while";
            do_ = "do";
            if_ = "if";
            else_ = "else";
            false_ = "false";
            true_ = "true";
            from_ = "from";
            to_ = "to";
            foreach_ = "foreach";
            in_ = "in";
            return_ = "return";
            const_ = "const";
            include = "include";

            identifier = "[a-zA-Z_]?[a-zA-Z0-9_]+";
            float_ = "[0-9]+\".\"[0-9]+";
            integer = "[0-9]+";
            litteral = "\\\"[^\\\"]*\\\"";

            left_parenth = '(';
            right_parenth = ')';
            left_brace = '{';
            right_brace = '}';
            left_bracket = '[';
            right_bracket = ']';

            stop = ';';
            comma = ',';

            /* Assignment operators */
            swap = "<=>";
            assign = '=';

            /* compound assignment operators */
            compound_add = "\\+=";
            compound_sub = "-=";
            compound_mul = "\\*=";
            compound_div = "\\/=";
            compound_mod = "%=";

            /* Math operators  */
            addition = '+';
            subtraction = '-';
            multiplication = '*';
            division = '/';
            modulo = '%';

            /* Suffix and prefix math operators  */
            increment = "\\+\\+";
            decrement = "--";

            /* Logical operators */
            and_ = "\\&\\&";
            or_ = "\\|\\|";

            /* Relational operators  */
            equals = "==";
            not_equals = "!=";
            greater = ">";
            less = "<";
            greater_equals = ">=";
            less_equals = "<=";

            whitespaces = "[ \\t\\n]+";
            multiline_comment = "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/";
            singleline_comment = "\\/\\/[^\n]*";

            //Ignore whitespaces
            this->self += whitespaces [lex::_pass = lex::pass_flags::pass_ignore];

            this->self += left_parenth | right_parenth | left_brace | right_brace | left_bracket | right_bracket;
            this->self += comma | stop;
            this->self += assign | swap;
            this->self += compound_add | compound_sub | compound_mul | compound_div | compound_mod;
            this->self += addition | subtraction | multiplication | division | modulo;
            this->self += increment | decrement;
            this->self += and_ | or_;
            this->self += for_ | do_ | while_ | true_ | false_ | if_ | else_ | from_ | to_ | in_ | foreach_ | return_ | const_ | include;
            this->self += equals | not_equals | greater_equals | less_equals | greater | less ;
            this->self += float_ | integer | identifier | litteral;

            //Ignore comments
            this->self += multiline_comment [lex::_pass = lex::pass_flags::pass_ignore];
            this->self += singleline_comment [lex::_pass = lex::pass_flags::pass_ignore];
        }

        typedef lex::token_def<lex::omit> ConsumedToken;
        typedef lex::token_def<std::string> StringToken;
        typedef lex::token_def<int> IntegerToken;
        typedef lex::token_def<char> CharToken;
        typedef lex::token_def<double> FloatToken;

        StringToken identifier, litteral;
        IntegerToken integer;
        FloatToken float_;

        CharToken addition, subtraction, multiplication, division, modulo;
        StringToken increment, decrement;
        StringToken compound_add, compound_sub, compound_mul, compound_div, compound_mod;
        StringToken equals, not_equals, greater, less, greater_equals, less_equals;
        StringToken and_, or_;

        ConsumedToken left_parenth, right_parenth, left_brace, right_brace, left_bracket, right_bracket;
        ConsumedToken stop, comma;
        ConsumedToken assign, swap;

        //Keywords
        ConsumedToken if_, else_, for_, while_, do_, from_, in_, to_, foreach_, return_;
        ConsumedToken true_, false_;
        ConsumedToken const_, include;

        //Ignored tokens
        ConsumedToken whitespaces, singleline_comment, multiline_comment;
};

typedef std::string::iterator base_iterator_type;
typedef boost::spirit::classic::position_iterator2<base_iterator_type> pos_iterator_type;
typedef boost::spirit::lex::lexertl::token<pos_iterator_type> Tok;
typedef lex::lexertl::actor_lexer<Tok> lexer_type;

//Typedef for the parsers
typedef lexer::lexer_type::iterator_type Iterator;
typedef lexer::SpiritLexer<lexer::lexer_type> Lexer;

} //end of lexer

} //end of eddic

#endif
<commit_msg>Add the float suffix the lexer<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#ifndef SPIRIT_LEXER_H
#define SPIRIT_LEXER_H

#include <fstream>
#include <string>
#include <utility>
#include <stack>

#include <boost/spirit/include/classic_position_iterator.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_functor_parser.hpp>
#include <boost/spirit/include/classic_attribute.hpp>
#include <boost/spirit/include/classic_symbols.hpp>

namespace eddic {

namespace lexer {

namespace spirit = boost::spirit;
namespace lex = boost::spirit::lex;

/*!
 * \class SimpleLexer
 * \brief The EDDI lexer.
 *
 * This class is used to do lexical analysis on an EDDI source file. This file is based on a Boost Spirit Lexer. It's
 * used by the parser to parse a source file.
 */
template<typename L>
class SpiritLexer : public lex::lexer<L> {
    public:
        SpiritLexer() {
            /* keywords  */
            for_ = "for";
            while_ = "while";
            do_ = "do";
            if_ = "if";
            else_ = "else";
            false_ = "false";
            true_ = "true";
            from_ = "from";
            to_ = "to";
            foreach_ = "foreach";
            in_ = "in";
            return_ = "return";
            const_ = "const";
            include = "include";

            /* Raw values  */
            identifier = "[a-zA-Z_]?[a-zA-Z0-9_]+";
            float_ = "[0-9]+\".\"[0-9]+";
            integer = "[0-9]+";
            litteral = "\\\"[^\\\"]*\\\"";

            /* Suffixes  */
            float_suffix = "f";

            /* Constructs  */
            left_parenth = '(';
            right_parenth = ')';
            left_brace = '{';
            right_brace = '}';
            left_bracket = '[';
            right_bracket = ']';

            stop = ';';
            comma = ',';

            /* Assignment operators */
            swap = "<=>";
            assign = '=';

            /* compound assignment operators */
            compound_add = "\\+=";
            compound_sub = "-=";
            compound_mul = "\\*=";
            compound_div = "\\/=";
            compound_mod = "%=";

            /* Math operators  */
            addition = '+';
            subtraction = '-';
            multiplication = '*';
            division = '/';
            modulo = '%';

            /* Suffix and prefix math operators  */
            increment = "\\+\\+";
            decrement = "--";

            /* Logical operators */
            and_ = "\\&\\&";
            or_ = "\\|\\|";

            /* Relational operators  */
            equals = "==";
            not_equals = "!=";
            greater = ">";
            less = "<";
            greater_equals = ">=";
            less_equals = "<=";

            whitespaces = "[ \\t\\n]+";
            multiline_comment = "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/";
            singleline_comment = "\\/\\/[^\n]*";

            //Ignore whitespaces
            this->self += whitespaces [lex::_pass = lex::pass_flags::pass_ignore];

            this->self += left_parenth | right_parenth | left_brace | right_brace | left_bracket | right_bracket;
            this->self += comma | stop;
            this->self += assign | swap;
            this->self += compound_add | compound_sub | compound_mul | compound_div | compound_mod;
            this->self += addition | subtraction | multiplication | division | modulo;
            this->self += increment | decrement;
            this->self += and_ | or_;
            this->self += for_ | do_ | while_ | true_ | false_ | if_ | else_ | from_ | to_ | in_ | foreach_ | return_ | const_ | include;
            this->self += equals | not_equals | greater_equals | less_equals | greater | less ;
            this->self += float_ | integer | identifier | litteral;
            this->self += float_suffix;

            //Ignore comments
            this->self += multiline_comment [lex::_pass = lex::pass_flags::pass_ignore];
            this->self += singleline_comment [lex::_pass = lex::pass_flags::pass_ignore];
        }

        typedef lex::token_def<lex::omit> ConsumedToken;
        typedef lex::token_def<std::string> StringToken;
        typedef lex::token_def<int> IntegerToken;
        typedef lex::token_def<char> CharToken;
        typedef lex::token_def<double> FloatToken;

        StringToken identifier, litteral;
        IntegerToken integer;
        FloatToken float_;

        CharToken addition, subtraction, multiplication, division, modulo;
        StringToken increment, decrement;
        StringToken compound_add, compound_sub, compound_mul, compound_div, compound_mod;
        StringToken equals, not_equals, greater, less, greater_equals, less_equals;
        StringToken and_, or_;
        StringToken float_suffix;

        ConsumedToken left_parenth, right_parenth, left_brace, right_brace, left_bracket, right_bracket;
        ConsumedToken stop, comma;
        ConsumedToken assign, swap;

        //Keywords
        ConsumedToken if_, else_, for_, while_, do_, from_, in_, to_, foreach_, return_;
        ConsumedToken true_, false_;
        ConsumedToken const_, include;

        //Ignored tokens
        ConsumedToken whitespaces, singleline_comment, multiline_comment;
};

typedef std::string::iterator base_iterator_type;
typedef boost::spirit::classic::position_iterator2<base_iterator_type> pos_iterator_type;
typedef boost::spirit::lex::lexertl::token<pos_iterator_type> Tok;
typedef lex::lexertl::actor_lexer<Tok> lexer_type;

//Typedef for the parsers
typedef lexer::lexer_type::iterator_type Iterator;
typedef lexer::SpiritLexer<lexer::lexer_type> Lexer;

} //end of lexer

} //end of eddic

#endif
<|endoftext|>


<commit_before>/*

Copyright (c) 2008, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TORRENT_ALLOCA

#include "libtorrent/config.hpp"

#ifdef TORRENT_WINDOWS

#include <malloc.h>
#define TORRENT_ALLOCA(t, n) static_cast<t*>(_alloca(sizeof(t) * (n)));

#else

#include <alloca.h>
#define TORRENT_ALLOCA(t, n) static_cast<t*>(alloca(sizeof(t) * (n)));

#endif

#endif

<commit_msg>remove unnecessary semicolon<commit_after>/*

Copyright (c) 2008, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TORRENT_ALLOCA

#include "libtorrent/config.hpp"

#ifdef TORRENT_WINDOWS

#include <malloc.h>
#define TORRENT_ALLOCA(t, n) static_cast<t*>(_alloca(sizeof(t) * (n)))

#else

#include <alloca.h>
#define TORRENT_ALLOCA(t, n) static_cast<t*>(alloca(sizeof(t) * (n)))

#endif

#endif

<|endoftext|>


<commit_before>#include "FCStdAfx.h"
#include "Quad_Multirotor_Motor_Mixer.h"
#include "uav_properties/Quad_Multirotor_Properties.h"
#include "physics/constants.h"

#include "hal.def.h"

namespace silk
{
namespace node
{

Quad_Multirotor_Motor_Mixer::Quad_Multirotor_Motor_Mixer(HAL& hal)
    : m_hal(hal)
    , m_descriptor(new hal::Quad_Multirotor_Motor_Mixer_Descriptor())
    , m_config(new hal::Quad_Multirotor_Motor_Mixer_Config())
{
}

ts::Result<void> Quad_Multirotor_Motor_Mixer::init(hal::INode_Descriptor const& descriptor)
{
    QLOG_TOPIC("Quad_Multirotor_Motor_Mixer::init");

    auto specialized = dynamic_cast<hal::Quad_Multirotor_Motor_Mixer_Descriptor const*>(&descriptor);
    if (!specialized)
    {
        return make_error("Wrong descriptor type");
    }
    *m_descriptor = *specialized;

    return init();
}

ts::Result<void> Quad_Multirotor_Motor_Mixer::init()
{
    std::shared_ptr<const Quad_Multirotor_Properties> multirotor_properties = m_hal.get_specialized_uav_properties<Quad_Multirotor_Properties>();
    if (!multirotor_properties)
    {
        return make_error("No quad multirotor properties found");
    }

    if (multirotor_properties->get_motors().size() != 4)
    {
        return make_error("Invalid quad multirotor properties found - motor count != 4");
    }

    const std::vector<Quad_Multirotor_Properties::Motor>& motors = multirotor_properties->get_motors();
    for (Quad_Multirotor_Properties::Motor const& mc: motors)
    {
        if (math::dot(mc.thrust_vector, math::vec3f(0, 0, 1.f)) < 0.9999f)
        {
            return make_error("Bad quad multirotor properties: motors have wrong thrust vectors");
        }
    }
    if (!math::equals(motors[0].position, -motors[2].position, 0.99f) ||
            motors[0].clockwise != motors[2].clockwise)
    {
        return make_error("Bad quad multirotor properties: motors 0 and 2 are not antipodal");
    }
    if (!math::equals(motors[1].position, -motors[3].position, 0.99f) ||
            motors[1].clockwise != motors[3].clockwise)
    {
        return make_error("Bad quad multirotor properties: motors 1 and 3 are not antipodal");
    }

    for (std::shared_ptr<Stream>& os: m_outputs)
    {
        os = std::make_shared<Stream>();
        os->rate = m_descriptor->get_rate();
    }

    return ts::success;
}

ts::Result<void> Quad_Multirotor_Motor_Mixer::start(Clock::time_point tp)
{
    //TODO - use an basic_output_stream instead of the ad-hoc streams
    return ts::success;
}

auto Quad_Multirotor_Motor_Mixer::get_inputs() const -> std::vector<Input>
{
    std::vector<Input> inputs =
    {{
        { stream::ITorque::TYPE, m_descriptor->get_rate(), "torque", m_accumulator.get_stream_path(0) },
        { stream::IFloat::TYPE, m_descriptor->get_rate(), "collective_thrust", m_accumulator.get_stream_path(1) }
    }};
    return inputs;
}
auto Quad_Multirotor_Motor_Mixer::get_outputs() const -> std::vector<Output>
{
    if (m_outputs.size() == 0 || m_outputs[0] == nullptr)
    {
        return std::vector<Output>();
    }

    std::vector<Output> outputs(m_outputs.size());
    for (size_t i = 0; i < m_outputs.size(); i++)
    {
        outputs[i].name = q::util::format<std::string>("throttle_{}", i);
        outputs[i].stream = m_outputs[i];
    }
    return outputs;
}

void Quad_Multirotor_Motor_Mixer::process()
{
    QLOG_TOPIC("Quad_Multirotor_Motor_Mixer::process");

    for (std::shared_ptr<Stream>& os: m_outputs)
    {
        os->samples.clear();
    }

    std::shared_ptr<const Quad_Multirotor_Properties> multirotor_properties = m_hal.get_specialized_uav_properties<Quad_Multirotor_Properties>();
    if (!multirotor_properties)
    {
        return;
    }
    if (multirotor_properties->get_motors().size() != m_outputs.size())
    {
        QLOGE("Motor count changed since initialization!!!! Case not handled");
        return;
    }

    m_accumulator.process([this, &multirotor_properties](stream::ITorque::Sample const& t_sample,
                                                stream::IFloat::Sample const& f_sample)
    {
        bool is_healthy = false;
        if (t_sample.is_healthy & f_sample.is_healthy)
        {
            compute_throttles(*multirotor_properties, f_sample.value, t_sample.value);
            is_healthy = true;
        }

        for (std::shared_ptr<Stream> output: m_outputs)
        {
            Stream::Sample& sample = output->last_sample;
            sample.value = output->throttle;
            sample.is_healthy = is_healthy;
            output->samples.push_back(sample);
        }
    });
}

static float compute_throttle_from_thrust(float max_thrust, float thrust)
{
    if (math::is_zero(max_thrust, math::epsilon<float>()))
    {
        return 0;
    }
    float ratio = thrust / max_thrust;

    //thrust increases approximately with throttle^2
    float throttle = math::sqrt<float, math::safe>(ratio);
    return throttle;
}
static float compute_thrust_from_throttle(float max_thrust, float throttle)
{
    float thrust = math::square(math::clamp(throttle, 0.f, 1.f)) * max_thrust;
    return thrust;
}

void Quad_Multirotor_Motor_Mixer::compute_throttles(Quad_Multirotor_Properties const& multirotor_properties, stream::IFloat::Value const& collective_thrust, stream::ITorque::Value const& _target)
{
    //T = r X F
    //T = |r| * |F| * sin(a)
    // T - torque
    // r - position of the force relative to the center of mass
    // F - force applied
    // a - angle between the force vector and the position vector (usually 90 degrees)

    if (collective_thrust < std::numeric_limits<float>::epsilon() * 10.f)
    {
        for (size_t i = 0; i < m_outputs.size(); i++)
        {
            Stream& out = *m_outputs[i];
            out.thrust = 0.f;
            out.throttle = 0.f;
        }
        return;
    }

    float motor_count = static_cast<float>(m_outputs.size());

    float collective_thrust_per_motor = collective_thrust / motor_count;

    //min and max allowed thrusts per motor
    float max_thrust_per_motor = multirotor_properties.get_motor_thrust();
    float min_thrust_per_motor = compute_thrust_from_throttle(m_config->get_armed_min_throttle(), max_thrust_per_motor);

    //the min collective thrust per motor is - at minimum - twice the min thrust per motor.
    //this is to that even at minimum, there is still some dynamic range left
    float min_collective_thrust_per_motor = math::max(collective_thrust_per_motor, min_thrust_per_motor * 2.f);
    collective_thrust_per_motor = math::max(collective_thrust_per_motor, min_collective_thrust_per_motor);

    //this is half the dynamic range, computed so that there is no spill either above max or below min
    float max_half_dynamic_range = math::min(
                collective_thrust_per_motor - min_thrust_per_motor, //how much space we have between the collective thrust and the min thrust
                (max_thrust_per_motor - min_thrust_per_motor) / 2.f //the middle thrust point
                );

    //split the target torque in XY (roll, pitch) and Z (yaw)
    stream::ITorque::Value xy_target = stream::ITorque::Value(_target.x, _target.y, 0.f);
    stream::ITorque::Value half_xy_target = xy_target / 2.f; //for xy, the quad has 2 halves participating

    float z_target_per_motor = _target.z / motor_count;

    float motor_z_torque = multirotor_properties.get_motor_z_torque();

    float max_computed_thrust_per_motor = std::numeric_limits<float>::lowest();
    float min_computed_thrust_per_motor = std::numeric_limits<float>::max();
    for (size_t i = 0; i < m_outputs.size(); i++)
    {
        Quad_Multirotor_Properties::Motor const& mc = multirotor_properties.get_motors()[i];
        Stream& out = *m_outputs[i];

        float thrust = 0.f;

        //XY torque
        if (math::length_sq(half_xy_target) > std::numeric_limits<float>::epsilon() * 10.f)
        {
            //the length of F depends on how much this motor can participate in the torque
            //a motor parallel with the torque will have |F| == 0
            //a motor perpendicular will have |F| = 1
            math::vec3f F = math::cross(math::normalized<float, math::safe>(half_xy_target), math::normalized<float, math::safe>(mc.position));

            //doing the dot product dives us directionality for the motor thrust
            float positional_factor = math::dot(F, mc.thrust_vector);

            //|F| = |T| / (|p| * sin a)
            //sin a == 1 (for a quad with the motor thrust pointing upwards)
            //so |F| = |T| / |p|
            thrust = positional_factor * math::length(half_xy_target) / math::length(mc.position);
        }

        //distribute the z torque
        {
            float mu = math::clamp(z_target_per_motor / (multirotor_properties.get_motor_z_torque() * (mc.clockwise ? 1 : -1)), 0.f, 1.f);
            thrust += mu * max_thrust_per_motor;
        }

        //clamp to half dynamic range
        thrust = math::sgn(thrust) * math::min(math::abs(thrust), max_half_dynamic_range);
        thrust += collective_thrust_per_motor;

        max_computed_thrust_per_motor = math::max(max_computed_thrust_per_motor, thrust);
        min_computed_thrust_per_motor = math::min(min_computed_thrust_per_motor, thrust);

        out.thrust = thrust;
    }

    //there could be some spill over the max now. Redistribute it to all motors
    QASSERT(min_computed_thrust_per_motor >= min_thrust_per_motor - math::epsilon<float>());
    if (max_computed_thrust_per_motor > max_thrust_per_motor + math::epsilon<float>())
    {
        float spill = max_computed_thrust_per_motor - max_thrust_per_motor;
        min_computed_thrust_per_motor -= spill;
        QASSERT(min_computed_thrust_per_motor >= min_thrust_per_motor - math::epsilon<float>());

        for (size_t i = 0; i < m_outputs.size(); i++)
        {
            Quad_Multirotor_Properties::Motor const& mc = multirotor_properties.get_motors()[i];
            Stream& out = *m_outputs[i];
            out.thrust = math::max(out.thrust - spill, min_thrust_per_motor);
        }
    }

    //convert thrust to throttle and clip
    for (auto& out: m_outputs)
    {
        out->throttle = compute_throttle_from_thrust(max_thrust_per_motor, out->thrust);
    }
}

ts::Result<void> Quad_Multirotor_Motor_Mixer::set_input_stream_path(size_t idx, std::string const& path)
{
    return m_accumulator.set_stream_path(idx, path, m_descriptor->get_rate(), m_hal);
}

ts::Result<void> Quad_Multirotor_Motor_Mixer::set_config(hal::INode_Config const& config)
{
    QLOG_TOPIC("Quad_Multirotor_Motor_Mixer::set_config");

    auto specialized = dynamic_cast<hal::Quad_Multirotor_Motor_Mixer_Config const*>(&config);
    if (!specialized)
    {
        return make_error("Wrong config type");
    }
    *m_config = *specialized;

    return ts::success;
}
auto Quad_Multirotor_Motor_Mixer::get_config() const -> std::shared_ptr<const hal::INode_Config>
{
    return m_config;
}

auto Quad_Multirotor_Motor_Mixer::get_descriptor() const -> std::shared_ptr<const hal::INode_Descriptor>
{
    return m_descriptor;
}
ts::Result<std::shared_ptr<messages::INode_Message>> Quad_Multirotor_Motor_Mixer::send_message(messages::INode_Message const& message)
{
    return make_error("Unknown message");
}

}
}
<commit_msg>Cosmetic fix<commit_after>#include "FCStdAfx.h"
#include "Quad_Multirotor_Motor_Mixer.h"
#include "uav_properties/Quad_Multirotor_Properties.h"
#include "physics/constants.h"

#include "hal.def.h"

namespace silk
{
namespace node
{

Quad_Multirotor_Motor_Mixer::Quad_Multirotor_Motor_Mixer(HAL& hal)
    : m_hal(hal)
    , m_descriptor(new hal::Quad_Multirotor_Motor_Mixer_Descriptor())
    , m_config(new hal::Quad_Multirotor_Motor_Mixer_Config())
{
}

ts::Result<void> Quad_Multirotor_Motor_Mixer::init(hal::INode_Descriptor const& descriptor)
{
    QLOG_TOPIC("Quad_Multirotor_Motor_Mixer::init");

    auto specialized = dynamic_cast<hal::Quad_Multirotor_Motor_Mixer_Descriptor const*>(&descriptor);
    if (!specialized)
    {
        return make_error("Wrong descriptor type");
    }
    *m_descriptor = *specialized;

    return init();
}

ts::Result<void> Quad_Multirotor_Motor_Mixer::init()
{
    std::shared_ptr<const Quad_Multirotor_Properties> multirotor_properties = m_hal.get_specialized_uav_properties<Quad_Multirotor_Properties>();
    if (!multirotor_properties)
    {
        return make_error("No quad multirotor properties found");
    }

    if (multirotor_properties->get_motors().size() != 4)
    {
        return make_error("Invalid quad multirotor properties found - motor count != 4");
    }

    const std::vector<Quad_Multirotor_Properties::Motor>& motors = multirotor_properties->get_motors();
    for (Quad_Multirotor_Properties::Motor const& mc: motors)
    {
        if (math::dot(mc.thrust_vector, math::vec3f(0, 0, 1.f)) < 0.9999f)
        {
            return make_error("Bad quad multirotor properties: motors have wrong thrust vectors");
        }
    }
    if (!math::equals(motors[0].position, -motors[2].position, 0.99f) ||
            motors[0].clockwise != motors[2].clockwise)
    {
        return make_error("Bad quad multirotor properties: motors 0 and 2 are not antipodal");
    }
    if (!math::equals(motors[1].position, -motors[3].position, 0.99f) ||
            motors[1].clockwise != motors[3].clockwise)
    {
        return make_error("Bad quad multirotor properties: motors 1 and 3 are not antipodal");
    }

    for (std::shared_ptr<Stream>& os: m_outputs)
    {
        os = std::make_shared<Stream>();
        os->rate = m_descriptor->get_rate();
    }

    return ts::success;
}

ts::Result<void> Quad_Multirotor_Motor_Mixer::start(Clock::time_point tp)
{
    //TODO - use an basic_output_stream instead of the ad-hoc streams
    return ts::success;
}

auto Quad_Multirotor_Motor_Mixer::get_inputs() const -> std::vector<Input>
{
    std::vector<Input> inputs =
    {{
        { stream::ITorque::TYPE, m_descriptor->get_rate(), "torque", m_accumulator.get_stream_path(0) },
        { stream::IFloat::TYPE, m_descriptor->get_rate(), "collective_thrust", m_accumulator.get_stream_path(1) }
    }};
    return inputs;
}
auto Quad_Multirotor_Motor_Mixer::get_outputs() const -> std::vector<Output>
{
    if (m_outputs.size() == 0 || m_outputs[0] == nullptr)
    {
        return std::vector<Output>();
    }

    std::vector<Output> outputs(m_outputs.size());
    for (size_t i = 0; i < m_outputs.size(); i++)
    {
        outputs[i].name = q::util::format<std::string>("throttle_{}", i);
        outputs[i].stream = m_outputs[i];
    }
    return outputs;
}

void Quad_Multirotor_Motor_Mixer::process()
{
    QLOG_TOPIC("Quad_Multirotor_Motor_Mixer::process");

    for (std::shared_ptr<Stream>& os: m_outputs)
    {
        os->samples.clear();
    }

    std::shared_ptr<const Quad_Multirotor_Properties> multirotor_properties = m_hal.get_specialized_uav_properties<Quad_Multirotor_Properties>();
    if (!multirotor_properties)
    {
        return;
    }
    if (multirotor_properties->get_motors().size() != m_outputs.size())
    {
        QLOGE("Motor count changed since initialization!!!! Case not handled");
        return;
    }

    m_accumulator.process([this, &multirotor_properties](stream::ITorque::Sample const& t_sample, stream::IFloat::Sample const& f_sample)
    {
        bool is_healthy = false;
        if (t_sample.is_healthy & f_sample.is_healthy)
        {
            compute_throttles(*multirotor_properties, f_sample.value, t_sample.value);
            is_healthy = true;
        }

        for (std::shared_ptr<Stream> output: m_outputs)
        {
            Stream::Sample& sample = output->last_sample;
            sample.value = output->throttle;
            sample.is_healthy = is_healthy;
            output->samples.push_back(sample);
        }
    });
}

static float compute_throttle_from_thrust(float max_thrust, float thrust)
{
    if (math::is_zero(max_thrust, math::epsilon<float>()))
    {
        return 0;
    }
    float ratio = thrust / max_thrust;

    //thrust increases approximately with throttle^2
    float throttle = math::sqrt<float, math::safe>(ratio);
    return throttle;
}
static float compute_thrust_from_throttle(float max_thrust, float throttle)
{
    float thrust = math::square(math::clamp(throttle, 0.f, 1.f)) * max_thrust;
    return thrust;
}

void Quad_Multirotor_Motor_Mixer::compute_throttles(Quad_Multirotor_Properties const& multirotor_properties, stream::IFloat::Value const& collective_thrust, stream::ITorque::Value const& _target)
{
    //T = r X F
    //T = |r| * |F| * sin(a)
    // T - torque
    // r - position of the force relative to the center of mass
    // F - force applied
    // a - angle between the force vector and the position vector (usually 90 degrees)

    if (collective_thrust < std::numeric_limits<float>::epsilon() * 10.f)
    {
        for (size_t i = 0; i < m_outputs.size(); i++)
        {
            Stream& out = *m_outputs[i];
            out.thrust = 0.f;
            out.throttle = 0.f;
        }
        return;
    }

    float motor_count = static_cast<float>(m_outputs.size());

    float collective_thrust_per_motor = collective_thrust / motor_count;

    //min and max allowed thrusts per motor
    float max_thrust_per_motor = multirotor_properties.get_motor_thrust();
    float min_thrust_per_motor = compute_thrust_from_throttle(m_config->get_armed_min_throttle(), max_thrust_per_motor);

    //the min collective thrust per motor is - at minimum - twice the min thrust per motor.
    //this is to that even at minimum, there is still some dynamic range left
    float min_collective_thrust_per_motor = math::max(collective_thrust_per_motor, min_thrust_per_motor * 2.f);
    collective_thrust_per_motor = math::max(collective_thrust_per_motor, min_collective_thrust_per_motor);

    //this is half the dynamic range, computed so that there is no spill either above max or below min
    float max_half_dynamic_range = math::min(
                collective_thrust_per_motor - min_thrust_per_motor, //how much space we have between the collective thrust and the min thrust
                (max_thrust_per_motor - min_thrust_per_motor) / 2.f //the middle thrust point
                );

    //split the target torque in XY (roll, pitch) and Z (yaw)
    stream::ITorque::Value xy_target = stream::ITorque::Value(_target.x, _target.y, 0.f);
    stream::ITorque::Value half_xy_target = xy_target / 2.f; //for xy, the quad has 2 halves participating

    float z_target_per_motor = _target.z / motor_count;

    float motor_z_torque = multirotor_properties.get_motor_z_torque();

    float max_computed_thrust_per_motor = std::numeric_limits<float>::lowest();
    float min_computed_thrust_per_motor = std::numeric_limits<float>::max();
    for (size_t i = 0; i < m_outputs.size(); i++)
    {
        Quad_Multirotor_Properties::Motor const& mc = multirotor_properties.get_motors()[i];
        Stream& out = *m_outputs[i];

        float thrust = 0.f;

        //XY torque
        if (math::length_sq(half_xy_target) > std::numeric_limits<float>::epsilon() * 10.f)
        {
            //the length of F depends on how much this motor can participate in the torque
            //a motor parallel with the torque will have |F| == 0
            //a motor perpendicular will have |F| = 1
            math::vec3f F = math::cross(math::normalized<float, math::safe>(half_xy_target), math::normalized<float, math::safe>(mc.position));

            //doing the dot product dives us directionality for the motor thrust
            float positional_factor = math::dot(F, mc.thrust_vector);

            //|F| = |T| / (|p| * sin a)
            //sin a == 1 (for a quad with the motor thrust pointing upwards)
            //so |F| = |T| / |p|
            thrust = positional_factor * math::length(half_xy_target) / math::length(mc.position);
        }

        //distribute the z torque
        {
            float mu = math::clamp(z_target_per_motor / (multirotor_properties.get_motor_z_torque() * (mc.clockwise ? 1 : -1)), 0.f, 1.f);
            thrust += mu * max_thrust_per_motor;
        }

        //clamp to half dynamic range
        thrust = math::sgn(thrust) * math::min(math::abs(thrust), max_half_dynamic_range);
        thrust += collective_thrust_per_motor;

        max_computed_thrust_per_motor = math::max(max_computed_thrust_per_motor, thrust);
        min_computed_thrust_per_motor = math::min(min_computed_thrust_per_motor, thrust);

        out.thrust = thrust;
    }

    //there could be some spill over the max now. Redistribute it to all motors
    QASSERT(min_computed_thrust_per_motor >= min_thrust_per_motor - math::epsilon<float>());
    if (max_computed_thrust_per_motor > max_thrust_per_motor + math::epsilon<float>())
    {
        float spill = max_computed_thrust_per_motor - max_thrust_per_motor;
        min_computed_thrust_per_motor -= spill;
        QASSERT(min_computed_thrust_per_motor >= min_thrust_per_motor - math::epsilon<float>());

        for (size_t i = 0; i < m_outputs.size(); i++)
        {
            Quad_Multirotor_Properties::Motor const& mc = multirotor_properties.get_motors()[i];
            Stream& out = *m_outputs[i];
            out.thrust = math::max(out.thrust - spill, min_thrust_per_motor);
        }
    }

    //convert thrust to throttle and clip
    for (std::shared_ptr<Stream>& out: m_outputs)
    {
        out->throttle = compute_throttle_from_thrust(max_thrust_per_motor, out->thrust);
    }
}

ts::Result<void> Quad_Multirotor_Motor_Mixer::set_input_stream_path(size_t idx, std::string const& path)
{
    return m_accumulator.set_stream_path(idx, path, m_descriptor->get_rate(), m_hal);
}

ts::Result<void> Quad_Multirotor_Motor_Mixer::set_config(hal::INode_Config const& config)
{
    QLOG_TOPIC("Quad_Multirotor_Motor_Mixer::set_config");

    auto specialized = dynamic_cast<hal::Quad_Multirotor_Motor_Mixer_Config const*>(&config);
    if (!specialized)
    {
        return make_error("Wrong config type");
    }
    *m_config = *specialized;

    return ts::success;
}
auto Quad_Multirotor_Motor_Mixer::get_config() const -> std::shared_ptr<const hal::INode_Config>
{
    return m_config;
}

auto Quad_Multirotor_Motor_Mixer::get_descriptor() const -> std::shared_ptr<const hal::INode_Descriptor>
{
    return m_descriptor;
}
ts::Result<std::shared_ptr<messages::INode_Message>> Quad_Multirotor_Motor_Mixer::send_message(messages::INode_Message const& message)
{
    return make_error("Unknown message");
}

}
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TORRENT_POLICY_HPP_INCLUDED
#define TORRENT_POLICY_HPP_INCLUDED

#include <algorithm>
#include <vector>

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include "libtorrent/peer.hpp"
#include "libtorrent/piece_picker.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/size_type.hpp"
#include "libtorrent/invariant_check.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/time.hpp"

namespace libtorrent
{

	class torrent;
	class peer_connection;

	enum
	{
		// the limits of the download queue size
		min_request_queue = 2,

		// the amount of free upload allowed before
		// the peer is choked
		free_upload_amount = 4 * 16 * 1024
	};

	void request_a_block(torrent& t, peer_connection& c);

	class TORRENT_EXPORT policy
	{
	public:

		policy(torrent* t);

		// this is called every 10 seconds to allow
		// for peer choking management
		void pulse();

		struct peer;
		// this is called once for every peer we get from
		// the tracker, pex, lsd or dht.
		policy::peer* peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid
			, int source, char flags);

		// false means duplicate connection
		bool update_peer_port(int port, policy::peer* p, int src);

		// called when an incoming connection is accepted
		// false means the connection was refused or failed
		bool new_connection(peer_connection& c);

		// the given connection was just closed
		void connection_closed(const peer_connection& c);

		// the peer has got at least one interesting piece
		void peer_is_interesting(peer_connection& c);

		// the peer unchoked us
		void unchoked(peer_connection& c);

		// the peer is interested in our pieces
		void interested(peer_connection& c);

		// the peer is not interested in our pieces
		void not_interested(peer_connection& c);

		void ip_filter_updated();

#ifndef NDEBUG
		bool has_connection(const peer_connection* p);

		void check_invariant() const;
#endif

		struct peer
		{
			enum connection_type { not_connectable, connectable };
			peer(tcp::endpoint const& ip, connection_type t, int src);

			size_type total_download() const;
			size_type total_upload() const;

			tcp::endpoint ip() const { return tcp::endpoint(addr, port); }
			void set_ip(tcp::endpoint const& endp)
			{ addr = endp.address(); port = endp.port(); }

			// this is the accumulated amount of
			// uploaded and downloaded data to this
			// peer. It only accounts for what was
			// shared during the last connection to
			// this peer. i.e. These are only updated
			// when the connection is closed. For the
			// total amount of upload and download
			// we'll have to add thes figures with the
			// statistics from the peer_connection.
			size_type prev_amount_upload;
			size_type prev_amount_download;

			// the ip address this peer is or was connected on
			address addr;

			// the time when this peer was optimistically unchoked
			// the last time.
			libtorrent::ptime last_optimistically_unchoked;

			// the time when the peer connected to us
			// or disconnected if it isn't connected right now
			libtorrent::ptime connected;

			// if the peer is connected now, this
			// will refer to a valid peer_connection
			peer_connection* connection;

#ifndef TORRENT_DISABLE_GEO_IP
#ifndef NDEBUG
			// only used in debug mode to assert that
			// the first entry in the AS pair keeps the same
			boost::uint16_t inet_as_num;
#endif
			// The AS this peer belongs to
			std::pair<const int, int>* inet_as;
#endif

			// the port this peer is or was connected on
			uint16_t port;

			// the number of failed connection attempts
			// this peer has
			boost::uint8_t failcount;

			// for every valid piece we receive where this
			// peer was one of the participants, we increase
			// this value. For every invalid piece we receive
			// where this peer was a participant, we decrease
			// this value. If it sinks below a threshold, its
			// considered a bad peer and will be banned.
			boost::int8_t trust_points;

			// a bitmap combining the peer_source flags
			// from peer_info.
			boost::uint8_t source;

			// the number of times this peer has been
			// part of a piece that failed the hash check
			boost::uint8_t hashfails;

			// type specifies if the connection was incoming
			// or outgoing. If we ever saw this peer as connectable
			// it will remain as connectable
			unsigned type:4;

			// the number of times we have allowed a fast
			// reconnect for this peer.
			unsigned fast_reconnects:4;

#ifndef TORRENT_DISABLE_ENCRYPTION
			// Hints encryption support of peer. Only effective
			// for and when the outgoing encryption policy
			// allows both encrypted and non encrypted
			// connections (pe_settings::out_enc_policy
			// == enabled). The initial state of this flag
			// determines the initial connection attempt
			// type (true = encrypted, false = standard).
			// This will be toggled everytime either an
			// encrypted or non-encrypted handshake fails.
			bool pe_support:1;
#endif
			// true if this peer currently is unchoked
			// because of an optimistic unchoke.
			// when the optimistic unchoke is moved to
			// another peer, this peer will be choked
			// if this is true
			bool optimistically_unchoked:1;

			// this is true if the peer is a seed
			bool seed:1;

			// if this is true, the peer has previously
			// participated in a piece that failed the piece
			// hash check. This will put the peer on parole
			// and only request entire pieces. If a piece pass
			// that was partially requested from this peer it
			// will leave parole mode and continue download
			// pieces as normal peers.
			bool on_parole:1;

			// is set to true if this peer has been banned
			bool banned:1;

#ifndef TORRENT_DISABLE_DHT
			// this is set to true when this peer as been
			// pinged by the DHT
			bool added_to_dht:1;
#endif
		};

		int num_peers() const { return m_peers.size(); }

		typedef std::multimap<address, peer>::iterator iterator;
		typedef std::multimap<address, peer>::const_iterator const_iterator;
		iterator begin_peer() { return m_peers.begin(); }
		iterator end_peer() { return m_peers.end(); }
		const_iterator begin_peer() const { return m_peers.begin(); }
		const_iterator end_peer() const { return m_peers.end(); }

		bool connect_one_peer();

		bool has_peer(policy::peer const* p) const;

		int num_seeds() const { return m_num_seeds; }
		int num_connect_candidates() const { return m_num_connect_candidates; }
		void recalculate_connect_candidates()
		{
			if (m_num_connect_candidates == 0)
				m_num_connect_candidates = 1;
		}

		void erase_peer(iterator i);

	private:

		bool compare_peer(policy::peer const& lhs, policy::peer const& rhs
			, address const& external_ip) const;

		iterator find_connect_candidate();

		bool is_connect_candidate(peer const& p, bool finished);

		std::multimap<address, peer> m_peers;

		// since the peer list can grow too large
		// to scan all of it, start at this iterator
		iterator m_round_robin;

		torrent* m_torrent;

		// free download we have got that hasn't
		// been distributed yet.
		size_type m_available_free_upload;

		// The number of peers in our peer list
		// that are connect candidates. i.e. they're
		// not already connected and they have not
		// yet reached their max try count and they
		// have the connectable state (we have a listen
		// port for them).
		int m_num_connect_candidates;

		// the number of seeds in the peer list
		int m_num_seeds;
	};

}

#endif // TORRENT_POLICY_HPP_INCLUDED

<commit_msg>Fix building with msvc<commit_after>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TORRENT_POLICY_HPP_INCLUDED
#define TORRENT_POLICY_HPP_INCLUDED

#include <algorithm>
#include <vector>

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include "libtorrent/peer.hpp"
#include "libtorrent/piece_picker.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/size_type.hpp"
#include "libtorrent/invariant_check.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/time.hpp"

namespace libtorrent
{

	class torrent;
	class peer_connection;

	enum
	{
		// the limits of the download queue size
		min_request_queue = 2,

		// the amount of free upload allowed before
		// the peer is choked
		free_upload_amount = 4 * 16 * 1024
	};

	void request_a_block(torrent& t, peer_connection& c);

	class TORRENT_EXPORT policy
	{
	public:

		policy(torrent* t);

		// this is called every 10 seconds to allow
		// for peer choking management
		void pulse();

		struct peer;
		// this is called once for every peer we get from
		// the tracker, pex, lsd or dht.
		policy::peer* peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid
			, int source, char flags);

		// false means duplicate connection
		bool update_peer_port(int port, policy::peer* p, int src);

		// called when an incoming connection is accepted
		// false means the connection was refused or failed
		bool new_connection(peer_connection& c);

		// the given connection was just closed
		void connection_closed(const peer_connection& c);

		// the peer has got at least one interesting piece
		void peer_is_interesting(peer_connection& c);

		// the peer unchoked us
		void unchoked(peer_connection& c);

		// the peer is interested in our pieces
		void interested(peer_connection& c);

		// the peer is not interested in our pieces
		void not_interested(peer_connection& c);

		void ip_filter_updated();

#ifndef NDEBUG
		bool has_connection(const peer_connection* p);

		void check_invariant() const;
#endif

		struct peer
		{
			enum connection_type { not_connectable, connectable };
			peer(tcp::endpoint const& ip, connection_type t, int src);

			size_type total_download() const;
			size_type total_upload() const;

			tcp::endpoint ip() const { return tcp::endpoint(addr, port); }
			void set_ip(tcp::endpoint const& endp)
			{ addr = endp.address(); port = endp.port(); }

			// this is the accumulated amount of
			// uploaded and downloaded data to this
			// peer. It only accounts for what was
			// shared during the last connection to
			// this peer. i.e. These are only updated
			// when the connection is closed. For the
			// total amount of upload and download
			// we'll have to add thes figures with the
			// statistics from the peer_connection.
			size_type prev_amount_upload;
			size_type prev_amount_download;

			// the ip address this peer is or was connected on
			address addr;

			// the time when this peer was optimistically unchoked
			// the last time.
			libtorrent::ptime last_optimistically_unchoked;

			// the time when the peer connected to us
			// or disconnected if it isn't connected right now
			libtorrent::ptime connected;

			// if the peer is connected now, this
			// will refer to a valid peer_connection
			peer_connection* connection;

#ifndef TORRENT_DISABLE_GEO_IP
#ifndef NDEBUG
			// only used in debug mode to assert that
			// the first entry in the AS pair keeps the same
			boost::uint16_t inet_as_num;
#endif
			// The AS this peer belongs to
			std::pair<const int, int>* inet_as;
#endif

			// the port this peer is or was connected on
			boost::uint16_t port;

			// the number of failed connection attempts
			// this peer has
			boost::uint8_t failcount;

			// for every valid piece we receive where this
			// peer was one of the participants, we increase
			// this value. For every invalid piece we receive
			// where this peer was a participant, we decrease
			// this value. If it sinks below a threshold, its
			// considered a bad peer and will be banned.
			boost::int8_t trust_points;

			// a bitmap combining the peer_source flags
			// from peer_info.
			boost::uint8_t source;

			// the number of times this peer has been
			// part of a piece that failed the hash check
			boost::uint8_t hashfails;

			// type specifies if the connection was incoming
			// or outgoing. If we ever saw this peer as connectable
			// it will remain as connectable
			unsigned type:4;

			// the number of times we have allowed a fast
			// reconnect for this peer.
			unsigned fast_reconnects:4;

#ifndef TORRENT_DISABLE_ENCRYPTION
			// Hints encryption support of peer. Only effective
			// for and when the outgoing encryption policy
			// allows both encrypted and non encrypted
			// connections (pe_settings::out_enc_policy
			// == enabled). The initial state of this flag
			// determines the initial connection attempt
			// type (true = encrypted, false = standard).
			// This will be toggled everytime either an
			// encrypted or non-encrypted handshake fails.
			bool pe_support:1;
#endif
			// true if this peer currently is unchoked
			// because of an optimistic unchoke.
			// when the optimistic unchoke is moved to
			// another peer, this peer will be choked
			// if this is true
			bool optimistically_unchoked:1;

			// this is true if the peer is a seed
			bool seed:1;

			// if this is true, the peer has previously
			// participated in a piece that failed the piece
			// hash check. This will put the peer on parole
			// and only request entire pieces. If a piece pass
			// that was partially requested from this peer it
			// will leave parole mode and continue download
			// pieces as normal peers.
			bool on_parole:1;

			// is set to true if this peer has been banned
			bool banned:1;

#ifndef TORRENT_DISABLE_DHT
			// this is set to true when this peer as been
			// pinged by the DHT
			bool added_to_dht:1;
#endif
		};

		int num_peers() const { return m_peers.size(); }

		typedef std::multimap<address, peer>::iterator iterator;
		typedef std::multimap<address, peer>::const_iterator const_iterator;
		iterator begin_peer() { return m_peers.begin(); }
		iterator end_peer() { return m_peers.end(); }
		const_iterator begin_peer() const { return m_peers.begin(); }
		const_iterator end_peer() const { return m_peers.end(); }

		bool connect_one_peer();

		bool has_peer(policy::peer const* p) const;

		int num_seeds() const { return m_num_seeds; }
		int num_connect_candidates() const { return m_num_connect_candidates; }
		void recalculate_connect_candidates()
		{
			if (m_num_connect_candidates == 0)
				m_num_connect_candidates = 1;
		}

		void erase_peer(iterator i);

	private:

		bool compare_peer(policy::peer const& lhs, policy::peer const& rhs
			, address const& external_ip) const;

		iterator find_connect_candidate();

		bool is_connect_candidate(peer const& p, bool finished);

		std::multimap<address, peer> m_peers;

		// since the peer list can grow too large
		// to scan all of it, start at this iterator
		iterator m_round_robin;

		torrent* m_torrent;

		// free download we have got that hasn't
		// been distributed yet.
		size_type m_available_free_upload;

		// The number of peers in our peer list
		// that are connect candidates. i.e. they're
		// not already connected and they have not
		// yet reached their max try count and they
		// have the connectable state (we have a listen
		// port for them).
		int m_num_connect_candidates;

		// the number of seeds in the peer list
		int m_num_seeds;
	};

}

#endif // TORRENT_POLICY_HPP_INCLUDED

<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * Copyright (c) 2015 Kohei Yoshida
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 ************************************************************************/

#include "mdds/global.hpp"

#include <cassert>
#include <algorithm>

#ifdef MDDS_TRIE_MAP_DEBUG
#include <iostream>
#endif

namespace mdds { namespace draft {

namespace detail {

struct trie_node
{
    char key;
    const void* value;

    std::deque<trie_node> children;

    trie_node(char _key) : key(_key), value(nullptr) {}
};

#ifdef MDDS_TRIE_MAP_DEBUG

template<typename _ValueT>
void dump_node(std::string& buffer, const trie_node& node)
{
    using namespace std;
    using value_type = _ValueT;

    if (node.value)
    {
        // This node has value.
        cout << buffer << ":" << *static_cast<const value_type*>(node.value) << endl;
    }

    std::for_each(node.children.begin(), node.children.end(),
        [&](const trie_node& node)
        {
            buffer.push_back(node.key);
            dump_node<value_type>(buffer, node);
            buffer.pop_back();
        }
    );
}

template<typename _ValueT>
void dump_trie(const trie_node& root)
{
    std::string buffer;
    dump_node<_ValueT>(buffer, root);
}

template<typename _ValueT>
void dump_packed_trie(const std::vector<uintptr_t>& packed)
{
    using namespace std;

    using value_type = _ValueT;

    cout << "packed size: " << packed.size() << endl;

    size_t n = packed.size();
    size_t i = 0;
    cout << i << ": root node offset: " << packed[i] << endl;
    ++i;

    while (i < n)
    {
        const value_type* value = reinterpret_cast<const value_type*>(packed[i]);
        cout << i << ": node value pointer: " << value;
        if (value)
            cout << ", value: " << *value;
        cout << endl;
        ++i;

        size_t index_size = packed[i];
        cout << i << ": index size: " << index_size << endl;
        ++i;
        index_size /= 2;

        for (size_t j = 0; j < index_size; ++j)
        {
            char key = packed[i];
            cout << i << ": key: " << key << endl;
            ++i;
            size_t offset = packed[i];
            cout << i << ": offset: " << offset << endl;
            ++i;
        }
    }
}

#endif

template<typename _ValueT>
void traverse_range(
    trie_node& root,
    const typename packed_trie_map<_ValueT>::entry* start,
    const typename packed_trie_map<_ValueT>::entry* end,
    size_t pos)
{
    using namespace std;
    using entry = typename packed_trie_map<_ValueT>::entry;

    const entry* p = start;
    const entry* range_start = start;
    const entry* range_end = nullptr;
    char range_char = 0;
    size_t range_count = 0;

    for (; p != end; ++p)
    {
        if (pos > p->keylen)
            continue;

        if (pos == p->keylen)
        {
            root.value = &p->value;
            continue;
        }

        ++range_count;
        char c = p->key[pos];

        if (!range_char)
            range_char = c;
        else if (range_char != c)
        {
            // End of current character range.
            range_end = p;

            root.children.emplace_back(range_char);
            traverse_range<_ValueT>(root.children.back(), range_start, range_end, pos+1);
            range_start = range_end;
            range_char = range_start->key[pos];
            range_end = nullptr;
            range_count = 1;
        }
    }

    if (range_count)
    {
        assert(range_char);
        root.children.emplace_back(range_char);
        traverse_range<_ValueT>(root.children.back(), range_start, end, pos+1);
    }
}

inline size_t compact_node(std::vector<uintptr_t>& packed, const trie_node& node)
{
    std::vector<std::tuple<size_t,char>> child_offsets;
    child_offsets.reserve(node.children.size());

    // Process child nodes first.
    std::for_each(node.children.begin(), node.children.end(),
        [&](const trie_node& node)
        {
            size_t child_offset = compact_node(packed, node);
            child_offsets.emplace_back(child_offset, node.key);
        }
    );

    // Process this node.
    size_t offset = packed.size();
    packed.push_back(uintptr_t(node.value));
    packed.push_back(uintptr_t(child_offsets.size()*2));

    std::for_each(child_offsets.begin(), child_offsets.end(),
        [&](const std::tuple<size_t,char>& v)
        {
            char key = std::get<1>(v);
            size_t child_offset = std::get<0>(v);
            packed.push_back(key);
            packed.push_back(offset-child_offset);
        }
    );

    return offset;
}

inline void compact(std::vector<uintptr_t>& packed, const trie_node& root)
{
    std::vector<uintptr_t> init(size_t(1), uintptr_t(0));
    packed.swap(init);

    size_t root_offset = compact_node(packed, root);
    packed[0] = root_offset;
}

}

template<typename _ValueT>
packed_trie_map<_ValueT>::packed_trie_map(
    const entry* entries, size_type entry_size, value_type null_value) :
    m_null_value(null_value)
{
    const entry* p = entries;
    const entry* p_end = p + entry_size;

    // Populate the normal tree first.
    detail::trie_node root(0);
    detail::traverse_range<value_type>(root, p, p_end, 0);
#if defined(MDDS_TRIE_MAP_DEBUG) && defined(MDDS_TREI_MAP_DEBUG_DUMP_TRIE)
    detail::dump_trie<value_type>(root);
#endif

    // Compact the trie into a packed array.
    detail::compact(m_packed, root);
#if defined(MDDS_TRIE_MAP_DEBUG) && defined(MDDS_TREI_MAP_DEBUG_DUMP_PACKED)
    detail::dump_packed_trie<value_type>(m_packed);
#endif
}

template<typename _ValueT>
typename packed_trie_map<_ValueT>::value_type
packed_trie_map<_ValueT>::find(const char* input, size_type len) const
{
    if (m_packed.empty())
        return m_null_value;

    const char* key_end = input + len;
    size_t root_offset = m_packed[0];
    const uintptr_t* root = m_packed.data() + root_offset;

    const value_type* pv = descend_node(root, input, key_end);
    return pv ? *pv : m_null_value;
}

template<typename _ValueT>
const typename packed_trie_map<_ValueT>::value_type*
packed_trie_map<_ValueT>::descend_node(
    const uintptr_t* p, const char* key, const char* key_end) const
{
    const value_type* v = reinterpret_cast<const value_type*>(*p);

    if (key == key_end)
        return v;

    const uintptr_t* p0 = p; // store the head offset position of this node.

    // Find the child node with a matching key character.

    ++p;
    size_t index_size = *p;
    size_t n = index_size / 2;
    ++p;
    for (size_t i = 0; i < n; ++i)
    {
        char node_key = *p++;
        size_t offset = *p++;

        if (*key != node_key)
            continue;

        const uintptr_t* p_child = p0 - offset;
        ++key;
        return descend_node(p_child, key, key_end);
    }

    return nullptr;
}

#ifdef MDDS_TRIE_MAP_DEBUG

template<typename _ValueT>
void packed_trie_map<_ValueT>::dump() const
{
    if (m_packed.empty())
        return;

    std::string buffer;
    size_t root_offset = m_packed[0];
    const uintptr_t* p = m_packed.data() + root_offset;
    dump_compact_trie_node(buffer, p);
}

template<typename _ValueT>
void packed_trie_map<_ValueT>::dump_compact_trie_node(std::string& buffer, const uintptr_t* p) const
{
    using namespace std;

    const uintptr_t* p0 = p; // store the head offset position of this node.

    const value_type* v = reinterpret_cast<const value_type*>(*p);
    if (v)
        cout << buffer << ": " << *v << endl;

    ++p;
    size_t index_size = *p;
    size_t n = index_size / 2;
    ++p;
    for (size_t i = 0; i < n; ++i)
    {
        char key = *p++;
        size_t offset = *p++;
        buffer.push_back(key);
        const uintptr_t* p_child = p0 - offset;
        dump_compact_trie_node(buffer, p_child);
        buffer.pop_back();
    }
}

#endif

}}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>Add TODO for later task.<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * Copyright (c) 2015 Kohei Yoshida
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 ************************************************************************/

#include "mdds/global.hpp"

#include <cassert>
#include <algorithm>

#ifdef MDDS_TRIE_MAP_DEBUG
#include <iostream>
#endif

namespace mdds { namespace draft {

namespace detail {

struct trie_node
{
    char key;
    const void* value;

    std::deque<trie_node> children;

    trie_node(char _key) : key(_key), value(nullptr) {}
};

#ifdef MDDS_TRIE_MAP_DEBUG

template<typename _ValueT>
void dump_node(std::string& buffer, const trie_node& node)
{
    using namespace std;
    using value_type = _ValueT;

    if (node.value)
    {
        // This node has value.
        cout << buffer << ":" << *static_cast<const value_type*>(node.value) << endl;
    }

    std::for_each(node.children.begin(), node.children.end(),
        [&](const trie_node& node)
        {
            buffer.push_back(node.key);
            dump_node<value_type>(buffer, node);
            buffer.pop_back();
        }
    );
}

template<typename _ValueT>
void dump_trie(const trie_node& root)
{
    std::string buffer;
    dump_node<_ValueT>(buffer, root);
}

template<typename _ValueT>
void dump_packed_trie(const std::vector<uintptr_t>& packed)
{
    using namespace std;

    using value_type = _ValueT;

    cout << "packed size: " << packed.size() << endl;

    size_t n = packed.size();
    size_t i = 0;
    cout << i << ": root node offset: " << packed[i] << endl;
    ++i;

    while (i < n)
    {
        const value_type* value = reinterpret_cast<const value_type*>(packed[i]);
        cout << i << ": node value pointer: " << value;
        if (value)
            cout << ", value: " << *value;
        cout << endl;
        ++i;

        size_t index_size = packed[i];
        cout << i << ": index size: " << index_size << endl;
        ++i;
        index_size /= 2;

        for (size_t j = 0; j < index_size; ++j)
        {
            char key = packed[i];
            cout << i << ": key: " << key << endl;
            ++i;
            size_t offset = packed[i];
            cout << i << ": offset: " << offset << endl;
            ++i;
        }
    }
}

#endif

template<typename _ValueT>
void traverse_range(
    trie_node& root,
    const typename packed_trie_map<_ValueT>::entry* start,
    const typename packed_trie_map<_ValueT>::entry* end,
    size_t pos)
{
    using namespace std;
    using entry = typename packed_trie_map<_ValueT>::entry;

    const entry* p = start;
    const entry* range_start = start;
    const entry* range_end = nullptr;
    char range_char = 0;
    size_t range_count = 0;

    for (; p != end; ++p)
    {
        if (pos > p->keylen)
            continue;

        if (pos == p->keylen)
        {
            root.value = &p->value;
            continue;
        }

        ++range_count;
        char c = p->key[pos];

        if (!range_char)
            range_char = c;
        else if (range_char != c)
        {
            // End of current character range.
            range_end = p;

            root.children.emplace_back(range_char);
            traverse_range<_ValueT>(root.children.back(), range_start, range_end, pos+1);
            range_start = range_end;
            range_char = range_start->key[pos];
            range_end = nullptr;
            range_count = 1;
        }
    }

    if (range_count)
    {
        assert(range_char);
        root.children.emplace_back(range_char);
        traverse_range<_ValueT>(root.children.back(), range_start, end, pos+1);
    }
}

inline size_t compact_node(std::vector<uintptr_t>& packed, const trie_node& node)
{
    std::vector<std::tuple<size_t,char>> child_offsets;
    child_offsets.reserve(node.children.size());

    // Process child nodes first.
    std::for_each(node.children.begin(), node.children.end(),
        [&](const trie_node& node)
        {
            size_t child_offset = compact_node(packed, node);
            child_offsets.emplace_back(child_offset, node.key);
        }
    );

    // Process this node.
    size_t offset = packed.size();
    packed.push_back(uintptr_t(node.value));
    packed.push_back(uintptr_t(child_offsets.size()*2));

    std::for_each(child_offsets.begin(), child_offsets.end(),
        [&](const std::tuple<size_t,char>& v)
        {
            char key = std::get<1>(v);
            size_t child_offset = std::get<0>(v);
            packed.push_back(key);
            packed.push_back(offset-child_offset);
        }
    );

    return offset;
}

inline void compact(std::vector<uintptr_t>& packed, const trie_node& root)
{
    std::vector<uintptr_t> init(size_t(1), uintptr_t(0));
    packed.swap(init);

    size_t root_offset = compact_node(packed, root);
    packed[0] = root_offset;
}

}

template<typename _ValueT>
packed_trie_map<_ValueT>::packed_trie_map(
    const entry* entries, size_type entry_size, value_type null_value) :
    m_null_value(null_value)
{
    const entry* p = entries;
    const entry* p_end = p + entry_size;

    // Populate the normal tree first.
    detail::trie_node root(0);
    detail::traverse_range<value_type>(root, p, p_end, 0);
#if defined(MDDS_TRIE_MAP_DEBUG) && defined(MDDS_TREI_MAP_DEBUG_DUMP_TRIE)
    detail::dump_trie<value_type>(root);
#endif

    // Compact the trie into a packed array.
    detail::compact(m_packed, root);
#if defined(MDDS_TRIE_MAP_DEBUG) && defined(MDDS_TREI_MAP_DEBUG_DUMP_PACKED)
    detail::dump_packed_trie<value_type>(m_packed);
#endif
}

template<typename _ValueT>
typename packed_trie_map<_ValueT>::value_type
packed_trie_map<_ValueT>::find(const char* input, size_type len) const
{
    if (m_packed.empty())
        return m_null_value;

    const char* key_end = input + len;
    size_t root_offset = m_packed[0];
    const uintptr_t* root = m_packed.data() + root_offset;

    const value_type* pv = descend_node(root, input, key_end);
    return pv ? *pv : m_null_value;
}

template<typename _ValueT>
const typename packed_trie_map<_ValueT>::value_type*
packed_trie_map<_ValueT>::descend_node(
    const uintptr_t* p, const char* key, const char* key_end) const
{
    const value_type* v = reinterpret_cast<const value_type*>(*p);

    if (key == key_end)
        return v;

    const uintptr_t* p0 = p; // store the head offset position of this node.

    // Find the child node with a matching key character.

    ++p;
    size_t index_size = *p;
    size_t n = index_size / 2;
    ++p;

    // TODO : turn this into a binary search.
    for (size_t i = 0; i < n; ++i)
    {
        const uintptr_t* p_this = p + i*2;
        char node_key = *p_this;
        size_t offset = *(p_this+1);

        if (*key != node_key)
            continue;

        const uintptr_t* p_child = p0 - offset;
        ++key;
        return descend_node(p_child, key, key_end);
    }

    return nullptr;
}

#ifdef MDDS_TRIE_MAP_DEBUG

template<typename _ValueT>
void packed_trie_map<_ValueT>::dump() const
{
    if (m_packed.empty())
        return;

    std::string buffer;
    size_t root_offset = m_packed[0];
    const uintptr_t* p = m_packed.data() + root_offset;
    dump_compact_trie_node(buffer, p);
}

template<typename _ValueT>
void packed_trie_map<_ValueT>::dump_compact_trie_node(std::string& buffer, const uintptr_t* p) const
{
    using namespace std;

    const uintptr_t* p0 = p; // store the head offset position of this node.

    const value_type* v = reinterpret_cast<const value_type*>(*p);
    if (v)
        cout << buffer << ": " << *v << endl;

    ++p;
    size_t index_size = *p;
    size_t n = index_size / 2;
    ++p;
    for (size_t i = 0; i < n; ++i)
    {
        char key = *p++;
        size_t offset = *p++;
        buffer.push_back(key);
        const uintptr_t* p_child = p0 - offset;
        dump_compact_trie_node(buffer, p_child);
        buffer.pop_back();
    }
}

#endif

}}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>#ifndef PLANET_FILESYSTEM_HPP
#define PLANET_FILESYSTEM_HPP

#include <planet/common.hpp>
#include <planet/fs_core.hpp>

namespace planet {

    class filesystem {
    private:
        shared_ptr<ops_type_db>         ops_db_;
        shared_ptr<core_file_system>    root_;

    public:
        typedef ops_type_db::priority priority;

        template<typename ...Types>
        filesystem(mode_t root_mode)
        {
            root_   = shared_ptr<core_file_system>(new core_file_system());
            ops_db_ = make_shared<ops_type_db>(root_);

            // This is not circular reference of shared_ptr
            root_->ops_db_ = ops_db_;    // root_.ops_db_ is a weak_ptr

            // Create root directory of this filesystem
            st_inode new_inode;
            new_inode.mode = root_mode | S_IFDIR;
            root_->root = std::make_shared<dentry>("/", "dir_ops_type", new_inode);

            // Install default file and directory operation
            root_-> template install_ops<file_ops_type>(priority::low);
            root_-> template install_ops<dir_ops_type>(priority::low);
        }

        ~filesystem()
        {
            // Destroy ops_db_ first because ops_db_ has a reference to root_
            ops_db_.reset();
            ::syslog(LOG_NOTICE, "filesystem: dtor: core_file_system: use_count=%ld", root_.use_count());
        }

        shared_ptr<core_file_system> root()
        {
            return root_;
        }

        std::vector<ops_type_db::info_type> info() const
        {
            return ops_db_->info();
        }
    };

}   // namespace planet

#endif  // PLANET_FILESYSTEM_HPP
<commit_msg>filesystem: Fix bug using destructed shared_ptr (SEGV)<commit_after>#ifndef PLANET_FILESYSTEM_HPP
#define PLANET_FILESYSTEM_HPP

#include <planet/common.hpp>
#include <planet/fs_core.hpp>

namespace planet {

    class filesystem {
    private:
        shared_ptr<ops_type_db>         ops_db_;
        shared_ptr<core_file_system>    root_;

    public:
        typedef ops_type_db::priority priority;

        template<typename ...Types>
        filesystem(mode_t root_mode)
        {
            root_   = shared_ptr<core_file_system>(new core_file_system());
            ops_db_ = make_shared<ops_type_db>(root_);

            // This is not circular reference of shared_ptr
            root_->ops_db_ = ops_db_;    // root_.ops_db_ is a weak_ptr

            // Create root directory of this filesystem
            st_inode new_inode;
            new_inode.mode = root_mode | S_IFDIR;
            root_->root = std::make_shared<dentry>("/", "dir_ops_type", new_inode);

            // Install default file and directory operation
            root_-> template install_ops<file_ops_type>(priority::low);
            root_-> template install_ops<dir_ops_type>(priority::low);
        }

        ~filesystem()
        {
            // Destroy ops_db_ first because ops_db_ has a reference to root_
            ops_db_->clear();
            ops_db_.reset();
            ::syslog(LOG_NOTICE, "filesystem: dtor: core_file_system: use_count=%ld", root_.use_count());
        }

        shared_ptr<core_file_system> root()
        {
            return root_;
        }

        std::vector<ops_type_db::info_type> info() const
        {
            return ops_db_->info();
        }
    };

}   // namespace planet

#endif  // PLANET_FILESYSTEM_HPP
<|endoftext|>