<commit_before>#include <gtest/gtest.h>
#include <uvw.hpp>

TEST(Thread, Run) {
    auto loop = uvw::Loop::getDefault();
    auto has_run = std::make_shared<bool>();

    auto handle = loop->resource<uvw::Thread>([](std::shared_ptr<void> data) {
        if(auto has_run = std::static_pointer_cast<bool>(data); has_run) {
            *has_run = true;
        }
    }, has_run);

    ASSERT_TRUE(handle->run());
    ASSERT_TRUE(handle->join());
    ASSERT_TRUE(*has_run);

    loop->run();
}

TEST(ThreadLocalStorage, SetGet) {
    auto loop = uvw::Loop::getDefault();
    auto localStorage = loop->resource<uvw::ThreadLocalStorage>();
    auto flag{true};

    localStorage->set<bool>(&flag);
    ASSERT_TRUE(localStorage->get<bool>());

    loop->run();
}

TEST(Mutex, LockUnlock) {
    auto loop = uvw::Loop::getDefault();
    auto mtx = loop->resource<uvw::Mutex>();

    mtx->lock();
    ASSERT_FALSE(mtx->tryLock());
    mtx->unlock();
    ASSERT_TRUE(mtx->tryLock());
    ASSERT_FALSE(mtx->tryLock());
    mtx->unlock();

    loop->run();
}

TEST(Mutex, RecursiveLockUnlock) {
    auto loop = uvw::Loop::getDefault();
    auto recursive_mtx = loop->resource<uvw::Mutex>(true);

    recursive_mtx->lock();
    recursive_mtx->unlock();

    recursive_mtx->lock();
    ASSERT_TRUE(recursive_mtx->tryLock());
    recursive_mtx->unlock();
    recursive_mtx->unlock();

    loop->run();
}
<commit_msg>test: make thread tests run fine also on Windows<commit_after>#include <gtest/gtest.h>
#include <uvw.hpp>

TEST(Thread, Run) {
    auto loop = uvw::Loop::getDefault();
    auto has_run = std::make_shared<bool>();

    auto handle = loop->resource<uvw::Thread>([](std::shared_ptr<void> data) {
        if(auto has_run = std::static_pointer_cast<bool>(data); has_run) {
            *has_run = true;
        }
    }, has_run);

    ASSERT_TRUE(handle->run());
    ASSERT_TRUE(handle->join());
    ASSERT_TRUE(*has_run);

    loop->run();
}

TEST(ThreadLocalStorage, SetGet) {
    auto loop = uvw::Loop::getDefault();
    auto localStorage = loop->resource<uvw::ThreadLocalStorage>();
    auto flag{true};

    localStorage->set<bool>(&flag);
    ASSERT_TRUE(localStorage->get<bool>());

    loop->run();
}

TEST(Mutex, LockUnlock) {
    auto loop = uvw::Loop::getDefault();
    auto mtx = loop->resource<uvw::Mutex>();

    mtx->lock();

#ifdef _MSC_VER
    // this is allowed by libuv on Windows
    ASSERT_TRUE(mtx->tryLock());
#else
    ASSERT_FALSE(mtx->tryLock());
#endif

    mtx->unlock();
    ASSERT_TRUE(mtx->tryLock());

#ifdef _MSC_VER
    // this is allowed by libuv on Windows
    ASSERT_TRUE(mtx->tryLock());
#else
    ASSERT_FALSE(mtx->tryLock());
#endif

    mtx->unlock();

    loop->run();
}

TEST(Mutex, RecursiveLockUnlock) {
    auto loop = uvw::Loop::getDefault();
    auto recursive_mtx = loop->resource<uvw::Mutex>(true);

    recursive_mtx->lock();
    recursive_mtx->unlock();

    recursive_mtx->lock();
    ASSERT_TRUE(recursive_mtx->tryLock());
    recursive_mtx->unlock();
    recursive_mtx->unlock();

    loop->run();
}
<|endoftext|>


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

  Program:   Visualization Toolkit
  Module:    finance.cxx

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

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

=========================================================================*/
#include "vtkActor.h"
#include "vtkAxes.h"
#include "vtkContourFilter.h"
#include "vtkDataSet.h"
#include "vtkFloatArray.h"
#include "vtkGaussianSplatter.h"
#include "vtkImageData.h"
#include "vtkPointData.h"
#include "vtkPoints.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkTubeFilter.h"
#include "vtkUnstructuredGrid.h"

static vtkDataSet *ReadFinancialData(const char *x, const char *y, const char *z, const char *s);
static int ParseFile(FILE *file, const char *tag, float *data);

int main( int argc, char *argv[] )
{
  float bounds[6];
  vtkDataSet *dataSet;

  // read data
  dataSet = ReadFinancialData("MONTHLY_PAYMENT","INTEREST_RATE",
                              "LOAN_AMOUNT","TIME_LATE");
  if ( ! dataSet ) exit(0);

  // construct pipeline for original population
  vtkGaussianSplatter *popSplatter = vtkGaussianSplatter::New();
    popSplatter->SetInput(dataSet);
    popSplatter->SetSampleDimensions(50,50,50);
    popSplatter->SetRadius(0.05);
    popSplatter->ScalarWarpingOff();
  vtkContourFilter *popSurface = vtkContourFilter::New();
    popSurface->SetInput(popSplatter->GetOutput());
    popSurface->SetValue(0,0.01);
  vtkPolyDataMapper *popMapper = vtkPolyDataMapper::New();
    popMapper->SetInput(popSurface->GetOutput());
    popMapper->ScalarVisibilityOff();
  vtkActor *popActor = vtkActor::New();
    popActor->SetMapper(popMapper);
    popActor->GetProperty()->SetOpacity(0.3);
    popActor->GetProperty()->SetColor(.9,.9,.9);

  // construct pipeline for delinquent population
  vtkGaussianSplatter *lateSplatter = vtkGaussianSplatter::New();
    lateSplatter->SetInput(dataSet);
    lateSplatter->SetSampleDimensions(50,50,50);
    lateSplatter->SetRadius(0.05);
    lateSplatter->SetScaleFactor(0.005);
  vtkContourFilter *lateSurface = vtkContourFilter::New();
    lateSurface->SetInput(lateSplatter->GetOutput());
    lateSurface->SetValue(0,0.01);
  vtkPolyDataMapper *lateMapper = vtkPolyDataMapper::New();
    lateMapper->SetInput(lateSurface->GetOutput());
    lateMapper->ScalarVisibilityOff();
  vtkActor *lateActor = vtkActor::New();
    lateActor->SetMapper(lateMapper);
    lateActor->GetProperty()->SetColor(1.0,0.0,0.0);

  // create axes
  popSplatter->Update();
  popSplatter->GetOutput()->GetBounds(bounds);
  vtkAxes *axes = vtkAxes::New();
    axes->SetOrigin(bounds[0], bounds[2], bounds[4]);
    axes->SetScaleFactor(popSplatter->GetOutput()->GetLength()/5);
  vtkTubeFilter *axesTubes = vtkTubeFilter::New();
    axesTubes->SetInput(axes->GetOutput());
    axesTubes->SetRadius(axes->GetScaleFactor()/25.0);
    axesTubes->SetNumberOfSides(6);
  vtkPolyDataMapper *axesMapper = vtkPolyDataMapper::New();
    axesMapper->SetInput(axesTubes->GetOutput());
  vtkActor *axesActor = vtkActor::New();
    axesActor->SetMapper(axesMapper);

  // graphics stuff
  vtkRenderer *renderer = vtkRenderer::New();
  vtkRenderWindow *renWin = vtkRenderWindow::New();
    renWin->AddRenderer(renderer);
  vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
    iren->SetRenderWindow(renWin);

  // read data  //set up renderer
  renderer->AddActor(lateActor);
  renderer->AddActor(axesActor);
  renderer->AddActor(popActor);
  renderer->SetBackground(1,1,1);
  renWin->SetSize(300,300);

  // interact with data
  iren->Initialize();

  renWin->Render();
  iren->Start();

  // Clean up
  renderer->Delete();
  renWin->Delete();
  iren->Delete();
  popSplatter->Delete();
  popSurface->Delete();
  popMapper->Delete();
  popActor->Delete();
  lateSplatter->Delete();
  lateSurface->Delete();
  lateMapper->Delete();
  lateActor->Delete();
  axes->Delete();
  axesTubes->Delete();
  axesMapper->Delete();
  axesActor->Delete();
  dataSet->Delete();

  return 0;
}

static vtkDataSet *ReadFinancialData(const char *x, const char *y, const char *z, const char *s)
{
  float xyz[3];
  FILE *file;
  int i, npts;
  char tag[80];
  const char *temp=getenv("VTK_DATA_ROOT");
  char filename[2048];

  if ( ! temp )
    {
    cerr << "ERROR: Please define environment variable VTK_DATA_ROOT\n";
    return NULL;
    }
  strcpy(filename,temp);
  strcat(filename,"/Data/financial.txt");

  if ( (file = fopen(filename,"r")) == 0 )
    {
    cerr << "ERROR: Can't read file: " << filename << "\n";
    return NULL;
    }

  fscanf (file, "%s %d", tag, &npts); // read number of points

  vtkUnstructuredGrid *dataSet = vtkUnstructuredGrid::New();
  float *xV = new float[npts];
  float *yV = new float[npts];
  float *zV = new float[npts];
  float *sV = new float[npts];

  if ( ! ParseFile(file, x, xV) || ! ParseFile(file, y, yV) ||
  ! ParseFile(file, z, zV) || ! ParseFile(file, s, sV) )
    {
    cerr << "Couldn't read data!\n";
    delete [] xV;
    delete [] yV;
    delete [] zV;
    delete [] sV;
    dataSet->Delete();
    return NULL;
    }

  vtkPoints *newPts = vtkPoints::New();
  vtkFloatArray *newScalars = vtkFloatArray::New();

  for (i=0; i<npts; i++)
    {
    xyz[0] = xV[i]; xyz[1] = yV[i]; xyz[2] = zV[i];
    newPts->InsertPoint(i, xyz);
    newScalars->InsertValue(i, sV[i]);
    }

  dataSet->SetPoints(newPts);
  dataSet->GetPointData()->SetScalars(newScalars);

  newPts->Delete(); //reference counted - it's okay
  newScalars->Delete();

  return dataSet;
}

static int ParseFile(FILE *file, const char *label, float *data)
{
  char tag[80];
  int i, npts, readData=0;
  float min=VTK_LARGE_FLOAT;
  float max=(-VTK_LARGE_FLOAT);

  if ( file == NULL || label == NULL ) return 0;

  rewind(file);

  fscanf(file, "%s %d", tag, &npts);

  while ( !readData && fscanf(file, "%s", tag) == 1 )
    {
    if ( ! strcmp(tag,label) )
      {
      readData = 1;
      for (i=0; i<npts; i++)
        {
        fscanf(file, "%f", data+i);
        if ( data[i] < min ) min = data[i];
        if ( data[i] > min ) max = data[i];
        }
      // normalize data
      for (i=0; i<npts; i++) data[i] = min + data[i]/(max-min);
      }
    else
      {
      for (i=0; i<npts; i++) fscanf(file, "%*f");
      }
    }

  if ( ! readData ) return 0;
  else return 1;
}
<commit_msg>bounds type from float to double<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    finance.cxx

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

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

=========================================================================*/
#include "vtkActor.h"
#include "vtkAxes.h"
#include "vtkContourFilter.h"
#include "vtkDataSet.h"
#include "vtkFloatArray.h"
#include "vtkGaussianSplatter.h"
#include "vtkImageData.h"
#include "vtkPointData.h"
#include "vtkPoints.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkTubeFilter.h"
#include "vtkUnstructuredGrid.h"

static vtkDataSet *ReadFinancialData(const char *x, const char *y, const char *z, const char *s);
static int ParseFile(FILE *file, const char *tag, float *data);

