<commit_before>#include <sequanto/QtWidgetAutomationEventFilter.h>
#include <sequanto/QtAutomationGetPropertyEvent.h>
#include <sequanto/QtAutomationMoveEvent.h>
#include <sequanto/QtAutomationResizeEvent.h>
#include <sequanto/QtWidgetNode.h>
#include <sequanto/ui.h>
#include <cassert>

#include <sequanto/QtWrapper.h>

using namespace sequanto::automation;

QtWidgetAutomationEventFilter::QtWidgetAutomationEventFilter ( QtWidgetNode * _node )
    : QObject(_node->widget()),
      m_node ( _node )
{
}

bool QtWidgetAutomationEventFilter::eventFilter ( QObject * _object, QEvent * _event )
{
    switch ( _event->type() )
    {
    case QEvent::ChildAdded:
       {
          QChildEvent * childEvent = dynamic_cast<QChildEvent*>(_event );
          QObject * child = childEvent->child();
          if ( child->isWidgetType () )
          {
             QtWrapper::Log ( QString("%1: Adding child %2").arg(m_node->GetFullName().c_str()).arg(QtWrapper::GetObjectName(child).c_str()) );
             if ( m_node->AddChildWidget ( qobject_cast<QWidget*>(child) ) )
             {
                m_node->SendChildrenUpdate();
             }
          }
       }
       break;

/*
    case QEvent::ChildRemoved:
       {
          QChildEvent * childEvent = dynamic_cast<QChildEvent*>(_event );
          QObject * child = childEvent->child();
          if ( child->isWidgetType () )
          {
             QtWrapper::Log ( QString("%1: Removing child %2").arg(m_node->GetFullName().c_str()).arg(QtWrapper::GetObjectName(child).c_str()) );
             if ( m_node->RemoveChildWidget ( qobject_cast<QWidget*>(child) ) )
             {
                m_node->SendChildrenUpdate();
             }
          }
       }
       break;
*/

    case QEvent::Move:

        if ( qobject_cast<QMainWindow*>(_object) != NULL || qobject_cast<QDialog*>(_object) != NULL )
        {
			Node * screenX = m_node->FindChild(SQ_UI_WINDOW_SCREEN_X);
			Node * screenY = m_node->FindChild(SQ_UI_WINDOW_SCREEN_Y);
			if ( screenX != NULL )
			{
				dynamic_cast<IntegerPropertyNode*> ( screenX )->SendUpdate();
			}
			if ( screenY != NULL )
			{
				dynamic_cast<IntegerPropertyNode*> ( screenY )->SendUpdate();
			}
        }
        else
        {
            dynamic_cast<IntegerPropertyNode*> ( m_node->FindChild(SQ_UI_NODE_X) )->SendUpdate();
            dynamic_cast<IntegerPropertyNode*> ( m_node->FindChild(SQ_UI_NODE_Y) )->SendUpdate();
        }
        break;

    case QEvent::Resize:
        dynamic_cast<IntegerPropertyNode*> ( m_node->FindChild(SQ_UI_NODE_WIDTH) )->SendUpdate();
        dynamic_cast<IntegerPropertyNode*> ( m_node->FindChild(SQ_UI_NODE_HEIGHT) )->SendUpdate();
        break;
    }

    if ( _event->type() == QtAutomationMoveEvent::ID )
    {
        QPoint position = dynamic_cast<QtAutomationMoveEvent*>(_event)->position();
        qobject_cast<QWidget*> ( _object )->move ( position );
        return true;
    }
    else if ( _event->type() == QtAutomationResizeEvent::ID )
    {
        QSize size = dynamic_cast<QtAutomationResizeEvent*>(_event)->size();
        qobject_cast<QWidget*> ( _object )->resize ( size );
        return true;
    }
    else if ( _event->type() == QtAutomationGetPropertyEvent::ID )
    {
        QtAutomationGetPropertyEvent * event = dynamic_cast<QtAutomationGetPropertyEvent*>(_event);

        if ( event->propertyName() == QtWrapper::screen_pos() )
        {
            QWidget * widget = qobject_cast<QWidget*>(_object);
            event->done ( widget->geometry().topLeft() );
        }
        else if ( event->propertyName() == QtWrapper::global_pos() )
        {
            QWidget * widget = qobject_cast<QWidget*>(_object);
            QWidget * window = widget->window();

            QPoint pos = widget->mapToGlobal(QPoint(0,0));
            QPoint windowTopLeft ( window->geometry().topLeft() );

            pos -= windowTopLeft;

            //QPoint pos = widget->mapTo ( window, widget->pos() );

            //qDebug() << "Filtered: " << pos;

            event->done ( pos );
        }
        else
        {
            event->done ( qobject_cast<QWidget*> ( _object )->property ( event->propertyName() ) );
        }
        return true;
    }
    else
    {
        return QObject::eventFilter(_object, _event );
    }
}

QtWidgetAutomationEventFilter::~QtWidgetAutomationEventFilter ()
{
   m_node->WidgetDestroyed();
}
<commit_msg>Support QEvent::EnabledChange event and send an !UPDATE event for the enabled property.<commit_after>#include <sequanto/QtWidgetAutomationEventFilter.h>
#include <sequanto/QtAutomationGetPropertyEvent.h>
#include <sequanto/QtAutomationMoveEvent.h>
#include <sequanto/QtAutomationResizeEvent.h>
#include <sequanto/QtWidgetNode.h>
#include <sequanto/ui.h>
#include <cassert>

#include <sequanto/QtWrapper.h>

using namespace sequanto::automation;

QtWidgetAutomationEventFilter::QtWidgetAutomationEventFilter ( QtWidgetNode * _node )
    : QObject(_node->widget()),
      m_node ( _node )
{
}

bool QtWidgetAutomationEventFilter::eventFilter ( QObject * _object, QEvent * _event )
{
    switch ( _event->type() )
    {
    case QEvent::ChildAdded:
       {
          QChildEvent * childEvent = dynamic_cast<QChildEvent*>(_event );
          QObject * child = childEvent->child();
          if ( child->isWidgetType () )
          {
             QtWrapper::Log ( QString("%1: Adding child %2").arg(m_node->GetFullName().c_str()).arg(QtWrapper::GetObjectName(child).c_str()) );
             if ( m_node->AddChildWidget ( qobject_cast<QWidget*>(child) ) )
             {
                m_node->SendChildrenUpdate();
             }
          }
       }
       break;

/*
    case QEvent::ChildRemoved:
       {
          QChildEvent * childEvent = dynamic_cast<QChildEvent*>(_event );
          QObject * child = childEvent->child();
          if ( child->isWidgetType () )
          {
             QtWrapper::Log ( QString("%1: Removing child %2").arg(m_node->GetFullName().c_str()).arg(QtWrapper::GetObjectName(child).c_str()) );
             if ( m_node->RemoveChildWidget ( qobject_cast<QWidget*>(child) ) )
             {
                m_node->SendChildrenUpdate();
             }
          }
       }
       break;
*/

    case QEvent::Move:

        if ( qobject_cast<QMainWindow*>(_object) != NULL || qobject_cast<QDialog*>(_object) != NULL )
        {
			Node * screenX = m_node->FindChild(SQ_UI_WINDOW_SCREEN_X);
			Node * screenY = m_node->FindChild(SQ_UI_WINDOW_SCREEN_Y);
			if ( screenX != NULL )
			{
				dynamic_cast<IntegerPropertyNode*> ( screenX )->SendUpdate();
			}
			if ( screenY != NULL )
			{
				dynamic_cast<IntegerPropertyNode*> ( screenY )->SendUpdate();
			}
        }
        else
        {
            dynamic_cast<IntegerPropertyNode*> ( m_node->FindChild(SQ_UI_NODE_X) )->SendUpdate();
            dynamic_cast<IntegerPropertyNode*> ( m_node->FindChild(SQ_UI_NODE_Y) )->SendUpdate();
        }
        break;

    case QEvent::Resize:
        dynamic_cast<IntegerPropertyNode*> ( m_node->FindChild(SQ_UI_NODE_WIDTH) )->SendUpdate();
        dynamic_cast<IntegerPropertyNode*> ( m_node->FindChild(SQ_UI_NODE_HEIGHT) )->SendUpdate();
        break;

    case QEvent::EnabledChange:
       dynamic_cast<BooleanPropertyNode*> ( m_node->FindChild(SQ_UI_NODE_ENABLED) )->SendUpdate();
       break;
    }

    if ( _event->type() == QtAutomationMoveEvent::ID )
    {
        QPoint position = dynamic_cast<QtAutomationMoveEvent*>(_event)->position();
        qobject_cast<QWidget*> ( _object )->move ( position );
        return true;
    }
    else if ( _event->type() == QtAutomationResizeEvent::ID )
    {
        QSize size = dynamic_cast<QtAutomationResizeEvent*>(_event)->size();
        qobject_cast<QWidget*> ( _object )->resize ( size );
        return true;
    }
    else if ( _event->type() == QtAutomationGetPropertyEvent::ID )
    {
        QtAutomationGetPropertyEvent * event = dynamic_cast<QtAutomationGetPropertyEvent*>(_event);

        if ( event->propertyName() == QtWrapper::screen_pos() )
        {
            QWidget * widget = qobject_cast<QWidget*>(_object);
            event->done ( widget->geometry().topLeft() );
        }
        else if ( event->propertyName() == QtWrapper::global_pos() )
        {
            QWidget * widget = qobject_cast<QWidget*>(_object);
            QWidget * window = widget->window();

            QPoint pos = widget->mapToGlobal(QPoint(0,0));
            QPoint windowTopLeft ( window->geometry().topLeft() );

            pos -= windowTopLeft;

            //QPoint pos = widget->mapTo ( window, widget->pos() );

            //qDebug() << "Filtered: " << pos;

            event->done ( pos );
        }
        else
        {
            event->done ( qobject_cast<QWidget*> ( _object )->property ( event->propertyName() ) );
        }
        return true;
    }
    else
    {
        return QObject::eventFilter(_object, _event );
    }
}

QtWidgetAutomationEventFilter::~QtWidgetAutomationEventFilter ()
{
   m_node->WidgetDestroyed();
}
<|endoftext|>


<commit_before>#include "CoreSuite.h"
#include "TestAssertTest.h"
#include <algorithm>

/*
 Note:
 - tests need to be added to test asserEquals() template function and
 use of assertion traits. Some check may need to be added to check
 the message content in Exception.
 - code need to be refactored with the use of a test caller that expect
 an exception.
 */

CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TestAssertTest,
                                       coreSuiteName() );

TestAssertTest::TestAssertTest()
{
}

TestAssertTest::~TestAssertTest()
{
}

void
TestAssertTest::setUp()
{
}

void
TestAssertTest::tearDown()
{
}

void
TestAssertTest::testAssertThrow()
{
   CPPUNIT_ASSERT_THROW( throw std::string(), std::string );

   try
   {
      int x;
      CPPUNIT_ASSERT_THROW( x = 1234, std::string );
   }
   catch ( CPPUNIT_NS::Exception & )
   {
      return;
   }

   throw std::exception();
}

void
TestAssertTest::testAssertNoThrow()
{
   int x;
   CPPUNIT_ASSERT_NO_THROW( x = 1234 );

   try
   {
      CPPUNIT_ASSERT_NO_THROW( throw std::exception() );
   }
   catch ( CPPUNIT_NS::Exception & )
   {
      return;
   }
   throw std::exception();
}

void
TestAssertTest::testAssertAssertionFail()
{
   CPPUNIT_ASSERT_ASSERTION_FAIL( throw CPPUNIT_NS::Exception() );

   try
   {
      int x;
      CPPUNIT_ASSERT_ASSERTION_FAIL( x = 1234 );
   }
   catch ( CPPUNIT_NS::Exception & )
   {
      return;
   }

   throw std::exception();
}

void
TestAssertTest::testAssertAssertionPass()
{
   int x;
   CPPUNIT_ASSERT_ASSERTION_PASS( x = 1234 );

   try
   {
      CPPUNIT_ASSERT_ASSERTION_PASS( throw CPPUNIT_NS::Exception() );
   }
   catch ( CPPUNIT_NS::Exception & )
   {
      return;
   }

   throw std::exception();
}

void
TestAssertTest::testAssert()
{
  CPPUNIT_ASSERT_ASSERTION_PASS( CPPUNIT_ASSERT( true ) );

  CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT( false ) );
}

static int foo() { return 1; }

void
TestAssertTest::testAssertEqual()
{
  CPPUNIT_ASSERT_ASSERTION_PASS( CPPUNIT_ASSERT_EQUAL( 1, 1 ) );
  CPPUNIT_ASSERT_ASSERTION_PASS( CPPUNIT_ASSERT_EQUAL( 1, foo() ) );
  CPPUNIT_ASSERT_ASSERTION_PASS( CPPUNIT_ASSERT_EQUAL( 12345678, 12345678 ) );

  CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_EQUAL( 1, 2 ) );
}

