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

Program:   Visualization Toolkit
Module:    TestRandomPContingencyStatisticsMPI.cxx

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*
 * Copyright 2009 Sandia Corporation.
 * Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
 * license for use of this work by or on behalf of the
 * U.S. Government. Redistribution and use in source and binary forms, with
 * or without modification, are permitted provided that this Notice and any
 * statement of authorship are reproduced on all copies.
 */
// .SECTION Thanks
// Thanks to Philippe Pebay for implementing this test.

#include <mpi.h>

#include "vtkContingencyStatistics.h"
#include "vtkPContingencyStatistics.h"

#include "vtkDoubleArray.h"
#include "vtkIdTypeArray.h"
#include "vtkIntArray.h"
#include "vtkMath.h"
#include "vtkMPIController.h"
#include "vtkMultiBlockDataSet.h"
#include "vtkStdString.h"
#include "vtkTable.h"
#include "vtkTimerLog.h"
#include "vtkVariantArray.h"

// For debugging purposes, output contingency table, which may be huge: it has the size O(span^2).
#define DEBUG_CONTINGENCY_TABLE 0
#define CONTINGENCY_BIG_CASE 1

struct RandomContingencyStatisticsArgs
{
  int nVals;
  double span;
  double absTol;
  int* retVal;
  int ioRank;
  int argc;
  char** argv;
};

// This will be called by all processes
void RandomContingencyStatistics( vtkMultiProcessController* controller, void* arg )
{
  // Get test parameters
  RandomContingencyStatisticsArgs* args = reinterpret_cast<RandomContingencyStatisticsArgs*>( arg );
  *(args->retVal) = 0;

  // Get MPI communicator
  vtkMPICommunicator* com = vtkMPICommunicator::SafeDownCast( controller->GetCommunicator() );

  // Get local rank
  int myRank = com->GetLocalProcessId();

  // Seed random number generator
  vtkMath::RandomSeed( static_cast<int>( vtkTimerLog::GetUniversalTime() ) * ( myRank + 1 ) );

  // Generate an input table that contains samples of mutually independent discrete random variables
  int nVariables = 3;
  vtkIntArray* intArray[3];
  vtkStdString columnNames[] = { "Rounded Normal 0",
                                 "Rounded Normal 1",
                                 "Rounded Normal 2" };

  vtkTable* inputData = vtkTable::New();
  // Discrete rounded normal samples
  for ( int c = 0; c < nVariables; ++ c )
    {
    intArray[c] = vtkIntArray::New();
    intArray[c]->SetNumberOfComponents( 1 );
    intArray[c]->SetName( columnNames[c] );

    for ( int r = 0; r < args->nVals; ++ r )
      {
      intArray[c]->InsertNextValue( static_cast<int>( vtkMath::Round( vtkMath::Gaussian() * args->span ) ) );
      }

    inputData->AddColumn( intArray[c] );
    intArray[c]->Delete();
    }

  // Entropies in the summary table should normally be retrieved as follows:
  //   column 2: H(X,Y)
  //   column 3: H(Y|X)
  //   column 4: H(X|Y)
  int iEntropies[] = { 2,
                       3,
                       4 };
  int nEntropies = 3; // correct number of entropies reported in the summary table

  // ************************** Contingency Statistics **************************

  // Synchronize and start clock
  com->Barrier();
  vtkTimerLog *timer=vtkTimerLog::New();
  timer->StartTimer();

  // Instantiate a parallel contingency statistics engine and set its ports
  vtkPContingencyStatistics* pcs = vtkPContingencyStatistics::New();
  pcs->SetInput( vtkStatisticsAlgorithm::INPUT_DATA, inputData );
  vtkMultiBlockDataSet* outputMetaDS = vtkMultiBlockDataSet::SafeDownCast( pcs->GetOutputDataObject( vtkStatisticsAlgorithm::OUTPUT_MODEL ) );

  // Select column pairs (uniform vs. uniform, normal vs. normal)
  pcs->AddColumnPair( columnNames[0], columnNames[1] );
#if CONTINGENCY_BIG_CASE
#else // CONTINGENCY_BIG_CASE
  pcs->AddColumnPair( columnNames[0], columnNames[2] );
#endif // CONTINGENCY_BIG_CASE

  // Test (in parallel) with Learn, Derive, and Assess options turned on
  pcs->SetLearnOption( true );
  pcs->SetDeriveOption( true );
  pcs->SetAssessOption( true );
  pcs->Update();

  // Synchronize and stop clock
  com->Barrier();
  timer->StopTimer();

  if ( com->GetLocalProcessId() == args->ioRank )
    {
    cout << "\n## Completed parallel calculation of contingency statistics (with assessment):\n"
         << "   Wall time: "
         << timer->GetElapsedTime()
         << " sec.\n";
    }

  // Now perform verifications
  vtkTable* outputSummary = vtkTable::SafeDownCast( outputMetaDS->GetBlock( 0 ) );
  vtkTable* outputContingency = vtkTable::SafeDownCast( outputMetaDS->GetBlock( 1 ) );

  vtkIdType nRowSumm = outputSummary->GetNumberOfRows();
  int testIntValue;
  double testDoubleValue;
  int numProcs = controller->GetNumberOfProcesses();

  // Verify that all processes have the same grand total
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    cout << "\n## Verifying that all processes have the same grand total.\n";
    }

  // Gather all grand totals
  int GT_l = outputContingency->GetValueByName( 0, "Cardinality" ).ToInt();
  int* GT_g = new int[numProcs];
  com->AllGather( &GT_l,
                  GT_g,
                  1 );

  // Use the first grand total as reference (as they all must be equal)
  testIntValue = GT_g[0];

  // Print out all grand totals
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    for ( int i = 0; i < numProcs; ++ i )
      {
      cout << "     On process "
           << i
           << ", grand total = "
           << GT_g[i]
           << ", contingency table size = "
           << outputContingency->GetNumberOfRows()
           << "\n";

      if ( GT_g[i] != testIntValue )
        {
        vtkGenericWarningMacro("Incorrect CDF.");
        *(args->retVal) = 1;
        }
      }
    }

  // Verify that information entropies on all processes make sense
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    cout << "\n## Verifying that information entropies are consistent on all processes.\n";
    }

  testIntValue = outputSummary->GetNumberOfColumns();

  if ( testIntValue != nEntropies + 2 )
    {
    vtkGenericWarningMacro("Reported an incorrect number of columns in the summary table: "
                           << testIntValue
                           << " != "
                           << nEntropies + 2
                           << ".");
    *(args->retVal) = 1;
    }
  else
    {
    // For each row in the summary table, fetch variable names and information entropies
    for ( vtkIdType k = 0; k < nRowSumm; ++ k )
      {
      // Get local information entropies from summary table
      double* H_l = new double[nEntropies];
      for ( vtkIdType c = 0; c < nEntropies; ++ c )
        {
        H_l[c] = outputSummary->GetValue( k, iEntropies[c] ).ToDouble();
        }

      // Gather all local entropies
      double* H_g = new double[nEntropies * numProcs];
      com->AllGather( H_l,
                      H_g,
                      nEntropies );

      // Print out all entropies
      if ( com->GetLocalProcessId() == args->ioRank )
        {
        // Get variable names
        cout << "   (X,Y) = ("
             << outputSummary->GetValue( k, 0 ).ToString()
             << ", "
             << outputSummary->GetValue( k, 1 ).ToString()
             << "):\n";

        for ( int i = 0; i < numProcs; ++ i )
          {
          cout << "     On process "
               << i;

          for ( vtkIdType c = 0; c < nEntropies; ++ c )
            {
            cout << ", "
                 << outputSummary->GetColumnName( iEntropies[c] )
                 << " = "
                 << H_g[nEntropies * i + c];
            }

          cout << "\n";

          // Make sure that H(X,Y) >= H(Y|X)+ H(X|Y)
          testDoubleValue = H_g[nEntropies * i + 1] + H_g[nEntropies * i + 2]; // H(Y|X)+ H(X|Y)

          if ( testDoubleValue > H_g[nEntropies * i] )
            {
            vtkGenericWarningMacro("Reported inconsistent information entropies: H(X,Y) = "
                                   << H_g[nEntropies * i]
                                   << " < "
                                   << testDoubleValue
                                   << " = H(Y|X)+ H(X|Y).");
            *(args->retVal) = 1;
            }
          }
        }

      // Clean up
      delete [] H_l;
      delete [] H_g;
      }
    }

  // Verify that the broadcasted reduced contingency tables all result in a CDF value of 1
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    cout << "\n## Verifying that broadcasted CDF sum to 1 on all processes.\n";
    }

  vtkIdTypeArray* keys = vtkIdTypeArray::SafeDownCast( outputContingency->GetColumnByName( "Key" ) );
  if ( ! keys )
    {
    cout << "*** Error: "
         << "Empty contingency table column 'Key' on process "
         << com->GetLocalProcessId()
         << ".\n";
    }

  vtkStdString proName = "P";
  vtkDoubleArray* prob = vtkDoubleArray::SafeDownCast( outputContingency->GetColumnByName( proName ) );
  if ( ! prob )
    {
    cout << "*** Error: "
         << "Empty contingency table column '"
         << proName
         << "' on process "
         << com->GetLocalProcessId()
         << ".\n";
    }

  // Calculate local CDFs
  double* cdf_l = new double[nRowSumm];
  for ( vtkIdType k = 0; k < nRowSumm; ++ k )
    {
    cdf_l[k] = 0;
    }

  int n = outputContingency->GetNumberOfRows();

  // Skip first entry which is reserved for the cardinality
  for ( vtkIdType r = 1; r < n; ++ r )
    {
    cdf_l[keys->GetValue( r )] += prob->GetValue( r );
    }

  // Gather all local CDFs
  double* cdf_g = new double[nRowSumm * numProcs];
  com->AllGather( cdf_l,
                  cdf_g,
                  nRowSumm );

  // Print out all CDFs
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    for ( vtkIdType k = 0; k < nRowSumm; ++ k )
      {
      // Get variable names
      cout << "   (X,Y) = ("
           << outputSummary->GetValue( k, 0 ).ToString()
           << ", "
           << outputSummary->GetValue( k, 1 ).ToString()
           << "):\n";

      for ( int i = 0; i < numProcs; ++ i )
        {
        testDoubleValue = cdf_g[i * nRowSumm + k];

        cout << "     On process "
             << i
             << ", CDF = "
             << testDoubleValue
             << "\n";

        // Verify that CDF = 1 (within absTol)
        if ( fabs ( 1. - testDoubleValue ) > args->absTol )
          {
          vtkGenericWarningMacro("Incorrect CDF.");
          *(args->retVal) = 1;
          }
        }
      }
    }