int main( int argc, char *argv[] )
{
  double bounds[6];
  vtkDataSet *dataSet;

  // read data
  dataSet = ReadFinancialData("MONTHLY_PAYMENT","INTEREST_RATE",
                              "LOAN_AMOUNT","TIME_LATE");
  if ( ! dataSet ) exit(0);

  // construct pipeline for original population
  vtkGaussianSplatter *popSplatter = vtkGaussianSplatter::New();
    popSplatter->SetInput(dataSet);
    popSplatter->SetSampleDimensions(50,50,50);
    popSplatter->SetRadius(0.05);
    popSplatter->ScalarWarpingOff();
  vtkContourFilter *popSurface = vtkContourFilter::New();
    popSurface->SetInput(popSplatter->GetOutput());
    popSurface->SetValue(0,0.01);
  vtkPolyDataMapper *popMapper = vtkPolyDataMapper::New();
    popMapper->SetInput(popSurface->GetOutput());
    popMapper->ScalarVisibilityOff();
  vtkActor *popActor = vtkActor::New();
    popActor->SetMapper(popMapper);
    popActor->GetProperty()->SetOpacity(0.3);
    popActor->GetProperty()->SetColor(.9,.9,.9);

  // construct pipeline for delinquent population
  vtkGaussianSplatter *lateSplatter = vtkGaussianSplatter::New();
    lateSplatter->SetInput(dataSet);
    lateSplatter->SetSampleDimensions(50,50,50);
    lateSplatter->SetRadius(0.05);
    lateSplatter->SetScaleFactor(0.005);
  vtkContourFilter *lateSurface = vtkContourFilter::New();
    lateSurface->SetInput(lateSplatter->GetOutput());
    lateSurface->SetValue(0,0.01);
  vtkPolyDataMapper *lateMapper = vtkPolyDataMapper::New();
    lateMapper->SetInput(lateSurface->GetOutput());
    lateMapper->ScalarVisibilityOff();
  vtkActor *lateActor = vtkActor::New();
    lateActor->SetMapper(lateMapper);
    lateActor->GetProperty()->SetColor(1.0,0.0,0.0);

  // create axes
  popSplatter->Update();
  popSplatter->GetOutput()->GetBounds(bounds);
  vtkAxes *axes = vtkAxes::New();
    axes->SetOrigin(bounds[0], bounds[2], bounds[4]);
    axes->SetScaleFactor(popSplatter->GetOutput()->GetLength()/5);
  vtkTubeFilter *axesTubes = vtkTubeFilter::New();
    axesTubes->SetInput(axes->GetOutput());
    axesTubes->SetRadius(axes->GetScaleFactor()/25.0);
    axesTubes->SetNumberOfSides(6);
  vtkPolyDataMapper *axesMapper = vtkPolyDataMapper::New();
    axesMapper->SetInput(axesTubes->GetOutput());
  vtkActor *axesActor = vtkActor::New();
    axesActor->SetMapper(axesMapper);

  // graphics stuff
  vtkRenderer *renderer = vtkRenderer::New();
  vtkRenderWindow *renWin = vtkRenderWindow::New();
    renWin->AddRenderer(renderer);
  vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
    iren->SetRenderWindow(renWin);

  // read data  //set up renderer
  renderer->AddActor(lateActor);
  renderer->AddActor(axesActor);
  renderer->AddActor(popActor);
  renderer->SetBackground(1,1,1);
  renWin->SetSize(300,300);

  // interact with data
  iren->Initialize();

  renWin->Render();
  iren->Start();

  // Clean up
  renderer->Delete();
  renWin->Delete();
  iren->Delete();
  popSplatter->Delete();
  popSurface->Delete();
  popMapper->Delete();
  popActor->Delete();
  lateSplatter->Delete();
  lateSurface->Delete();
  lateMapper->Delete();
  lateActor->Delete();
  axes->Delete();
  axesTubes->Delete();
  axesMapper->Delete();
  axesActor->Delete();
  dataSet->Delete();

  return 0;
}

static vtkDataSet *ReadFinancialData(const char *x, const char *y, const char *z, const char *s)
{
  float xyz[3];
  FILE *file;
  int i, npts;
  char tag[80];
  const char *temp=getenv("VTK_DATA_ROOT");
  char filename[2048];

  if ( ! temp )
    {
    cerr << "ERROR: Please define environment variable VTK_DATA_ROOT\n";
    return NULL;
    }
  strcpy(filename,temp);
  strcat(filename,"/Data/financial.txt");

  if ( (file = fopen(filename,"r")) == 0 )
    {
    cerr << "ERROR: Can't read file: " << filename << "\n";
    return NULL;
    }

  fscanf (file, "%s %d", tag, &npts); // read number of points

  vtkUnstructuredGrid *dataSet = vtkUnstructuredGrid::New();
  float *xV = new float[npts];
  float *yV = new float[npts];
  float *zV = new float[npts];
  float *sV = new float[npts];

  if ( ! ParseFile(file, x, xV) || ! ParseFile(file, y, yV) ||
  ! ParseFile(file, z, zV) || ! ParseFile(file, s, sV) )
    {
    cerr << "Couldn't read data!\n";
    delete [] xV;
    delete [] yV;
    delete [] zV;
    delete [] sV;
    dataSet->Delete();
    return NULL;
    }

  vtkPoints *newPts = vtkPoints::New();
  vtkFloatArray *newScalars = vtkFloatArray::New();

  for (i=0; i<npts; i++)
    {
    xyz[0] = xV[i]; xyz[1] = yV[i]; xyz[2] = zV[i];
    newPts->InsertPoint(i, xyz);
    newScalars->InsertValue(i, sV[i]);
    }

  dataSet->SetPoints(newPts);
  dataSet->GetPointData()->SetScalars(newScalars);

  newPts->Delete(); //reference counted - it's okay
  newScalars->Delete();

  return dataSet;
}

static int ParseFile(FILE *file, const char *label, float *data)
{
  char tag[80];
  int i, npts, readData=0;
  float min=VTK_LARGE_FLOAT;
  float max=(-VTK_LARGE_FLOAT);

  if ( file == NULL || label == NULL ) return 0;

  rewind(file);

  fscanf(file, "%s %d", tag, &npts);

  while ( !readData && fscanf(file, "%s", tag) == 1 )
    {
    if ( ! strcmp(tag,label) )
      {
      readData = 1;
      for (i=0; i<npts; i++)
        {
        fscanf(file, "%f", data+i);
        if ( data[i] < min ) min = data[i];
        if ( data[i] > min ) max = data[i];
        }
      // normalize data
      for (i=0; i<npts; i++) data[i] = min + data[i]/(max-min);
      }
    else
      {
      for (i=0; i<npts; i++) fscanf(file, "%*f");
      }
    }

  if ( ! readData ) return 0;
  else return 1;
}
<|endoftext|>


<commit_before>/*!
 * Copyright (c) 2017 by Contributors
 * \file binary_layer.h
 * \brief implementation of xnor-gemm operator for cpu
 * \author HPI-DeepLearning
*/

#include "binary_layer.h"
#include <cstdlib>

namespace mxnet {
namespace op {

#define SetBit(A,k)     ( A |= (1 << (k)) )
//#define SetBit(A,k)     ( A[(k/32)] |= (1 << (k%32)) )
//#define ClearBit(A,k)   ( A[(k/32)] &= ~(1 << (k%32)) )
//#define ClearBit(A,k)   ( A &= ~(1 << (k)) )
#define TestBit(A,k)    ( A & (1 << (k)) )
//#define TestBit(A,k)    ( A[(k/32)] & (1 << (k%32)) )

//  public  ------------------------------------------------------------------------------------------------------------

BinaryLayer::BinaryLayer(int input_channels, int input_width, int input_height, int num_filters, int kernel_width, int kernel_height, int padding_x, int padding_y):
        input_channels(input_channels),
        input_width(input_width),
        input_height(input_height),
        num_filters(num_filters),
        kernel_width(kernel_width),
        kernel_height(kernel_height),
        padding_x(padding_x),
        padding_y(padding_y) {
  CHECK_EQ(padding_x, padding_y) << "differing padding in x and y direction, unknown if supported";

  float output_size = ((input_width - kernel_width + 2 * padding_x) / stride) + 1;

  CHECK_EQ(ceilf(output_size), output_size) << "invalid output size of binary convolution layer: " << output_size;

  // padded input size
  int input_width_padded = input_width + 2 * padding_x;
  int input_height_padded = input_height + 2 * padding_y;

  binary_input = (BINARY_WORD *) calloc(input_channels * input_width_padded * input_height_padded / BITS_PER_BINARY_WORD, sizeof(BINARY_WORD));
  binary_weights = (BINARY_WORD *) calloc(num_filters * input_channels * kernel_width * kernel_height / BITS_PER_BINARY_WORD, sizeof(BINARY_WORD));

  output = (float *) calloc(input_channels * input_width_padded * input_height_padded, sizeof(float));
}

BinaryLayer::~BinaryLayer() {
  if (binary_input) free(binary_input);
  if (binary_weights) free(binary_weights);
  if (output) free(output);
  if (alpha) free(alpha);
  if (beta) free(beta);
}

void BinaryLayer::set_inputs(const mshadow::Tensor<cpu, 3, float> input) {
  float_to_binary(input, binary_input);

  if (beta) free(beta);
  beta = (float *) calloc (input.size(1) * input.size(2), sizeof(float));

  calculate_beta(beta, input);
}

void BinaryLayer::set_weights(const mshadow::Tensor<cpu, 3, float> &wmat) {
  float_to_binary(wmat, binary_weights);

  if (alpha) free(alpha);
  alpha = (float *) calloc (wmat.size(1) * wmat.size(2), sizeof(float));

  calculate_alpha(alpha, wmat);
}

void BinaryLayer::get_output(const mshadow::Tensor<cpu, 3, float> &out) {
  // @todo: what about padding?
  memcpy(out.dptr_, output, out.size(0) * out.size(1) * out.size(2) * sizeof(float));
  //binary_to_float(out);
}
//  private  -----------------------------------------------------------------------------------------------------------

/* @brief converts a 3d float tensor into a bitset
 *
 * @param input 3d float tensor
 * @param output pointer to zero-initialized memory for the bitset
 */
void BinaryLayer::float_to_binary(mshadow::Tensor<cpu, 3, float> input, BINARY_WORD *output) {
  // @todo: does this work and not run over the size of dptr_/miss some of it if it doesnt divide by 32?
  for (int i = 0; i < input.size(0) * input.size(1) * input.size(2); i += BITS_PER_BINARY_WORD) {
    BINARY_WORD tmp = 0x00000000;
    // @todo: why do we reverse the order inside one word? endianes?
    for (int x = 0; x < BITS_PER_BINARY_WORD; ++x) {
      if (std::signbit(input.dptr_[x + i]) == 0) SetBit(tmp, (BITS_PER_BINARY_WORD - 1) - x);
    }
    output[i / BITS_PER_BINARY_WORD] = tmp;
  }
}

/* @brief converts a bitset into a float tensor
 *
 * @param out a 3d output tensor
 */
void BinaryLayer::binary_to_float(const mshadow::Tensor<cpu, 3, float> &out) {
  CHECK(false) << "this method is untested!";
  int total_elements = out.size(0) * out.size(1) * out.size(2);

  for (int i = 0; i < total_elements; i += BITS_PER_BINARY_WORD) {
    BINARY_WORD tmp = (BINARY_WORD) output[i / BITS_PER_BINARY_WORD];
    for (int x = 0; x < BITS_PER_BINARY_WORD; ++x) {
      if (TestBit(tmp, (BITS_PER_BINARY_WORD - 1) - x)) {
        out.dptr_[i + x] = 1.f;
      } else {
        out.dptr_[i + x] = -1.f;
      }
    }
  }
  if (total_elements % BITS_PER_BINARY_WORD == 0) {
    return;
  }
  // there are some bits left
  BINARY_WORD tmp = (BINARY_WORD) output[total_elements / BITS_PER_BINARY_WORD];
  for (int i = total_elements - (total_elements % BITS_PER_BINARY_WORD); i < total_elements; i++) {
    if (TestBit(tmp, (BITS_PER_BINARY_WORD - 1) - i % BITS_PER_BINARY_WORD)) {
      out.dptr_[i] = 1.f;
    } else {
      out.dptr_[i] = -1.f;
    }
  }
}

/* @brief calculate mean of first dimension accross second and third dimension and save as 2d plane
 *
 * @param output_plane at every x,y this plane will contain the mean of all z values in input
 * @param input_volume expected layout: z,x,y
 */
void BinaryLayer::calculate_alpha(float *output_plane, mshadow::Tensor<cpu, 3, float> input_volume) {
  int depth = input_volume.size(0);
  int width = input_volume.size(1);
  int height = input_volume.size(2);

  for (int y = 0; y < height; ++y) {
    for (int x = 0; x < width; ++x) {
      int out = y * width + x;
      float accum = 0.0;
      for (int z = 0; z < depth; ++z) {
        accum += input_volume.dptr_[out * depth + z];
      }

      output_plane[out] = accum / depth;
    }
  }
}

// @todo ???
void BinaryLayer::calculate_beta(float *output_plane, mshadow::Tensor<cpu, 3, float> input_volume) {
  calculate_alpha(output_plane, input_volume);
}

}} // namespace mxnet { namespace op {