void
TestAssertTest::testAssertMessageTrue()
{
  CPPUNIT_ASSERT_ASSERTION_PASS(
     CPPUNIT_ASSERT_MESSAGE( "This test should not failed", true ) );
}

void
TestAssertTest::testAssertMessageFalse()
{
  bool exceptionCaught = false;
  std::string message( "This test message should not be seen" );
  try
  {
    CPPUNIT_ASSERT_MESSAGE( message, 2==3 );
  }
  catch( CPPUNIT_NS::Exception &e )
  {
    exceptionCaught = true; // ok, we were expecting an exception.
    checkMessageContains( &e, message );
  }

  CPPUNIT_ASSERT( exceptionCaught );
}

void
TestAssertTest::testAssertDoubleEquals()
{
  CPPUNIT_ASSERT_ASSERTION_PASS( CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.1, 1.2, 0.101 ) );
  CPPUNIT_ASSERT_ASSERTION_PASS( CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.2, 1.1, 0.101 ) );

  CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.1, 1.2, 0.09 ) );
  CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.2, 1.1, 0.09 ) );
}

void
TestAssertTest::testFail()
{
  bool exceptionCaught = false;
  std::string failure( "FailureMessage" );
  try
  {
    CPPUNIT_FAIL( failure );
  }
  catch( CPPUNIT_NS::Exception &e )
  {
    exceptionCaught = true;
    checkMessageContains( &e, failure );
  }
  CPPUNIT_ASSERT( exceptionCaught );
}

void
TestAssertTest::checkMessageContains( CPPUNIT_NS::Exception *e,
                                      std::string expected )
{
  std::string actual = e->what();
  CPPUNIT_ASSERT_MESSAGE( "Expected message not found: " + expected +
                          ", was: " + actual,
      std::search( actual.begin(), actual.end(),
                   expected.begin(), expected.end() ) != actual.end() );
}
<commit_msg>Add unit tests for CPPUNIT_ASSERT_DOUBLES_EQUAL() that test with non-finite values.<commit_after>#include "CoreSuite.h"
#include "TestAssertTest.h"
#include <algorithm>

/*
 Note:
 - tests need to be added to test asserEquals() template function and
 use of assertion traits. Some check may need to be added to check
 the message content in Exception.
 - code need to be refactored with the use of a test caller that expect
 an exception.
 */

CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( TestAssertTest,
                                       coreSuiteName() );

TestAssertTest::TestAssertTest()
{
}

TestAssertTest::~TestAssertTest()
{
}

void
TestAssertTest::setUp()
{
}

void
TestAssertTest::tearDown()
{
}

void
TestAssertTest::testAssertThrow()
{
   CPPUNIT_ASSERT_THROW( throw std::string(), std::string );

   try
   {
      int x;
      CPPUNIT_ASSERT_THROW( x = 1234, std::string );
   }
   catch ( CPPUNIT_NS::Exception & )
   {
      return;
   }

   throw std::exception();
}

void
TestAssertTest::testAssertNoThrow()
{
   int x;
   CPPUNIT_ASSERT_NO_THROW( x = 1234 );

   try
   {
      CPPUNIT_ASSERT_NO_THROW( throw std::exception() );
   }
   catch ( CPPUNIT_NS::Exception & )
   {
      return;
   }
   throw std::exception();
}

void
TestAssertTest::testAssertAssertionFail()
{
   CPPUNIT_ASSERT_ASSERTION_FAIL( throw CPPUNIT_NS::Exception() );

   try
   {
      int x;
      CPPUNIT_ASSERT_ASSERTION_FAIL( x = 1234 );
   }
   catch ( CPPUNIT_NS::Exception & )
   {
      return;
   }

   throw std::exception();
}

void
TestAssertTest::testAssertAssertionPass()
{
   int x;
   CPPUNIT_ASSERT_ASSERTION_PASS( x = 1234 );

   try
   {
      CPPUNIT_ASSERT_ASSERTION_PASS( throw CPPUNIT_NS::Exception() );
   }
   catch ( CPPUNIT_NS::Exception & )
   {
      return;
   }

   throw std::exception();
}

void
TestAssertTest::testAssert()
{
  CPPUNIT_ASSERT_ASSERTION_PASS( CPPUNIT_ASSERT( true ) );

  CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT( false ) );
}

static int foo() { return 1; }

void
TestAssertTest::testAssertEqual()
{
  CPPUNIT_ASSERT_ASSERTION_PASS( CPPUNIT_ASSERT_EQUAL( 1, 1 ) );
  CPPUNIT_ASSERT_ASSERTION_PASS( CPPUNIT_ASSERT_EQUAL( 1, foo() ) );
  CPPUNIT_ASSERT_ASSERTION_PASS( CPPUNIT_ASSERT_EQUAL( 12345678, 12345678 ) );

  CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_EQUAL( 1, 2 ) );
}

void
TestAssertTest::testAssertMessageTrue()
{
  CPPUNIT_ASSERT_ASSERTION_PASS(
     CPPUNIT_ASSERT_MESSAGE( "This test should not failed", true ) );
}

void
TestAssertTest::testAssertMessageFalse()
{
  bool exceptionCaught = false;
  std::string message( "This test message should not be seen" );
  try
  {
    CPPUNIT_ASSERT_MESSAGE( message, 2==3 );
  }
  catch( CPPUNIT_NS::Exception &e )
  {
    exceptionCaught = true; // ok, we were expecting an exception.
    checkMessageContains( &e, message );
  }

  CPPUNIT_ASSERT( exceptionCaught );
}

void
TestAssertTest::testAssertDoubleEquals()
{
  CPPUNIT_ASSERT_ASSERTION_PASS( CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.1, 1.2, 0.101 ) );
  CPPUNIT_ASSERT_ASSERTION_PASS( CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.2, 1.1, 0.101 ) );

  CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.1, 1.2, 0.09 ) );
  CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.2, 1.1, 0.09 ) );

  double inf = std::numeric_limits<double>::infinity();
  CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_DOUBLES_EQUAL( inf, 0.0, 1.0 ) );
  CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, inf, 1.0 ) );
  CPPUNIT_ASSERT_ASSERTION_PASS( CPPUNIT_ASSERT_DOUBLES_EQUAL( inf, inf, 1.0 ) );

  double nan = std::numeric_limits<double>::quiet_NaN();
  CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_DOUBLES_EQUAL( nan, 0.0, 1.0 ) );
  CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_DOUBLES_EQUAL( nan, nan, 1.0 ) );
  CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_DOUBLES_EQUAL( nan, inf, 1.0 ) );
  CPPUNIT_ASSERT_ASSERTION_FAIL( CPPUNIT_ASSERT_DOUBLES_EQUAL( inf, nan, 1.0 ) );
}

void
TestAssertTest::testFail()
{
  bool exceptionCaught = false;
  std::string failure( "FailureMessage" );
  try
  {
    CPPUNIT_FAIL( failure );
  }
  catch( CPPUNIT_NS::Exception &e )
  {
    exceptionCaught = true;
    checkMessageContains( &e, failure );
  }
  CPPUNIT_ASSERT( exceptionCaught );
}

void
TestAssertTest::checkMessageContains( CPPUNIT_NS::Exception *e,
                                      std::string expected )
{
  std::string actual = e->what();
  CPPUNIT_ASSERT_MESSAGE( "Expected message not found: " + expected +
                          ", was: " + actual,
      std::search( actual.begin(), actual.end(),
                   expected.begin(), expected.end() ) != actual.end() );
}
<|endoftext|>


<commit_before>/// COMPONENT
#include <csapex/model/node.h>

/// COMPONENT
#include <csapex_vision/roi_message.h>
#include <csapex_vision/cv_mat_message.h>
#include <csapex_core_plugins/vector_message.h>

/// PROJECT
#include <csapex/param/range_parameter.h>
#include <csapex/param/parameter_factory.h>
#include <csapex/msg/io.h>
#include <csapex/model/node_modifier.h>
#include <csapex/utility/register_apex_plugin.h>

namespace csapex {

using namespace connection_types;

class GridArrangedRois : public csapex::Node
{
public:
    GridArrangedRois()
    {

    }

public:
    virtual void setup(csapex::NodeModifier& node_modifier) override
    {
        input_  = node_modifier.addInput<CvMatMessage>("Image");
        output_ = node_modifier.addOutput<VectorMessage, RoiMessage>("ROIs");
    }

    virtual void setupParameters(Parameterizable &parameters) override
    {
        parameters.addParameter(csapex::param::ParameterFactory::declareRange("dimension x", 1, 1000, 64, 1));
        parameters.addParameter(csapex::param::ParameterFactory::declareRange("dimension y", 1, 1000, 48, 1));
        parameters.addParameter(csapex::param::ParameterFactory::declareRange("class id", -1, 255, -1, 1));
        parameters.addParameter(csapex::param::ParameterFactory::declareColorParameter("color", 255,0,0));
    }

    virtual void process() override
    {
        CvMatMessage::ConstPtr in = msg::getMessage<connection_types::CvMatMessage>(input_);
        VectorMessage::Ptr out(VectorMessage::make<RoiMessage>());

        int dim_x = readParameter<int>("dimension x");
        int dim_y = readParameter<int>("dimension y");
        int class_id = readParameter<int>("class id");
        const std::vector<int>& c = readParameter<std::vector<int> >("color");
        cv::Scalar color = cv::Scalar(c[2], c[1], c[0]);
        int cell_height = in->value.rows / dim_y;
        int rest_height = in->value.rows % dim_y;
        int cell_width  = in->value.cols / dim_x;
        int rest_witdh  = in->value.cols % dim_x;

        cv::Rect rect;
        for(int i = 0 ; i < dim_y ; ++i) {
            for(int j = 0 ; j < dim_x ; ++j) {
                RoiMessage::Ptr roi(new RoiMessage);
                rect.x = cell_width  * j;
                rect.y = cell_height * i;
                rect.width  = cell_width  + ((j == dim_x - 1) ? rest_witdh : 0);
                rect.height = cell_height + ((i == dim_y - 1) ? rest_height : 0);
                roi->value.setRect(rect);
                roi->value.setColor(color);
                roi->value.setClassification(class_id);
                out->value.push_back(roi);
            }
        }

        msg::publish(output_, out);
    }

private:
    Input*  input_;
    Output* output_;
};
}

CSAPEX_REGISTER_CLASS(csapex::GridArrangedRois, csapex::Node)
<commit_msg>generic<commit_after>/// COMPONENT
#include <csapex/model/node.h>

/// COMPONENT
#include <csapex_vision/roi_message.h>
#include <csapex_vision/cv_mat_message.h>
#include <csapex_core_plugins/vector_message.h>

/// PROJECT
#include <csapex/param/range_parameter.h>
#include <csapex/param/parameter_factory.h>
#include <csapex/msg/io.h>
#include <csapex/model/node_modifier.h>
#include <csapex/utility/register_apex_plugin.h>

namespace csapex {

using namespace connection_types;

class GridArrangedRois : public csapex::Node
{
public:
    GridArrangedRois()
    {

    }

public:
    virtual void setup(csapex::NodeModifier& node_modifier) override
    {
        input_  = node_modifier.addInput<CvMatMessage>("Image");
        output_ = node_modifier.addOutput<GenericVectorMessage, RoiMessage>("ROIs");
    }

    virtual void setupParameters(Parameterizable &parameters) override
    {
        parameters.addParameter(csapex::param::ParameterFactory::declareRange("dimension x", 1, 1000, 64, 1));
        parameters.addParameter(csapex::param::ParameterFactory::declareRange("dimension y", 1, 1000, 48, 1));
        parameters.addParameter(csapex::param::ParameterFactory::declareRange("class id", -1, 255, -1, 1));
        parameters.addParameter(csapex::param::ParameterFactory::declareColorParameter("color", 255,0,0));
    }