#if DEBUG_CONTINGENCY_TABLE
  outputContingency->Dump();
#endif // DEBUG_CONTINGENCY_TABLE

  // Clean up
  delete [] cdf_l;
  delete [] cdf_g;
  pcs->Delete();
  inputData->Delete();
  timer->Delete();
}

//----------------------------------------------------------------------------
int main( int argc, char** argv )
{
  // **************************** MPI Initialization ***************************
  vtkMPIController* controller = vtkMPIController::New();
  controller->Initialize( &argc, &argv );

  // If an MPI controller was not created, terminate in error.
  if ( ! controller->IsA( "vtkMPIController" ) )
    {
    vtkGenericWarningMacro("Failed to initialize a MPI controller.");
    controller->Delete();
    return 1;
    }

  vtkMPICommunicator* com = vtkMPICommunicator::SafeDownCast( controller->GetCommunicator() );

  // ************************** Find an I/O node ********************************
  int* ioPtr;
  int ioRank;
  int flag;

  MPI_Attr_get( MPI_COMM_WORLD,
                MPI_IO,
                &ioPtr,
                &flag );

  if ( ( ! flag ) || ( *ioPtr == MPI_PROC_NULL ) )
    {
    // Getting MPI attributes did not return any I/O node found.
    ioRank = MPI_PROC_NULL;
    vtkGenericWarningMacro("No MPI I/O nodes found.");

    // As no I/O node was found, we need an unambiguous way to report the problem.
    // This is the only case when a testValue of -1 will be returned
    controller->Finalize();
    controller->Delete();

    return -1;
    }
  else
    {
    if ( *ioPtr == MPI_ANY_SOURCE )
      {
      // Anyone can do the I/O trick--just pick node 0.
      ioRank = 0;
      }
    else
      {
      // Only some nodes can do I/O. Make sure everyone agrees on the choice (min).
      com->AllReduce( ioPtr,
                      &ioRank,
                      1,
                      vtkCommunicator::MIN_OP );
      }
    }

  // ************************** Initialize test *********************************
  if ( com->GetLocalProcessId() == ioRank )
    {
    cout << "\n# Process "
         << ioRank
         << " will be the I/O node.\n";
    }

  // Parameters for regression test.
  int testValue = 0;
  RandomContingencyStatisticsArgs args;

#if CONTINGENCY_BIG_CASE
  args.nVals = 1000000;
  args.span = 200.;
#else // CONTINGENCY_BIG_CASE
  args.nVals = 10;
  args.span = 3.;
#endif // CONTINGENCY_BIG_CASE

  args.absTol = 1.e-6;
  args.retVal = &testValue;
  args.ioRank = ioRank;
  args.argc = argc;
  args.argv = argv;

  // Check how many processes have been made available
  int numProcs = controller->GetNumberOfProcesses();
  if ( controller->GetLocalProcessId() == ioRank )
    {
    cout << "\n# Running test with "
         << numProcs
         << " processes and standard deviation = "
         << args.span
         << ".\n";
    }

  // Execute the function named "process" on both processes
  controller->SetSingleMethod( RandomContingencyStatistics, &args );
  controller->SingleMethodExecute();

  // Clean up and exit
  if ( com->GetLocalProcessId() == ioRank )
    {
    cout << "\n# Test completed.\n\n";
    }

  controller->Finalize();
  controller->Delete();

  return testValue;
}
<commit_msg>ENH: also print out relative tolerance for parametric testing purposes STYLE: changed a variable name that was REALLY confusing (even to the author himself!): span <> standard deviation...<commit_after>/*=========================================================================

Program:   Visualization Toolkit
Module:    TestRandomPContingencyStatisticsMPI.cxx

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*
 * Copyright 2009 Sandia Corporation.
 * Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
 * license for use of this work by or on behalf of the
 * U.S. Government. Redistribution and use in source and binary forms, with
 * or without modification, are permitted provided that this Notice and any
 * statement of authorship are reproduced on all copies.
 */
// .SECTION Thanks
// Thanks to Philippe Pebay for implementing this test.

#include <mpi.h>

#include "vtkContingencyStatistics.h"
#include "vtkPContingencyStatistics.h"

#include "vtkDoubleArray.h"
#include "vtkIdTypeArray.h"
#include "vtkIntArray.h"
#include "vtkMath.h"
#include "vtkMPIController.h"
#include "vtkMultiBlockDataSet.h"
#include "vtkStdString.h"
#include "vtkTable.h"
#include "vtkTimerLog.h"
#include "vtkVariantArray.h"

// For debugging purposes, output contingency table, which may be huge: it has the size O(span^2).
#define DEBUG_CONTINGENCY_TABLE 0
#define CONTINGENCY_BIG_CASE 1

struct RandomContingencyStatisticsArgs
{
  int nVals;
  double stdev;
  double absTol;
  int* retVal;
  int ioRank;
  int argc;
  char** argv;
};