<commit_msg>use abs value for beta<commit_after>/*!
 * Copyright (c) 2017 by Contributors
 * \file binary_layer.h
 * \brief implementation of xnor-gemm operator for cpu
 * \author HPI-DeepLearning
*/

#include "binary_layer.h"
#include <cstdlib>

namespace mxnet {
namespace op {

#define SetBit(A,k)     ( A |= (1 << (k)) )
//#define SetBit(A,k)     ( A[(k/32)] |= (1 << (k%32)) )
//#define ClearBit(A,k)   ( A[(k/32)] &= ~(1 << (k%32)) )
//#define ClearBit(A,k)   ( A &= ~(1 << (k)) )
#define TestBit(A,k)    ( A & (1 << (k)) )
//#define TestBit(A,k)    ( A[(k/32)] & (1 << (k%32)) )

//  public  ------------------------------------------------------------------------------------------------------------

BinaryLayer::BinaryLayer(int input_channels, int input_width, int input_height, int num_filters, int kernel_width, int kernel_height, int padding_x, int padding_y):
        input_channels(input_channels),
        input_width(input_width),
        input_height(input_height),
        num_filters(num_filters),
        kernel_width(kernel_width),
        kernel_height(kernel_height),
        padding_x(padding_x),
        padding_y(padding_y) {
  CHECK_EQ(padding_x, padding_y) << "differing padding in x and y direction, unknown if supported";

  float output_size = ((input_width - kernel_width + 2 * padding_x) / stride) + 1;

  CHECK_EQ(ceilf(output_size), output_size) << "invalid output size of binary convolution layer: " << output_size;

  // padded input size
  int input_width_padded = input_width + 2 * padding_x;
  int input_height_padded = input_height + 2 * padding_y;

  binary_input = (BINARY_WORD *) calloc(input_channels * input_width_padded * input_height_padded / BITS_PER_BINARY_WORD, sizeof(BINARY_WORD));
  binary_weights = (BINARY_WORD *) calloc(num_filters * input_channels * kernel_width * kernel_height / BITS_PER_BINARY_WORD, sizeof(BINARY_WORD));

  output = (float *) calloc(input_channels * input_width_padded * input_height_padded, sizeof(float));
}

BinaryLayer::~BinaryLayer() {
  if (binary_input) free(binary_input);
  if (binary_weights) free(binary_weights);
  if (output) free(output);
  if (alpha) free(alpha);
  if (beta) free(beta);
}

void BinaryLayer::set_inputs(const mshadow::Tensor<cpu, 3, float> input) {
  float_to_binary(input, binary_input);

  if (beta) free(beta);
  beta = (float *) calloc (input.size(1) * input.size(2), sizeof(float));

  calculate_beta(beta, input);
}

void BinaryLayer::set_weights(const mshadow::Tensor<cpu, 3, float> &wmat) {
  float_to_binary(wmat, binary_weights);

  if (alpha) free(alpha);
  alpha = (float *) calloc (wmat.size(1) * wmat.size(2), sizeof(float));

  calculate_alpha(alpha, wmat);
}

void BinaryLayer::get_output(const mshadow::Tensor<cpu, 3, float> &out) {
  // @todo: what about padding?
  memcpy(out.dptr_, output, out.size(0) * out.size(1) * out.size(2) * sizeof(float));
  //binary_to_float(out);
}
//  private  -----------------------------------------------------------------------------------------------------------

/* @brief converts a 3d float tensor into a bitset
 *
 * @param input 3d float tensor
 * @param output pointer to zero-initialized memory for the bitset
 */
void BinaryLayer::float_to_binary(mshadow::Tensor<cpu, 3, float> input, BINARY_WORD *output) {
  // @todo: does this work and not run over the size of dptr_/miss some of it if it doesnt divide by 32?
  for (int i = 0; i < input.size(0) * input.size(1) * input.size(2); i += BITS_PER_BINARY_WORD) {
    BINARY_WORD tmp = 0x00000000;
    // @todo: why do we reverse the order inside one word? endianes?
    for (int x = 0; x < BITS_PER_BINARY_WORD; ++x) {
      if (std::signbit(input.dptr_[x + i]) == 0) SetBit(tmp, (BITS_PER_BINARY_WORD - 1) - x);
    }
    output[i / BITS_PER_BINARY_WORD] = tmp;
  }
}

/* @brief converts a bitset into a float tensor
 *
 * @param out a 3d output tensor
 */
void BinaryLayer::binary_to_float(const mshadow::Tensor<cpu, 3, float> &out) {
  CHECK(false) << "this method is untested!";
  int total_elements = out.size(0) * out.size(1) * out.size(2);

  for (int i = 0; i < total_elements; i += BITS_PER_BINARY_WORD) {
    BINARY_WORD tmp = (BINARY_WORD) output[i / BITS_PER_BINARY_WORD];
    for (int x = 0; x < BITS_PER_BINARY_WORD; ++x) {
      if (TestBit(tmp, (BITS_PER_BINARY_WORD - 1) - x)) {
        out.dptr_[i + x] = 1.f;
      } else {
        out.dptr_[i + x] = -1.f;
      }
    }
  }
  if (total_elements % BITS_PER_BINARY_WORD == 0) {
    return;
  }
  // there are some bits left
  BINARY_WORD tmp = (BINARY_WORD) output[total_elements / BITS_PER_BINARY_WORD];
  for (int i = total_elements - (total_elements % BITS_PER_BINARY_WORD); i < total_elements; i++) {
    if (TestBit(tmp, (BITS_PER_BINARY_WORD - 1) - i % BITS_PER_BINARY_WORD)) {
      out.dptr_[i] = 1.f;
    } else {
      out.dptr_[i] = -1.f;
    }
  }
}

/* @brief calculate mean of first dimension accross second and third dimension and save as 2d plane
 *
 * @param output_plane at every x,y this plane will contain the mean of all z values in input
 * @param input_volume expected layout: z,x,y
 */
void BinaryLayer::calculate_alpha(float *output_plane, mshadow::Tensor<cpu, 3, float> input_volume) {
  int depth = input_volume.size(0);
  int width = input_volume.size(1);
  int height = input_volume.size(2);

  for (int y = 0; y < height; ++y) {
    for (int x = 0; x < width; ++x) {
      int out = y * width + x;
      float accum = 0.0;
      for (int z = 0; z < depth; ++z) {
        accum += fabs(input_volume.dptr_[out * depth + z]);
      }

      output_plane[out] = accum / depth;
    }
  }
}

// @todo ???
void BinaryLayer::calculate_beta(float *output_plane, mshadow::Tensor<cpu, 3, float> input_volume) {
  calculate_alpha(output_plane, input_volume);
}

}} // namespace mxnet { namespace op {

<|endoftext|>


<commit_before>#include <algorithm>
#include <set>
#include <sstream>
#include <string>
#include <utility>
#include <vector>

#include "testing/reporter.h"

namespace firebase {
namespace testing {
namespace cppsdk {
ReportRow::ReportRow()
    : fake_(""),
      result_(""),
      platform_(kAny),
      args_(std::vector<std::string>()) {}

ReportRow::ReportRow(std::string fake, std::string result,
                     std::vector<std::string> args)
    : fake_(std::move(fake)),
      result_(std::move(result)),
      platform_(kAny),
      args_(std::move(args)) {}

ReportRow::ReportRow(std::string fake, std::string result, Platform platform,
                     std::initializer_list<std::string> args)
    : fake_(std::move(fake)),
      result_(std::move(result)),
      platform_(platform),
      args_(args) {}

std::string ReportRow::getFake() const { return fake_; }

std::string ReportRow::getResult() const { return result_; }

Platform ReportRow::getPlatform() const { return platform_; }

std::string ReportRow::getPlatformString() const {
  switch (platform_) {
    case kAny:
      return "any";
    case kAndroid:
      return "android";
    case kIos:
      return "ios";
    default:
      return "undefined";
  }
}

std::vector<std::string> ReportRow::getArgs() const { return args_; }

bool ReportRow::operator==(const ReportRow& other) const {
  if (fake_ != other.fake_ || result_ != other.result_ ||
      args_.size() != other.args_.size()) {
    return false;
  }
  for (int i = 0; i < args_.size(); i++) {
    if (args_[i] != other.args_[i]) return false;
  }
  return true;
}

bool ReportRow::operator!=(const ReportRow& other) const {
  return !((*this) == other);
}

bool ReportRow::operator<(const ReportRow& other) const {
  return fake_ < other.fake_;
}

std::string ReportRow::toString() const {
  std::ostringstream stream;

  stream << fake_ << " " << result_ << " " << getPlatformString() << " [";
  for (int i = 0; i < args_.size(); i++) {
    stream << args_[i];
    if (i < args_.size()-1)
      stream << " ";
  }
  stream << "]";

  return stream.str();
}

::std::ostream& operator<<(::std::ostream& os, const ReportRow& r) {
  return os << r.toString();
}

void Reporter::addExpectation(const ReportRow& expectation) {
  if (expectation.getPlatform() == kAny) {
    expectations_.push_back(expectation);
    return;
  }
#if defined(FIREBASE_ANDROID_FOR_DESKTOP) || defined(__ANDROID__)
  if (expectation.getPlatform() == kAndroid)
    expectations_.push_back(expectation);
#elif defined(__APPLE__)
  if (expectation.getPlatform() == kIos) expectations_.push_back(expectation);
#endif  // defined(FIREBASE_ANDROID_FOR_DESKTOP) || defined(__ANDROID__)
}

void Reporter::addExpectation(std::string fake, std::string result,
                              Platform platform,
                              std::initializer_list<std::string> args) {
  addExpectation(ReportRow(std::move(fake), std::move(result), platform, args));
}

std::vector<ReportRow> Reporter::getExpectations() {
  std::sort(expectations_.begin(), expectations_.end());
  return expectations_;
}

std::vector<ReportRow> Reporter::getFakeReports() {
  std::vector<std::string> fake_fucntions = getAllFakes();
  std::vector<ReportRow> fake_reports;

  for (const auto& fake_function : fake_fucntions) {
    ReportRow r = ReportRow(fake_function, getFakeResult(fake_function),
                            getFakeArgs(fake_function));
    fake_reports.push_back(r);
  }
  std::sort(fake_reports.begin(), fake_reports.end());
  return fake_reports;
}

}  // namespace cppsdk
}  // namespace testing
}  // namespace firebase
<commit_msg>Make the testing framework handle MacOS and iOS separately<commit_after>#include <algorithm>
#include <set>
#include <sstream>
#include <string>
#include <utility>
#include <vector>

#ifdef __APPLE__
#include "TargetConditionals.h"
#endif

#include "testing/reporter.h"