    virtual void process() override
    {
        CvMatMessage::ConstPtr in = msg::getMessage<connection_types::CvMatMessage>(input_);
        std::shared_ptr<std::vector<RoiMessage>> out(new std::vector<RoiMessage>);

        int dim_x = readParameter<int>("dimension x");
        int dim_y = readParameter<int>("dimension y");
        int class_id = readParameter<int>("class id");
        const std::vector<int>& c = readParameter<std::vector<int> >("color");
        cv::Scalar color = cv::Scalar(c[2], c[1], c[0]);
        int cell_height = in->value.rows / dim_y;
        int rest_height = in->value.rows % dim_y;
        int cell_width  = in->value.cols / dim_x;
        int rest_witdh  = in->value.cols % dim_x;

        cv::Rect rect;
        for(int i = 0 ; i < dim_y ; ++i) {
            for(int j = 0 ; j < dim_x ; ++j) {
                RoiMessage roi;
                rect.x = cell_width  * j;
                rect.y = cell_height * i;
                rect.width  = cell_width  + ((j == dim_x - 1) ? rest_witdh : 0);
                rect.height = cell_height + ((i == dim_y - 1) ? rest_height : 0);
                roi.value.setRect(rect);
                roi.value.setColor(color);
                roi.value.setClassification(class_id);
                out->push_back(roi);
            }
        }

        msg::publish<GenericVectorMessage, RoiMessage>(output_, out);
    }

private:
    Input*  input_;
    Output* output_;
};
}