// This will be called by all processes
void RandomContingencyStatistics( vtkMultiProcessController* controller, void* arg )
{
  // Get test parameters
  RandomContingencyStatisticsArgs* args = reinterpret_cast<RandomContingencyStatisticsArgs*>( arg );
  *(args->retVal) = 0;

  // Get MPI communicator
  vtkMPICommunicator* com = vtkMPICommunicator::SafeDownCast( controller->GetCommunicator() );

  // Get local rank
  int myRank = com->GetLocalProcessId();

  // Seed random number generator
  vtkMath::RandomSeed( static_cast<int>( vtkTimerLog::GetUniversalTime() ) * ( myRank + 1 ) );

  // Generate an input table that contains samples of mutually independent discrete random variables
  int nVariables = 3;
  vtkIntArray* intArray[3];
  vtkStdString columnNames[] = { "Rounded Normal 0",
                                 "Rounded Normal 1",
                                 "Rounded Normal 2" };

  vtkTable* inputData = vtkTable::New();
  // Discrete rounded normal samples
  for ( int c = 0; c < nVariables; ++ c )
    {
    intArray[c] = vtkIntArray::New();
    intArray[c]->SetNumberOfComponents( 1 );
    intArray[c]->SetName( columnNames[c] );

    for ( int r = 0; r < args->nVals; ++ r )
      {
      intArray[c]->InsertNextValue( static_cast<int>( vtkMath::Round( vtkMath::Gaussian() * args->stdev ) ) );
      }

    inputData->AddColumn( intArray[c] );
    intArray[c]->Delete();
    }

  // Entropies in the summary table should normally be retrieved as follows:
  //   column 2: H(X,Y)
  //   column 3: H(Y|X)
  //   column 4: H(X|Y)
  int iEntropies[] = { 2,
                       3,
                       4 };
  int nEntropies = 3; // correct number of entropies reported in the summary table

  // ************************** Contingency Statistics **************************

  // Synchronize and start clock
  com->Barrier();
  vtkTimerLog *timer=vtkTimerLog::New();
  timer->StartTimer();

  // Instantiate a parallel contingency statistics engine and set its ports
  vtkPContingencyStatistics* pcs = vtkPContingencyStatistics::New();
  pcs->SetInput( vtkStatisticsAlgorithm::INPUT_DATA, inputData );
  vtkMultiBlockDataSet* outputMetaDS = vtkMultiBlockDataSet::SafeDownCast( pcs->GetOutputDataObject( vtkStatisticsAlgorithm::OUTPUT_MODEL ) );

  // Select column pairs (uniform vs. uniform, normal vs. normal)
  pcs->AddColumnPair( columnNames[0], columnNames[1] );
#if CONTINGENCY_BIG_CASE
#else // CONTINGENCY_BIG_CASE
  pcs->AddColumnPair( columnNames[0], columnNames[2] );
#endif // CONTINGENCY_BIG_CASE

  // Test (in parallel) with Learn, Derive, and Assess options turned on
  pcs->SetLearnOption( true );
  pcs->SetDeriveOption( true );
  pcs->SetAssessOption( true );
  pcs->Update();

  // Synchronize and stop clock
  com->Barrier();
  timer->StopTimer();

  if ( com->GetLocalProcessId() == args->ioRank )
    {
    cout << "\n## Completed parallel calculation of contingency statistics (with assessment):\n"
         << "   Wall time: "
         << timer->GetElapsedTime()
         << " sec.\n";
    }

  // Now perform verifications
  vtkTable* outputSummary = vtkTable::SafeDownCast( outputMetaDS->GetBlock( 0 ) );
  vtkTable* outputContingency = vtkTable::SafeDownCast( outputMetaDS->GetBlock( 1 ) );

  vtkIdType nRowSumm = outputSummary->GetNumberOfRows();
  int testIntValue;
  double testDoubleValue;
  int numProcs = controller->GetNumberOfProcesses();

  // Verify that all processes have the same grand total
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    cout << "\n## Verifying that all processes have the same grand total.\n";
    }

  // Gather all grand totals
  int GT_l = outputContingency->GetValueByName( 0, "Cardinality" ).ToInt();
  int* GT_g = new int[numProcs];
  com->AllGather( &GT_l,
                  GT_g,
                  1 );

  // Use the first grand total as reference (as they all must be equal)
  testIntValue = GT_g[0];

  // Print out all grand totals
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    for ( int i = 0; i < numProcs; ++ i )
      {
      cout << "     On process "
           << i
           << ", grand total = "
           << GT_g[i]
           << ", contingency table size = "
           << outputContingency->GetNumberOfRows()
           << "\n";

      if ( GT_g[i] != testIntValue )
        {
        vtkGenericWarningMacro("Incorrect CDF.");
        *(args->retVal) = 1;
        }
      }
    }

  // Verify that information entropies on all processes make sense
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    cout << "\n## Verifying that information entropies are consistent on all processes.\n";
    }

  testIntValue = outputSummary->GetNumberOfColumns();

  if ( testIntValue != nEntropies + 2 )
    {
    vtkGenericWarningMacro("Reported an incorrect number of columns in the summary table: "
                           << testIntValue
                           << " != "
                           << nEntropies + 2
                           << ".");
    *(args->retVal) = 1;
    }
  else
    {
    // For each row in the summary table, fetch variable names and information entropies
    for ( vtkIdType k = 0; k < nRowSumm; ++ k )
      {
      // Get local information entropies from summary table
      double* H_l = new double[nEntropies];
      for ( vtkIdType c = 0; c < nEntropies; ++ c )
        {
        H_l[c] = outputSummary->GetValue( k, iEntropies[c] ).ToDouble();
        }

      // Gather all local entropies
      double* H_g = new double[nEntropies * numProcs];
      com->AllGather( H_l,
                      H_g,
                      nEntropies );

      // Print out all entropies
      if ( com->GetLocalProcessId() == args->ioRank )
        {
        // Get variable names
        cout << "   (X,Y) = ("
             << outputSummary->GetValue( k, 0 ).ToString()
             << ", "
             << outputSummary->GetValue( k, 1 ).ToString()
             << "):\n";

        for ( int i = 0; i < numProcs; ++ i )
          {
          cout << "     On process "
               << i;

          for ( vtkIdType c = 0; c < nEntropies; ++ c )
            {
            cout << ", "
                 << outputSummary->GetColumnName( iEntropies[c] )
                 << " = "
                 << H_g[nEntropies * i + c];
            }

          cout << "\n";

          // Make sure that H(X,Y) >= H(Y|X)+ H(X|Y)
          testDoubleValue = H_g[nEntropies * i + 1] + H_g[nEntropies * i + 2]; // H(Y|X)+ H(X|Y)

          if ( testDoubleValue > H_g[nEntropies * i] )
            {
            vtkGenericWarningMacro("Reported inconsistent information entropies: H(X,Y) = "
                                   << H_g[nEntropies * i]
                                   << " < "
                                   << testDoubleValue
                                   << " = H(Y|X)+ H(X|Y).");
            *(args->retVal) = 1;
            }
          }
        }

      // Clean up
      delete [] H_l;
      delete [] H_g;
      }
    }

  // Verify that the broadcasted reduced contingency tables all result in a CDF value of 1
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    cout << "\n## Verifying that broadcasted CDF sum to 1 on all processes.\n";
    }

  vtkIdTypeArray* keys = vtkIdTypeArray::SafeDownCast( outputContingency->GetColumnByName( "Key" ) );
  if ( ! keys )
    {
    cout << "*** Error: "
         << "Empty contingency table column 'Key' on process "
         << com->GetLocalProcessId()
         << ".\n";
    }

  vtkStdString proName = "P";
  vtkDoubleArray* prob = vtkDoubleArray::SafeDownCast( outputContingency->GetColumnByName( proName ) );
  if ( ! prob )
    {
    cout << "*** Error: "
         << "Empty contingency table column '"
         << proName
         << "' on process "
         << com->GetLocalProcessId()
         << ".\n";
    }

  // Calculate local CDFs
  double* cdf_l = new double[nRowSumm];
  for ( vtkIdType k = 0; k < nRowSumm; ++ k )
    {
    cdf_l[k] = 0;
    }

  int n = outputContingency->GetNumberOfRows();

  // Skip first entry which is reserved for the cardinality
  for ( vtkIdType r = 1; r < n; ++ r )
    {
    cdf_l[keys->GetValue( r )] += prob->GetValue( r );
    }

  // Gather all local CDFs
  double* cdf_g = new double[nRowSumm * numProcs];
  com->AllGather( cdf_l,
                  cdf_g,
                  nRowSumm );

  // Print out all CDFs
  if ( com->GetLocalProcessId() == args->ioRank )
    {
    for ( vtkIdType k = 0; k < nRowSumm; ++ k )
      {
      // Get variable names
      cout << "   (X,Y) = ("
           << outputSummary->GetValue( k, 0 ).ToString()
           << ", "
           << outputSummary->GetValue( k, 1 ).ToString()
           << "):\n";

      for ( int i = 0; i < numProcs; ++ i )
        {
        testDoubleValue = cdf_g[i * nRowSumm + k];

        cout << "     On process "
             << i
             << ", CDF = "
             << testDoubleValue
             << " (within "
             << args->absTol
             << " relative tolerance)\n";

        // Verify that CDF = 1 (within absTol)
        if ( fabs ( 1. - testDoubleValue ) > args->absTol )
          {
          vtkGenericWarningMacro("Incorrect CDF.");
          *(args->retVal) = 1;
          }
        }
      }
    }

#if DEBUG_CONTINGENCY_TABLE
  outputContingency->Dump();
#endif // DEBUG_CONTINGENCY_TABLE

  // Clean up
  delete [] cdf_l;
  delete [] cdf_g;
  pcs->Delete();
  inputData->Delete();
  timer->Delete();
}

//----------------------------------------------------------------------------
int main( int argc, char** argv )
{
  // **************************** MPI Initialization ***************************
  vtkMPIController* controller = vtkMPIController::New();
  controller->Initialize( &argc, &argv );

  // If an MPI controller was not created, terminate in error.
  if ( ! controller->IsA( "vtkMPIController" ) )
    {
    vtkGenericWarningMacro("Failed to initialize a MPI controller.");
    controller->Delete();
    return 1;
    }

  vtkMPICommunicator* com = vtkMPICommunicator::SafeDownCast( controller->GetCommunicator() );

  // ************************** Find an I/O node ********************************
  int* ioPtr;
  int ioRank;
  int flag;

  MPI_Attr_get( MPI_COMM_WORLD,
                MPI_IO,
                &ioPtr,
                &flag );

  if ( ( ! flag ) || ( *ioPtr == MPI_PROC_NULL ) )
    {
    // Getting MPI attributes did not return any I/O node found.
    ioRank = MPI_PROC_NULL;
    vtkGenericWarningMacro("No MPI I/O nodes found.");

    // As no I/O node was found, we need an unambiguous way to report the problem.
    // This is the only case when a testValue of -1 will be returned
    controller->Finalize();
    controller->Delete();

    return -1;
    }
  else
    {
    if ( *ioPtr == MPI_ANY_SOURCE )
      {
      // Anyone can do the I/O trick--just pick node 0.
      ioRank = 0;
      }
    else
      {
      // Only some nodes can do I/O. Make sure everyone agrees on the choice (min).
      com->AllReduce( ioPtr,
                      &ioRank,
                      1,
                      vtkCommunicator::MIN_OP );
      }
    }

  // ************************** Initialize test *********************************
  if ( com->GetLocalProcessId() == ioRank )
    {
    cout << "\n# Process "
         << ioRank
         << " will be the I/O node.\n";
    }

  // Parameters for regression test.
  int testValue = 0;
  RandomContingencyStatisticsArgs args;

#if CONTINGENCY_BIG_CASE
  args.nVals = 4000000;
  args.stdev = 5.;
#else // CONTINGENCY_BIG_CASE
  args.nVals = 10;
  args.stdev = 3.;
#endif // CONTINGENCY_BIG_CASE

  args.absTol = 1.e-6;
  args.retVal = &testValue;
  args.ioRank = ioRank;
  args.argc = argc;
  args.argv = argv;

  // Check how many processes have been made available
  int numProcs = controller->GetNumberOfProcesses();
  if ( controller->GetLocalProcessId() == ioRank )
    {
    cout << "\n# Running test with "
         << numProcs
         << " processes and standard deviation = "
         << args.stdev
         << ".\n";
    }

  // Execute the function named "process" on both processes
  controller->SetSingleMethod( RandomContingencyStatistics, &args );
  controller->SingleMethodExecute();

  // Clean up and exit
  if ( com->GetLocalProcessId() == ioRank )
    {
    cout << "\n# Test completed.\n\n";
    }

  controller->Finalize();
  controller->Delete();

  return testValue;
}
<|endoftext|>


<commit_before>#include "dsa_common.h"

#include "module/logger.h"
#include "stcp_server_connection.h"

#define DEBUG 0