namespace firebase {
namespace testing {
namespace cppsdk {
ReportRow::ReportRow()
    : fake_(""),
      result_(""),
      platform_(kAny),
      args_(std::vector<std::string>()) {}

ReportRow::ReportRow(std::string fake, std::string result,
                     std::vector<std::string> args)
    : fake_(std::move(fake)),
      result_(std::move(result)),
      platform_(kAny),
      args_(std::move(args)) {}

ReportRow::ReportRow(std::string fake, std::string result, Platform platform,
                     std::initializer_list<std::string> args)
    : fake_(std::move(fake)),
      result_(std::move(result)),
      platform_(platform),
      args_(args) {}

std::string ReportRow::getFake() const { return fake_; }

std::string ReportRow::getResult() const { return result_; }

Platform ReportRow::getPlatform() const { return platform_; }

std::string ReportRow::getPlatformString() const {
  switch (platform_) {
    case kAny:
      return "any";
    case kAndroid:
      return "android";
    case kIos:
      return "ios";
    default:
      return "undefined";
  }
}

std::vector<std::string> ReportRow::getArgs() const { return args_; }

bool ReportRow::operator==(const ReportRow& other) const {
  if (fake_ != other.fake_ || result_ != other.result_ ||
      args_.size() != other.args_.size()) {
    return false;
  }
  for (int i = 0; i < args_.size(); i++) {
    if (args_[i] != other.args_[i]) return false;
  }
  return true;
}

bool ReportRow::operator!=(const ReportRow& other) const {
  return !((*this) == other);
}

bool ReportRow::operator<(const ReportRow& other) const {
  return fake_ < other.fake_;
}

std::string ReportRow::toString() const {
  std::ostringstream stream;

  stream << fake_ << " " << result_ << " " << getPlatformString() << " [";
  for (int i = 0; i < args_.size(); i++) {
    stream << args_[i];
    if (i < args_.size()-1)
      stream << " ";
  }
  stream << "]";

  return stream.str();
}

::std::ostream& operator<<(::std::ostream& os, const ReportRow& r) {
  return os << r.toString();
}

void Reporter::addExpectation(const ReportRow& expectation) {
  if (expectation.getPlatform() == kAny) {
    expectations_.push_back(expectation);
    return;
  }
#if defined(FIREBASE_ANDROID_FOR_DESKTOP) || defined(__ANDROID__)
  if (expectation.getPlatform() == kAndroid)
    expectations_.push_back(expectation);
#elif defined(__APPLE__) && TARGET_OS_IPHONE
  if (expectation.getPlatform() == kIos) expectations_.push_back(expectation);
#endif  // defined(FIREBASE_ANDROID_FOR_DESKTOP) || defined(__ANDROID__)
}

void Reporter::addExpectation(std::string fake, std::string result,
                              Platform platform,
                              std::initializer_list<std::string> args) {
  addExpectation(ReportRow(std::move(fake), std::move(result), platform, args));
}

std::vector<ReportRow> Reporter::getExpectations() {
  std::sort(expectations_.begin(), expectations_.end());
  return expectations_;
}

std::vector<ReportRow> Reporter::getFakeReports() {
  std::vector<std::string> fake_fucntions = getAllFakes();
  std::vector<ReportRow> fake_reports;

  for (const auto& fake_function : fake_fucntions) {
    ReportRow r = ReportRow(fake_function, getFakeResult(fake_function),
                            getFakeArgs(fake_function));
    fake_reports.push_back(r);
  }
  std::sort(fake_reports.begin(), fake_reports.end());
  return fake_reports;
}

}  // namespace cppsdk
}  // namespace testing
}  // namespace firebase
<|endoftext|>


<commit_before>#pragma once
//=====================================================================//
/*!	@file
	@brief	シリアルＩ／Ｏ（WIN32）@n
			※要ライブラリ： hid setupapi ksguid
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2018 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/glfw_app/blob/master/LICENSE
*/
//=====================================================================//
#include <windows.h>
#include <winioctl.h>
#include <setupapi.h>
#include <string>
#include <vector>