CSAPEX_REGISTER_CLASS(csapex::GridArrangedRois, csapex::Node)
<|endoftext|>


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

 QGroundControl Open Source Ground Control Station

 (c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>

 This file is part of the QGROUNDCONTROL project

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

 QGROUNDCONTROL 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 QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

 ======================================================================*/

/// @file
///     @brief Object which exposes a FactMetaData
///
///     @author Don Gagne <don@thegagnes.com>

#include "FactMetaData.h"

#include <QDebug>

#include <limits>

FactMetaData::FactMetaData(ValueType_t type, QObject* parent) :
    QObject(parent),
    _group("Default Group"),
    _type(type),
    _defaultValue(0),
    _defaultValueAvailable(false),
    _min(_minForType()),
    _max(_maxForType())
{

}

QVariant FactMetaData::defaultValue(void)
{
    if (_defaultValueAvailable) {
        return _defaultValue;
    } else {
        qWarning() << "Attempt to access unavailable default value";
        return QVariant(0);
    }
}

void FactMetaData::setDefaultValue(const QVariant& defaultValue)
{
    if (_min <= defaultValue && defaultValue <= _max) {
        _defaultValue = defaultValue;
        _defaultValueAvailable = true;
    } else {
        qWarning() << "Attempt to set default value which is outside min/max range";
    }
}

void FactMetaData::setMin(const QVariant& min)
{
    if (min > _minForType()) {
        _min = min;
    } else {
        qWarning() << "Attempt to set min below allowable value";
        _min = _minForType();
    }
}

void FactMetaData::setMax(const QVariant& max)
{
    if (max > _maxForType()) {
        qWarning() << "Attempt to set max above allowable value";
        _max = _maxForType();
    } else {
        _max = max;
    }
}

QVariant FactMetaData::_minForType(void)
{
    switch (_type) {
        case valueTypeUint8:
            return QVariant(std::numeric_limits<unsigned char>::min());
        case valueTypeInt8:
            return QVariant(std::numeric_limits<signed char>::min());
        case valueTypeUint16:
            return QVariant(std::numeric_limits<unsigned short int>::min());
        case valueTypeInt16:
            return QVariant(std::numeric_limits<short int>::min());
        case valueTypeUint32:
            return QVariant(std::numeric_limits<unsigned int>::min());
        case valueTypeInt32:
            return QVariant(std::numeric_limits<int>::min());
        case valueTypeFloat:
            return QVariant(-std::numeric_limits<float>::max());
        case valueTypeDouble:
            return QVariant(-std::numeric_limits<double>::max());
    }

    // Make windows compiler happy, even switch is full cased
    return QVariant();
}

QVariant FactMetaData::_maxForType(void)
{
    switch (_type) {
        case valueTypeUint8:
            return QVariant(std::numeric_limits<unsigned char>::max());
        case valueTypeInt8:
            return QVariant(std::numeric_limits<signed char>::max());
        case valueTypeUint16:
            return QVariant(std::numeric_limits<unsigned short int>::max());
        case valueTypeInt16:
            return QVariant(std::numeric_limits<short int>::max());
        case valueTypeUint32:
            return QVariant(std::numeric_limits<unsigned int>::max());
        case valueTypeInt32:
            return QVariant(std::numeric_limits<int>::max());
        case valueTypeFloat:
            return QVariant(std::numeric_limits<float>::max());
        case valueTypeDouble:
            return QVariant(std::numeric_limits<double>::max());
    }

    // Make windows compiler happy, even switch is full cased
    return QVariant();
}
<commit_msg>Force Default Group to top of list<commit_after>/*=====================================================================

 QGroundControl Open Source Ground Control Station

 (c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>

 This file is part of the QGROUNDCONTROL project

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

 QGROUNDCONTROL 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 QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

 ======================================================================*/

/// @file
///     @brief Object which exposes a FactMetaData
///
///     @author Don Gagne <don@thegagnes.com>

#include "FactMetaData.h"

#include <QDebug>

#include <limits>

FactMetaData::FactMetaData(ValueType_t type, QObject* parent) :
    QObject(parent),
    _group("*Default Group"),
    _type(type),
    _defaultValue(0),
    _defaultValueAvailable(false),
    _min(_minForType()),
    _max(_maxForType())
{

}

QVariant FactMetaData::defaultValue(void)
{
    if (_defaultValueAvailable) {
        return _defaultValue;
    } else {
        qWarning() << "Attempt to access unavailable default value";
        return QVariant(0);
    }
}

void FactMetaData::setDefaultValue(const QVariant& defaultValue)
{
    if (_min <= defaultValue && defaultValue <= _max) {
        _defaultValue = defaultValue;
        _defaultValueAvailable = true;
    } else {
        qWarning() << "Attempt to set default value which is outside min/max range";
    }
}

void FactMetaData::setMin(const QVariant& min)
{
    if (min > _minForType()) {
        _min = min;
    } else {
        qWarning() << "Attempt to set min below allowable value";
        _min = _minForType();
    }
}

void FactMetaData::setMax(const QVariant& max)
{
    if (max > _maxForType()) {
        qWarning() << "Attempt to set max above allowable value";
        _max = _maxForType();
    } else {
        _max = max;
    }
}

QVariant FactMetaData::_minForType(void)
{
    switch (_type) {
        case valueTypeUint8:
            return QVariant(std::numeric_limits<unsigned char>::min());
        case valueTypeInt8:
            return QVariant(std::numeric_limits<signed char>::min());
        case valueTypeUint16:
            return QVariant(std::numeric_limits<unsigned short int>::min());
        case valueTypeInt16:
            return QVariant(std::numeric_limits<short int>::min());
        case valueTypeUint32:
            return QVariant(std::numeric_limits<unsigned int>::min());
        case valueTypeInt32:
            return QVariant(std::numeric_limits<int>::min());
        case valueTypeFloat:
            return QVariant(-std::numeric_limits<float>::max());
        case valueTypeDouble:
            return QVariant(-std::numeric_limits<double>::max());
    }

    // Make windows compiler happy, even switch is full cased
    return QVariant();
}

QVariant FactMetaData::_maxForType(void)
{
    switch (_type) {
        case valueTypeUint8:
            return QVariant(std::numeric_limits<unsigned char>::max());
        case valueTypeInt8:
            return QVariant(std::numeric_limits<signed char>::max());
        case valueTypeUint16:
            return QVariant(std::numeric_limits<unsigned short int>::max());
        case valueTypeInt16:
            return QVariant(std::numeric_limits<short int>::max());
        case valueTypeUint32:
            return QVariant(std::numeric_limits<unsigned int>::max());
        case valueTypeInt32:
            return QVariant(std::numeric_limits<int>::max());
        case valueTypeFloat:
            return QVariant(std::numeric_limits<float>::max());
        case valueTypeDouble:
            return QVariant(std::numeric_limits<double>::max());
    }

    // Make windows compiler happy, even switch is full cased
    return QVariant();
}
<|endoftext|>


<commit_before>
#include <cstdlib>

#include <ATK/Delay/FixedDelayLineFilter.h>
#include <ATK/Delay/UniversalVariableDelayLineFilter.h>

#include <ATK/Mock/SimpleSinusGeneratorFilter.h>

#include <ATK/Tools/CachedSinusGeneratorFilter.h>
#include <ATK/Tools/DryWetFilter.h>

int main(int argc, char** argv)
{
  ATK::SimpleSinusGeneratorFilter<double> generator;
  generator.set_output_sampling_rate(1024 * 64);
  generator.set_amplitude(1);
  generator.set_frequency(1000);
  ATK::CachedSinusGeneratorFilter<double> generatordelay(10);
  generatordelay.set_output_sampling_rate(1024 * 64);
  generatordelay.set_volume(5);
  generatordelay.set_offset(20);

  ATK::FixedDelayLineFilter<double> fixedDelayLineFilter(1024*64);
  fixedDelayLineFilter.set_input_sampling_rate(1024 * 64);
  fixedDelayLineFilter.set_output_sampling_rate(1024 * 64);
  ATK::UniversalVariableDelayLineFilter<float> filter(48000);
  filter.set_blend(0.5);
  filter.set_feedback(0.1);
  filter.set_feedforward(1);
  filter.set_input_sampling_rate(1024 * 64);
  filter.set_output_sampling_rate(1024 * 64);
  filter.set_input_port(0, &fixedDelayLineFilter, 0);
  filter.set_input_port(1, &generatordelay, 0);
  ATK::DryWetFilter<double> drywetFilter;
  drywetFilter.set_input_sampling_rate(1024 * 64);
  drywetFilter.set_output_sampling_rate(1024 * 64);

  fixedDelayLineFilter.set_input_port(0, &generator, 0);

  drywetFilter.set_input_port(0, &filter, 0);
  drywetFilter.set_input_port(1, &generator, 0);

  drywetFilter.set_dry(0.5);

  for(size_t i = 0; i < 1024*1024; ++i)
    drywetFilter.process(1024);

  return EXIT_SUCCESS;
}<commit_msg>Fix type<commit_after>
#include <cstdlib>

#include <ATK/Delay/FixedDelayLineFilter.h>
#include <ATK/Delay/UniversalVariableDelayLineFilter.h>

#include <ATK/Mock/SimpleSinusGeneratorFilter.h>

#include <ATK/Tools/CachedSinusGeneratorFilter.h>
#include <ATK/Tools/DryWetFilter.h>

int main(int argc, char** argv)
{
  ATK::SimpleSinusGeneratorFilter<double> generator;
  generator.set_output_sampling_rate(1024 * 64);
  generator.set_amplitude(1);
  generator.set_frequency(1000);
  ATK::CachedSinusGeneratorFilter<double> generatordelay(10);
  generatordelay.set_output_sampling_rate(1024 * 64);
  generatordelay.set_volume(5);
  generatordelay.set_offset(20);

  ATK::FixedDelayLineFilter<double> fixedDelayLineFilter(1024*64);
  fixedDelayLineFilter.set_input_sampling_rate(1024 * 64);
  fixedDelayLineFilter.set_output_sampling_rate(1024 * 64);
  ATK::UniversalVariableDelayLineFilter<double> filter(48000);
  filter.set_blend(0.5);
  filter.set_feedback(0.1);
  filter.set_feedforward(1);
  filter.set_input_sampling_rate(1024 * 64);
  filter.set_output_sampling_rate(1024 * 64);
  filter.set_input_port(0, &fixedDelayLineFilter, 0);
  filter.set_input_port(1, &generatordelay, 0);
  ATK::DryWetFilter<double> drywetFilter;
  drywetFilter.set_input_sampling_rate(1024 * 64);
  drywetFilter.set_output_sampling_rate(1024 * 64);

  fixedDelayLineFilter.set_input_port(0, &generator, 0);

  drywetFilter.set_input_port(0, &filter, 0);
  drywetFilter.set_input_port(1, &generator, 0);

  drywetFilter.set_dry(0.5);

  for(size_t i = 0; i < 1024*1024; ++i)
    drywetFilter.process(1024);

  return EXIT_SUCCESS;
}<|endoftext|>


<commit_before>#pragma once

#include <cassert>
#include <iod/symbol.hh>
#include <iod/symbols.hh>
#include <iod/stringview.hh>

namespace iod
{
  using namespace s;

  // struct json_error
  // {
  //   json_error& operator=(const json_error&) = default;
  //   operator bool() { return code != 0; }
  //   int code; //const char* what;
  //   //std::string what;
  // };

  // // json_error make_json_error(const char* what) { return {1, std::string("metajson error: ") + what}; }
  // // json_error json_no_error() { return {0, ""}; }

  // json_error make_json_error(const char* what) { return {1}; }
  // json_error json_no_error() { return {0}; }

  // static json_error json_ok = json_no_error();

  // template <typename... W>
  // auto make_json_error(W... what)
  // {
  //   // std::stringstream ss;

  //   // ss << "metajson error: ";
  //   // auto add = [&ss] (auto w) { ss << w; };
  //   // apply_each(add, what...);
  //   //return json_error{1, ss.str()};
  //   return json_error{1};
  // }

  template <typename O>
  inline decltype(auto) wrap_json_output_stream(O&& s)
  {
    return D(_append = [&s] (char c) { s << c; });
  }

  inline decltype(auto) wrap_json_output_stream(std::stringstream& s)
  {
    return D(_append = [&s] (char c) { s << c; });
  }

  inline decltype(auto) wrap_json_output_stream(std::string& s)
  {
    return D(_append = [&s] (char c) { s.append(1, c); });
  }

  inline decltype(auto)
  wrap_json_input_stream(std::stringstream& s) { return s; }
  namespace json_internals {
    struct json_parser;
  }
  inline decltype(auto)
  wrap_json_input_stream(json_internals::json_parser& s) { return s; }
  inline decltype(auto)
  wrap_json_input_stream(const iod::stringview& s) { return std::stringstream(s.to_std_string()); }
  inline decltype(auto)
  wrap_json_input_stream(const std::string& s) { return std::stringstream(s); }
  inline decltype(auto)
  wrap_json_input_stream(const char* s) { return std::stringstream(std::string(s)); }

  namespace unicode_impl
  {
    template <typename S, typename T>
    auto json_to_utf8(S&& s, T&& o);

    template <typename S, typename T>
    auto utf8_to_json(S&& s, T&& o);
  }

  template <typename I, typename O>
  auto json_to_utf8(I&& i, O&& o)
  {
    return unicode_impl::json_to_utf8(wrap_json_input_stream(std::forward<I>(i)),
                                      wrap_json_output_stream(std::forward<O>(o)));
  }

  template <typename I, typename O>
  auto utf8_to_json(I&& i, O&& o)
  {
    return unicode_impl::utf8_to_json(wrap_json_input_stream(std::forward<I>(i)),
                              wrap_json_output_stream(std::forward<O>(o)));
  }

  enum json_encodings
    {
      UTF32BE,
      UTF32LE,
      UTF16BE,
      UTF16LE,
      UTF8
    };

  // Detection of encoding depending on the pattern of the
  // first fourth characters.
  auto detect_encoding(char a, char b, char c, char d)
  {
    // 00 00 00 xx  UTF-32BE
    // xx 00 00 00  UTF-32LE
    // 00 xx 00 xx  UTF-16BE
    // xx 00 xx 00  UTF-16LE
    // xx xx xx xx  UTF-8

    if (a == 0 and b == 0) return UTF32BE;
    else if (c == 0 and d == 0) return UTF32LE;
    else if (a == 0) return UTF16BE;
    else if (b == 0) return UTF16LE;
    else return UTF8;
  }

  // The JSON RFC escapes character codepoints prefixed with a \uXXXX (7-11 bits codepoints)
  // or \uXXXX\uXXXX (20 bits codepoints)

  // uft8 string have 4 kinds of character representation encoding the codepoint of the character.

  // 1 byte : 0xxxxxxx  -> 7 bits codepoint ASCII chars from 0x00 to 0x7F
  // 2 bytes: 110xxxxx 10xxxxxx -> 11 bits codepoint
  // 3 bytes: 1110xxxx 10xxxxxx 10xxxxxx -> 11 bits codepoint

  // 1 and 3 bytes representation are escaped as \uXXXX with X a char in the 0-9A-F range. It
  // is possible since the codepoint is less than 16 bits.

  // the 4 bytes representation uses the UTF-16 surrogate pair (high and low surrogate).

  // The high surrogate is in the 0xD800..0xDBFF range (HR) and
  // the low surrogate is in the 0xDC00..0xDFFF range (LR).

  // to encode a given 20bits codepoint c to the surrogate pair.
  //  - substract 0x10000 to c
  //  - separate the result in a high (first 10 bits) and low (last 10bits) surrogate.
  //  - Add 0xD800 to the high surrogate
  //  - Add 0xDC00 to the low surrogate
  //  - the 32 bits code is (high << 16) + low.

  // and to json-escape the high-low(H-L) surrogates representation (16+16bits):
  //  - Check that H and L are respectively in the HR and LR ranges.
  //  - add to H-L 0x0001_0000 - 0xD800_DC00 to get the 20bits codepoint c.
  //  - Encode the codepoint in a string of \uXXXX\uYYYY with X and Y the respective hex digits
  //    of the high and low sequence of 10 bits.

  // In addition to utf8, JSON escape characters ( " \ / ) with a backslash and translate
  // \n \r \t \b \r in their matching two characters string, for example '\n' to  '\' followed by 'n'.

  namespace unicode_impl
  {
    template <typename S, typename T>
    auto json_to_utf8(S&& s, T&& o)
    {
      // Convert a JSON string into an UTF-8 string.
      if (s.get() != '"')
        throw std::runtime_error("json_to_utf8: JSON strings should start with a double quote.");

      while (true)
      {
        // Copy until we find the escaping backslash or the end of the string (double quote).
        while (s.peek() != EOF and s.peek() != '"' and s.peek() != '\\')
          o.append(s.get());

        // If eof found before the end of the string, return an error.
        if (s.eof()) throw std::runtime_error("json_to_utf8: Unexpected end of string when parsing a string.");

        // If end of json string, return
        if (s.peek() == '"')
        {
          break;
        }

        // Get the '\'.
        assert(s.peek() ==  '\\');
        s.get();

        switch (s.get())
        {
          // List of escaped char from http://www.json.org/
        default:
          throw std::runtime_error("json_to_utf8: Bad JSON escaped character.");
        case '"': o.append('"'); break;
        case '\\': o.append('\\'); break;
        case '/': o.append('/'); break;
        case 'n': o.append('\n'); break;
        case 'r': o.append('\r'); break;
        case 't': o.append('\t'); break;
        case 'b': o.append('\b'); break;
        case 'f': o.append('\f'); break;
        case 'u':
          char a,b,c,d;

          a = s.get();
          b = s.get();
          c = s.get();
          d = s.get();

          if (s.eof())
            throw std::runtime_error("json_to_utf8: Unexpected end of string when decoding an utf8 character");

          auto decode_hex_c = [] (char c) {
            if (c >= '0' and c <= '9') return c - '0';
            else return (10 + std::toupper(c) - 'A');
          };

          uint16_t x =
            (decode_hex_c(a) << 12) +
            (decode_hex_c(b) << 8) +
            (decode_hex_c(c) << 4) +
            decode_hex_c(d);

          // If x in the  0xD800..0xDBFF range -> decode a surrogate pair \uXXXX\uYYYY -> 20 bits codepoint.
          if (x >= 0xD800 and x <= 0xDBFF)
          {
            if (s.get() != '\\' or s.get() != 'u')
              throw std::runtime_error("json_to_utf8: Missing low surrogate.");

            uint16_t y =
              (decode_hex_c(s.get()) << 12) +
              (decode_hex_c(s.get()) << 8) +
              (decode_hex_c(s.get()) << 4) +
              decode_hex_c(s.get());

            if (s.eof())
              throw std::runtime_error("json_to_utf8: Unexpected end of string when decoding an utf8 character");

            x -= 0xD800;
            y -= 0xDC00;

            int cp = (x << 10) + y + 0x10000;

            o.append(0b11110000 | (cp >> 18));
            o.append(0b10000000 | ((cp & 0x3F000) >> 12));
            o.append(0b10000000 | ((cp & 0x00FC0) >> 6));
            o.append(0b10000000 | (cp & 0x003F));

          }
          // else encode the codepoint with the 1-2, or 3 bytes utf8 representation.
          else
          {
            if (x <= 0x007F) // 7bits codepoints, ASCII 0xxxxxxx.
            {
              o.append(uint8_t(x));
            }
            else if (x >= 0x0080 and x <= 0x07FF) // 11bits codepoint -> 110xxxxx 10xxxxxx
            {
              o.append(0b11000000 | (x >> 6));
              o.append(0b10000000 | (x & 0x003F));
            }
            else if (x >= 0x0800 and x <= 0xFFFF) //16bits codepoint -> 1110xxxx 10xxxxxx 10xxxxxx
            {
              o.append(0b11100000 | (x >> 12));
              o.append(0b10000000 | ((x & 0x0FC0) >> 6));
              o.append(0b10000000 | (x & 0x003F));
            }
            else
              throw std::runtime_error("json_to_utf8: Bad UTF8 codepoint.");
          }
          break;
        }
      }

      if (s.get() != '"')
        throw std::runtime_error("JSON strings must end with a double quote.");

    }

    template <typename S, typename T>
    auto utf8_to_json(S&& s, T&& o)
    {
      o.append('"');

      auto encode_16bits = [&] (uint16_t b)
        {
          const char lt[] = "0123456789ABCDEF";
          o.append(lt[b >> 12]);
          o.append(lt[(b & 0x0F00) >> 8]);
          o.append(lt[(b & 0x00F0) >> 4]);
          o.append(lt[b & 0x000F]);
        };

      while (!s.eof())
      {
        // 7-bits codepoint
        while (s.good() and s.peek() <= 0x7F and s.peek() != EOF)
        {
          switch (s.peek())
          {
          case '"': o.append('\\'); o.append('"'); break;
          case '\\': o.append('\\'); o.append('\\'); break;
            //case '/': o.append('/'); break; Do not escape /
          case '\n': o.append('\\'); o.append('n'); break;
          case '\r': o.append('\\'); o.append('r'); break;
          case '\t': o.append('\\'); o.append('t'); break;
          case '\b': o.append('\\'); o.append('b'); break;
          case '\f': o.append('\\'); o.append('f'); break;
          default:
            o.append(s.peek());
          }
          s.get();
        }

        if (s.eof()) break;

        // uft8 prefix \u.
        o.append('\\');
        o.append('u');

        uint8_t c1 = s.get();
        uint8_t c2 = s.get();
        {
          // extract codepoints.
          if (c1 < 0b11100000) // 11bits - 2 char: 110xxxxx	10xxxxxx
          {
            uint16_t cp = ((c1 & 0b00011111) << 6) +
              (c2 & 0b00111111);
            if (cp >= 0x0080 and cp <= 0x07FF)
              encode_16bits(cp);
            else
              throw std::runtime_error("utf8_to_json: Bad UTF8 codepoint.");
          }
          else if (c1 < 0b11110000) // 16 bits - 3 char: 1110xxxx	10xxxxxx	10xxxxxx
          {
            uint16_t cp = ((c1 & 0b00001111) << 12) +
              ((c2 & 0b00111111) << 6) +
              (s.get() & 0b00111111);

            if (cp >= 0x0800 and cp <= 0xFFFF)
              encode_16bits(cp);
            else
              throw std::runtime_error("utf8_to_json: Bad UTF8 codepoint.");
          }
          else // 21 bits - 4 chars: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
          {
            int cp =
              ((c1 & 0b00000111) << 18) +
              ((c2 & 0b00111111) << 12) +
              ((s.get() & 0b00111111) << 6) +
              (s.get() & 0b00111111);

            cp -= 0x10000;

            uint16_t H = (cp >> 10) + 0xD800;
            uint16_t L = (cp & 0x03FF) + 0xDC00;

            // check if we are in the right range.
            // The high surrogate is in the 0xD800..0xDBFF range (HR) and
            // the low surrogate is in the 0xDC00..0xDFFF range (LR).
            assert(H >= 0xD800 and H <= 0xDBFF and
                   L >= 0xDC00 and L <= 0xDFFF);

            encode_16bits(H);
            o.append('\\');
            o.append('u');
            encode_16bits(L);
          }

        }
      }
      o.append('"');
    }
  }
}
<commit_msg>Declare `detect_encoding(…)` as inline<commit_after>#pragma once

#include <cassert>
#include <iod/symbol.hh>
#include <iod/symbols.hh>
#include <iod/stringview.hh>

namespace iod
{
  using namespace s;

  // struct json_error
  // {
  //   json_error& operator=(const json_error&) = default;
  //   operator bool() { return code != 0; }
  //   int code; //const char* what;
  //   //std::string what;
  // };

  // // json_error make_json_error(const char* what) { return {1, std::string("metajson error: ") + what}; }
  // // json_error json_no_error() { return {0, ""}; }

  // json_error make_json_error(const char* what) { return {1}; }
  // json_error json_no_error() { return {0}; }

  // static json_error json_ok = json_no_error();

  // template <typename... W>
  // auto make_json_error(W... what)
  // {
  //   // std::stringstream ss;

  //   // ss << "metajson error: ";
  //   // auto add = [&ss] (auto w) { ss << w; };
  //   // apply_each(add, what...);
  //   //return json_error{1, ss.str()};
  //   return json_error{1};
  // }

  template <typename O>
  inline decltype(auto) wrap_json_output_stream(O&& s)
  {
    return D(_append = [&s] (char c) { s << c; });
  }

  inline decltype(auto) wrap_json_output_stream(std::stringstream& s)
  {
    return D(_append = [&s] (char c) { s << c; });
  }

  inline decltype(auto) wrap_json_output_stream(std::string& s)
  {
    return D(_append = [&s] (char c) { s.append(1, c); });
  }

  inline decltype(auto)
  wrap_json_input_stream(std::stringstream& s) { return s; }
  namespace json_internals {
    struct json_parser;
  }
  inline decltype(auto)
  wrap_json_input_stream(json_internals::json_parser& s) { return s; }
  inline decltype(auto)
  wrap_json_input_stream(const iod::stringview& s) { return std::stringstream(s.to_std_string()); }
  inline decltype(auto)
  wrap_json_input_stream(const std::string& s) { return std::stringstream(s); }
  inline decltype(auto)
  wrap_json_input_stream(const char* s) { return std::stringstream(std::string(s)); }

  namespace unicode_impl
  {
    template <typename S, typename T>
    auto json_to_utf8(S&& s, T&& o);

    template <typename S, typename T>
    auto utf8_to_json(S&& s, T&& o);
  }

  template <typename I, typename O>
  auto json_to_utf8(I&& i, O&& o)
  {
    return unicode_impl::json_to_utf8(wrap_json_input_stream(std::forward<I>(i)),
                                      wrap_json_output_stream(std::forward<O>(o)));
  }

  template <typename I, typename O>
  auto utf8_to_json(I&& i, O&& o)
  {
    return unicode_impl::utf8_to_json(wrap_json_input_stream(std::forward<I>(i)),
                              wrap_json_output_stream(std::forward<O>(o)));
  }

  enum json_encodings
    {
      UTF32BE,
      UTF32LE,
      UTF16BE,
      UTF16LE,
      UTF8
    };

  // Detection of encoding depending on the pattern of the
  // first fourth characters.
  inline auto detect_encoding(char a, char b, char c, char d)
  {
    // 00 00 00 xx  UTF-32BE
    // xx 00 00 00  UTF-32LE
    // 00 xx 00 xx  UTF-16BE
    // xx 00 xx 00  UTF-16LE
    // xx xx xx xx  UTF-8

    if (a == 0 and b == 0) return UTF32BE;
    else if (c == 0 and d == 0) return UTF32LE;
    else if (a == 0) return UTF16BE;
    else if (b == 0) return UTF16LE;
    else return UTF8;
  }

  // The JSON RFC escapes character codepoints prefixed with a \uXXXX (7-11 bits codepoints)
  // or \uXXXX\uXXXX (20 bits codepoints)

  // uft8 string have 4 kinds of character representation encoding the codepoint of the character.

  // 1 byte : 0xxxxxxx  -> 7 bits codepoint ASCII chars from 0x00 to 0x7F
  // 2 bytes: 110xxxxx 10xxxxxx -> 11 bits codepoint
  // 3 bytes: 1110xxxx 10xxxxxx 10xxxxxx -> 11 bits codepoint

  // 1 and 3 bytes representation are escaped as \uXXXX with X a char in the 0-9A-F range. It
  // is possible since the codepoint is less than 16 bits.

  // the 4 bytes representation uses the UTF-16 surrogate pair (high and low surrogate).

  // The high surrogate is in the 0xD800..0xDBFF range (HR) and
  // the low surrogate is in the 0xDC00..0xDFFF range (LR).

  // to encode a given 20bits codepoint c to the surrogate pair.
  //  - substract 0x10000 to c
  //  - separate the result in a high (first 10 bits) and low (last 10bits) surrogate.
  //  - Add 0xD800 to the high surrogate
  //  - Add 0xDC00 to the low surrogate
  //  - the 32 bits code is (high << 16) + low.

  // and to json-escape the high-low(H-L) surrogates representation (16+16bits):
  //  - Check that H and L are respectively in the HR and LR ranges.
  //  - add to H-L 0x0001_0000 - 0xD800_DC00 to get the 20bits codepoint c.
  //  - Encode the codepoint in a string of \uXXXX\uYYYY with X and Y the respective hex digits
  //    of the high and low sequence of 10 bits.

  // In addition to utf8, JSON escape characters ( " \ / ) with a backslash and translate
  // \n \r \t \b \r in their matching two characters string, for example '\n' to  '\' followed by 'n'.

  namespace unicode_impl
  {
    template <typename S, typename T>
    auto json_to_utf8(S&& s, T&& o)
    {
      // Convert a JSON string into an UTF-8 string.
      if (s.get() != '"')
        throw std::runtime_error("json_to_utf8: JSON strings should start with a double quote.");

      while (true)
      {
        // Copy until we find the escaping backslash or the end of the string (double quote).
        while (s.peek() != EOF and s.peek() != '"' and s.peek() != '\\')
          o.append(s.get());

        // If eof found before the end of the string, return an error.
        if (s.eof()) throw std::runtime_error("json_to_utf8: Unexpected end of string when parsing a string.");

        // If end of json string, return
        if (s.peek() == '"')
        {
          break;
        }

        // Get the '\'.
        assert(s.peek() ==  '\\');
        s.get();

        switch (s.get())
        {
          // List of escaped char from http://www.json.org/
        default:
          throw std::runtime_error("json_to_utf8: Bad JSON escaped character.");
        case '"': o.append('"'); break;
        case '\\': o.append('\\'); break;
        case '/': o.append('/'); break;
        case 'n': o.append('\n'); break;
        case 'r': o.append('\r'); break;
        case 't': o.append('\t'); break;
        case 'b': o.append('\b'); break;
        case 'f': o.append('\f'); break;
        case 'u':
          char a,b,c,d;

          a = s.get();
          b = s.get();
          c = s.get();
          d = s.get();

          if (s.eof())
            throw std::runtime_error("json_to_utf8: Unexpected end of string when decoding an utf8 character");

          auto decode_hex_c = [] (char c) {
            if (c >= '0' and c <= '9') return c - '0';
            else return (10 + std::toupper(c) - 'A');
          };

          uint16_t x =
            (decode_hex_c(a) << 12) +
            (decode_hex_c(b) << 8) +
            (decode_hex_c(c) << 4) +
            decode_hex_c(d);

          // If x in the  0xD800..0xDBFF range -> decode a surrogate pair \uXXXX\uYYYY -> 20 bits codepoint.
          if (x >= 0xD800 and x <= 0xDBFF)
          {
            if (s.get() != '\\' or s.get() != 'u')
              throw std::runtime_error("json_to_utf8: Missing low surrogate.");

            uint16_t y =
              (decode_hex_c(s.get()) << 12) +
              (decode_hex_c(s.get()) << 8) +
              (decode_hex_c(s.get()) << 4) +
              decode_hex_c(s.get());

            if (s.eof())
              throw std::runtime_error("json_to_utf8: Unexpected end of string when decoding an utf8 character");

            x -= 0xD800;
            y -= 0xDC00;

            int cp = (x << 10) + y + 0x10000;

            o.append(0b11110000 | (cp >> 18));
            o.append(0b10000000 | ((cp & 0x3F000) >> 12));
            o.append(0b10000000 | ((cp & 0x00FC0) >> 6));
            o.append(0b10000000 | (cp & 0x003F));

          }
          // else encode the codepoint with the 1-2, or 3 bytes utf8 representation.
          else
          {
            if (x <= 0x007F) // 7bits codepoints, ASCII 0xxxxxxx.
            {
              o.append(uint8_t(x));
            }
            else if (x >= 0x0080 and x <= 0x07FF) // 11bits codepoint -> 110xxxxx 10xxxxxx
            {
              o.append(0b11000000 | (x >> 6));
              o.append(0b10000000 | (x & 0x003F));
            }
            else if (x >= 0x0800 and x <= 0xFFFF) //16bits codepoint -> 1110xxxx 10xxxxxx 10xxxxxx
            {
              o.append(0b11100000 | (x >> 12));
              o.append(0b10000000 | ((x & 0x0FC0) >> 6));
              o.append(0b10000000 | (x & 0x003F));
            }
            else
              throw std::runtime_error("json_to_utf8: Bad UTF8 codepoint.");
          }
          break;
        }
      }

      if (s.get() != '"')
        throw std::runtime_error("JSON strings must end with a double quote.");

    }

    template <typename S, typename T>
    auto utf8_to_json(S&& s, T&& o)
    {
      o.append('"');

      auto encode_16bits = [&] (uint16_t b)
        {
          const char lt[] = "0123456789ABCDEF";
          o.append(lt[b >> 12]);
          o.append(lt[(b & 0x0F00) >> 8]);
          o.append(lt[(b & 0x00F0) >> 4]);
          o.append(lt[b & 0x000F]);
        };

      while (!s.eof())
      {
        // 7-bits codepoint
        while (s.good() and s.peek() <= 0x7F and s.peek() != EOF)
        {
          switch (s.peek())
          {
          case '"': o.append('\\'); o.append('"'); break;
          case '\\': o.append('\\'); o.append('\\'); break;
            //case '/': o.append('/'); break; Do not escape /
          case '\n': o.append('\\'); o.append('n'); break;
          case '\r': o.append('\\'); o.append('r'); break;
          case '\t': o.append('\\'); o.append('t'); break;
          case '\b': o.append('\\'); o.append('b'); break;
          case '\f': o.append('\\'); o.append('f'); break;
          default:
            o.append(s.peek());
          }
          s.get();
        }

        if (s.eof()) break;

        // uft8 prefix \u.
        o.append('\\');
        o.append('u');

        uint8_t c1 = s.get();
        uint8_t c2 = s.get();
        {
          // extract codepoints.
          if (c1 < 0b11100000) // 11bits - 2 char: 110xxxxx	10xxxxxx
          {
            uint16_t cp = ((c1 & 0b00011111) << 6) +
              (c2 & 0b00111111);
            if (cp >= 0x0080 and cp <= 0x07FF)
              encode_16bits(cp);
            else
              throw std::runtime_error("utf8_to_json: Bad UTF8 codepoint.");
          }
          else if (c1 < 0b11110000) // 16 bits - 3 char: 1110xxxx	10xxxxxx	10xxxxxx
          {
            uint16_t cp = ((c1 & 0b00001111) << 12) +
              ((c2 & 0b00111111) << 6) +
              (s.get() & 0b00111111);

            if (cp >= 0x0800 and cp <= 0xFFFF)
              encode_16bits(cp);
            else
              throw std::runtime_error("utf8_to_json: Bad UTF8 codepoint.");
          }
          else // 21 bits - 4 chars: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
          {
            int cp =
              ((c1 & 0b00000111) << 18) +
              ((c2 & 0b00111111) << 12) +
              ((s.get() & 0b00111111) << 6) +
              (s.get() & 0b00111111);

            cp -= 0x10000;

            uint16_t H = (cp >> 10) + 0xD800;
            uint16_t L = (cp & 0x03FF) + 0xDC00;

            // check if we are in the right range.
            // The high surrogate is in the 0xD800..0xDBFF range (HR) and
            // the low surrogate is in the 0xDC00..0xDFFF range (LR).
            assert(H >= 0xD800 and H <= 0xDBFF and
                   L >= 0xDC00 and L <= 0xDFFF);

            encode_16bits(H);
            o.append('\\');
            o.append('u');
            encode_16bits(L);
          }

        }
      }
      o.append('"');
    }
  }
}
<|endoftext|>


<commit_before>/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is [Open Source Virtual Machine.].
*
* The Initial Developer of the Original Code is
* Adobe System Incorporated.
* Portions created by the Initial Developer are Copyright (C) 1993-2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*   Adobe AS3 Team
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */

#include "avmplus.h"
#include "SpinLockWin.h"

#include <Mmsystem.h>

namespace avmplus
{
	int WeekDay(double t);
	double MakeDate(double day, double time);
	double MakeDay(double year, double month, double date);
	double MakeTime(double hour, double min, double sec, double ms);
	int YearFromTime(double t);
}

/*
* Windows implementation of platform-dependent date and time code
*/
static const double kMsecPerDay       = 86400000;
static const double kMsecPerHour      = 3600000;
static const double kMsecPerSecond    = 1000;
static const double kMsecPerMinute    = 60000;

static TIME_ZONE_INFORMATION UpdateTimeZoneInfo()
{
	static SpinLockWin gTimeZoneInfoLock;		// protects gTimeZoneInfo and gGmtCache
	static TIME_ZONE_INFORMATION gTimeZoneInfo;
	static SYSTEMTIME gGmtCache;

	SYSTEMTIME gmt;
	GetSystemTime(&gmt);
	gTimeZoneInfoLock.Acquire();
	if ((gmt.wMinute != gGmtCache.wMinute) ||
		(gmt.wHour != gGmtCache.wHour) ||
		(gmt.wDay != gGmtCache.wDay) ||
		(gmt.wMonth != gGmtCache.wMonth) ||
		(gmt.wYear != gGmtCache.wYear)
		)
	{
		// Cache is invalid.
		GetTimeZoneInformation(&gTimeZoneInfo);
		gGmtCache = gmt;
	}
	TIME_ZONE_INFORMATION tz = gTimeZoneInfo;
	gTimeZoneInfoLock.Release();
	return tz;
}

double VMPI_getLocalTimeOffset()
{
	TIME_ZONE_INFORMATION tz = UpdateTimeZoneInfo();
	return -tz.Bias * 60.0 * 1000.0;
}

static double ConvertWin32DST(int year, SYSTEMTIME *st)
{
	// The StandardDate and DaylightDate members of
	// TIMEZONE_INFORMATION may be specified in two ways:
	// 1. An absolute time and date
	// 2. A month, day of week and week in month, and a time of day

	if (st->wYear != 0) {
		return avmplus::MakeDate(avmplus::MakeDay(year,
			st->wMonth - 1,
			st->wDay),
			avmplus::MakeTime(st->wHour,
			st->wMinute,
			st->wSecond,
			st->wMilliseconds));
	}

	double timeValue = avmplus::MakeDate(avmplus::MakeDay(year,
		st->wMonth-1,
		1),
		avmplus::MakeTime(st->wHour,
		st->wMinute,
		st->wSecond,
		st->wMilliseconds));

	double nextMonth;
	if (st->wMonth < 12) {
		nextMonth = avmplus::MakeDate(avmplus::MakeDay(year, st->wMonth, 1), 0);
	} else {
		nextMonth = avmplus::MakeDate(avmplus::MakeDay(year + 1, 0, 1), 0);
	}

	int dayOfWeek = avmplus::WeekDay(timeValue);
	if (dayOfWeek != st->wDayOfWeek) {
		int dayDelta = st->wDayOfWeek - dayOfWeek;
		if (dayDelta < 0) {
			dayDelta += 7;
		}
		timeValue += (double)kMsecPerDay * dayDelta;
	}

	// Advance appropriate # of weeks
	timeValue += (double)kMsecPerDay * 7.0 * (st->wDay - 1);

	// Cap it at the end of the month
	while (timeValue >= nextMonth) {
		timeValue -= (double)kMsecPerDay * 7.0;
	}

	return timeValue;
}

double VMPI_getDaylightSavingsTA(double time)
{
	// On Windows, ask the OS what the daylight saving time bias
	// is.  If it's zero, perform no adjustment.
	TIME_ZONE_INFORMATION tz = UpdateTimeZoneInfo();
	if (tz.DaylightBias != -60 || tz.DaylightDate.wMonth == 0) {
		return 0;
	}

	// In most of the US, Daylight Saving Time begins on the
	// first Sunday of April at 2 AM.  It ends at 2 am on
	// the last Sunday of October.

	// 1. Compute year this time represents.
	int year = avmplus::YearFromTime(time);

	// 2. Compute time that daylight saving time begins
	double timeD = ConvertWin32DST(year, &tz.DaylightDate);

	// 3. Compute time that standard time begins
	double timeS = ConvertWin32DST(year, &tz.StandardDate);

	// Subtract the daylight bias from the standard transition time
	timeS -= kMsecPerHour;

	// The times we have calculated are in local time,
	// but "time" was passed in as UTC.  Convert it to local time.
	time += VMPI_getLocalTimeOffset();

	// Does time fall in the daylight saving period?

	// Where Daylight savings time is earlier in the year than standard time
	if(timeS > timeD)
	{
		if (time >= timeD && time < timeS)
			return kMsecPerHour;
		else
			return 0;
	}
	// Where Daylight savings time is later in the year than standard time
	else
	{
		if (time >= timeS && time < timeD)
			return 0;
		else
			return kMsecPerHour;
	}

}

#define FILETIME_EPOCH_BIAS ((LONGLONG)116444736000000000)
#define FILETIME_MS_FACTOR (10000.0)

static double NormalizeFileTime(FILETIME* ft)
{
	LARGE_INTEGER li;
	li.LowPart = ft->dwLowDateTime;
	li.HighPart = ft->dwHighDateTime;

	return ((double) (li.QuadPart - FILETIME_EPOCH_BIAS)) / FILETIME_MS_FACTOR;
}

static double NormalizeSystemTime(SYSTEMTIME* st)
{
	FILETIME ft;
	SystemTimeToFileTime(st, &ft);
	return NormalizeFileTime(&ft);
}

double VMPI_getDate()
{
	SYSTEMTIME stime;
	GetSystemTime(&stime);
	return NormalizeSystemTime(&stime);
}

uint64_t VMPI_getTime()
{
	return timeGetTime();
}

void* VMPI_alloc(size_t size)
{
	return HeapAlloc(GetProcessHeap(), 0, size);
}

void VMPI_free(void* ptr)
{
	HeapFree(GetProcessHeap(), 0, ptr);
}

size_t VMPI_size(void* ptr)
{
	return HeapSize(GetProcessHeap(), 0, ptr);
}

typedef void (*LoggingFunction)(const char*);

LoggingFunction logFunc = NULL;

void RedirectLogOutput(LoggingFunction func)
{
	logFunc = func;
}

void VMPI_log(const char* message)
{
#ifndef UNDER_CE
	::OutputDebugStringA(message);
#endif

	if(logFunc)
		logFunc(message);
	else
		printf("%s",message);
}

bool VMPI_isMemoryProfilingEnabled()
{
#if defined (UNDER_CE) && defined(MMGC_MEMORY_PROFILER)
	return true;
#else
	//read the mmgc profiling option switch
	const char *env = VMPI_getenv("MMGC_PROFILE");
	return (env && (VMPI_strncmp(env, "1", 1) == 0));
#endif
}

void VMPI_setPageProtection(void *address,
							size_t size,
							bool executableFlag,
							bool writeableFlag)
{
	DWORD oldProtectFlags = 0;
	DWORD newProtectFlags = 0;
	if ( executableFlag && writeableFlag ) {
		newProtectFlags = PAGE_EXECUTE_READWRITE;
	} else if ( executableFlag ) {
		newProtectFlags = PAGE_EXECUTE_READ;
	} else if ( writeableFlag ) {
		newProtectFlags = PAGE_READWRITE;
	} else {
		newProtectFlags = PAGE_READONLY;
	}

	BOOL retval;
	MEMORY_BASIC_INFORMATION mbi;
	do {
		VirtualQuery(address, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
		size_t markSize = size > mbi.RegionSize ? mbi.RegionSize : size;

		retval = VirtualProtect(address, markSize, newProtectFlags, &oldProtectFlags);
		GCAssert(retval);

		address = (char*) address + markSize;
		size -= markSize;
	} while(size > 0 && retval);

	// We should not be clobbering PAGE_GUARD protections
	GCAssert((oldProtectFlags & PAGE_GUARD) == 0);
}

const char *VMPI_getenv(const char *env)
{
	const char *val = NULL;
	(void)env;
#ifndef UNDER_CE
	val = getenv(env);
#endif
	return val;
}
<commit_msg>Flush output when using printf for VMPI_log (r+self)<commit_after>/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is [Open Source Virtual Machine.].
*
* The Initial Developer of the Original Code is
* Adobe System Incorporated.
* Portions created by the Initial Developer are Copyright (C) 1993-2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*   Adobe AS3 Team
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */

#include "avmplus.h"
#include "SpinLockWin.h"

#include <Mmsystem.h>

namespace avmplus
{
	int WeekDay(double t);
	double MakeDate(double day, double time);
	double MakeDay(double year, double month, double date);
	double MakeTime(double hour, double min, double sec, double ms);
	int YearFromTime(double t);
}

/*
* Windows implementation of platform-dependent date and time code
*/
static const double kMsecPerDay       = 86400000;
static const double kMsecPerHour      = 3600000;
static const double kMsecPerSecond    = 1000;
static const double kMsecPerMinute    = 60000;

static TIME_ZONE_INFORMATION UpdateTimeZoneInfo()
{
	static SpinLockWin gTimeZoneInfoLock;		// protects gTimeZoneInfo and gGmtCache
	static TIME_ZONE_INFORMATION gTimeZoneInfo;
	static SYSTEMTIME gGmtCache;

	SYSTEMTIME gmt;
	GetSystemTime(&gmt);
	gTimeZoneInfoLock.Acquire();
	if ((gmt.wMinute != gGmtCache.wMinute) ||
		(gmt.wHour != gGmtCache.wHour) ||
		(gmt.wDay != gGmtCache.wDay) ||
		(gmt.wMonth != gGmtCache.wMonth) ||
		(gmt.wYear != gGmtCache.wYear)
		)
	{
		// Cache is invalid.
		GetTimeZoneInformation(&gTimeZoneInfo);
		gGmtCache = gmt;
	}
	TIME_ZONE_INFORMATION tz = gTimeZoneInfo;
	gTimeZoneInfoLock.Release();
	return tz;
}

double VMPI_getLocalTimeOffset()
{
	TIME_ZONE_INFORMATION tz = UpdateTimeZoneInfo();
	return -tz.Bias * 60.0 * 1000.0;
}

static double ConvertWin32DST(int year, SYSTEMTIME *st)
{
	// The StandardDate and DaylightDate members of
	// TIMEZONE_INFORMATION may be specified in two ways:
	// 1. An absolute time and date
	// 2. A month, day of week and week in month, and a time of day

	if (st->wYear != 0) {
		return avmplus::MakeDate(avmplus::MakeDay(year,
			st->wMonth - 1,
			st->wDay),
			avmplus::MakeTime(st->wHour,
			st->wMinute,
			st->wSecond,
			st->wMilliseconds));
	}

	double timeValue = avmplus::MakeDate(avmplus::MakeDay(year,
		st->wMonth-1,
		1),
		avmplus::MakeTime(st->wHour,
		st->wMinute,
		st->wSecond,
		st->wMilliseconds));

	double nextMonth;
	if (st->wMonth < 12) {
		nextMonth = avmplus::MakeDate(avmplus::MakeDay(year, st->wMonth, 1), 0);
	} else {
		nextMonth = avmplus::MakeDate(avmplus::MakeDay(year + 1, 0, 1), 0);
	}

	int dayOfWeek = avmplus::WeekDay(timeValue);
	if (dayOfWeek != st->wDayOfWeek) {
		int dayDelta = st->wDayOfWeek - dayOfWeek;
		if (dayDelta < 0) {
			dayDelta += 7;
		}
		timeValue += (double)kMsecPerDay * dayDelta;
	}

	// Advance appropriate # of weeks
	timeValue += (double)kMsecPerDay * 7.0 * (st->wDay - 1);

	// Cap it at the end of the month
	while (timeValue >= nextMonth) {
		timeValue -= (double)kMsecPerDay * 7.0;
	}

	return timeValue;
}

double VMPI_getDaylightSavingsTA(double time)
{
	// On Windows, ask the OS what the daylight saving time bias
	// is.  If it's zero, perform no adjustment.
	TIME_ZONE_INFORMATION tz = UpdateTimeZoneInfo();
	if (tz.DaylightBias != -60 || tz.DaylightDate.wMonth == 0) {
		return 0;
	}

	// In most of the US, Daylight Saving Time begins on the
	// first Sunday of April at 2 AM.  It ends at 2 am on
	// the last Sunday of October.

	// 1. Compute year this time represents.
	int year = avmplus::YearFromTime(time);

	// 2. Compute time that daylight saving time begins
	double timeD = ConvertWin32DST(year, &tz.DaylightDate);

	// 3. Compute time that standard time begins
	double timeS = ConvertWin32DST(year, &tz.StandardDate);

	// Subtract the daylight bias from the standard transition time
	timeS -= kMsecPerHour;

	// The times we have calculated are in local time,
	// but "time" was passed in as UTC.  Convert it to local time.
	time += VMPI_getLocalTimeOffset();

	// Does time fall in the daylight saving period?

	// Where Daylight savings time is earlier in the year than standard time
	if(timeS > timeD)
	{
		if (time >= timeD && time < timeS)
			return kMsecPerHour;
		else
			return 0;
	}
	// Where Daylight savings time is later in the year than standard time
	else
	{
		if (time >= timeS && time < timeD)
			return 0;
		else
			return kMsecPerHour;
	}

}

#define FILETIME_EPOCH_BIAS ((LONGLONG)116444736000000000)
#define FILETIME_MS_FACTOR (10000.0)

static double NormalizeFileTime(FILETIME* ft)
{
	LARGE_INTEGER li;
	li.LowPart = ft->dwLowDateTime;
	li.HighPart = ft->dwHighDateTime;

	return ((double) (li.QuadPart - FILETIME_EPOCH_BIAS)) / FILETIME_MS_FACTOR;
}

static double NormalizeSystemTime(SYSTEMTIME* st)
{
	FILETIME ft;
	SystemTimeToFileTime(st, &ft);
	return NormalizeFileTime(&ft);
}

double VMPI_getDate()
{
	SYSTEMTIME stime;
	GetSystemTime(&stime);
	return NormalizeSystemTime(&stime);
}

uint64_t VMPI_getTime()
{
	return timeGetTime();
}

void* VMPI_alloc(size_t size)
{
	return HeapAlloc(GetProcessHeap(), 0, size);
}

void VMPI_free(void* ptr)
{
	HeapFree(GetProcessHeap(), 0, ptr);
}

size_t VMPI_size(void* ptr)
{
	return HeapSize(GetProcessHeap(), 0, ptr);
}

typedef void (*LoggingFunction)(const char*);

LoggingFunction logFunc = NULL;

void RedirectLogOutput(LoggingFunction func)
{
	logFunc = func;
}

void VMPI_log(const char* message)
{
#ifndef UNDER_CE
	::OutputDebugStringA(message);
#endif

	if(logFunc)
		logFunc(message);
	else {
		printf("%s",message);
		fflush(stdout);
	}
}

bool VMPI_isMemoryProfilingEnabled()
{
#if defined (UNDER_CE) && defined(MMGC_MEMORY_PROFILER)
	return true;
#else
	//read the mmgc profiling option switch
	const char *env = VMPI_getenv("MMGC_PROFILE");
	return (env && (VMPI_strncmp(env, "1", 1) == 0));
#endif
}

void VMPI_setPageProtection(void *address,
							size_t size,
							bool executableFlag,
							bool writeableFlag)
{
	DWORD oldProtectFlags = 0;
	DWORD newProtectFlags = 0;
	if ( executableFlag && writeableFlag ) {
		newProtectFlags = PAGE_EXECUTE_READWRITE;
	} else if ( executableFlag ) {
		newProtectFlags = PAGE_EXECUTE_READ;
	} else if ( writeableFlag ) {
		newProtectFlags = PAGE_READWRITE;
	} else {
		newProtectFlags = PAGE_READONLY;
	}

	BOOL retval;
	MEMORY_BASIC_INFORMATION mbi;
	do {
		VirtualQuery(address, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
		size_t markSize = size > mbi.RegionSize ? mbi.RegionSize : size;

		retval = VirtualProtect(address, markSize, newProtectFlags, &oldProtectFlags);
		GCAssert(retval);

		address = (char*) address + markSize;
		size -= markSize;
	} while(size > 0 && retval);

	// We should not be clobbering PAGE_GUARD protections
	GCAssert((oldProtectFlags & PAGE_GUARD) == 0);
}

const char *VMPI_getenv(const char *env)
{
	const char *val = NULL;
	(void)env;
#ifndef UNDER_CE
	val = getenv(env);
#endif
	return val;
}
<|endoftext|>


<commit_before>//
// sync_client.cpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// 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)
//

#include <iostream>
#include <istream>
#include <ostream>
#include <string>
#include <regex>
#include <boost/algorithm/string.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <fc/variant.hpp>
#include <fc/io/json.hpp>
#include <fc/network/platform_root_ca.hpp>
#include <eosio/chain/exceptions.hpp>
#include <eosio/http_plugin/http_plugin.hpp>
#include <eosio/chain_plugin/chain_plugin.hpp>
#include <boost/asio/ssl/rfc2818_verification.hpp>
#include "httpc.hpp"

using boost::asio::ip::tcp;
using namespace eosio::chain;
namespace eosio { namespace client { namespace http {

   namespace detail {
      class http_context_impl {
         public:
            boost::asio::io_service ios;
      };

      void http_context_deleter::operator()(http_context_impl* p) const {
         delete p;
      }
   }

   http_context create_http_context() {
      return http_context(new detail::http_context_impl, detail::http_context_deleter());
   }

   void do_connect(tcp::socket& sock, const resolved_url& url) {
      // Get a list of endpoints corresponding to the server name.
      vector<tcp::endpoint> endpoints;
      endpoints.reserve(url.resolved_addresses.size());
      for (const auto& addr: url.resolved_addresses) {
         endpoints.emplace_back(boost::asio::ip::make_address(addr), url.resolved_port);
      }
      boost::asio::connect(sock, endpoints);
   }

   template<class T>
   std::string do_txrx(T& socket, boost::asio::streambuf& request_buff, unsigned int& status_code) {
      // Send the request.
      boost::asio::write(socket, request_buff);

      // Read the response status line. The response streambuf will automatically
      // grow to accommodate the entire line. The growth may be limited by passing
      // a maximum size to the streambuf constructor.
      boost::asio::streambuf response;
      boost::asio::read_until(socket, response, "\r\n");

      // Check that response is OK.
      std::istream response_stream(&response);
      std::string http_version;
      response_stream >> http_version;
      response_stream >> status_code;

      EOS_ASSERT( status_code != 400, invalid_http_request, "The server has rejected the request as invalid!");

      std::string status_message;
      std::getline(response_stream, status_message);
      EOS_ASSERT( !(!response_stream || http_version.substr(0, 5) != "HTTP/"), invalid_http_response, "Invalid Response" );

      // Read the response headers, which are terminated by a blank line.
      boost::asio::read_until(socket, response, "\r\n\r\n");

      // Process the response headers.
      std::string header;
      int response_content_length = -1;
      std::regex clregex(R"xx(^content-length:\s+(\d+))xx", std::regex_constants::icase);
      while (std::getline(response_stream, header) && header != "\r") {
         std::smatch match;
         if(std::regex_search(header, match, clregex))
            response_content_length = std::stoi(match[1]);
      }
      EOS_ASSERT(response_content_length >= 0, invalid_http_response, "Invalid content-length response");

      std::stringstream re;
      // Write whatever content we already have to output.
      response_content_length -= response.size();
      if (response.size() > 0)
         re << &response;

      boost::asio::read(socket, response, boost::asio::transfer_exactly(response_content_length));
      re << &response;

      return re.str();
   }

   parsed_url parse_url( const string& server_url ) {
      parsed_url res;

      //unix socket doesn't quite follow classical "URL" rules so deal with it manually
      if(boost::algorithm::starts_with(server_url, "unix://")) {
         res.scheme = "unix";
         res.server = server_url.substr(strlen("unix://"));
         return res;
      }

      //via rfc3986 and modified a bit to suck out the port number
      //Sadly this doesn't work for ipv6 addresses
      std::regex rgx(R"xx(^(([^:/?#]+):)?(//([^:/?#]*)(:(\d+))?)?([^?#]*)(\?([^#]*))?(#(.*))?)xx");
      std::smatch match;
      if(std::regex_search(server_url.begin(), server_url.end(), match, rgx)) {
         res.scheme = match[2];
         res.server = match[4];
         res.port = match[6];
         res.path = match[7];
      }
      if(res.scheme != "http" && res.scheme != "https")
         EOS_THROW(fail_to_resolve_host, "Unrecognized URL scheme (${s}) in URL \"${u}\"", ("s", res.scheme)("u", server_url));
      if(res.server.empty())
         EOS_THROW(fail_to_resolve_host, "No server parsed from URL \"${u}\"", ("u", server_url));
      if(res.port.empty())
         res.port = res.scheme == "http" ? "80" : "443";
      boost::trim_right_if(res.path, boost::is_any_of("/"));
      return res;
   }

   resolved_url resolve_url( const http_context& context, const parsed_url& url ) {
      if(url.scheme == "unix")
         return resolved_url(url);

      tcp::resolver resolver(context->ios);
      boost::system::error_code ec;
      auto result = resolver.resolve(tcp::v4(), url.server, url.port, ec);
      if (ec) {
         EOS_THROW(fail_to_resolve_host, "Error resolving \"${server}:${port}\" : ${m}", ("server", url.server)("port",url.port)("m",ec.message()));
      }

      // non error results are guaranteed to return a non-empty range
      vector<string> resolved_addresses;
      resolved_addresses.reserve(result.size());
      optional<uint16_t> resolved_port;
      bool is_loopback = true;

      for(const auto& r : result) {
         const auto& addr = r.endpoint().address();
         if (addr.is_v6()) continue;
         uint16_t port = r.endpoint().port();
         resolved_addresses.emplace_back(addr.to_string());
         is_loopback = is_loopback && addr.is_loopback();

         if (resolved_port) {
            EOS_ASSERT(*resolved_port == port, resolved_to_multiple_ports, "Service name \"${port}\" resolved to multiple ports and this is not supported!", ("port",url.port));
         } else {
            resolved_port = port;
         }
      }

      return resolved_url(url, std::move(resolved_addresses), *resolved_port, is_loopback);
   }

   string format_host_header(const resolved_url& url) {
      // common practice is to only make the port explicit when it is the non-default port
      if (
         (url.scheme == "https" && url.resolved_port == 443) ||
         (url.scheme == "http" && url.resolved_port == 80)
      ) {
         return url.server;
      } else {
         return url.server + ":" + url.port;
      }
   }

   fc::variant do_http_call( const connection_param& cp,
                             const fc::variant& postdata,
                             bool print_request,
                             bool print_response ) {
   std::string postjson;
   if( !postdata.is_null() ) {
      postjson = print_request ? fc::json::to_pretty_string( postdata ) : fc::json::to_string( postdata );
   }

   const auto& url = cp.url;

   boost::asio::streambuf request;
   std::ostream request_stream(&request);
   auto host_header_value = format_host_header(url);
   request_stream << "POST " << url.path << " HTTP/1.0\r\n";
   request_stream << "Host: " << host_header_value << "\r\n";
   request_stream << "content-length: " << postjson.size() << "\r\n";
   request_stream << "Accept: */*\r\n";
   request_stream << "Connection: close\r\n";
   // append more customized headers
   std::vector<string>::iterator itr;
   for (itr = cp.headers.begin(); itr != cp.headers.end(); itr++) {
      request_stream << *itr << "\r\n";
   }
   request_stream << "\r\n";
   request_stream << postjson;

   if ( print_request ) {
      string s(request.size(), '\0');
      buffer_copy(boost::asio::buffer(s), request.data());
      std::cerr << "REQUEST:" << std::endl
                << "---------------------" << std::endl
                << s << std::endl
                << "---------------------" << std::endl;
   }

   unsigned int status_code;
   std::string re;

   try {
      if(url.scheme == "unix") {
         boost::asio::local::stream_protocol::socket unix_socket(cp.context->ios);
         unix_socket.connect(boost::asio::local::stream_protocol::endpoint(url.server));
         re = do_txrx(unix_socket, request, status_code);
      }
      else if(url.scheme == "http") {
         tcp::socket socket(cp.context->ios);
         do_connect(socket, url);
         re = do_txrx(socket, request, status_code);
      }
      else { //https
         boost::asio::ssl::context ssl_context(boost::asio::ssl::context::sslv23_client);
         fc::add_platform_root_cas_to_context(ssl_context);

         boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket(cp.context->ios, ssl_context);
         SSL_set_tlsext_host_name(socket.native_handle(), url.server.c_str());
         if(cp.verify_cert) {
            socket.set_verify_mode(boost::asio::ssl::verify_peer);
            socket.set_verify_callback(boost::asio::ssl::rfc2818_verification(url.server));
         }
         do_connect(socket.next_layer(), url);
         socket.handshake(boost::asio::ssl::stream_base::client);
         re = do_txrx(socket, request, status_code);
         //try and do a clean shutdown; but swallow if this fails (other side could have already gave TCP the ax)
         try {socket.shutdown();} catch(...) {}
      }
   } catch ( invalid_http_request& e ) {
      e.append_log( FC_LOG_MESSAGE( info, "Please verify this url is valid: ${url}", ("url", url.scheme + "://" + url.server + ":" + url.port + url.path) ) );
      e.append_log( FC_LOG_MESSAGE( info, "If the condition persists, please contact the RPC server administrator for ${server}!", ("server", url.server) ) );
      throw;
   }

   const auto response_result = fc::json::from_string(re);
   if( print_response ) {
      std::cerr << "RESPONSE:" << std::endl
                << "---------------------" << std::endl
                << fc::json::to_pretty_string( response_result ) << std::endl
                << "---------------------" << std::endl;
   }
   if( status_code == 200 || status_code == 201 || status_code == 202 ) {
      return response_result;
   } else if( status_code == 404 ) {
      // Unknown endpoint
      if (url.path.compare(0, chain_func_base.size(), chain_func_base) == 0) {
         throw chain::missing_chain_api_plugin_exception(FC_LOG_MESSAGE(error, "Chain API plugin is not enabled"));
      } else if (url.path.compare(0, wallet_func_base.size(), wallet_func_base) == 0) {
         throw chain::missing_wallet_api_plugin_exception(FC_LOG_MESSAGE(error, "Wallet is not available"));
      } else if (url.path.compare(0, history_func_base.size(), history_func_base) == 0) {
         throw chain::missing_history_api_plugin_exception(FC_LOG_MESSAGE(error, "History API plugin is not enabled"));
      } else if (url.path.compare(0, net_func_base.size(), net_func_base) == 0) {
         throw chain::missing_net_api_plugin_exception(FC_LOG_MESSAGE(error, "Net API plugin is not enabled"));
      }
   } else {
      auto &&error_info = response_result.as<eosio::error_results>().error;
      // Construct fc exception from error
      const auto &error_details = error_info.details;

      fc::log_messages logs;
      for (auto itr = error_details.begin(); itr != error_details.end(); itr++) {
         const auto& context = fc::log_context(fc::log_level::error, itr->file.data(), itr->line_number, itr->method.data());
         logs.emplace_back(fc::log_message(context, itr->message));
      }

      throw fc::exception(logs, error_info.code, error_info.name, error_info.what);
   }

   EOS_ASSERT( status_code == 200, http_request_fail, "Error code ${c}\n: ${msg}\n", ("c", status_code)("msg", re) );
   return response_result;
   }
}}}
<commit_msg>cleos: handle no Content-length header in http response<commit_after>//
// sync_client.cpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// 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)
//

#include <iostream>
#include <istream>
#include <ostream>
#include <string>
#include <regex>
#include <boost/algorithm/string.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <fc/variant.hpp>
#include <fc/io/json.hpp>
#include <fc/network/platform_root_ca.hpp>
#include <eosio/chain/exceptions.hpp>
#include <eosio/http_plugin/http_plugin.hpp>
#include <eosio/chain_plugin/chain_plugin.hpp>
#include <boost/asio/ssl/rfc2818_verification.hpp>
#include "httpc.hpp"

using boost::asio::ip::tcp;
using namespace eosio::chain;
namespace eosio { namespace client { namespace http {

   namespace detail {
      class http_context_impl {
         public:
            boost::asio::io_service ios;
      };

      void http_context_deleter::operator()(http_context_impl* p) const {
         delete p;
      }
   }

   http_context create_http_context() {
      return http_context(new detail::http_context_impl, detail::http_context_deleter());
   }

   void do_connect(tcp::socket& sock, const resolved_url& url) {
      // Get a list of endpoints corresponding to the server name.
      vector<tcp::endpoint> endpoints;
      endpoints.reserve(url.resolved_addresses.size());
      for (const auto& addr: url.resolved_addresses) {
         endpoints.emplace_back(boost::asio::ip::make_address(addr), url.resolved_port);
      }
      boost::asio::connect(sock, endpoints);
   }

   template<class T>
   std::string do_txrx(T& socket, boost::asio::streambuf& request_buff, unsigned int& status_code) {
      // Send the request.
      boost::asio::write(socket, request_buff);

      // Read the response status line. The response streambuf will automatically
      // grow to accommodate the entire line. The growth may be limited by passing
      // a maximum size to the streambuf constructor.
      boost::asio::streambuf response;
      boost::asio::read_until(socket, response, "\r\n");

      // Check that response is OK.
      std::istream response_stream(&response);
      std::string http_version;
      response_stream >> http_version;
      response_stream >> status_code;

      EOS_ASSERT( status_code != 400, invalid_http_request, "The server has rejected the request as invalid!");

      std::string status_message;
      std::getline(response_stream, status_message);
      EOS_ASSERT( !(!response_stream || http_version.substr(0, 5) != "HTTP/"), invalid_http_response, "Invalid Response" );

      // Read the response headers, which are terminated by a blank line.
      boost::asio::read_until(socket, response, "\r\n\r\n");

      // Process the response headers.
      std::string header;
      int response_content_length = -1;
      std::regex clregex(R"xx(^content-length:\s+(\d+))xx", std::regex_constants::icase);
      while (std::getline(response_stream, header) && header != "\r") {
         std::smatch match;
         if(std::regex_search(header, match, clregex))
            response_content_length = std::stoi(match[1]);
      }

      std::stringstream re;
      if( response_content_length >= 0 ) {
         // Write whatever content we already have to output.
         response_content_length -= response.size();
         if (response.size() > 0)
            re << &response;

         boost::asio::read(socket, response, boost::asio::transfer_exactly(response_content_length));
      } else {
         boost::system::error_code ec;
         boost::asio::read(socket, response, boost::asio::transfer_all(), ec);
         EOS_ASSERT(!ec || ec == boost::asio::ssl::error::stream_truncated, http_exception, "Unable to read http response: ${err}", ("err",ec.message()));
      }

      re << &response;
      return re.str();
   }

   parsed_url parse_url( const string& server_url ) {
      parsed_url res;

      //unix socket doesn't quite follow classical "URL" rules so deal with it manually
      if(boost::algorithm::starts_with(server_url, "unix://")) {
         res.scheme = "unix";
         res.server = server_url.substr(strlen("unix://"));
         return res;
      }

      //via rfc3986 and modified a bit to suck out the port number
      //Sadly this doesn't work for ipv6 addresses
      std::regex rgx(R"xx(^(([^:/?#]+):)?(//([^:/?#]*)(:(\d+))?)?([^?#]*)(\?([^#]*))?(#(.*))?)xx");
      std::smatch match;
      if(std::regex_search(server_url.begin(), server_url.end(), match, rgx)) {
         res.scheme = match[2];
         res.server = match[4];
         res.port = match[6];
         res.path = match[7];
      }
      if(res.scheme != "http" && res.scheme != "https")
         EOS_THROW(fail_to_resolve_host, "Unrecognized URL scheme (${s}) in URL \"${u}\"", ("s", res.scheme)("u", server_url));
      if(res.server.empty())
         EOS_THROW(fail_to_resolve_host, "No server parsed from URL \"${u}\"", ("u", server_url));
      if(res.port.empty())
         res.port = res.scheme == "http" ? "80" : "443";
      boost::trim_right_if(res.path, boost::is_any_of("/"));
      return res;
   }

   resolved_url resolve_url( const http_context& context, const parsed_url& url ) {
      if(url.scheme == "unix")
         return resolved_url(url);

      tcp::resolver resolver(context->ios);
      boost::system::error_code ec;
      auto result = resolver.resolve(tcp::v4(), url.server, url.port, ec);
      if (ec) {
         EOS_THROW(fail_to_resolve_host, "Error resolving \"${server}:${port}\" : ${m}", ("server", url.server)("port",url.port)("m",ec.message()));
      }

      // non error results are guaranteed to return a non-empty range
      vector<string> resolved_addresses;
      resolved_addresses.reserve(result.size());
      optional<uint16_t> resolved_port;
      bool is_loopback = true;

      for(const auto& r : result) {
         const auto& addr = r.endpoint().address();
         if (addr.is_v6()) continue;
         uint16_t port = r.endpoint().port();
         resolved_addresses.emplace_back(addr.to_string());
         is_loopback = is_loopback && addr.is_loopback();

         if (resolved_port) {
            EOS_ASSERT(*resolved_port == port, resolved_to_multiple_ports, "Service name \"${port}\" resolved to multiple ports and this is not supported!", ("port",url.port));
         } else {
            resolved_port = port;
         }
      }

      return resolved_url(url, std::move(resolved_addresses), *resolved_port, is_loopback);
   }

   string format_host_header(const resolved_url& url) {
      // common practice is to only make the port explicit when it is the non-default port
      if (
         (url.scheme == "https" && url.resolved_port == 443) ||
         (url.scheme == "http" && url.resolved_port == 80)
      ) {
         return url.server;
      } else {
         return url.server + ":" + url.port;
      }
   }

   fc::variant do_http_call( const connection_param& cp,
                             const fc::variant& postdata,
                             bool print_request,
                             bool print_response ) {
   std::string postjson;
   if( !postdata.is_null() ) {
      postjson = print_request ? fc::json::to_pretty_string( postdata ) : fc::json::to_string( postdata );
   }

   const auto& url = cp.url;

   boost::asio::streambuf request;
   std::ostream request_stream(&request);
   auto host_header_value = format_host_header(url);
   request_stream << "POST " << url.path << " HTTP/1.0\r\n";
   request_stream << "Host: " << host_header_value << "\r\n";
   request_stream << "content-length: " << postjson.size() << "\r\n";
   request_stream << "Accept: */*\r\n";
   request_stream << "Connection: close\r\n";
   // append more customized headers
   std::vector<string>::iterator itr;
   for (itr = cp.headers.begin(); itr != cp.headers.end(); itr++) {
      request_stream << *itr << "\r\n";
   }
   request_stream << "\r\n";
   request_stream << postjson;

   if ( print_request ) {
      string s(request.size(), '\0');
      buffer_copy(boost::asio::buffer(s), request.data());
      std::cerr << "REQUEST:" << std::endl
                << "---------------------" << std::endl
                << s << std::endl
                << "---------------------" << std::endl;
   }

   unsigned int status_code;
   std::string re;

   try {
      if(url.scheme == "unix") {
         boost::asio::local::stream_protocol::socket unix_socket(cp.context->ios);
         unix_socket.connect(boost::asio::local::stream_protocol::endpoint(url.server));
         re = do_txrx(unix_socket, request, status_code);
      }
      else if(url.scheme == "http") {
         tcp::socket socket(cp.context->ios);
         do_connect(socket, url);
         re = do_txrx(socket, request, status_code);
      }
      else { //https
         boost::asio::ssl::context ssl_context(boost::asio::ssl::context::sslv23_client);
         fc::add_platform_root_cas_to_context(ssl_context);

         boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket(cp.context->ios, ssl_context);
         SSL_set_tlsext_host_name(socket.native_handle(), url.server.c_str());
         if(cp.verify_cert) {
            socket.set_verify_mode(boost::asio::ssl::verify_peer);
            socket.set_verify_callback(boost::asio::ssl::rfc2818_verification(url.server));
         }
         do_connect(socket.next_layer(), url);
         socket.handshake(boost::asio::ssl::stream_base::client);
         re = do_txrx(socket, request, status_code);
         //try and do a clean shutdown; but swallow if this fails (other side could have already gave TCP the ax)
         try {socket.shutdown();} catch(...) {}
      }
   } catch ( invalid_http_request& e ) {
      e.append_log( FC_LOG_MESSAGE( info, "Please verify this url is valid: ${url}", ("url", url.scheme + "://" + url.server + ":" + url.port + url.path) ) );
      e.append_log( FC_LOG_MESSAGE( info, "If the condition persists, please contact the RPC server administrator for ${server}!", ("server", url.server) ) );
      throw;
   }

   const auto response_result = fc::json::from_string(re);
   if( print_response ) {
      std::cerr << "RESPONSE:" << std::endl
                << "---------------------" << std::endl
                << fc::json::to_pretty_string( response_result ) << std::endl
                << "---------------------" << std::endl;
   }
   if( status_code == 200 || status_code == 201 || status_code == 202 ) {
      return response_result;
   } else if( status_code == 404 ) {
      // Unknown endpoint
      if (url.path.compare(0, chain_func_base.size(), chain_func_base) == 0) {
         throw chain::missing_chain_api_plugin_exception(FC_LOG_MESSAGE(error, "Chain API plugin is not enabled"));
      } else if (url.path.compare(0, wallet_func_base.size(), wallet_func_base) == 0) {
         throw chain::missing_wallet_api_plugin_exception(FC_LOG_MESSAGE(error, "Wallet is not available"));
      } else if (url.path.compare(0, history_func_base.size(), history_func_base) == 0) {
         throw chain::missing_history_api_plugin_exception(FC_LOG_MESSAGE(error, "History API plugin is not enabled"));
      } else if (url.path.compare(0, net_func_base.size(), net_func_base) == 0) {
         throw chain::missing_net_api_plugin_exception(FC_LOG_MESSAGE(error, "Net API plugin is not enabled"));
      }
   } else {
      auto &&error_info = response_result.as<eosio::error_results>().error;
      // Construct fc exception from error
      const auto &error_details = error_info.details;

      fc::log_messages logs;
      for (auto itr = error_details.begin(); itr != error_details.end(); itr++) {
         const auto& context = fc::log_context(fc::log_level::error, itr->file.data(), itr->line_number, itr->method.data());
         logs.emplace_back(fc::log_message(context, itr->message));
      }

      throw fc::exception(logs, error_info.code, error_info.name, error_info.what);
   }

   EOS_ASSERT( status_code == 200, http_request_fail, "Error code ${c}\n: ${msg}\n", ("c", status_code)("msg", re) );
   return response_result;
   }
}}}
<|endoftext|>