namespace dsa {

StcpServerConnection::StcpServerConnection(LinkStrandRef &strand,
                                           boost::asio::ssl::context &context,
                                           const string_ &dsid_prefix,
                                           const string_ &path)
    : StcpConnection(strand, context, dsid_prefix, path) {}

void StcpServerConnection::accept() {
  _socket.async_handshake(boost::asio::ssl::stream_base::server, [
    this, sthis = shared_from_this()
  ](const boost::system::error_code &error)->void {
    return handle_handshake(error);
  });
}

void StcpServerConnection::handle_handshake(
    const boost::system::error_code &error) {
  if (error != boost::system::errc::success) {
    LOG_ERROR(_strand->logger(),
              LOG << "Server SSL handshake failed: " << error << "\n");
  } else {
    {
      std::lock_guard<std::mutex> lock(mutex);
      on_read_message = [this](MessageRef message) {
        on_receive_f0(std::move(message));
      };
    }

    StcpConnection::start_read(share_this<StcpServerConnection>(), 0, 0);
    start_deadline_timer(15);
  }
}

}  // namespace dsa
<commit_msg>fix read_loop for linux<commit_after>#include "dsa_common.h"

#include "module/logger.h"
#include "stcp_server_connection.h"

#define DEBUG 0

namespace dsa {

StcpServerConnection::StcpServerConnection(LinkStrandRef &strand,
                                           boost::asio::ssl::context &context,
                                           const string_ &dsid_prefix,
                                           const string_ &path)
    : StcpConnection(strand, context, dsid_prefix, path) {}

void StcpServerConnection::accept() {
  _socket.async_handshake(boost::asio::ssl::stream_base::server, [
    this, sthis = shared_from_this()
  ](const boost::system::error_code &error)->void {
    return handle_handshake(error);
  });
}

void StcpServerConnection::handle_handshake(
    const boost::system::error_code &error) {
  if (error != boost::system::errc::success) {
    LOG_ERROR(_strand->logger(),
              LOG << "Server SSL handshake failed: " << error << "\n");
  } else {
    {
      std::lock_guard<std::mutex> lock(mutex);
      on_read_message = [this](MessageRef message) {
        on_receive_f0(std::move(message));
      };
    }

    StcpConnection::start_read(share_this<StcpServerConnection>());
    start_deadline_timer(15);
  }
}

}  // namespace dsa
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013-2016 Alexander Saprykin <xelfium@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
 */

#ifndef PLIB_TESTS_STATIC
#  define BOOST_TEST_DYN_LINK
#endif

#define BOOST_TEST_MODULE pfile_test

#include "plib.h"

#include <stdio.h>

#ifdef PLIB_TESTS_STATIC
#  include <boost/test/included/unit_test.hpp>
#else
#  include <boost/test/unit_test.hpp>
#endif

#define PFILE_TEST_FILE "."P_DIR_SEPARATOR"pfile_test_file.txt"

BOOST_AUTO_TEST_SUITE (BOOST_TEST_MODULE)