namespace device {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief	シリアル通信クラス（WIN32)
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	class serial_win32 {
	public:

		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		/*!
			@brief	シリアル・ポート名構造体
		*/
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		struct name_t {
			std::string		port;	///< ポート名
			std::string		info;	///< ポート情報
		};
		typedef std::vector<name_t> name_list;

		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		/*!
			@brief	パリティー識別子
		*/
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		enum class PARITY {
			NONE,	///< パリティー無し
			EVEN,	///< 偶数パリティー
			ODD,	///< 奇数パリティー
		};

		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		/*!
			@brief	フロー制御識別子
		*/
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		enum class FLOW {
			NONE,	///< フロー制御無し
			HARD,	///< RTS/CTS 信号によるハードウェアー制御
			SOFT,	///< Xon/Xoff によるソフトウェアー制御
		};

	private:
		HANDLE		fd_;

		std::string	error_;

		name_list	name_list_;

		bool scan_port_(const name_list& list, const std::string& port) const
		{
			for(const auto& t : list) {
				if(t.port == port) return true;
			}
			return false;
		}

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief	コンストラクター
		*/
		//-----------------------------------------------------------------//
		serial_win32() : fd_(INVALID_HANDLE_VALUE) { }

		//-----------------------------------------------------------------//
		/*!
			@brief	ポート・リストを作成
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool create_list()
		{
			name_list_.clear();

			// デバイス情報セットを取得
			HDEVINFO hi = SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT, 0, 0,
				DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
			if(hi == 0) {
				return false;
			}

			SP_DEVINFO_DATA data = { 0 };
			data.cbSize = sizeof(data);
			int idx = 0;
			while(SetupDiEnumDeviceInfo(hi, idx, &data)) {
				name_t t;
				// COM ポート名の取得
				HKEY key = SetupDiOpenDevRegKey(hi, &data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
				if(key) {
					BYTE name[256];
					DWORD type = 0;
					DWORD size = sizeof(name);
					RegQueryValueEx(key, _T("PortName"), NULL, &type, name, &size);
					t.port = reinterpret_cast<const char*>(name);
				}

				//	デバイスの情報を取得
				DWORD dt = 0;
				PBYTE ptr = nullptr;
				DWORD size = 0;
				SetupDiGetDeviceRegistryProperty(hi, &data, SPDRP_DEVICEDESC, &dt, ptr, size, &size);
				if(size > 0) {
					ptr = new BYTE[size];
					SetupDiGetDeviceRegistryProperty(hi, &data, SPDRP_DEVICEDESC, &dt, ptr, size, &size);
					t.info = reinterpret_cast<const char*>(ptr);
					delete[] ptr;
				}
				++idx;
				name_list_.push_back(t);
			}

			// デバイス情報セットを解放
			SetupDiDestroyDeviceInfoList(hi);

			return !name_list_.empty();
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	ポート・リストを取得
			@return ポート・リスト
		*/
		//-----------------------------------------------------------------//
		const name_list& get_list() const { return name_list_; }

		//-----------------------------------------------------------------//
		/*!
			@brief	ポート・リスト比較（新規デバイスでリスト更新確認など）
			@return 同じなら「true」
		*/
		//-----------------------------------------------------------------//
		bool compare(const name_list& list) const
		{
			if(name_list_.empty()) return false;

			for(const auto& t : list) {
				if(!scan_port_(name_list_, t.port)) return false;
			}
			return true;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	ポート情報の取得
			@param[in]	port	ポート名
			@return ポート情報
		*/
		//-----------------------------------------------------------------//
		const std::string& get_info(const std::string& port) const
		{
			static std::string tmp;
			if(name_list_.empty()) return tmp;

			for(const auto& t : name_list_) {
				if(t.port == port) {
					return t.info;
				}
			}
			return tmp;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	オープン
			@param[in]	port	ポート名
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool open(const std::string& port)
		{
			close();

			std::string full("\\\\.\\");
			full += port;

			fd_ = CreateFile(full.c_str(),
					GENERIC_READ | GENERIC_WRITE,
                    0,
                    NULL,
                    OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL,
                    0);

			bool ret = false;
			if(INVALID_HANDLE_VALUE == fd_) {
				int error_num = GetLastError();
				char error_string[1024];
				FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
					NULL,
					error_num,
					MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
					error_string,
					sizeof error_string,
					NULL);
///					std::cout << fd_ << ": " << error_string << std::endl;
					error_ = error_string;
			} else {
				DCB dcb;
				GetCommState(fd_, &dcb);
				dcb.BaudRate = CBR_115200;
				dcb.ByteSize = 8;
				dcb.StopBits = ONESTOPBIT;
				dcb.Parity   = NOPARITY;
				dcb.fParity  = FALSE;
				dcb.fBinary  = TRUE;
//				dcb.fRtsControl = FALSE;
				SetCommState(fd_, &dcb);

				COMMTIMEOUTS tos;  // 単位（ミリ秒）
				tos.ReadIntervalTimeout         = 250;
				tos.ReadTotalTimeoutConstant    = 250;
				tos.ReadTotalTimeoutMultiplier  = 200;
				tos.WriteTotalTimeoutConstant   = 0;
				tos.WriteTotalTimeoutMultiplier = 0;
				SetCommTimeouts(fd_, &tos);
				FlushFileBuffers(fd_);
				ret = true;
			}
			return ret;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	フロー制御の設定
			@param[in]	type	フロー制御識別子
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool set_flow(FLOW type)
		{
			bool ret = false;
			switch(type) {
			case FLOW::NONE:

				break;
			case FLOW::HARD:

				break;
			case FLOW::SOFT:

				break;
			default:
				break;
			}
			return ret;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	ボーレートの設定
			@param[in]	baud	ボーレート
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool set_baudrate(int baud)
		{
			DCB dcb;
			GetCommState(fd_, &dcb);
			dcb.BaudRate = baud;
			return SetCommState(fd_, &dcb) != 0;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	パリティーの設定
			@param[in]	parity	パリティー・タイプ
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool set_parity(PARITY parity)
		{
			DCB dcb;
			GetCommState(fd_, &dcb);
			switch(parity) {
			case PARITY::EVEN:
				dcb.Parity = EVENPARITY;
				break;
			case PARITY::ODD:
				dcb.Parity = ODDPARITY;
				break;
			case PARITY::NONE:
				dcb.Parity = NOPARITY;
				break;
			default:
				return false;
				break;
			}
			return SetCommState(fd_, &dcb) != 0;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	DTR の設定（Data Terminal Ready)
			@param[in]	value	設定値
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool set_dtr(bool value)
		{
			int command;
			if(value) {
				command = SETDTR;
			} else {
				command = CLRDTR;
			}
			return EscapeCommFunction(fd_, command) != 0;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	RTS の設定（Request To Send)
			@param[in]	value	設定値
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool set_rts(bool value)
		{
			int command;
			if(value) {
				command = SETRTS;
			} else {
				command = CLRRTS;
			}
			return EscapeCommFunction(fd_, command) != 0;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	TXD の設定
			@param[in]	value	設定値
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool set_txd(bool value)
		{
			int command;
			if(value) {
				command = SETBREAK;
			} else {
				command = CLRBREAK;

			}
			return EscapeCommFunction(fd_, command) != 0;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	フラッシュ
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool flush()
		{
//			if(4 <= verbose_level) {
//				printf("\t\tFlush IO buffers\n");
//			}
			return PurgeComm(fd_, PURGE_RXCLEAR | PURGE_TXCLEAR) != 0;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	ライト
			@param[in]	src	転送元
			@param[in]	len	長さ（バイト）
			@return 書き込んだサイズ
		*/
		//-----------------------------------------------------------------//
		uint32_t write(const void* src, uint32_t len)
		{
			if(src == nullptr || len == 0) return 0;

			int bytes_left = len;
			DWORD bytes_written;
			const uint8_t* p = reinterpret_cast<const uint8_t*>(src);
			do {
				if(0 == WriteFile(fd_, p, bytes_left, &bytes_written, NULL)) {
//					fprintf(stderr, "Failed to write to port.\n");
					return false;
				}
				p += bytes_written;
				bytes_left -= bytes_written;
			} while(0 < bytes_left) ;
			return len - bytes_left;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	リード @n
					※リード要求より少ない場合がある。
			@param[out]	dst	転送先
			@param[in]	len	リード要求バイト数
			@return リード・バイト数
		*/
		//-----------------------------------------------------------------//
		uint32_t read(void *dst, int len)
		{
			if(dst == nullptr || len == 0) return 0;

			DWORD errors;
			COMSTAT stat;
			ClearCommError(fd_, &errors, &stat);
			DWORD count = stat.cbInQue;
			DWORD rb;
			uint8_t* p = reinterpret_cast<uint8_t*>(dst);
			if(ReadFile(fd_, p, count, &rb, NULL) == 0) {
				return 0;
			}
			return static_cast<uint32_t>(rb);
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	クローズ
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool close()
		{
			bool ret = false;
			if(fd_ != INVALID_HANDLE_VALUE) {
				ret = CloseHandle(fd_) != 0;
				fd_ = INVALID_HANDLE_VALUE;
			}
			return ret;
		}
	};
}
<commit_msg>update: cleanup<commit_after>#pragma once
//=====================================================================//
/*!	@file
	@brief	シリアルＩ／Ｏ（WIN32）@n
			※要ライブラリ： hid setupapi ksguid
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2018 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/glfw_app/blob/master/LICENSE
*/
//=====================================================================//
#include <windows.h>
#include <winioctl.h>
#include <setupapi.h>
#include <string>
#include <vector>

namespace device {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief	シリアル通信クラス（WIN32)
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	class serial_win32 {
	public:

		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		/*!
			@brief	シリアル・ポート名構造体
		*/
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		struct name_t {
			std::string		port;	///< ポート名
			std::string		info;	///< ポート情報
		};
		typedef std::vector<name_t> name_list;

		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		/*!
			@brief	パリティー識別子
		*/
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		enum class PARITY {
			NONE,	///< パリティー無し
			EVEN,	///< 偶数パリティー
			ODD,	///< 奇数パリティー
		};

		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		/*!
			@brief	フロー制御識別子
		*/
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		enum class FLOW {
			NONE,	///< フロー制御無し
			HARD,	///< RTS/CTS 信号によるハードウェアー制御
			SOFT,	///< Xon/Xoff によるソフトウェアー制御
		};

	private:
		HANDLE		fd_;

		std::string	error_;

		name_list	name_list_;

		bool scan_port_(const name_list& list, const std::string& port) const
		{
			for(const auto& t : list) {
				if(t.port == port) return true;
			}
			return false;
		}

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief	コンストラクター
		*/
		//-----------------------------------------------------------------//
		serial_win32() : fd_(INVALID_HANDLE_VALUE) { }

		//-----------------------------------------------------------------//
		/*!
			@brief	ポート・リストを作成
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool create_list()
		{
			name_list_.clear();

			// デバイス情報セットを取得
			HDEVINFO hi = SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT, 0, 0,
				DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
			if(hi == 0) {
				return false;
			}

			SP_DEVINFO_DATA data = { 0 };
			data.cbSize = sizeof(data);
			int idx = 0;
			while(SetupDiEnumDeviceInfo(hi, idx, &data)) {
				name_t t;
				// COM ポート名の取得
				HKEY key = SetupDiOpenDevRegKey(hi, &data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
				if(key) {
					BYTE name[256];
					DWORD type = 0;
					DWORD size = sizeof(name);
					RegQueryValueExA(key, "PortName", NULL, &type, name, &size);
					t.port = reinterpret_cast<const char*>(name);
				}

				//	デバイスの情報を取得
				DWORD dt = 0;
				PBYTE ptr = nullptr;
				DWORD size = 0;
				SetupDiGetDeviceRegistryProperty(hi, &data, SPDRP_DEVICEDESC, &dt, ptr, size, &size);
				if(size > 0) {
					ptr = new BYTE[size];
					SetupDiGetDeviceRegistryProperty(hi, &data, SPDRP_DEVICEDESC, &dt, ptr, size, &size);
					t.info = reinterpret_cast<const char*>(ptr);
					delete[] ptr;
				}
				++idx;
				name_list_.push_back(t);
			}

			// デバイス情報セットを解放
			SetupDiDestroyDeviceInfoList(hi);

			return !name_list_.empty();
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	ポート・リストを取得
			@return ポート・リスト
		*/
		//-----------------------------------------------------------------//
		const name_list& get_list() const { return name_list_; }

		//-----------------------------------------------------------------//
		/*!
			@brief	ポート・リスト比較（新規デバイスでリスト更新確認など）
			@return 同じなら「true」
		*/
		//-----------------------------------------------------------------//
		bool compare(const name_list& list) const
		{
			if(name_list_.empty()) return false;

			for(const auto& t : list) {
				if(!scan_port_(name_list_, t.port)) return false;
			}
			return true;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	ポート情報の取得
			@param[in]	port	ポート名
			@return ポート情報
		*/
		//-----------------------------------------------------------------//
		const std::string& get_info(const std::string& port) const
		{
			static std::string tmp;
			if(name_list_.empty()) return tmp;

			for(const auto& t : name_list_) {
				if(t.port == port) {
					return t.info;
				}
			}
			return tmp;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	オープン
			@param[in]	port	ポート名
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool open(const std::string& port)
		{
			close();

			std::string full("\\\\.\\");
			full += port;

			fd_ = CreateFile(full.c_str(),
					GENERIC_READ | GENERIC_WRITE,
                    0,
                    NULL,
                    OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL,
                    0);

			bool ret = false;
			if(INVALID_HANDLE_VALUE == fd_) {
				int error_num = GetLastError();
				char error_string[1024];
				FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
					NULL,
					error_num,
					MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
					error_string,
					sizeof error_string,
					NULL);
///					std::cout << fd_ << ": " << error_string << std::endl;
					error_ = error_string;
			} else {
				DCB dcb;
				GetCommState(fd_, &dcb);
				dcb.BaudRate = CBR_115200;
				dcb.ByteSize = 8;
				dcb.StopBits = ONESTOPBIT;
				dcb.Parity   = NOPARITY;
				dcb.fParity  = FALSE;
				dcb.fBinary  = TRUE;
//				dcb.fRtsControl = FALSE;
				SetCommState(fd_, &dcb);

				COMMTIMEOUTS tos;  // 単位（ミリ秒）
				tos.ReadIntervalTimeout         = 250;
				tos.ReadTotalTimeoutConstant    = 250;
				tos.ReadTotalTimeoutMultiplier  = 200;
				tos.WriteTotalTimeoutConstant   = 0;
				tos.WriteTotalTimeoutMultiplier = 0;
				SetCommTimeouts(fd_, &tos);
				FlushFileBuffers(fd_);
				ret = true;
			}
			return ret;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	フロー制御の設定
			@param[in]	type	フロー制御識別子
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool set_flow(FLOW type)
		{
			bool ret = false;
			switch(type) {
			case FLOW::NONE:

				break;
			case FLOW::HARD:

				break;
			case FLOW::SOFT:

				break;
			default:
				break;
			}
			return ret;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	ボーレートの設定
			@param[in]	baud	ボーレート
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool set_baudrate(int baud)
		{
			DCB dcb;
			GetCommState(fd_, &dcb);
			dcb.BaudRate = baud;
			return SetCommState(fd_, &dcb) != 0;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	パリティーの設定
			@param[in]	parity	パリティー・タイプ
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool set_parity(PARITY parity)
		{
			DCB dcb;
			GetCommState(fd_, &dcb);
			switch(parity) {
			case PARITY::EVEN:
				dcb.Parity = EVENPARITY;
				break;
			case PARITY::ODD:
				dcb.Parity = ODDPARITY;
				break;
			case PARITY::NONE:
				dcb.Parity = NOPARITY;
				break;
			default:
				return false;
				break;
			}
			return SetCommState(fd_, &dcb) != 0;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	DTR の設定（Data Terminal Ready)
			@param[in]	value	設定値
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool set_dtr(bool value)
		{
			int command;
			if(value) {
				command = SETDTR;
			} else {
				command = CLRDTR;
			}
			return EscapeCommFunction(fd_, command) != 0;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	RTS の設定（Request To Send)
			@param[in]	value	設定値
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool set_rts(bool value)
		{
			int command;
			if(value) {
				command = SETRTS;
			} else {
				command = CLRRTS;
			}
			return EscapeCommFunction(fd_, command) != 0;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	TXD の設定
			@param[in]	value	設定値
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool set_txd(bool value)
		{
			int command;
			if(value) {
				command = SETBREAK;
			} else {
				command = CLRBREAK;

			}
			return EscapeCommFunction(fd_, command) != 0;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	フラッシュ
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool flush()
		{
//			if(4 <= verbose_level) {
//				printf("\t\tFlush IO buffers\n");
//			}
			return PurgeComm(fd_, PURGE_RXCLEAR | PURGE_TXCLEAR) != 0;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	ライト
			@param[in]	src	転送元
			@param[in]	len	長さ（バイト）
			@return 書き込んだサイズ
		*/
		//-----------------------------------------------------------------//
		uint32_t write(const void* src, uint32_t len)
		{
			if(src == nullptr || len == 0) return 0;

			int bytes_left = len;
			DWORD bytes_written;
			const uint8_t* p = reinterpret_cast<const uint8_t*>(src);
			do {
				if(0 == WriteFile(fd_, p, bytes_left, &bytes_written, NULL)) {
//					fprintf(stderr, "Failed to write to port.\n");
					return false;
				}
				p += bytes_written;
				bytes_left -= bytes_written;
			} while(0 < bytes_left) ;
			return len - bytes_left;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	リード @n
					※リード要求より少ない場合がある。
			@param[out]	dst	転送先
			@param[in]	len	リード要求バイト数
			@return リード・バイト数
		*/
		//-----------------------------------------------------------------//
		uint32_t read(void *dst, int len)
		{
			if(dst == nullptr || len == 0) return 0;

			DWORD errors;
			COMSTAT stat;
			ClearCommError(fd_, &errors, &stat);
			DWORD count = stat.cbInQue;
			DWORD rb;
			uint8_t* p = reinterpret_cast<uint8_t*>(dst);
			if(ReadFile(fd_, p, count, &rb, NULL) == 0) {
				return 0;
			}
			return static_cast<uint32_t>(rb);
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	クローズ
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool close()
		{
			bool ret = false;
			if(fd_ != INVALID_HANDLE_VALUE) {
				ret = CloseHandle(fd_) != 0;
				fd_ = INVALID_HANDLE_VALUE;
			}
			return ret;
		}
	};
}
<|endoftext|>


<commit_before>/* GNE - Game Networking Engine, a portable multithreaded networking library.
 * Copyright (C) 2001 Jason Winnebeck (gillius@mail.rit.edu)
 * Project website: http://www.rit.edu/~jpw9607/
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

//NOTES ON THREADS:
// Threads will manage their own memory, so they will be deleted when they
//  have stopped running and you don't have references to it anymore.
// You MUST call the "setThisPointer" method in your static creation
//  function.
// Threads MUST be managed by a SmartPtr.
// If you get an assert failure of "this_" it is because you didn't call
//  the "setThisPointer" method.

#include <gnelib.h>
#include <cassert>

#include <sys/timeb.h>

using namespace GNE;
using namespace GNE::Console;

class HelloWorld : public Thread {
public:
  //define these typedefs just for convienence
  typedef SmartPtr<HelloWorld> sptr;
  typedef WeakPtr<HelloWorld> wptr;

protected:
  //we can't allow straight construction of this object
  HelloWorld( std::string myName, Mutex& mutexToTest )
    : Thread(myName), testMutex(mutexToTest) {
    mprintf("%s is born\n", myName.c_str());
  }

public:
  /**
   * Creates a new HelloWorld thread.
   */
  static sptr create( std::string myName, Mutex& mutexToTest ) {
    sptr ret( new HelloWorld( myName, mutexToTest ) );
    ret->setThisPointer( ret );
    return ret;
  }

  virtual ~HelloWorld() {
    mprintf("%s dies.\n", getName().c_str());
  }

protected:
  void run() {
    //Note that if we were sitting in an infinite loop, we would have to
    //respond to the shutdown variable, as shown in comments.  The commented
    //code would be the proper code if we wanted to repeat an action forever.
    //You would call shutDown then join on this thread to stop it in this
    //case.
    //while (!shutdown) {
    //  do_stuff();
    //}

    //Test the mutex we were given.  In normal operation the acquire should
    //not deadlock.  If it does there was a problem with unlocking the mutex
    //in the main thread.
    //The sleeps are to give the other threads a chance to go.  We should
    //never see two threads given the same mutex intertwine their prints.
    testMutex.acquire();

    mprintf("Hello World!  My name is %s.\n", getName().c_str());
    Thread::sleep(50);  //sleep(50) will work too, Thread:: is not needed...

    mprintf(" %s Ref: %x %x\n", getName().c_str(), Thread::currentThread().get(), this);

    assert(Thread::currentThread().get() == this);
    assert(Thread::currentThread() == getThisPointer() );

    testMutex.release();
  }

private:
  //We try to acquire this mutex just to test it to see if it was really
  //unlocked in the main thread.
  Mutex& testMutex;
};

class WaitingThread : public Thread {
public:
  //define these typedefs just for convienence
  typedef SmartPtr<WaitingThread> sptr;
  typedef WeakPtr<WaitingThread> wptr;

  virtual ~WaitingThread() {}

  static sptr create() {
    sptr ret( new WaitingThread() );
    ret->setThisPointer( ret );
    return ret;
  }

protected:
  WaitingThread() {}

  void run() {
    sleep(1000);
  }
};

int main(int argc, char* argv[]) {
  initGNE(NO_NET, atexit);
  initConsole(atexit);
  setTitle("GNE Threads Example");

  mprintf("Testing to see if recursive locks work:");
  Mutex test;
  test.acquire();
  test.acquire();
  test.release();
  test.release();
  mprintf(" success.\n");

  Mutex test2;
  mprintf("Testing recursive locks with LockMutex:");
  try {
    //The LockMutex class locks the mutex when it is created.  When it is
    //destroyed the mutex is unlocked, and this is useful if a function has
    //mutliple return statements or throws exceptions.  You would never want
    //to use them as they are here (nested).  Only one LockMutex is ever
    //needed.
    LockMutex lock(test2);
    LockMutex lock2(test2);

    //When we throw an exception, variables in this block like lock and lock2
    //will be destroyed, and when a LockMutex is destroyed it unlocked the
    //mutex that it locked when it was created.
    throw 0;
  } catch (...) {
  }
  mprintf(" success.\n");

  //We make the HelloWorld threads, giving them some mutexes to test.  If two
  //threads are using the same mutex, they can't go at the same time.  So all
  //of Sally's statements should come before or after all of Joe's.  Bob uses
  //a different mutex so his statements will be mixed in with Sally's and
  //Joe's.
  HelloWorld::sptr bob = HelloWorld::create("Bob", test);
  assert( !bob->hasStarted() );
  assert( !bob->isRunning() );
  bob->start();      //Tells bob to start his job.
  bob.reset();
  //There is never a need to reset explicitly, but we do this to show that
  //you don't need to keep any pointers to Threads after you start them.
  //In the old GNE API, this was equivalent to detach(true)

  HelloWorld::sptr sally = HelloWorld::create("Sally", test2);
  sally->start();

  HelloWorld::sptr joe = HelloWorld::create("Joe", test2);
  joe->start();

  assert(sally->hasStarted());
  assert(sally->isRunning()); //Sally should be running.  Note this is true
                              //because sally->start() was called, and not
                              //because the thread has ACTUALLY started
                              //executing by the OS having given it any CPU
                              //time.
  mprintf("Waiting for Sally to end.\n");
  sally->join(); //we must either join or detach every thread.
  mprintf("Sally ended.  Killing Sally.\n");
  assert(!sally->isRunning());
  //In the old API, you used to have to manually delete Sally, but no need to
  //anymore.  We test deleting her explicitly through a reset call, but it is
  //not needed.
  sally.reset();

  mprintf("Sally died.  Now waiting for Joe to end.\n");

  //A busy wait is a very bad idea.  You should always use join, but isRunning
  //is possibly if you want to check for when joe dies while doing other stuff.
  while (joe->isRunning()) {}
  mprintf("Joe has ended.\n");

  //Sleep, measuring our sleep time
  Time lastTime = Timer::getCurrentTime();

  mprintf("Sleeping for 2459 milliseconds.\n");
  Thread::sleep(2459);

  Time diffTime = Timer::getCurrentTime();
  diffTime = diffTime - lastTime;

  mprintf("GNE timers report sleeping time of %i microseconds (us)\n", diffTime.getTotaluSec());

  mprintf("Now testing Thread::waitForAllThreads.\n");
  WaitingThread::create()->start();
  //this should timeout as the waiter thread waits for 1000 ms.
  bool timeout = Thread::waitForAllThreads(150);
  if (timeout)
    mprintf("Thread::waitForAllThreads timed out properly.\n");
  else
    mprintf("Thread::waitForAllThreads method failed.\n");

  //The waiter thread should definitely finish during this call.
  timeout = Thread::waitForAllThreads(INT_MAX);
  if (timeout)
    mprintf("Thread::waitForAllThreads method failed.\n");
  else
    mprintf("Thread::waitForAllThreads 2nd call completed successfully.\n");

  mprintf( "You should see joe die after you press a key (when main ends), if you have\n" );
  mprintf( " the window stay open after the program exits.\n" );
  mprintf("Press a key to continue.");
  getch();

  //At this point, all threads that are running will be terminated.  When
  //main exits, everything goes.  Use join if you want to guarantee your
  //threads have exited, or use waitForAllThreads to definitively verify that
  //detached threads have ended.
  //The threads will be FORCEFULLY terminated so this would be an abnormal
  //program close if any threads are still running, and their destructors
  //won't get called!
  return 0;
}
<commit_msg>console doesn't clear on exit now.<commit_after>/* GNE - Game Networking Engine, a portable multithreaded networking library.
 * Copyright (C) 2001 Jason Winnebeck (gillius@mail.rit.edu)
 * Project website: http://www.rit.edu/~jpw9607/
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

//NOTES ON THREADS:
// Threads will manage their own memory, so they will be deleted when they
//  have stopped running and you don't have references to it anymore.
// You MUST call the "setThisPointer" method in your static creation
//  function.
// Threads MUST be managed by a SmartPtr.
// If you get an assert failure of "this_" it is because you didn't call
//  the "setThisPointer" method.

#include <gnelib.h>
#include <cassert>

#include <sys/timeb.h>

using namespace GNE;
using namespace GNE::Console;

class HelloWorld : public Thread {
public:
  //define these typedefs just for convienence
  typedef SmartPtr<HelloWorld> sptr;
  typedef WeakPtr<HelloWorld> wptr;

protected:
  //we can't allow straight construction of this object
  HelloWorld( std::string myName, Mutex& mutexToTest )
    : Thread(myName), testMutex(mutexToTest) {
    mprintf("%s is born\n", myName.c_str());
  }

public:
  /**
   * Creates a new HelloWorld thread.
   */
  static sptr create( std::string myName, Mutex& mutexToTest ) {
    sptr ret( new HelloWorld( myName, mutexToTest ) );
    ret->setThisPointer( ret );
    return ret;
  }

  virtual ~HelloWorld() {
    mprintf("%s dies.\n", getName().c_str());
  }

protected:
  void run() {
    //Note that if we were sitting in an infinite loop, we would have to
    //respond to the shutdown variable, as shown in comments.  The commented
    //code would be the proper code if we wanted to repeat an action forever.
    //You would call shutDown then join on this thread to stop it in this
    //case.
    //while (!shutdown) {
    //  do_stuff();
    //}

    //Test the mutex we were given.  In normal operation the acquire should
    //not deadlock.  If it does there was a problem with unlocking the mutex
    //in the main thread.
    //The sleeps are to give the other threads a chance to go.  We should
    //never see two threads given the same mutex intertwine their prints.
    testMutex.acquire();

    mprintf("Hello World!  My name is %s.\n", getName().c_str());
    Thread::sleep(50);  //sleep(50) will work too, Thread:: is not needed...

    mprintf(" %s Ref: %x %x\n", getName().c_str(), Thread::currentThread().get(), this);

    assert(Thread::currentThread().get() == this);
    assert(Thread::currentThread() == getThisPointer() );

    testMutex.release();
  }

private:
  //We try to acquire this mutex just to test it to see if it was really
  //unlocked in the main thread.
  Mutex& testMutex;
};

class WaitingThread : public Thread {
public:
  //define these typedefs just for convienence
  typedef SmartPtr<WaitingThread> sptr;
  typedef WeakPtr<WaitingThread> wptr;

  virtual ~WaitingThread() {}

  static sptr create() {
    sptr ret( new WaitingThread() );
    ret->setThisPointer( ret );
    return ret;
  }

protected:
  WaitingThread() {}

  void run() {
    sleep(1000);
  }
};

int main(int argc, char* argv[]) {
  initGNE(NO_NET, atexit);
  initConsole(false);
  setTitle("GNE Threads Example");

  mprintf("Testing to see if recursive locks work:");
  Mutex test;
  test.acquire();
  test.acquire();
  test.release();
  test.release();
  mprintf(" success.\n");

  Mutex test2;
  mprintf("Testing recursive locks with LockMutex:");
  try {
    //The LockMutex class locks the mutex when it is created.  When it is
    //destroyed the mutex is unlocked, and this is useful if a function has
    //mutliple return statements or throws exceptions.  You would never want
    //to use them as they are here (nested).  Only one LockMutex is ever
    //needed.
    LockMutex lock(test2);
    LockMutex lock2(test2);

    //When we throw an exception, variables in this block like lock and lock2
    //will be destroyed, and when a LockMutex is destroyed it unlocked the
    //mutex that it locked when it was created.
    throw 0;
  } catch (...) {
  }
  mprintf(" success.\n");

  //We make the HelloWorld threads, giving them some mutexes to test.  If two
  //threads are using the same mutex, they can't go at the same time.  So all
  //of Sally's statements should come before or after all of Joe's.  Bob uses
  //a different mutex so his statements will be mixed in with Sally's and
  //Joe's.
  HelloWorld::sptr bob = HelloWorld::create("Bob", test);
  assert( !bob->hasStarted() );
  assert( !bob->isRunning() );
  bob->start();      //Tells bob to start his job.
  bob.reset();
  //There is never a need to reset explicitly, but we do this to show that
  //you don't need to keep any pointers to Threads after you start them.
  //In the old GNE API, this was equivalent to detach(true)

  HelloWorld::sptr sally = HelloWorld::create("Sally", test2);
  sally->start();

  HelloWorld::sptr joe = HelloWorld::create("Joe", test2);
  joe->start();

  assert(sally->hasStarted());
  assert(sally->isRunning()); //Sally should be running.  Note this is true
                              //because sally->start() was called, and not
                              //because the thread has ACTUALLY started
                              //executing by the OS having given it any CPU
                              //time.
  mprintf("Waiting for Sally to end.\n");
  sally->join(); //we must either join or detach every thread.
  mprintf("Sally ended.  Killing Sally.\n");
  assert(!sally->isRunning());
  //In the old API, you used to have to manually delete Sally, but no need to
  //anymore.  We test deleting her explicitly through a reset call, but it is
  //not needed.
  sally.reset();

  mprintf("Sally died.  Now waiting for Joe to end.\n");

  //A busy wait is a very bad idea.  You should always use join, but isRunning
  //is possibly if you want to check for when joe dies while doing other stuff.
  while (joe->isRunning()) {}
  mprintf("Joe has ended.\n");

  //Sleep, measuring our sleep time
  Time lastTime = Timer::getCurrentTime();

  mprintf("Sleeping for 2459 milliseconds.\n");
  Thread::sleep(2459);

  Time diffTime = Timer::getCurrentTime();
  diffTime = diffTime - lastTime;

  mprintf("GNE timers report sleeping time of %i microseconds (us)\n", diffTime.getTotaluSec());

  mprintf("Now testing Thread::waitForAllThreads.\n");
  WaitingThread::create()->start();
  //this should timeout as the waiter thread waits for 1000 ms.
  bool timeout = Thread::waitForAllThreads(150);
  if (timeout)
    mprintf("Thread::waitForAllThreads timed out properly.\n");
  else
    mprintf("Thread::waitForAllThreads method failed.\n");

  //The waiter thread should definitely finish during this call.
  timeout = Thread::waitForAllThreads(INT_MAX);
  if (timeout)
    mprintf("Thread::waitForAllThreads method failed.\n");
  else
    mprintf("Thread::waitForAllThreads 2nd call completed successfully.\n");

  mprintf( "You should see joe die after you press a key (when main ends), if you have\n" );
  mprintf( " the window stay open after the program exits.\n" );
  mprintf( "Press a key to continue.\n" );
  getch();

  //At this point, all threads that are running will be terminated.  When
  //main exits, everything goes.  Use join if you want to guarantee your
  //threads have exited, or use waitForAllThreads to definitively verify that
  //detached threads have ended.
  //The threads will be FORCEFULLY terminated so this would be an abnormal
  //program close if any threads are still running, and their destructors
  //won't get called!
  return 0;
}
<|endoftext|>


<commit_before>/*
 * HTTPConnection.cpp
 *****************************************************************************
 * Copyright (C) 2014-2015 - VideoLAN and VLC Authors
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "HTTPConnection.hpp"
#include "ConnectionParams.hpp"
#include "Sockets.hpp"
#include "../adaptive/tools/Helper.h"

#include <sstream>
#include <vlc_stream.h>

using namespace adaptive::http;

AbstractConnection::AbstractConnection(vlc_object_t *p_object_)
{
    p_object = p_object_;
    available = true;
    bytesRead = 0;
    contentLength = 0;
}

AbstractConnection::~AbstractConnection()
{

}

bool AbstractConnection::prepare(const ConnectionParams &params_)
{
    if (!available)
        return false;
    params = params_;
    available = false;
    return true;
}

size_t AbstractConnection::getContentLength() const
{
    return contentLength;
}

HTTPConnection::HTTPConnection(vlc_object_t *p_object_, Socket *socket_, bool persistent)
    : AbstractConnection( p_object_ )
{
    socket = socket_;
    psz_useragent = var_InheritString(p_object_, "http-user-agent");
    queryOk = false;
    retries = 0;
    connectionClose = !persistent;
    chunked = false;
    chunked_eof = false;
    chunkLength = 0;
}

HTTPConnection::~HTTPConnection()
{
    free(psz_useragent);
    delete socket;
}

bool HTTPConnection::canReuse(const ConnectionParams &params_) const
{
    return ( available &&
             params.getHostname() == params_.getHostname() &&
             params.getScheme() == params_.getScheme() &&
             params.getPort() == params_.getPort() );
}

bool HTTPConnection::connect()
{
    return socket->connect(p_object, params.getHostname().c_str(),
                                     params.getPort());
}

bool HTTPConnection::connected() const
{
    return socket->connected();
}

void HTTPConnection::disconnect()
{
    queryOk = false;
    bytesRead = 0;
    contentLength = 0;
    chunked = false;
    chunkLength = 0;
    bytesRange = BytesRange();
    socket->disconnect();
}

int HTTPConnection::request(const std::string &path, const BytesRange &range)
{
    queryOk = false;
    chunked = false;
    chunked_eof = false;
    chunkLength = 0;

    /* Set new path for this query */
    params.setPath(path);

    msg_Dbg(p_object, "Retrieving %s @%zu", params.getUrl().c_str(),
                       range.isValid() ? range.getStartByte() : 0);

    if(!connected() && ( params.getHostname().empty() || !connect() ))
        return VLC_EGENERIC;

    bytesRange = range;
    if(range.isValid() && range.getEndByte() > 0)
        contentLength = range.getEndByte() - range.getStartByte() + 1;

    std::string header = buildRequestHeader(path);
    if(connectionClose)
        header.append("Connection: close\r\n");
    header.append("\r\n");

    if(!send( header ))
    {
        socket->disconnect();
        if(!connectionClose)
        {
            /* server closed connection pipeline after last req. need new */
            connectionClose = true;
            return request(path, range);
        }
        return VLC_EGENERIC;
    }

    int i_ret = parseReply();
    if(i_ret == VLC_SUCCESS)
    {
        queryOk = true;
    }
    else if(i_ret == VLC_EGENERIC)
    {
        socket->disconnect();
        if(!connectionClose)
        {
            connectionClose = true;
            return request(path, range);
        }
    }

    return i_ret;
}

ssize_t HTTPConnection::read(void *p_buffer, size_t len)
{
    if( !connected() ||
       (!queryOk && bytesRead == 0) )
        return VLC_EGENERIC;

    if(len == 0)
        return VLC_SUCCESS;

    queryOk = false;

    const size_t toRead = (contentLength) ? contentLength - bytesRead : len;
    if (toRead == 0)
        return VLC_SUCCESS;

    if(len > toRead)
        len = toRead;

    ssize_t ret = ( chunked ) ? readChunk(p_buffer, len)
                              : socket->read(p_object, p_buffer, len);
    if(ret >= 0)
        bytesRead += ret;

    if(ret < 0 || (size_t)ret < len || /* set EOF */
       contentLength == bytesRead )
    {
        socket->disconnect();
        return ret;
    }

    return ret;
}

bool HTTPConnection::send(const std::string &data)
{
    return send(data.c_str(), data.length());
}

bool HTTPConnection::send(const void *buf, size_t size)
{
    return socket->send(p_object, buf, size);
}

int HTTPConnection::parseReply()
{
    std::string line = readLine();

    if(line.empty())
        return VLC_EGENERIC;

    if (line.compare(0, 9, "HTTP/1.1 ")!=0)
    {
        if(line.compare(0, 9, "HTTP/1.0 ")!=0)
            return VLC_ENOOBJ;
        else
            connectionClose = true;
    }

    std::istringstream ss(line.substr(9));
    ss.imbue(std::locale("C"));
    int replycode;
    ss >> replycode;
    if (replycode != 200 && replycode != 206)
        return VLC_ENOOBJ;

    line = readLine();

    while(!line.empty() && line.compare("\r\n"))
    {
        size_t split = line.find_first_of(':');
        size_t value = split + 1;

        while(line.at(value) == ' ')
            value++;

        onHeader(line.substr(0, split), line.substr(value));
        line = readLine();
    }

    return VLC_SUCCESS;
}

ssize_t HTTPConnection::readChunk(void *p_buffer, size_t len)
{
    size_t copied = 0;

    for( ; copied < len && !chunked_eof; )
    {
        /* adapted from access/http/chunked.c */
        if(chunkLength == 0)
        {
            std::string line = readLine();
            int end;
            if (std::sscanf(line.c_str(), "%zx%n", &chunkLength, &end) < 1
                    || (line[end] != '\0' && line[end] != ';' /* ignore extension(s) */))
                return -1;
        }

        if(chunkLength > 0)
        {
            size_t toread = len - copied;
            if(toread > chunkLength)
                toread = chunkLength;

            ssize_t in = socket->read(p_object, &((uint8_t*)p_buffer)[copied], toread);
            if(in < 0)
            {
                return (copied == 0) ? in : copied;
            }
            else if((size_t)in < toread)
            {
               return copied + in;
            }
            copied += in;
            chunkLength -= in;
        }
        else chunked_eof = true;

        if(chunkLength == 0)
        {
            char crlf[2];
            ssize_t in = socket->read(p_object, &crlf, 2);
            if(in < 2 || memcmp(crlf, "\r\n", 2))
                return (copied == 0) ? -1 : copied;
        }
    }

    return copied;
}

std::string HTTPConnection::readLine()
{
    return socket->readline(p_object);
}

void HTTPConnection::setUsed( bool b )
{
    available = !b;
    if(available)
    {
        if(!connectionClose && contentLength == bytesRead )
        {
            queryOk = false;
            bytesRead = 0;
            contentLength = 0;
            bytesRange = BytesRange();
        }
        else  /* We can't resend request if we haven't finished reading */
            disconnect();
    }
}

void HTTPConnection::onHeader(const std::string &key,
                              const std::string &value)
{
    if(key == "Content-Length")
    {
        std::istringstream ss(value);
        ss.imbue(std::locale("C"));
        size_t length;
        ss >> length;
        contentLength = length;
    }
    else if (key == "Connection" && value =="close")
    {
        connectionClose = true;
    }
    else if (key == "Transfer-Encoding" && value == "chunked")
    {
        chunked = true;
    }
}

std::string HTTPConnection::buildRequestHeader(const std::string &path) const
{
    std::stringstream req;
    req << "GET " << path << " HTTP/1.1\r\n" <<
           "Host: " << params.getHostname() << "\r\n" <<
           "Cache-Control: no-cache" << "\r\n" <<
           "User-Agent: " << std::string(psz_useragent) << "\r\n";
    req << extraRequestHeaders();
    return req.str();
}

std::string HTTPConnection::extraRequestHeaders() const
{
    std::stringstream ss;
    ss.imbue(std::locale("C"));
    if(bytesRange.isValid())
    {
        ss << "Range: bytes=" << bytesRange.getStartByte() << "-";
        if(bytesRange.getEndByte())
            ss << bytesRange.getEndByte();
        ss << "\r\n";
    }
    return ss.str();
}

StreamUrlConnection::StreamUrlConnection(vlc_object_t *p_object)
    : AbstractConnection(p_object)
{
    p_streamurl = NULL;
    bytesRead = 0;
    contentLength = 0;
}

StreamUrlConnection::~StreamUrlConnection()
{
    reset();
}

void StreamUrlConnection::reset()
{
    if(p_streamurl)
        vlc_stream_Delete(p_streamurl);
    p_streamurl = NULL;
    bytesRead = 0;
    contentLength = 0;
    bytesRange = BytesRange();
}

bool StreamUrlConnection::canReuse(const ConnectionParams &) const
{
    return available;
}

int StreamUrlConnection::request(const std::string &path, const BytesRange &range)
{
    reset();

    /* Set new path for this query */
    params.setPath(path);

    msg_Dbg(p_object, "Retrieving %s @%zu", params.getUrl().c_str(),
                      range.isValid() ? range.getStartByte() : 0);

    p_streamurl = vlc_stream_NewMRL(p_object, params.getUrl().c_str());
    if(!p_streamurl)
        return VLC_EGENERIC;

    if(range.isValid() && range.getEndByte() > 0)
    {
        if(vlc_stream_Seek(p_streamurl, range.getStartByte()) != VLC_SUCCESS)
        {
            vlc_stream_Delete(p_streamurl);
            return VLC_EGENERIC;
        }
        bytesRange = range;
        contentLength = range.getEndByte() - range.getStartByte() + 1;
    }

    int64_t i_size = stream_Size(p_streamurl);
    if(i_size > -1)
    {
        if(!range.isValid() || contentLength > (size_t) i_size)
            contentLength = (size_t) i_size;
    }
    return VLC_SUCCESS;
}

ssize_t StreamUrlConnection::read(void *p_buffer, size_t len)
{
    if( !p_streamurl )
        return VLC_EGENERIC;

    if(len == 0)
        return VLC_SUCCESS;

    const size_t toRead = (contentLength) ? contentLength - bytesRead : len;
    if (toRead == 0)
        return VLC_SUCCESS;

    if(len > toRead)
        len = toRead;

    ssize_t ret = vlc_stream_Read(p_streamurl, p_buffer, len);
    if(ret >= 0)
        bytesRead += ret;

    if(ret < 0 || (size_t)ret < len || /* set EOF */
       contentLength == bytesRead )
    {
        reset();
        return ret;
    }

    return ret;
}

void StreamUrlConnection::setUsed( bool b )
{
    available = !b;
    if(available && contentLength == bytesRead)
       reset();
}

ConnectionFactory::ConnectionFactory()
{
}

ConnectionFactory::~ConnectionFactory()
{
}

AbstractConnection * ConnectionFactory::createConnection(vlc_object_t *p_object,
                                                         const ConnectionParams &params)
{
    if((params.getScheme() != "http" && params.getScheme() != "https") || params.getHostname().empty())
        return NULL;

    const int sockettype = (params.getScheme() == "https") ? TLSSocket::TLS : Socket::REGULAR;
    Socket *socket = (sockettype == TLSSocket::TLS) ? new (std::nothrow) TLSSocket()
                                                    : new (std::nothrow) Socket();
    if(!socket)
        return NULL;

    /* disable pipelined tls until we have ticket/resume session support */
    HTTPConnection *conn = new (std::nothrow)
            HTTPConnection(p_object, socket, sockettype != TLSSocket::TLS);
    if(!conn)
    {
        delete socket;
        return NULL;
    }

    return conn;
}

AbstractConnection * StreamUrlConnectionFactory::createConnection(vlc_object_t *p_object,
                                                                  const ConnectionParams &)
{
    return new (std::nothrow) StreamUrlConnection(p_object);
}
<commit_msg>demux: adaptive: fixup for commit b99bc394843b4967<commit_after>/*
 * HTTPConnection.cpp
 *****************************************************************************
 * Copyright (C) 2014-2015 - VideoLAN and VLC Authors
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "HTTPConnection.hpp"
#include "ConnectionParams.hpp"
#include "Sockets.hpp"
#include "../adaptive/tools/Helper.h"

#include <cstdio>
#include <sstream>
#include <vlc_stream.h>

using namespace adaptive::http;

AbstractConnection::AbstractConnection(vlc_object_t *p_object_)
{
    p_object = p_object_;
    available = true;
    bytesRead = 0;
    contentLength = 0;
}

AbstractConnection::~AbstractConnection()
{

}

bool AbstractConnection::prepare(const ConnectionParams &params_)
{
    if (!available)
        return false;
    params = params_;
    available = false;
    return true;
}

size_t AbstractConnection::getContentLength() const
{
    return contentLength;
}

HTTPConnection::HTTPConnection(vlc_object_t *p_object_, Socket *socket_, bool persistent)
    : AbstractConnection( p_object_ )
{
    socket = socket_;
    psz_useragent = var_InheritString(p_object_, "http-user-agent");
    queryOk = false;
    retries = 0;
    connectionClose = !persistent;
    chunked = false;
    chunked_eof = false;
    chunkLength = 0;
}

HTTPConnection::~HTTPConnection()
{
    free(psz_useragent);
    delete socket;
}

bool HTTPConnection::canReuse(const ConnectionParams &params_) const
{
    return ( available &&
             params.getHostname() == params_.getHostname() &&
             params.getScheme() == params_.getScheme() &&
             params.getPort() == params_.getPort() );
}

bool HTTPConnection::connect()
{
    return socket->connect(p_object, params.getHostname().c_str(),
                                     params.getPort());
}

bool HTTPConnection::connected() const
{
    return socket->connected();
}

void HTTPConnection::disconnect()
{
    queryOk = false;
    bytesRead = 0;
    contentLength = 0;
    chunked = false;
    chunkLength = 0;
    bytesRange = BytesRange();
    socket->disconnect();
}

int HTTPConnection::request(const std::string &path, const BytesRange &range)
{
    queryOk = false;
    chunked = false;
    chunked_eof = false;
    chunkLength = 0;

    /* Set new path for this query */
    params.setPath(path);

    msg_Dbg(p_object, "Retrieving %s @%zu", params.getUrl().c_str(),
                       range.isValid() ? range.getStartByte() : 0);

    if(!connected() && ( params.getHostname().empty() || !connect() ))
        return VLC_EGENERIC;

    bytesRange = range;
    if(range.isValid() && range.getEndByte() > 0)
        contentLength = range.getEndByte() - range.getStartByte() + 1;

    std::string header = buildRequestHeader(path);
    if(connectionClose)
        header.append("Connection: close\r\n");
    header.append("\r\n");

    if(!send( header ))
    {
        socket->disconnect();
        if(!connectionClose)
        {
            /* server closed connection pipeline after last req. need new */
            connectionClose = true;
            return request(path, range);
        }
        return VLC_EGENERIC;
    }

    int i_ret = parseReply();
    if(i_ret == VLC_SUCCESS)
    {
        queryOk = true;
    }
    else if(i_ret == VLC_EGENERIC)
    {
        socket->disconnect();
        if(!connectionClose)
        {
            connectionClose = true;
            return request(path, range);
        }
    }

    return i_ret;
}

ssize_t HTTPConnection::read(void *p_buffer, size_t len)
{
    if( !connected() ||
       (!queryOk && bytesRead == 0) )
        return VLC_EGENERIC;

    if(len == 0)
        return VLC_SUCCESS;

    queryOk = false;

    const size_t toRead = (contentLength) ? contentLength - bytesRead : len;
    if (toRead == 0)
        return VLC_SUCCESS;

    if(len > toRead)
        len = toRead;

    ssize_t ret = ( chunked ) ? readChunk(p_buffer, len)
                              : socket->read(p_object, p_buffer, len);
    if(ret >= 0)
        bytesRead += ret;

    if(ret < 0 || (size_t)ret < len || /* set EOF */
       contentLength == bytesRead )
    {
        socket->disconnect();
        return ret;
    }

    return ret;
}

bool HTTPConnection::send(const std::string &data)
{
    return send(data.c_str(), data.length());
}

bool HTTPConnection::send(const void *buf, size_t size)
{
    return socket->send(p_object, buf, size);
}

int HTTPConnection::parseReply()
{
    std::string line = readLine();

    if(line.empty())
        return VLC_EGENERIC;

    if (line.compare(0, 9, "HTTP/1.1 ")!=0)
    {
        if(line.compare(0, 9, "HTTP/1.0 ")!=0)
            return VLC_ENOOBJ;
        else
            connectionClose = true;
    }

    std::istringstream ss(line.substr(9));
    ss.imbue(std::locale("C"));
    int replycode;
    ss >> replycode;
    if (replycode != 200 && replycode != 206)
        return VLC_ENOOBJ;

    line = readLine();

    while(!line.empty() && line.compare("\r\n"))
    {
        size_t split = line.find_first_of(':');
        size_t value = split + 1;

        while(line.at(value) == ' ')
            value++;

        onHeader(line.substr(0, split), line.substr(value));
        line = readLine();
    }

    return VLC_SUCCESS;
}

ssize_t HTTPConnection::readChunk(void *p_buffer, size_t len)
{
    size_t copied = 0;

    for( ; copied < len && !chunked_eof; )
    {
        /* adapted from access/http/chunked.c */
        if(chunkLength == 0)
        {
            std::string line = readLine();
            int end;
            if (std::sscanf(line.c_str(), "%zx%n", &chunkLength, &end) < 1
                    || (line[end] != '\0' && line[end] != ';' /* ignore extension(s) */))
                return -1;
        }

        if(chunkLength > 0)
        {
            size_t toread = len - copied;
            if(toread > chunkLength)
                toread = chunkLength;

            ssize_t in = socket->read(p_object, &((uint8_t*)p_buffer)[copied], toread);
            if(in < 0)
            {
                return (copied == 0) ? in : copied;
            }
            else if((size_t)in < toread)
            {
               return copied + in;
            }
            copied += in;
            chunkLength -= in;
        }
        else chunked_eof = true;

        if(chunkLength == 0)
        {
            char crlf[2];
            ssize_t in = socket->read(p_object, &crlf, 2);
            if(in < 2 || memcmp(crlf, "\r\n", 2))
                return (copied == 0) ? -1 : copied;
        }
    }

    return copied;
}

std::string HTTPConnection::readLine()
{
    return socket->readline(p_object);
}

void HTTPConnection::setUsed( bool b )
{
    available = !b;
    if(available)
    {
        if(!connectionClose && contentLength == bytesRead )
        {
            queryOk = false;
            bytesRead = 0;
            contentLength = 0;
            bytesRange = BytesRange();
        }
        else  /* We can't resend request if we haven't finished reading */
            disconnect();
    }
}

void HTTPConnection::onHeader(const std::string &key,
                              const std::string &value)
{
    if(key == "Content-Length")
    {
        std::istringstream ss(value);
        ss.imbue(std::locale("C"));
        size_t length;
        ss >> length;
        contentLength = length;
    }
    else if (key == "Connection" && value =="close")
    {
        connectionClose = true;
    }
    else if (key == "Transfer-Encoding" && value == "chunked")
    {
        chunked = true;
    }
}

std::string HTTPConnection::buildRequestHeader(const std::string &path) const
{
    std::stringstream req;
    req << "GET " << path << " HTTP/1.1\r\n" <<
           "Host: " << params.getHostname() << "\r\n" <<
           "Cache-Control: no-cache" << "\r\n" <<
           "User-Agent: " << std::string(psz_useragent) << "\r\n";
    req << extraRequestHeaders();
    return req.str();
}

std::string HTTPConnection::extraRequestHeaders() const
{
    std::stringstream ss;
    ss.imbue(std::locale("C"));
    if(bytesRange.isValid())
    {
        ss << "Range: bytes=" << bytesRange.getStartByte() << "-";
        if(bytesRange.getEndByte())
            ss << bytesRange.getEndByte();
        ss << "\r\n";
    }
    return ss.str();
}

StreamUrlConnection::StreamUrlConnection(vlc_object_t *p_object)
    : AbstractConnection(p_object)
{
    p_streamurl = NULL;
    bytesRead = 0;
    contentLength = 0;
}

StreamUrlConnection::~StreamUrlConnection()
{
    reset();
}

void StreamUrlConnection::reset()
{
    if(p_streamurl)
        vlc_stream_Delete(p_streamurl);
    p_streamurl = NULL;
    bytesRead = 0;
    contentLength = 0;
    bytesRange = BytesRange();
}

bool StreamUrlConnection::canReuse(const ConnectionParams &) const
{
    return available;
}

int StreamUrlConnection::request(const std::string &path, const BytesRange &range)
{
    reset();

    /* Set new path for this query */
    params.setPath(path);

    msg_Dbg(p_object, "Retrieving %s @%zu", params.getUrl().c_str(),
                      range.isValid() ? range.getStartByte() : 0);

    p_streamurl = vlc_stream_NewMRL(p_object, params.getUrl().c_str());
    if(!p_streamurl)
        return VLC_EGENERIC;

    if(range.isValid() && range.getEndByte() > 0)
    {
        if(vlc_stream_Seek(p_streamurl, range.getStartByte()) != VLC_SUCCESS)
        {
            vlc_stream_Delete(p_streamurl);
            return VLC_EGENERIC;
        }
        bytesRange = range;
        contentLength = range.getEndByte() - range.getStartByte() + 1;
    }

    int64_t i_size = stream_Size(p_streamurl);
    if(i_size > -1)
    {
        if(!range.isValid() || contentLength > (size_t) i_size)
            contentLength = (size_t) i_size;
    }
    return VLC_SUCCESS;
}

ssize_t StreamUrlConnection::read(void *p_buffer, size_t len)
{
    if( !p_streamurl )
        return VLC_EGENERIC;

    if(len == 0)
        return VLC_SUCCESS;

    const size_t toRead = (contentLength) ? contentLength - bytesRead : len;
    if (toRead == 0)
        return VLC_SUCCESS;

    if(len > toRead)
        len = toRead;

    ssize_t ret = vlc_stream_Read(p_streamurl, p_buffer, len);
    if(ret >= 0)
        bytesRead += ret;

    if(ret < 0 || (size_t)ret < len || /* set EOF */
       contentLength == bytesRead )
    {
        reset();
        return ret;
    }

    return ret;
}

void StreamUrlConnection::setUsed( bool b )
{
    available = !b;
    if(available && contentLength == bytesRead)
       reset();
}

ConnectionFactory::ConnectionFactory()
{
}

ConnectionFactory::~ConnectionFactory()
{
}

AbstractConnection * ConnectionFactory::createConnection(vlc_object_t *p_object,
                                                         const ConnectionParams &params)
{
    if((params.getScheme() != "http" && params.getScheme() != "https") || params.getHostname().empty())
        return NULL;

    const int sockettype = (params.getScheme() == "https") ? TLSSocket::TLS : Socket::REGULAR;
    Socket *socket = (sockettype == TLSSocket::TLS) ? new (std::nothrow) TLSSocket()
                                                    : new (std::nothrow) Socket();
    if(!socket)
        return NULL;

    /* disable pipelined tls until we have ticket/resume session support */
    HTTPConnection *conn = new (std::nothrow)
            HTTPConnection(p_object, socket, sockettype != TLSSocket::TLS);
    if(!conn)
    {
        delete socket;
        return NULL;
    }

    return conn;
}

AbstractConnection * StreamUrlConnectionFactory::createConnection(vlc_object_t *p_object,
                                                                  const ConnectionParams &)
{
    return new (std::nothrow) StreamUrlConnection(p_object);
}
<|endoftext|>


<commit_before><commit_msg>planning: revert the previous incorrect commit<commit_after><|endoftext|>