BOOST_AUTO_TEST_CASE (pfile_general_test)
{
	p_lib_init ();

	BOOST_CHECK (p_file_is_exists (PFILE_TEST_FILE) == FALSE);
	BOOST_CHECK (p_file_remove ("."P_DIR_SEPARATOR"pfile_test_file_remove.txt", NULL) == FALSE);

	FILE *file = fopen (PFILE_TEST_FILE, "w");
	BOOST_REQUIRE (file != NULL);
	BOOST_CHECK (p_file_is_exists (PFILE_TEST_FILE) == TRUE);

	fprintf (file, "This is a test file string\n");

	BOOST_CHECK (fclose (file) == 0);
	BOOST_CHECK (p_file_remove (PFILE_TEST_FILE, NULL) == TRUE);

	p_lib_shutdown ();
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>tests: Add check for p_file_remove() with invalid input data<commit_after>/*
 * Copyright (C) 2013-2016 Alexander Saprykin <xelfium@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
 */

#ifndef PLIB_TESTS_STATIC
#  define BOOST_TEST_DYN_LINK
#endif

#define BOOST_TEST_MODULE pfile_test

#include "plib.h"

#include <stdio.h>

#ifdef PLIB_TESTS_STATIC
#  include <boost/test/included/unit_test.hpp>
#else
#  include <boost/test/unit_test.hpp>
#endif

#define PFILE_TEST_FILE "."P_DIR_SEPARATOR"pfile_test_file.txt"

BOOST_AUTO_TEST_SUITE (BOOST_TEST_MODULE)

BOOST_AUTO_TEST_CASE (pfile_general_test)
{
	p_lib_init ();

	BOOST_CHECK (p_file_remove (NULL, NULL) == FALSE);

	BOOST_CHECK (p_file_is_exists (PFILE_TEST_FILE) == FALSE);
	BOOST_CHECK (p_file_remove ("."P_DIR_SEPARATOR"pfile_test_file_remove.txt", NULL) == FALSE);

	FILE *file = fopen (PFILE_TEST_FILE, "w");
	BOOST_REQUIRE (file != NULL);
	BOOST_CHECK (p_file_is_exists (PFILE_TEST_FILE) == TRUE);

	fprintf (file, "This is a test file string\n");

	BOOST_CHECK (fclose (file) == 0);
	BOOST_CHECK (p_file_remove (PFILE_TEST_FILE, NULL) == TRUE);

	p_lib_shutdown ();
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2003-2016 Rony Shapiro <ronys@pwsafe.org>.
 * All rights reserved. Use of the code is allowed under the
 * Artistic License 2.0 terms, as specified in the LICENSE file
 * distributed with this code, or available from
 * http://www.opensource.org/licenses/artistic-license-2.0.php
 */

#include <wx/clipbrd.h>
#include <wx/dataobj.h>

/*

  NOTE: In VS2013 wxWidgets 3.0.x builds:
    Both <wx/clipbrd.h> & <wx/dataobj.h> cause 51 warnings about using unsecure
    versions of standard calls, such as 'wcscpy' instead of 'wcscpy_s', if any
    previously inluded header file includes <string> even though pre-processor
    variables _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES and
    _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT are defined.

    The solution is to ensure that any header files containing <string>, e.g.
    "core/StringX.h", are placed after these two wxWidgets include statements.

  This issue did not occur with wxWidgets 2.8.12.

  For this reason, "pwsclip.h", which includes "core/StringX.h" that also includes
  <string>, is placed here after <wx/clipbrd.h> & <wx/dataobj.h>.

*/

#include "pwsclip.h"
#ifdef __WXMSW__
#include <wx/msw/msvcrt.h>
#endif

PWSclipboard *PWSclipboard::self = nullptr;

/**
 * Get pointer to single instance of clipboard manager
 */
PWSclipboard *PWSclipboard::GetInstance()
{
  if (self == nullptr) {
    self = new PWSclipboard();
  }
  return self;
}

/**
 * Destroy the instance
*/
void PWSclipboard::DeleteInstance()
{
  delete self;
  self = nullptr;
}

PWSclipboard::PWSclipboard(): m_set(false)
{
  memset(m_digest, 0, sizeof(m_digest));
}

/**
 * Put text data to clipboard
 * @param[in] data data to store in clipboard
 * @param isSensitive if data sensitive, we remeber it's hash and will clear on ClearData() call
 * @return \c true, if we could open the clipboard and put the data
*/
bool PWSclipboard::SetData(const StringX &data)
{
  wxMutexLocker clip(m_clipboardMutex);

  bool res = false;
  if (wxTheClipboard->Open()) {
    res = wxTheClipboard->SetData(new wxTextDataObject(data.c_str()));
    wxTheClipboard->Close();
  }
  m_set = true;
  if (res) {
    // identify data in clipboard as ours, so as not to clear the wrong data later
    // of course, we don't want an extra copy of a password floating around
    // in memory, so we'll use the hash
    SHA256 ctx;
    const wchar_t *str = data.c_str();
    ctx.Update(reinterpret_cast<const unsigned char *>(str), data.length()*sizeof(wchar_t));
    ctx.Final(m_digest);
  }
  return res;
}

/**
 * Clear from clipboard data, that we put there previously
 * @return \c true, if we cleared our data, or stored data don't belong to us
*/
bool PWSclipboard::ClearData()
{
  wxMutexLocker clip(m_clipboardMutex);

  if (m_set && wxTheClipboard->Open()) {
    wxTextDataObject obj;
    if (wxTheClipboard->IsSupported(wxDF_UNICODETEXT) && wxTheClipboard->GetData(obj)) {
      StringX buf(obj.GetText().data(), obj.GetText().size());
      if (buf.length()) {
        // check if the data on the clipboard is the same we put there
        unsigned char digest[SHA256::HASHLEN];
        SHA256 ctx;

        ctx.Update(reinterpret_cast<const unsigned char *>(buf.c_str()), buf.length()*sizeof(wchar_t));
        ctx.Final(digest);
        if (memcmp(digest, m_digest, SHA256::HASHLEN) == 0) {
          // clear & reset
          wxTheClipboard->Clear();
          memset(m_digest, 0, SHA256::HASHLEN);
          m_set = false;
          // Also trash data in buffer and clipboard somehow?
          pws_os::Trace0(L"Cleared our data from buffer.\n");
        }
        else{
          pws_os::Trace0(L"Buffer doesn't contain our data. Nothing to clear.\n");
        }
      }
    }
    wxTheClipboard->Close();
  }
  return !m_set;
}

#if defined(__X__) || defined(__WXGTK__)
/**
 * Set current clipboard buffer
 * @param primary if set to \c true, will use PRIMARY selection, otherwise CLIPBOARD X11
 * @param clearOnChange if set to \c true, our previous data will be cleared from previous buffer
 */
void PWSclipboard::UsePrimarySelection(bool primary, bool clearOnChange) {
  if (primary != wxTheClipboard->IsUsingPrimarySelection()) {
    if (clearOnChange)
      ClearData();
    wxTheClipboard->UsePrimarySelection(primary);
  }
}
#endif
<commit_msg>spelling: included<commit_after>/*
 * Copyright (c) 2003-2016 Rony Shapiro <ronys@pwsafe.org>.
 * All rights reserved. Use of the code is allowed under the
 * Artistic License 2.0 terms, as specified in the LICENSE file
 * distributed with this code, or available from
 * http://www.opensource.org/licenses/artistic-license-2.0.php
 */

#include <wx/clipbrd.h>
#include <wx/dataobj.h>

/*

  NOTE: In VS2013 wxWidgets 3.0.x builds:
    Both <wx/clipbrd.h> & <wx/dataobj.h> cause 51 warnings about using unsecure
    versions of standard calls, such as 'wcscpy' instead of 'wcscpy_s', if any
    previously included header file includes <string> even though pre-processor
    variables _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES and
    _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT are defined.

    The solution is to ensure that any header files containing <string>, e.g.
    "core/StringX.h", are placed after these two wxWidgets include statements.

  This issue did not occur with wxWidgets 2.8.12.

  For this reason, "pwsclip.h", which includes "core/StringX.h" that also includes
  <string>, is placed here after <wx/clipbrd.h> & <wx/dataobj.h>.

*/

#include "pwsclip.h"
#ifdef __WXMSW__
#include <wx/msw/msvcrt.h>
#endif

PWSclipboard *PWSclipboard::self = nullptr;

/**
 * Get pointer to single instance of clipboard manager
 */
PWSclipboard *PWSclipboard::GetInstance()
{
  if (self == nullptr) {
    self = new PWSclipboard();
  }
  return self;
}

/**
 * Destroy the instance
*/
void PWSclipboard::DeleteInstance()
{
  delete self;
  self = nullptr;
}

PWSclipboard::PWSclipboard(): m_set(false)
{
  memset(m_digest, 0, sizeof(m_digest));
}

/**
 * Put text data to clipboard
 * @param[in] data data to store in clipboard
 * @param isSensitive if data sensitive, we remeber it's hash and will clear on ClearData() call
 * @return \c true, if we could open the clipboard and put the data
*/
bool PWSclipboard::SetData(const StringX &data)
{
  wxMutexLocker clip(m_clipboardMutex);

  bool res = false;
  if (wxTheClipboard->Open()) {
    res = wxTheClipboard->SetData(new wxTextDataObject(data.c_str()));
    wxTheClipboard->Close();
  }
  m_set = true;
  if (res) {
    // identify data in clipboard as ours, so as not to clear the wrong data later
    // of course, we don't want an extra copy of a password floating around
    // in memory, so we'll use the hash
    SHA256 ctx;
    const wchar_t *str = data.c_str();
    ctx.Update(reinterpret_cast<const unsigned char *>(str), data.length()*sizeof(wchar_t));
    ctx.Final(m_digest);
  }
  return res;
}

/**
 * Clear from clipboard data, that we put there previously
 * @return \c true, if we cleared our data, or stored data don't belong to us
*/
bool PWSclipboard::ClearData()
{
  wxMutexLocker clip(m_clipboardMutex);

  if (m_set && wxTheClipboard->Open()) {
    wxTextDataObject obj;
    if (wxTheClipboard->IsSupported(wxDF_UNICODETEXT) && wxTheClipboard->GetData(obj)) {
      StringX buf(obj.GetText().data(), obj.GetText().size());
      if (buf.length()) {
        // check if the data on the clipboard is the same we put there
        unsigned char digest[SHA256::HASHLEN];
        SHA256 ctx;

        ctx.Update(reinterpret_cast<const unsigned char *>(buf.c_str()), buf.length()*sizeof(wchar_t));
        ctx.Final(digest);
        if (memcmp(digest, m_digest, SHA256::HASHLEN) == 0) {
          // clear & reset
          wxTheClipboard->Clear();
          memset(m_digest, 0, SHA256::HASHLEN);
          m_set = false;
          // Also trash data in buffer and clipboard somehow?
          pws_os::Trace0(L"Cleared our data from buffer.\n");
        }
        else{
          pws_os::Trace0(L"Buffer doesn't contain our data. Nothing to clear.\n");
        }
      }
    }
    wxTheClipboard->Close();
  }
  return !m_set;
}

#if defined(__X__) || defined(__WXGTK__)
/**
 * Set current clipboard buffer
 * @param primary if set to \c true, will use PRIMARY selection, otherwise CLIPBOARD X11
 * @param clearOnChange if set to \c true, our previous data will be cleared from previous buffer
 */
void PWSclipboard::UsePrimarySelection(bool primary, bool clearOnChange) {
  if (primary != wxTheClipboard->IsUsingPrimarySelection()) {
    if (clearOnChange)
      ClearData();
    wxTheClipboard->UsePrimarySelection(primary);
  }
}
#endif
<|endoftext|>


<commit_before>#include <atomic>
#include "../shell.h"
#include "../coroutine.h"
#include <teelogging/teelogging.h>
#include <gtest/gtest.h>

class CoroTest : testing::Test { };

using namespace cu;

TEST(CoroTest, Test1)
{
	std::vector<std::string> lines;
	cmd(find("../.."), grep("test_"), out(lines));
	for (auto& line : lines)
		std::cout << line << std::endl;
}

TEST(CoroTest, Test2)
{
	cmd(find("../.."),
		grep(".*\\.cpp$|.*\\.h$"),
		cat(),
		grep("class|struct|typedef|using|void|int|double|float"),
		grep_v("enable_if|;|\"|\'"),
		split(" "),
		trim(),
		uniq(),
		join(" "),
		out());
}

TEST(CoroTest, TestFind)
{
	std::cout << "using unix find" << std::endl;
	cmd(run("find ../.. -name '*.cpp' -o -name '*.h'"), out());
	std::cout << "using cppunix find" << std::endl;
	cmd(find("../.."), grep(".*\\.cpp$|.*\\.h$"), out());
}

TEST(CoroTest, DISABLED_TestCut)
{
	cmd(in("hello big world"), cut(0), out());
	cmd(in("hello big world"), cut(1), out());
	cmd(in("hello big world"), cut(2), out());
}

TEST(CoroTest, TestGrep)
{
	cmd(in("line1\nline2\nline3\n"), split("\n"), quote(), out());
	// cmd(in("line1\nline2\nline3\n"), split("\n"), grep("line2"), out(), assert_string("line2"), assert_count(1));
}

namespace cu {

class scheduler {
public:
	using control_type = void;

	template <typename Function>
	void spawn(Function&& func)
	{
		_running.emplace_back(cu::make_generator<control_type>(
			[f = std::move(func)](auto& yield) {
				yield();
				f(yield);
			}
		));
	}

	/*
	return true if any is updated
	*/
	bool run()
	{
		bool any_updated = false;
		_pid = 0;
		LOGD("total = %d", _running.size());
		for(auto& c : _running)
		{
			LOGD("ticking = %d", getpid());
			if(*c)
			{
				(*c)();
				any_updated = true;
			}
			++_pid;
		}
		return any_updated;
	}

	/*
	return true if have pending work
	*/
	// run_forever()
	// run_until_complete()
	void run_until_complete()
	{
		bool any_updated = true;
		while(any_updated)
		{
			any_updated = run();
		}
	}

	void run_forever()
	{
		while(true)
		{
			run();
		}
	}

	int getpid() const {return _pid;}

	inline void lock()
	{
		// cid id = whoiam_cpproutine()
		// if(is.is_running())
		// {
		//	move id to blocked
		// }
	}

	// auto create_semaphore();

	// each semaphore have 2 channels and ret code

protected:
	// normal running
	std::vector<cu::pull_type_ptr<control_type> > _running;
	// locked
	// std::vector<cu::pull_type_ptr<control_type> > _blocked;
	// unlocked and prepared for return
	// std::vector<cu::pull_type_ptr<control_type> > _ready;
private:
	int _pid;
};

class semaphore
{
public:
	explicit semaphore(scheduler& sche, int count = 0, int count_max = 1)
		: _sche(sche)
		, _count(count)
		, _count_max(count_max)
	{
		assert((1 <= count_max) || (0 <= count));
		assert(count <= count_max);
	}

	///
	/// Reduce el valor del semaforo. Bloquea la regi�n critica. Esta operaci�n tiene m�ltiples
	/// nombres.
	///  * wait (s)
	///	 * {
	///		  if s > 0
	///				s--
	///		  else // s == 0
	///				bloqueo
	///		}
	///
	///		esperar / wait / lock / down / sleep / P
	///
	inline void lock()
	{
		if(_count > 0)
		{
			--_count;
		}
		else
		{
			// bloquear esta cpproutine
			_sche.lock(); // (*this)
		}
	}
	///
	/// Aumenta el semaforo. Libera la region critica.
	///    signal(s)
	///    {
	///        if s == 0
	///            s++
	///        else // s > 0
	///            if s < MAX
	///                s++
	///    }
	///
	///	avisar / signal / unlock / up / wakeup / release / V
	///
	inline void unlock()
	{
		if((_count == 0) || (_count < _count_max))
		{
			++_count;
		}
		else
		{
			// ejecutar al primero que se bloqueo
		}
	}
protected:
	scheduler& _sche;
	int _count;
	int _count_max;
};

}

TEST(CoroTest, Test3)
{
	const int N = 16;
	cu::scheduler sch;
	for(int i=0; i<N; ++i)
	{
		sch.spawn([&sch, i](auto& yield) {
			std::cout << "create " << i << " - pid: " << sch.getpid() << std::endl;
			yield();
			std::cout << "download " << i << " - pid: " << sch.getpid() << std::endl;
			yield();
			std::cout << "patching " << i << " - pid: " << sch.getpid() << std::endl;
			yield();
			std::cout << "compile " << i << " - pid: " << sch.getpid() << std::endl;
			yield();
			std::cout << "tests " << i << " - pid: " << sch.getpid() << std::endl;
			yield();
			std::cout << "packing " << i << " - pid: " << sch.getpid() << std::endl;
			yield();
			std::cout << "destroy " << i << " - pid: " << sch.getpid() << std::endl;
		});
	}
	sch.run_until_complete();
}
<commit_msg>Update test_shell.cpp<commit_after>#include <atomic>
#include "../shell.h"
#include "../coroutine.h"
#include <teelogging/teelogging.h>
#include <gtest/gtest.h>

class CoroTest : testing::Test { };

using namespace cu;

TEST(CoroTest, DISABLED_Test1)
{
	std::vector<std::string> lines;
	cmd(find("../.."), grep("test_"), out(lines));
	for (auto& line : lines)
		std::cout << line << std::endl;
}

TEST(CoroTest, DISABLED_Test2)
{
	cmd(find("../.."),
		grep(".*\\.cpp$|.*\\.h$"),
		cat(),
		grep("class|struct|typedef|using|void|int|double|float"),
		grep_v("enable_if|;|\"|\'"),
		split(" "),
		trim(),
		uniq(),
		join(" "),
		out());
}

TEST(CoroTest, DISABLED_TestFind)
{
	std::cout << "using unix find" << std::endl;
	cmd(run("find ../.. -name '*.cpp' -o -name '*.h'"), out());
	std::cout << "using cppunix find" << std::endl;
	cmd(find("../.."), grep(".*\\.cpp$|.*\\.h$"), out());
}

TEST(CoroTest, DISABLED_TestCut)
{
	cmd(in("hello big world"), cut(0), out());
	cmd(in("hello big world"), cut(1), out());
	cmd(in("hello big world"), cut(2), out());
}

TEST(CoroTest, TestGrep)
{
	cmd(in("line1\nline2\nline3\n"), split("\n"), quote(), out());
	// cmd(in("line1\nline2\nline3\n"), split("\n"), grep("line2"), out(), assert_string("line2"), assert_count(1));
}

namespace cu {

class scheduler {
public:
	using control_type = void;

	template <typename Function>
	void spawn(Function&& func)
	{
		_running.emplace_back(cu::make_generator<control_type>(
			[f = std::move(func)](auto& yield) {
				yield();
				f(yield);
			}
		));
	}

	/*
	return true if any is updated
	*/
	bool run()
	{
		bool any_updated = false;
		_pid = 0;
		LOGD("total = %d", _running.size());
		for(auto& c : _running)
		{
			LOGD("ticking = %d", getpid());
			if(*c)
			{
				(*c)();
				any_updated = true;
			}
			++_pid;
		}
		return any_updated;
	}

	/*
	return true if have pending work
	*/
	// run_forever()
	// run_until_complete()
	void run_until_complete()
	{
		bool any_updated = true;
		while(any_updated)
		{
			any_updated = run();
		}
	}

	void run_forever()
	{
		while(true)
		{
			run();
		}
	}

	int getpid() const {return _pid;}

	inline void lock()
	{
		// cid id = whoiam_cpproutine()
		// if(is.is_running())
		// {
		//	move id to blocked
		// }
	}

	// auto create_semaphore();

	// each semaphore have 2 channels and ret code

protected:
	// normal running
	std::vector<cu::pull_type_ptr<control_type> > _running;
	// locked
	// std::vector<cu::pull_type_ptr<control_type> > _blocked;
	// unlocked and prepared for return
	// std::vector<cu::pull_type_ptr<control_type> > _ready;
private:
	int _pid;
};

class semaphore
{
public:
	explicit semaphore(scheduler& sche, int count = 0, int count_max = 1)
		: _sche(sche)
		, _count(count)
		, _count_max(count_max)
	{
		assert((1 <= count_max) || (0 <= count));
		assert(count <= count_max);
	}

	///
	/// Reduce el valor del semaforo. Bloquea la regi�n critica. Esta operaci�n tiene m�ltiples
	/// nombres.
	///  * wait (s)
	///	 * {
	///		  if s > 0
	///				s--
	///		  else // s == 0
	///				bloqueo
	///		}
	///
	///		esperar / wait / lock / down / sleep / P
	///
	inline void lock()
	{
		if(_count > 0)
		{
			--_count;
		}
		else
		{
			// bloquear esta cpproutine
			_sche.lock(); // (*this)
		}
	}
	///
	/// Aumenta el semaforo. Libera la region critica.
	///    signal(s)
	///    {
	///        if s == 0
	///            s++
	///        else // s > 0
	///            if s < MAX
	///                s++
	///    }
	///
	///	avisar / signal / unlock / up / wakeup / release / V
	///
	inline void unlock()
	{
		if((_count == 0) || (_count < _count_max))
		{
			++_count;
		}
		else
		{
			// ejecutar al primero que se bloqueo
		}
	}
protected:
	scheduler& _sche;
	int _count;
	int _count_max;
};

}

TEST(CoroTest, Test3)
{
	const int N = 16;
	cu::scheduler sch;
	for(int i=0; i<N; ++i)
	{
		sch.spawn([&sch, i](auto& yield) {
			std::cout << "create " << i << " - pid: " << sch.getpid() << std::endl;
			yield();
			std::cout << "download " << i << " - pid: " << sch.getpid() << std::endl;
			yield();
			std::cout << "patching " << i << " - pid: " << sch.getpid() << std::endl;
			yield();
			std::cout << "compile " << i << " - pid: " << sch.getpid() << std::endl;
			yield();
			std::cout << "tests " << i << " - pid: " << sch.getpid() << std::endl;
			yield();
			std::cout << "packing " << i << " - pid: " << sch.getpid() << std::endl;
			yield();
			std::cout << "destroy " << i << " - pid: " << sch.getpid() << std::endl;
		});
	}
	sch.run_until_complete();
}
<|endoftext|>


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

#include    <cstdio>
#include    <random>

#if     qPlatform_POSIX
//@todo see how many of these includes are needed
#include    <unistd.h>
#include    <arpa/inet.h>
#include    <net/if.h>
#include    <netinet/in.h>
#include    <netdb.h>
#include    <sys/socket.h>
#include    <sys/ioctl.h>
#if     qPlatform_Linux
#include    <linux/ethtool.h>
#include    <linux/sockios.h>
#endif
#elif   qPlatform_Windows
#include    <WinSock2.h>
#include    <WS2tcpip.h>
#include    <Iphlpapi.h>
#include    <netioapi.h>
#endif

#include    "../../Characters/CString/Utilities.h"
#include    "../../Characters/Format.h"
#include    "../../Containers/Collection.h"
#include    "../../Containers/Mapping.h"
#include    "../../Execution/ErrNoException.h"
#include    "../../Execution/Finally.h"
#if     qPlatform_Windows
#include    "../../../Foundation/Execution/Platform/Windows/Exception.h"
#endif
#include    "../../Execution/Synchronized.h"
#include    "../../Memory/SmallStackBuffer.h"

#include    "Socket.h"

#include    "Interface.h"

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

#if     defined (_MSC_VER)
// support use of Iphlpapi - but better to reference here than in lib entry of project file cuz
// easiser to see/modularize (and only pulled in if this module is referenced)
#pragma comment (lib, "Iphlpapi.lib")
#endif

/*
 ********************************************************************************
 **************************** Network::Interface ********************************
 ********************************************************************************
 */
const Configuration::EnumNames<Interface::Status>   Interface::Stroika_Enum_Names(Status)
{
    { Interface::Status::eConnected, L"Connected" },
    { Interface::Status::eRunning, L"Running" },
};

const Configuration::EnumNames<Interface::Type>   Interface::Stroika_Enum_Names(Type)
{
    { Interface::Type::eLoopback, L"Loopback" },
    { Interface::Type::eWiredEthernet, L"WiredEthernet" },
    { Interface::Type::eWIFI, L"WIFI" },
    { Interface::Type::eTunnel, L"Tunnel" },
    { Interface::Type::eOther, L"Other" },
};

#if     qPlatform_Linux
// Hack for centos5 support:
//      Overload with linux version so other one wins, but this gets called if other doesnt exist
//      TRY --LGP 2015-05-19
template    <typename HACK = int>
static __inline__ __u32 ethtool_cmd_speed (const struct ethtool_cmd* ep, HACK i = 0)
{
    //return (ep->speed_hi << 16) | ep->speed;
    return ep->speed;
}
#endif

/*
 ********************************************************************************
 ************************** Network::GetInterfaces ******************************
 ********************************************************************************
 */
Traversal::Iterable<Interface>  Network::GetInterfaces ()
{
    Collection<Interface>   result;
#if     qPlatform_POSIX
    auto getFlags = [] (int sd, const char* name) {
        char buf[1024];

        struct ifreq ifreq;
        memset(&ifreq, 0, sizeof (ifreq));
        strcpy (ifreq.ifr_name, name);

        int r = ioctl (sd, SIOCGIFFLAGS, (char*)&ifreq);
        Assert (r == 0);
        return ifreq.ifr_flags;
    };

    struct ifreq ifreqs[64];
    struct ifconf ifconf;
    memset (&ifconf, 0, sizeof(ifconf));
    ifconf.ifc_req = ifreqs;
    ifconf.ifc_len = sizeof(ifreqs);

    int sd = socket (PF_INET, SOCK_STREAM, 0);
    Assert (sd >= 0);
    Execution::Finally cleanup ([sd] () {
        close (sd);
    });

    int r = ioctl (sd, SIOCGIFCONF, (char*)&ifconf);
    Assert (r == 0);

    for (int i = 0; i < ifconf.ifc_len / sizeof(struct ifreq); ++i) {
#if     defined (_AIX)
        // I don't understand the logic behind this, but without this, we get errors
        // in getFlags(). We could check there - and handle that with an extra return value, but this
        // is simpler.
        //
        // And - its somewhat prescribed in https://www.ibm.com/developerworks/community/forums/html/topic?id=77777777-0000-0000-0000-000014698597
        //
        if (ifreqs[i].ifr_addr.sa_family != AF_INET) {
            continue;
        }
#endif
        Interface   newInterface;
        String interfaceName { String::FromSDKString (ifreqs[i].ifr_name) };
        newInterface.fInternalInterfaceID = interfaceName;
        newInterface.fFriendlyName = interfaceName;         // not great - maybe find better name - but this will do for now...
        int flags = getFlags (sd, ifreqs[i].ifr_name);

        if (flags & IFF_LOOPBACK) {
            newInterface.fType = Interface::Type::eLoopback;
        }
        else {
            // NYI
            newInterface.fType = Interface::Type::eWiredEthernet;    // WAY - not the right way to tell!
        }

#if     qPlatform_Linux
        {
            auto getSpeed = [] (int sd, const char* name) -> Optional<uint64_t> {
                struct ifreq ifreq;
                memset(&ifreq, 0, sizeof (ifreq));
                strcpy (ifreq.ifr_name, name);
                struct ethtool_cmd edata;
                memset (&edata, 0, sizeof (edata));
                ifreq.ifr_data = reinterpret_cast<caddr_t> (&edata);
                edata.cmd = ETHTOOL_GSET;
                int r = ioctl(sd, SIOCETHTOOL, &ifreq);
                if (r != 0)
                {
                    DbgTrace ("No speed for interface %s, errno=%d", name, errno);
                    return Optional<uint64_t> ();
                }
                constexpr uint64_t kMegabit_ = 1000 * 1000;
                DbgTrace ("ethtool_cmd_speed (&edata)=%d", ethtool_cmd_speed (&edata));
                switch (ethtool_cmd_speed (&edata))
                {
                    case SPEED_10:
                        return 10 * kMegabit_;
                    case SPEED_100:
                        return 100 * kMegabit_;
                    case SPEED_1000:
                        return 1000 * kMegabit_;
                    case SPEED_2500:
                        return 2500 * kMegabit_;
                    case SPEED_10000:
                        return 10000 * kMegabit_;
                    default:
                        return Optional<uint64_t> ();
                }
            };
            newInterface.fTransmitSpeedBaud = getSpeed (sd, ifreqs[i].ifr_name);
            newInterface.fReceiveLinkSpeedBaud = newInterface.fTransmitSpeedBaud;
        }
#endif

        {
            Containers::Set<Interface::Status>  status;
            if (flags & IFF_RUNNING) {
                // not right!!! But a start...
                status.Add (Interface::Status::eConnected);
                status.Add (Interface::Status::eRunning);
            }
            newInterface.fStatus = status;
        }
        newInterface.fBindings.Add (InternetAddress (((struct sockaddr_in*)&ifreqs[i].ifr_addr)->sin_addr));    // @todo fix so works for ipv6 addresses as well!
        result.Add (newInterface);
    }
#elif   qPlatform_Windows
    ULONG flags = GAA_FLAG_INCLUDE_PREFIX;
    ULONG family = AF_UNSPEC;       // Both IPv4 and IPv6 addresses
    Memory::SmallStackBuffer<Byte>  buf(0);
Again:
    ULONG ulOutBufLen = static_cast<ULONG> (buf.GetSize ());
    PIP_ADAPTER_ADDRESSES   pAddresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES> (buf.begin ());
    // NB: we use GetAdapaterAddresses () instead of GetInterfaceInfo  () so we get non-ipv4 addresses
    DWORD dwRetVal = ::GetAdaptersAddresses (family, flags, nullptr, pAddresses, &ulOutBufLen);
    if (dwRetVal == NO_ERROR) {
        for (PIP_ADAPTER_ADDRESSES currAddresses  = pAddresses; currAddresses != nullptr; currAddresses = currAddresses->Next) {
            Interface   newInterface;
            String      adapterName     { String::FromNarrowSDKString (currAddresses->AdapterName) };
            newInterface.fInternalInterfaceID = adapterName;
            newInterface.fFriendlyName = currAddresses->FriendlyName;
            newInterface.fDescription = currAddresses->Description;
            switch (currAddresses->IfType) {
                case    IF_TYPE_SOFTWARE_LOOPBACK:
                    newInterface.fType = Interface::Type::eLoopback;
                    break;
                case    IF_TYPE_IEEE80211:
                    newInterface.fType = Interface::Type::eWIFI;
                    break;
                case    IF_TYPE_ETHERNET_CSMACD:
                    newInterface.fType = Interface::Type::eWiredEthernet;
                    break;
                default:
                    newInterface.fType = Interface::Type::eOther;
                    break;
            }
            if (currAddresses->TunnelType != TUNNEL_TYPE_NONE) {
                newInterface.fType = Interface::Type::eTunnel;
            }
            switch (currAddresses->OperStatus) {
                case    IfOperStatusUp:
                    newInterface.fStatus = Set<Interface::Status> ({Interface::Status::eConnected, Interface::Status::eRunning});
                    break;
                case    IfOperStatusDown:
                    newInterface.fStatus = Set<Interface::Status> ();
                    break;
                default:
                    // Dont know how to interpret the other status states
                    break;
            }
            for (PIP_ADAPTER_UNICAST_ADDRESS pu = currAddresses->FirstUnicastAddress; pu != nullptr; pu = pu->Next) {
                SocketAddress sa { pu->Address };
                if (sa.IsInternetAddress ()) {
                    newInterface.fBindings.Add (sa.GetInternetAddress ());
                }
            }
            for (PIP_ADAPTER_ANYCAST_ADDRESS pa = currAddresses->FirstAnycastAddress; pa != nullptr; pa = pa->Next) {
                SocketAddress sa { pa->Address };
                if (sa.IsInternetAddress ()) {
                    newInterface.fBindings.Add (sa.GetInternetAddress ());
                }
            }
            for (PIP_ADAPTER_MULTICAST_ADDRESS  pm = currAddresses->FirstMulticastAddress; pm != nullptr; pm = pm->Next) {
                SocketAddress sa { pm->Address };
                if (sa.IsInternetAddress ()) {
                    newInterface.fBindings.Add (sa.GetInternetAddress ());
                }

            }
#if     (NTDDI_VERSION >= NTDDI_WIN6)
            newInterface.fTransmitSpeedBaud = currAddresses->TransmitLinkSpeed;
            newInterface.fReceiveLinkSpeedBaud = currAddresses->ReceiveLinkSpeed;
#endif
            result.Add (newInterface);
        }
    }
    else if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
        buf.GrowToSize (ulOutBufLen);
        goto Again;
    }
    else if (dwRetVal == ERROR_NO_DATA) {
        DbgTrace ("There are no network adapters with IPv4 enabled on the local system");
    }
    else {
        Execution::Platform::Windows::Exception::DoThrow (dwRetVal);
    }
#else
    AssertNotImplemented ();
#endif
    return result;
}

/*
 ********************************************************************************
 ************************** Network::GetInterfaceById ***************************
 ********************************************************************************
 */
Optional<Interface>  Network::GetInterfaceById (const String& internalInterfaceID)
{
    // @todo - a much more efficent implemenation
    for (Interface i : Network::GetInterfaces ()) {
        if (i.fInternalInterfaceID == internalInterfaceID) {
            return i;
        }
    }
    return Optional<Interface> ();
}
<commit_msg>fix includes for RedHat5<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2015.  All rights reserved
 */
#include    "../../StroikaPreComp.h"

#include    <cstdio>
#include    <random>

#if     qPlatform_POSIX
//@todo see how many of these includes are needed
#include    <unistd.h>
#include    <arpa/inet.h>
#include    <net/if.h>
#include    <netinet/in.h>
#include    <netdb.h>
#include    <sys/socket.h>
#include    <sys/ioctl.h>
#if     qPlatform_Linux
#include    <linux/types.h>     // needed on RedHat5
#include    <linux/ethtool.h>
#include    <linux/sockios.h>
#endif
#elif   qPlatform_Windows
#include    <WinSock2.h>
#include    <WS2tcpip.h>
#include    <Iphlpapi.h>
#include    <netioapi.h>
#endif

#include    "../../Characters/CString/Utilities.h"
#include    "../../Characters/Format.h"
#include    "../../Containers/Collection.h"
#include    "../../Containers/Mapping.h"
#include    "../../Execution/ErrNoException.h"
#include    "../../Execution/Finally.h"
#if     qPlatform_Windows
#include    "../../../Foundation/Execution/Platform/Windows/Exception.h"
#endif
#include    "../../Execution/Synchronized.h"
#include    "../../Memory/SmallStackBuffer.h"

#include    "Socket.h"

#include    "Interface.h"

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

#if     defined (_MSC_VER)
// support use of Iphlpapi - but better to reference here than in lib entry of project file cuz
// easiser to see/modularize (and only pulled in if this module is referenced)
#pragma comment (lib, "Iphlpapi.lib")
#endif

/*
 ********************************************************************************
 **************************** Network::Interface ********************************
 ********************************************************************************
 */
const Configuration::EnumNames<Interface::Status>   Interface::Stroika_Enum_Names(Status)
{
    { Interface::Status::eConnected, L"Connected" },
    { Interface::Status::eRunning, L"Running" },
};

const Configuration::EnumNames<Interface::Type>   Interface::Stroika_Enum_Names(Type)
{
    { Interface::Type::eLoopback, L"Loopback" },
    { Interface::Type::eWiredEthernet, L"WiredEthernet" },
    { Interface::Type::eWIFI, L"WIFI" },
    { Interface::Type::eTunnel, L"Tunnel" },
    { Interface::Type::eOther, L"Other" },
};

#if     qPlatform_Linux
// Hack for centos5 support:
//      Overload with linux version so other one wins, but this gets called if other doesnt exist
//      TRY --LGP 2015-05-19
template    <typename HACK = int>
static __inline__ __u32 ethtool_cmd_speed (const struct ethtool_cmd* ep, HACK i = 0)
{
    //return (ep->speed_hi << 16) | ep->speed;
    return ep->speed;
}
#endif

/*
 ********************************************************************************
 ************************** Network::GetInterfaces ******************************
 ********************************************************************************
 */
Traversal::Iterable<Interface>  Network::GetInterfaces ()
{
    Collection<Interface>   result;
#if     qPlatform_POSIX
    auto getFlags = [] (int sd, const char* name) {
        char buf[1024];

        struct ifreq ifreq;
        memset(&ifreq, 0, sizeof (ifreq));
        strcpy (ifreq.ifr_name, name);

        int r = ioctl (sd, SIOCGIFFLAGS, (char*)&ifreq);
        Assert (r == 0);
        return ifreq.ifr_flags;
    };

    struct ifreq ifreqs[64];
    struct ifconf ifconf;
    memset (&ifconf, 0, sizeof(ifconf));
    ifconf.ifc_req = ifreqs;
    ifconf.ifc_len = sizeof(ifreqs);

    int sd = socket (PF_INET, SOCK_STREAM, 0);
    Assert (sd >= 0);
    Execution::Finally cleanup ([sd] () {
        close (sd);
    });

    int r = ioctl (sd, SIOCGIFCONF, (char*)&ifconf);
    Assert (r == 0);

    for (int i = 0; i < ifconf.ifc_len / sizeof(struct ifreq); ++i) {
#if     defined (_AIX)
        // I don't understand the logic behind this, but without this, we get errors
        // in getFlags(). We could check there - and handle that with an extra return value, but this
        // is simpler.
        //
        // And - its somewhat prescribed in https://www.ibm.com/developerworks/community/forums/html/topic?id=77777777-0000-0000-0000-000014698597
        //
        if (ifreqs[i].ifr_addr.sa_family != AF_INET) {
            continue;
        }
#endif
        Interface   newInterface;
        String interfaceName { String::FromSDKString (ifreqs[i].ifr_name) };
        newInterface.fInternalInterfaceID = interfaceName;
        newInterface.fFriendlyName = interfaceName;         // not great - maybe find better name - but this will do for now...
        int flags = getFlags (sd, ifreqs[i].ifr_name);

        if (flags & IFF_LOOPBACK) {
            newInterface.fType = Interface::Type::eLoopback;
        }
        else {
            // NYI
            newInterface.fType = Interface::Type::eWiredEthernet;    // WAY - not the right way to tell!
        }

#if     qPlatform_Linux
        {
            auto getSpeed = [] (int sd, const char* name) -> Optional<uint64_t> {
                struct ifreq ifreq;
                memset(&ifreq, 0, sizeof (ifreq));
                strcpy (ifreq.ifr_name, name);
                struct ethtool_cmd edata;
                memset (&edata, 0, sizeof (edata));
                ifreq.ifr_data = reinterpret_cast<caddr_t> (&edata);
                edata.cmd = ETHTOOL_GSET;
                int r = ioctl(sd, SIOCETHTOOL, &ifreq);
                if (r != 0)
                {
                    DbgTrace ("No speed for interface %s, errno=%d", name, errno);
                    return Optional<uint64_t> ();
                }
                constexpr uint64_t kMegabit_ = 1000 * 1000;
                DbgTrace ("ethtool_cmd_speed (&edata)=%d", ethtool_cmd_speed (&edata));
                switch (ethtool_cmd_speed (&edata))
                {
                    case SPEED_10:
                        return 10 * kMegabit_;
                    case SPEED_100:
                        return 100 * kMegabit_;
                    case SPEED_1000:
                        return 1000 * kMegabit_;
                    case SPEED_2500:
                        return 2500 * kMegabit_;
                    case SPEED_10000:
                        return 10000 * kMegabit_;
                    default:
                        return Optional<uint64_t> ();
                }
            };
            newInterface.fTransmitSpeedBaud = getSpeed (sd, ifreqs[i].ifr_name);
            newInterface.fReceiveLinkSpeedBaud = newInterface.fTransmitSpeedBaud;
        }
#endif

        {
            Containers::Set<Interface::Status>  status;
            if (flags & IFF_RUNNING) {
                // not right!!! But a start...
                status.Add (Interface::Status::eConnected);
                status.Add (Interface::Status::eRunning);
            }
            newInterface.fStatus = status;
        }
        newInterface.fBindings.Add (InternetAddress (((struct sockaddr_in*)&ifreqs[i].ifr_addr)->sin_addr));    // @todo fix so works for ipv6 addresses as well!
        result.Add (newInterface);
    }
#elif   qPlatform_Windows
    ULONG flags = GAA_FLAG_INCLUDE_PREFIX;
    ULONG family = AF_UNSPEC;       // Both IPv4 and IPv6 addresses
    Memory::SmallStackBuffer<Byte>  buf(0);
Again:
    ULONG ulOutBufLen = static_cast<ULONG> (buf.GetSize ());
    PIP_ADAPTER_ADDRESSES   pAddresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES> (buf.begin ());
    // NB: we use GetAdapaterAddresses () instead of GetInterfaceInfo  () so we get non-ipv4 addresses
    DWORD dwRetVal = ::GetAdaptersAddresses (family, flags, nullptr, pAddresses, &ulOutBufLen);
    if (dwRetVal == NO_ERROR) {
        for (PIP_ADAPTER_ADDRESSES currAddresses  = pAddresses; currAddresses != nullptr; currAddresses = currAddresses->Next) {
            Interface   newInterface;
            String      adapterName     { String::FromNarrowSDKString (currAddresses->AdapterName) };
            newInterface.fInternalInterfaceID = adapterName;
            newInterface.fFriendlyName = currAddresses->FriendlyName;
            newInterface.fDescription = currAddresses->Description;
            switch (currAddresses->IfType) {
                case    IF_TYPE_SOFTWARE_LOOPBACK:
                    newInterface.fType = Interface::Type::eLoopback;
                    break;
                case    IF_TYPE_IEEE80211:
                    newInterface.fType = Interface::Type::eWIFI;
                    break;
                case    IF_TYPE_ETHERNET_CSMACD:
                    newInterface.fType = Interface::Type::eWiredEthernet;
                    break;
                default:
                    newInterface.fType = Interface::Type::eOther;
                    break;
            }
            if (currAddresses->TunnelType != TUNNEL_TYPE_NONE) {
                newInterface.fType = Interface::Type::eTunnel;
            }
            switch (currAddresses->OperStatus) {
                case    IfOperStatusUp:
                    newInterface.fStatus = Set<Interface::Status> ({Interface::Status::eConnected, Interface::Status::eRunning});
                    break;
                case    IfOperStatusDown:
                    newInterface.fStatus = Set<Interface::Status> ();
                    break;
                default:
                    // Dont know how to interpret the other status states
                    break;
            }
            for (PIP_ADAPTER_UNICAST_ADDRESS pu = currAddresses->FirstUnicastAddress; pu != nullptr; pu = pu->Next) {
                SocketAddress sa { pu->Address };
                if (sa.IsInternetAddress ()) {
                    newInterface.fBindings.Add (sa.GetInternetAddress ());
                }
            }
            for (PIP_ADAPTER_ANYCAST_ADDRESS pa = currAddresses->FirstAnycastAddress; pa != nullptr; pa = pa->Next) {
                SocketAddress sa { pa->Address };
                if (sa.IsInternetAddress ()) {
                    newInterface.fBindings.Add (sa.GetInternetAddress ());
                }
            }
            for (PIP_ADAPTER_MULTICAST_ADDRESS  pm = currAddresses->FirstMulticastAddress; pm != nullptr; pm = pm->Next) {
                SocketAddress sa { pm->Address };
                if (sa.IsInternetAddress ()) {
                    newInterface.fBindings.Add (sa.GetInternetAddress ());
                }

            }
#if     (NTDDI_VERSION >= NTDDI_WIN6)
            newInterface.fTransmitSpeedBaud = currAddresses->TransmitLinkSpeed;
            newInterface.fReceiveLinkSpeedBaud = currAddresses->ReceiveLinkSpeed;
#endif
            result.Add (newInterface);
        }
    }
    else if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
        buf.GrowToSize (ulOutBufLen);
        goto Again;
    }
    else if (dwRetVal == ERROR_NO_DATA) {
        DbgTrace ("There are no network adapters with IPv4 enabled on the local system");
    }
    else {
        Execution::Platform::Windows::Exception::DoThrow (dwRetVal);
    }
#else
    AssertNotImplemented ();
#endif
    return result;
}

/*
 ********************************************************************************
 ************************** Network::GetInterfaceById ***************************
 ********************************************************************************
 */
Optional<Interface>  Network::GetInterfaceById (const String& internalInterfaceID)
{
    // @todo - a much more efficent implemenation
    for (Interface i : Network::GetInterfaces ()) {
        if (i.fInternalInterfaceID == internalInterfaceID) {
            return i;
        }
    }
    return Optional<Interface> ();
}
<|endoftext|>