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

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.

Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.

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

#ifndef mitkStatisticsImageFilter_hxx
#define mitkStatisticsImageFilter_hxx

#include <mitkStatisticsImageFilter.h>
#include <itkImageScanlineConstIterator.h>

template <typename TInputImage>
mitk::StatisticsImageFilter<TInputImage>::StatisticsImageFilter()
  : m_CalculateHistogram(false),
    m_HistogramSize(0),
    m_HistogramLowerBound(itk::NumericTraits<RealType>::NonpositiveMin()),
    m_HistogramUpperBound(itk::NumericTraits<RealType>::max()),
    m_Sum(1),
    m_SumOfPositivePixels(1),
    m_SumOfSquares(1),
    m_SumOfCubes(1),
    m_SumOfQuadruples(1),
    m_Count(1),
    m_CountOfPositivePixels(1),
    m_Min(1),
    m_Max(1)
{
  this->SetNumberOfRequiredInputs(1);

  this->SetMinimum(itk::NumericTraits<PixelType>::max());
  this->SetMaximum(itk::NumericTraits<PixelType>::NonpositiveMin());
  this->SetMean(itk::NumericTraits<RealType>::max());
  this->SetSigma(itk::NumericTraits<RealType>::max());
  this->SetVariance(itk::NumericTraits<RealType>::max());
  this->SetSum(0);
  this->SetSumOfSquares(0);
  this->SetSumOfCubes(0);
  this->SetSumOfQuadruples(0);
  this->SetSkewness(0);
  this->SetKurtosis(0);
  this->SetMPP(0);
  this->SetEntropy(-1.0);
  this->SetUniformity(0);
  this->SetUPP(0);
  this->SetMedian(0);
}

template <typename TInputImage>
mitk::StatisticsImageFilter<TInputImage>::~StatisticsImageFilter()
{
}

template <typename TInputImage>
typename mitk::StatisticsImageFilter<TInputImage>::DataObjectPointer mitk::StatisticsImageFilter<TInputImage>::MakeOutput(const DataObjectIdentifierType& name)
{
  if (name == "Minimum" ||
      name == "Maximum")
  {
    return PixelObjectType::New();
  }

  if (name == "Mean" ||
      name == "Sigma" ||
      name == "Variance" ||
      name == "Sum" ||
      name == "SumOfSquares" ||
      name == "SumOfCubes" ||
      name == "SumOfQuadruples" ||
      name == "Skewness" ||
      name == "Kurtosis" ||
      name == "MPP" ||
      name == "Entropy" ||
      name == "Uniformity" ||
      name == "UPP" ||
      name == "Median")
  {
    return RealObjectType::New();
  }

  if (name == "Histogram")
  {
    return HistogramType::New();
  }

  return Superclass::MakeOutput(name);
}

template <typename TInputImage>
void mitk::StatisticsImageFilter<TInputImage>::SetHistogramParameters(unsigned int size, RealType lowerBound, RealType upperBound)
{
  bool modified = false;

  if (m_HistogramSize != size)
  {
    m_HistogramSize = size;
    modified = true;
  }

  if (m_HistogramLowerBound != lowerBound)
  {
    m_HistogramLowerBound = lowerBound;
    modified = true;
  }

  if (m_HistogramUpperBound != upperBound)
  {
    m_HistogramUpperBound = upperBound;
    modified = true;
  }

  m_CalculateHistogram = true;

  if (modified)
    this->Modified();
}

template <typename TInputImage>
auto mitk::StatisticsImageFilter<TInputImage>::CreateInitializedHistogram() const -> HistogramPointer
{
  typename HistogramType::SizeType size;
  size.SetSize(1);
  size[0] = m_HistogramSize;

  typename HistogramType::MeasurementVectorType lowerBound;
  lowerBound.SetSize(1);
  lowerBound[0] = m_HistogramLowerBound;

  typename HistogramType::MeasurementVectorType upperBound;
  upperBound.SetSize(1);
  upperBound.[0] = m_HistogramUpperBound;

  auto histogram = HistogramType::New();
  histogram->Initialize(size, lowerBound, upperBound);

  return histogram;
}

template <typename TInputImage>
void mitk::StatisticsImageFilter<TInputImage>::BeforeStreamedGenerateData()
{
  Superclass::BeforeStreamedGenerateData();

  m_Sum = 0;
  m_SumOfPositivePixels = 0;
  m_SumOfSquares = 0;
  m_SumOfCubes = 0;
  m_SumOfQuadruples = 0;
  m_Count = 0;
  m_CountOfPositivePixels = 0;
  m_Min = itk::NumericTraits<PixelType>::max();
  m_Max = itk::NumericTraits<PixelType>::NonpositiveMin();

  if (m_CalculateHistogram)
    m_Histogram = this->CreateInitializedHistogram();
}

template <typename TInputImage>
void mitk::StatisticsImageFilter<TInputImage>::ThreadedStreamedGenerateData(const RegionType& regionForThread)
{
  itk::CompensatedSummation<RealType> sum = 0;
  itk::CompensatedSummation<RealType> sumOfPositivePixels = 0;
  itk::CompensatedSummation<RealType> sumOfSquares = 0;
  itk::CompensatedSummation<RealType> sumOfCubes = 0;
  itk::CompensatedSummation<RealType> sumOfQuadruples = 0;
  itk::SizeValueType count = 0;
  itk::SizeValueType countOfPositivePixels = 0;
  auto min = itk::NumericTraits<PixelType>::max();
  auto max = itk::NumericTraits<PixelType>::NonpositiveMin();
  RealType realValue = 0;
  RealType squareValue = 0;

  HistogramPointer histogram;
  typename HistogramType::MeasurementVectorType histogramMeasurement;
  typename HistogramType::IndexType histogramIndex;

  if (m_CalculateHistogram) // Initialize histogram
  {
    histogram = this->CreateInitializedHistogram();
    histogramMeasurement.SetSize(1);
  }

  itk::ImageScanlineConstIterator<TInputImage> it(this->GetInput(), regionForThread);

  while (!it.IsAtEnd())
  {
    while (!it.IsAtEndOfLine())
    {
      const auto& value = it.Get();
      realValue = static_cast<RealType>(value);

      if (m_CalculateHistogram) // Compute histogram
      {
        histogramMeasurement[0] = realValue;
        histogram->GetIndex(histogramMeasurement, histogramIndex);
        histogram->IncreaseFrequencyOfIndex(histogramIndex, 1);
      }

      min = std::min(min, value);
      max = std::max(max, value);
      squareValue = realValue * realValue;

      sum += realValue;
      sumOfSquares += squareValue;
      sumOfCubes += squareValue * realValue;
      sumOfQuadruples += squareValue * squareValue;
      ++count;

      if (0 < realValue)
      {
        sumOfPositivePixels += realValue;
        ++countOfPositivePixels;
      }

      ++it;
    }

    it.NextLine();
  }

  std::lock_guard<std::mutex> mutexHolder(m_Mutex);

  if (m_CalculateHistogram) // Merge histograms
  {
    typename HistogramType::ConstIterator histogramIt = histogram->Begin();
    typename HistogramType::ConstIterator histogramEnd = histogram->End();

    while (histogramIt != histogramEnd)
    {
      m_Histogram->GetIndex(histogramIt.GetMeasurementVector(), histogramIndex);
      m_Histogram->IncreaseFrequencyOfIndex(histogramIndex, histogramIt.GetFrequency());
      ++histogramIt;
    }
  }

  m_Sum += sum;
  m_SumOfPositivePixels += sumOfPositivePixels;
  m_SumOfSquares += sumOfSquares;
  m_SumOfCubes += sumOfCubes;
  m_SumOfQuadruples += sumOfQuadruples;
  m_Count += count;
  m_CountOfPositivePixels += countOfPositivePixels;
  m_Min = std::min(min, m_Min);
  m_Max = std::max(max, m_Max);
}

template <typename TInputImage>
void mitk::StatisticsImageFilter<TInputImage>::AfterStreamedGenerateData()
{
  Superclass::AfterStreamedGenerateData();

  const RealType sum = m_Sum.GetSum();
  const RealType sumOfPositivePixels = m_SumOfPositivePixels.GetSum();
  const RealType sumOfSquares = m_SumOfSquares.GetSum();
  const RealType sumOfCubes = m_SumOfCubes.GetSum();
  const RealType sumOfQuadruples = m_SumOfQuadruples.GetSum();
  const itk::SizeValueType count = m_Count;
  const itk::SizeValueType countOfPositivePixels = m_CountOfPositivePixels;
  const PixelType minimum = m_Min;
  const PixelType maximum = m_Max;

  const RealType mean = sum / static_cast<RealType>(count);
  const RealType variance = (sumOfSquares - (sum * sum / static_cast<RealType>(count))) / (static_cast<RealType>(count) - 1);
  const RealType sigma = std::sqrt(variance);

  const RealType secondMoment = sumOfSquares / static_cast<RealType>(count);
  const RealType thirdMoment = sumOfCubes / static_cast<RealType>(count);
  const RealType fourthMoment = sumOfQuadruples / static_cast<RealType>(count);
  const RealType skewness = (thirdMoment - 3 * secondMoment * mean + 2 * std::pow(mean, 3)) / std::pow(secondMoment - std::pow(mean, 2), 1.5);
  const RealType kurtosis = (fourthMoment - 4 * thirdMoment * mean + 6 * secondMoment * std::pow(mean, 2) - 3 * std::pow(mean, 4)) / std::pow(secondMoment - std::pow(mean, 2), 2);
  const RealType meanOfPositivePixels = sumOfPositivePixels / static_cast<RealType>(countOfPositivePixels);

  this->SetMinimum(minimum);
  this->SetMaximum(maximum);
  this->SetMean(mean);
  this->SetSigma(sigma);
  this->SetVariance(variance);
  this->SetSum(sum);
  this->SetSumOfSquares(sumOfSquares);
  this->SetSumOfCubes(sumOfCubes);
  this->SetSumOfQuadruples(sumOfQuadruples);
  this->SetSkewness(skewness);
  this->SetKurtosis(kurtosis);
  this->SetMPP(meanOfPositivePixels);

  if (m_CalculateHistogram)
  {
    this->SetHistogram(m_Histogram);

    mitk::HistogramStatisticsCalculator histogramStatisticsCalculator;
    histogramStatisticsCalculator.SetHistogram(m_Histogram);
    histogramStatisticsCalculator.CalculateStatistics();

    this->SetEntropy(histogramStatisticsCalculator.GetEntropy());
    this->SetUniformity(histogramStatisticsCalculator.GetUniformity());
    this->SetUPP(histogramStatisticsCalculator.GetUPP());
    this->SetMedian(histogramStatisticsCalculator.GetMedian());
  }
}

template <typename TInputImage>
void mitk::StatisticsImageFilter<TInputImage>::PrintSelf(std::ostream& os, itk::Indent indent) const
{
  Superclass::PrintSelf(os, indent);

  os << indent << "SumOfCubes: " << this->GetSumOfCubes() << std::endl;
  os << indent << "SumOfQuadruples: " << this->GetSumOfQuadruples() << std::endl;
  os << indent << "Skewness: " << this->GetSkewness() << std::endl;
  os << indent << "Kurtosis: " << this->GetKurtosis() << std::endl;
  os << indent << "MPP: " << this->GetMPP() << std::endl;
  os << indent << "Entropy: " << this->GetEntropy() << std::endl;
  os << indent << "Uniformity: " << this->GetUniformity() << std::endl;
  os << indent << "UPP: " << this->GetUPP() << std::endl;
  os << indent << "Median: " << this->GetMedian() << std::endl;
}

#endif
<commit_msg>Set measurement vector size of histogram before its initialization<commit_after>/*============================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.

Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.

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

#ifndef mitkStatisticsImageFilter_hxx
#define mitkStatisticsImageFilter_hxx

#include <mitkStatisticsImageFilter.h>
#include <mitkHistogramStatisticsCalculator.h>
#include <itkImageScanlineConstIterator.h>

template <typename TInputImage>
mitk::StatisticsImageFilter<TInputImage>::StatisticsImageFilter()
  : m_CalculateHistogram(false),
    m_HistogramSize(0),
    m_HistogramLowerBound(itk::NumericTraits<RealType>::NonpositiveMin()),
    m_HistogramUpperBound(itk::NumericTraits<RealType>::max()),
    m_Sum(1),
    m_SumOfPositivePixels(1),
    m_SumOfSquares(1),
    m_SumOfCubes(1),
    m_SumOfQuadruples(1),
    m_Count(1),
    m_CountOfPositivePixels(1),
    m_Min(1),
    m_Max(1)
{
  this->SetNumberOfRequiredInputs(1);

  this->SetMinimum(itk::NumericTraits<PixelType>::max());
  this->SetMaximum(itk::NumericTraits<PixelType>::NonpositiveMin());
  this->SetMean(itk::NumericTraits<RealType>::max());
  this->SetSigma(itk::NumericTraits<RealType>::max());
  this->SetVariance(itk::NumericTraits<RealType>::max());
  this->SetSum(0);
  this->SetSumOfSquares(0);
  this->SetSumOfCubes(0);
  this->SetSumOfQuadruples(0);
  this->SetSkewness(0);
  this->SetKurtosis(0);
  this->SetMPP(0);
  this->SetEntropy(-1.0);
  this->SetUniformity(0);
  this->SetUPP(0);
  this->SetMedian(0);
}

template <typename TInputImage>
mitk::StatisticsImageFilter<TInputImage>::~StatisticsImageFilter()
{
}

template <typename TInputImage>
typename mitk::StatisticsImageFilter<TInputImage>::DataObjectPointer mitk::StatisticsImageFilter<TInputImage>::MakeOutput(const DataObjectIdentifierType& name)
{
  if (name == "Minimum" ||
      name == "Maximum")
  {
    return PixelObjectType::New();
  }

  if (name == "Mean" ||
      name == "Sigma" ||
      name == "Variance" ||
      name == "Sum" ||
      name == "SumOfSquares" ||
      name == "SumOfCubes" ||
      name == "SumOfQuadruples" ||
      name == "Skewness" ||
      name == "Kurtosis" ||
      name == "MPP" ||
      name == "Entropy" ||
      name == "Uniformity" ||
      name == "UPP" ||
      name == "Median")
  {
    return RealObjectType::New();
  }

  if (name == "Histogram")
  {
    return HistogramType::New();
  }

  return Superclass::MakeOutput(name);
}

template <typename TInputImage>
void mitk::StatisticsImageFilter<TInputImage>::SetHistogramParameters(unsigned int size, RealType lowerBound, RealType upperBound)
{
  bool modified = false;

  if (m_HistogramSize != size)
  {
    m_HistogramSize = size;
    modified = true;
  }

  if (m_HistogramLowerBound != lowerBound)
  {
    m_HistogramLowerBound = lowerBound;
    modified = true;
  }

  if (m_HistogramUpperBound != upperBound)
  {
    m_HistogramUpperBound = upperBound;
    modified = true;
  }

  m_CalculateHistogram = true;

  if (modified)
    this->Modified();
}

template <typename TInputImage>
auto mitk::StatisticsImageFilter<TInputImage>::CreateInitializedHistogram() const -> HistogramPointer
{
  typename HistogramType::SizeType size;
  size.SetSize(1);
  size.Fill(m_HistogramSize);

  typename HistogramType::MeasurementVectorType lowerBound;
  lowerBound.SetSize(1);
  lowerBound.Fill(m_HistogramLowerBound);

  typename HistogramType::MeasurementVectorType upperBound;
  upperBound.SetSize(1);
  upperBound.Fill(m_HistogramUpperBound);

  auto histogram = HistogramType::New();
  histogram->SetMeasurementVectorSize(1);
  histogram->Initialize(size, lowerBound, upperBound);

  return histogram;
}

template <typename TInputImage>
void mitk::StatisticsImageFilter<TInputImage>::BeforeStreamedGenerateData()
{
  Superclass::BeforeStreamedGenerateData();

  m_Sum = 0;
  m_SumOfPositivePixels = 0;
  m_SumOfSquares = 0;
  m_SumOfCubes = 0;
  m_SumOfQuadruples = 0;
  m_Count = 0;
  m_CountOfPositivePixels = 0;
  m_Min = itk::NumericTraits<PixelType>::max();
  m_Max = itk::NumericTraits<PixelType>::NonpositiveMin();

  if (m_CalculateHistogram)
    m_Histogram = this->CreateInitializedHistogram();
}

template <typename TInputImage>
void mitk::StatisticsImageFilter<TInputImage>::ThreadedStreamedGenerateData(const RegionType& regionForThread)
{
  itk::CompensatedSummation<RealType> sum = 0;
  itk::CompensatedSummation<RealType> sumOfPositivePixels = 0;
  itk::CompensatedSummation<RealType> sumOfSquares = 0;
  itk::CompensatedSummation<RealType> sumOfCubes = 0;
  itk::CompensatedSummation<RealType> sumOfQuadruples = 0;
  itk::SizeValueType count = 0;
  itk::SizeValueType countOfPositivePixels = 0;
  auto min = itk::NumericTraits<PixelType>::max();
  auto max = itk::NumericTraits<PixelType>::NonpositiveMin();
  RealType realValue = 0;
  RealType squareValue = 0;

  HistogramPointer histogram;
  typename HistogramType::MeasurementVectorType histogramMeasurement;
  typename HistogramType::IndexType histogramIndex;

  if (m_CalculateHistogram) // Initialize histogram
  {
    histogram = this->CreateInitializedHistogram();
    histogramMeasurement.SetSize(1);
  }

  itk::ImageScanlineConstIterator<TInputImage> it(this->GetInput(), regionForThread);

  while (!it.IsAtEnd())
  {
    while (!it.IsAtEndOfLine())
    {
      const auto& value = it.Get();
      realValue = static_cast<RealType>(value);

      if (m_CalculateHistogram) // Compute histogram
      {
        histogramMeasurement[0] = realValue;
        histogram->GetIndex(histogramMeasurement, histogramIndex);
        histogram->IncreaseFrequencyOfIndex(histogramIndex, 1);
      }

      min = std::min(min, value);
      max = std::max(max, value);
      squareValue = realValue * realValue;

      sum += realValue;
      sumOfSquares += squareValue;
      sumOfCubes += squareValue * realValue;
      sumOfQuadruples += squareValue * squareValue;
      ++count;

      if (0 < realValue)
      {
        sumOfPositivePixels += realValue;
        ++countOfPositivePixels;
      }

      ++it;
    }

    it.NextLine();
  }

  std::lock_guard<std::mutex> mutexHolder(m_Mutex);

  if (m_CalculateHistogram) // Merge histograms
  {
    typename HistogramType::ConstIterator histogramIt = histogram->Begin();
    typename HistogramType::ConstIterator histogramEnd = histogram->End();

    while (histogramIt != histogramEnd)
    {
      m_Histogram->GetIndex(histogramIt.GetMeasurementVector(), histogramIndex);
      m_Histogram->IncreaseFrequencyOfIndex(histogramIndex, histogramIt.GetFrequency());
      ++histogramIt;
    }
  }

  m_Sum += sum;
  m_SumOfPositivePixels += sumOfPositivePixels;
  m_SumOfSquares += sumOfSquares;
  m_SumOfCubes += sumOfCubes;
  m_SumOfQuadruples += sumOfQuadruples;
  m_Count += count;
  m_CountOfPositivePixels += countOfPositivePixels;
  m_Min = std::min(min, m_Min);
  m_Max = std::max(max, m_Max);
}

template <typename TInputImage>
void mitk::StatisticsImageFilter<TInputImage>::AfterStreamedGenerateData()
{
  Superclass::AfterStreamedGenerateData();

  const RealType sum = m_Sum.GetSum();
  const RealType sumOfPositivePixels = m_SumOfPositivePixels.GetSum();
  const RealType sumOfSquares = m_SumOfSquares.GetSum();
  const RealType sumOfCubes = m_SumOfCubes.GetSum();
  const RealType sumOfQuadruples = m_SumOfQuadruples.GetSum();
  const itk::SizeValueType count = m_Count;
  const itk::SizeValueType countOfPositivePixels = m_CountOfPositivePixels;
  const PixelType minimum = m_Min;
  const PixelType maximum = m_Max;

  const RealType mean = sum / static_cast<RealType>(count);
  const RealType variance = (sumOfSquares - (sum * sum / static_cast<RealType>(count))) / (static_cast<RealType>(count) - 1);
  const RealType sigma = std::sqrt(variance);

  const RealType secondMoment = sumOfSquares / static_cast<RealType>(count);
  const RealType thirdMoment = sumOfCubes / static_cast<RealType>(count);
  const RealType fourthMoment = sumOfQuadruples / static_cast<RealType>(count);
  const RealType skewness = (thirdMoment - 3 * secondMoment * mean + 2 * std::pow(mean, 3)) / std::pow(secondMoment - std::pow(mean, 2), 1.5);
  const RealType kurtosis = (fourthMoment - 4 * thirdMoment * mean + 6 * secondMoment * std::pow(mean, 2) - 3 * std::pow(mean, 4)) / std::pow(secondMoment - std::pow(mean, 2), 2);
  const RealType meanOfPositivePixels = sumOfPositivePixels / static_cast<RealType>(countOfPositivePixels);

  this->SetMinimum(minimum);
  this->SetMaximum(maximum);
  this->SetMean(mean);
  this->SetSigma(sigma);
  this->SetVariance(variance);
  this->SetSum(sum);
  this->SetSumOfSquares(sumOfSquares);
  this->SetSumOfCubes(sumOfCubes);
  this->SetSumOfQuadruples(sumOfQuadruples);
  this->SetSkewness(skewness);
  this->SetKurtosis(kurtosis);
  this->SetMPP(meanOfPositivePixels);

  if (m_CalculateHistogram)
  {
    this->SetHistogram(m_Histogram);

    mitk::HistogramStatisticsCalculator histogramStatisticsCalculator;
    histogramStatisticsCalculator.SetHistogram(m_Histogram);
    histogramStatisticsCalculator.CalculateStatistics();

    this->SetEntropy(histogramStatisticsCalculator.GetEntropy());
    this->SetUniformity(histogramStatisticsCalculator.GetUniformity());
    this->SetUPP(histogramStatisticsCalculator.GetUPP());
    this->SetMedian(histogramStatisticsCalculator.GetMedian());
  }
}

template <typename TInputImage>
void mitk::StatisticsImageFilter<TInputImage>::PrintSelf(std::ostream& os, itk::Indent indent) const
{
  Superclass::PrintSelf(os, indent);

  os << indent << "SumOfCubes: " << this->GetSumOfCubes() << std::endl;
  os << indent << "SumOfQuadruples: " << this->GetSumOfQuadruples() << std::endl;
  os << indent << "Skewness: " << this->GetSkewness() << std::endl;
  os << indent << "Kurtosis: " << this->GetKurtosis() << std::endl;
  os << indent << "MPP: " << this->GetMPP() << std::endl;
  os << indent << "Entropy: " << this->GetEntropy() << std::endl;
  os << indent << "Uniformity: " << this->GetUniformity() << std::endl;
  os << indent << "UPP: " << this->GetUPP() << std::endl;
  os << indent << "Median: " << this->GetMedian() << std::endl;
}

#endif
<|endoftext|>


<commit_before>
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "gl/GLTestContext.h"

#define GL_GLEXT_PROTOTYPES
#include <GLES2/gl2.h>

#include <EGL/egl.h>
#include <EGL/eglext.h>

#include "gl/GrGLDefines.h"
#include "gl/GrGLUtil.h"

namespace {

// TODO: Share this class with ANGLE if/when it gets support for EGL_KHR_fence_sync.
class EGLFenceSync : public sk_gpu_test::FenceSync {
public:
    static std::unique_ptr<EGLFenceSync> MakeIfSupported(EGLDisplay);

    sk_gpu_test::PlatformFence SK_WARN_UNUSED_RESULT insertFence() const override;
    bool waitFence(sk_gpu_test::PlatformFence fence) const override;
    void deleteFence(sk_gpu_test::PlatformFence fence) const override;

private:
    EGLFenceSync(EGLDisplay display);

    PFNEGLCREATESYNCKHRPROC       fEGLCreateSyncKHR;
    PFNEGLCLIENTWAITSYNCKHRPROC   fEGLClientWaitSyncKHR;
    PFNEGLDESTROYSYNCKHRPROC      fEGLDestroySyncKHR;

    EGLDisplay                    fDisplay;

    typedef sk_gpu_test::FenceSync INHERITED;
};

std::function<void()> context_restorer() {
    auto display = eglGetCurrentDisplay();
    auto dsurface = eglGetCurrentSurface(EGL_DRAW);
    auto rsurface = eglGetCurrentSurface(EGL_READ);
    auto context = eglGetCurrentContext();
    return [display, dsurface, rsurface, context] {
        eglMakeCurrent(display, dsurface, rsurface, context);
    };
}

class EGLGLTestContext : public sk_gpu_test::GLTestContext {
public:
    EGLGLTestContext(GrGLStandard forcedGpuAPI, EGLGLTestContext* shareContext);
    ~EGLGLTestContext() override;

    GrEGLImage texture2DToEGLImage(GrGLuint texID) const override;
    void destroyEGLImage(GrEGLImage) const override;
    GrGLuint eglImageToExternalTexture(GrEGLImage) const override;
    std::unique_ptr<sk_gpu_test::GLTestContext> makeNew() const override;

private:
    void destroyGLContext();

    void onPlatformMakeCurrent() const override;
    std::function<void()> onPlatformGetAutoContextRestore() const override;
    void onPlatformSwapBuffers() const override;
    GrGLFuncPtr onPlatformGetProcAddress(const char*) const override;

    EGLContext fContext;
    EGLDisplay fDisplay;
    EGLSurface fSurface;
};

static EGLContext create_gles_egl_context(EGLDisplay display,
                                          EGLConfig surfaceConfig,
                                          EGLContext eglShareContext,
                                          EGLint eglContextClientVersion) {
    const EGLint contextAttribsForOpenGLES[] = {
        EGL_CONTEXT_CLIENT_VERSION,
        eglContextClientVersion,
        EGL_NONE
    };
    return eglCreateContext(display, surfaceConfig, eglShareContext, contextAttribsForOpenGLES);
}
static EGLContext create_gl_egl_context(EGLDisplay display,
                                        EGLConfig surfaceConfig,
                                        EGLContext eglShareContext) {
    const EGLint contextAttribsForOpenGL[] = {
        EGL_NONE
    };
    return eglCreateContext(display, surfaceConfig, eglShareContext, contextAttribsForOpenGL);
}

EGLGLTestContext::EGLGLTestContext(GrGLStandard forcedGpuAPI, EGLGLTestContext* shareContext)
    : fContext(EGL_NO_CONTEXT)
    , fDisplay(EGL_NO_DISPLAY)
    , fSurface(EGL_NO_SURFACE) {

    EGLContext eglShareContext = shareContext ? shareContext->fContext : nullptr;

    static const GrGLStandard kStandards[] = {
        kGL_GrGLStandard,
        kGLES_GrGLStandard,
    };

    size_t apiLimit = SK_ARRAY_COUNT(kStandards);
    size_t api = 0;
    if (forcedGpuAPI == kGL_GrGLStandard) {
        apiLimit = 1;
    } else if (forcedGpuAPI == kGLES_GrGLStandard) {
        api = 1;
    }
    SkASSERT(forcedGpuAPI == kNone_GrGLStandard || kStandards[api] == forcedGpuAPI);

    sk_sp<const GrGLInterface> gl;

    for (; nullptr == gl.get() && api < apiLimit; ++api) {
        fDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);

        EGLint majorVersion;
        EGLint minorVersion;
        eglInitialize(fDisplay, &majorVersion, &minorVersion);

#if 0
        SkDebugf("VENDOR: %s\n", eglQueryString(fDisplay, EGL_VENDOR));
        SkDebugf("APIS: %s\n", eglQueryString(fDisplay, EGL_CLIENT_APIS));
        SkDebugf("VERSION: %s\n", eglQueryString(fDisplay, EGL_VERSION));
        SkDebugf("EXTENSIONS %s\n", eglQueryString(fDisplay, EGL_EXTENSIONS));
#endif
        bool gles = kGLES_GrGLStandard == kStandards[api];

        if (!eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API)) {
            continue;
        }

        EGLint numConfigs = 0;
        const EGLint configAttribs[] = {
            EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
            EGL_RENDERABLE_TYPE, gles ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT,
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_ALPHA_SIZE, 8,
            EGL_NONE
        };

        EGLConfig surfaceConfig;
        if (!eglChooseConfig(fDisplay, configAttribs, &surfaceConfig, 1, &numConfigs)) {
            SkDebugf("eglChooseConfig failed. EGL Error: 0x%08x\n", eglGetError());
            continue;
        }

        if (0 == numConfigs) {
            SkDebugf("No suitable EGL config found.\n");
            continue;
        }

        if (gles) {
            fContext = create_gles_egl_context(fDisplay, surfaceConfig, eglShareContext, 3);
            if (EGL_NO_CONTEXT == fContext) {
                fContext = create_gles_egl_context(fDisplay, surfaceConfig, eglShareContext, 2);
            }
        } else {
            fContext = create_gl_egl_context(fDisplay, surfaceConfig, eglShareContext);
        }
        if (EGL_NO_CONTEXT == fContext) {
            SkDebugf("eglCreateContext failed.  EGL Error: 0x%08x\n", eglGetError());
            continue;
        }

        static const EGLint kSurfaceAttribs[] = {
            EGL_WIDTH, 1,
            EGL_HEIGHT, 1,
            EGL_NONE
        };

        fSurface = eglCreatePbufferSurface(fDisplay, surfaceConfig, kSurfaceAttribs);
        if (EGL_NO_SURFACE == fSurface) {
            SkDebugf("eglCreatePbufferSurface failed. EGL Error: 0x%08x\n", eglGetError());
            this->destroyGLContext();
            continue;
        }

        SkScopeExit restorer(context_restorer());
        if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
            SkDebugf("eglMakeCurrent failed.  EGL Error: 0x%08x\n", eglGetError());
            this->destroyGLContext();
            continue;
        }

        gl = GrGLMakeNativeInterface();
        if (!gl) {
            SkDebugf("Failed to create gl interface.\n");
            this->destroyGLContext();
            continue;
        }

        if (!gl->validate()) {
            SkDebugf("Failed to validate gl interface.\n");
            this->destroyGLContext();
            continue;
        }

        this->init(std::move(gl), EGLFenceSync::MakeIfSupported(fDisplay));
        break;
    }
}

EGLGLTestContext::~EGLGLTestContext() {
    this->teardown();
    this->destroyGLContext();
}

void EGLGLTestContext::destroyGLContext() {
    if (fDisplay) {
        if (fContext) {
            if (eglGetCurrentContext() == fContext) {
                // This will ensure that the context is immediately deleted.
                eglMakeCurrent(fDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
            }
            eglDestroyContext(fDisplay, fContext);
            fContext = EGL_NO_CONTEXT;
        }

        if (fSurface) {
            eglDestroySurface(fDisplay, fSurface);
            fSurface = EGL_NO_SURFACE;
        }

        //TODO should we close the display?
        fDisplay = EGL_NO_DISPLAY;
    }
}

GrEGLImage EGLGLTestContext::texture2DToEGLImage(GrGLuint texID) const {
    if (!this->gl()->hasExtension("EGL_KHR_gl_texture_2D_image")) {
        return GR_EGL_NO_IMAGE;
    }
    GrEGLImage img;
    GrEGLint attribs[] = { GR_EGL_GL_TEXTURE_LEVEL, 0, GR_EGL_NONE };
    GrEGLClientBuffer clientBuffer = reinterpret_cast<GrEGLClientBuffer>(texID);
    GR_GL_CALL_RET(this->gl(), img,
                   EGLCreateImage(fDisplay, fContext, GR_EGL_GL_TEXTURE_2D, clientBuffer, attribs));
    return img;
}

void EGLGLTestContext::destroyEGLImage(GrEGLImage image) const {
    GR_GL_CALL(this->gl(), EGLDestroyImage(fDisplay, image));
}

GrGLuint EGLGLTestContext::eglImageToExternalTexture(GrEGLImage image) const {
    GrGLClearErr(this->gl());
    if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) {
        return 0;
    }
    typedef GrGLvoid (*EGLImageTargetTexture2DProc)(GrGLenum, GrGLeglImage);

    EGLImageTargetTexture2DProc glEGLImageTargetTexture2D =
        (EGLImageTargetTexture2DProc) eglGetProcAddress("glEGLImageTargetTexture2DOES");
    if (!glEGLImageTargetTexture2D) {
        return 0;
    }
    GrGLuint texID;
    GR_GL_CALL(this->gl(), GenTextures(1, &texID));
    if (!texID) {
        return 0;
    }
    GR_GL_CALL_NOERRCHECK(this->gl(), BindTexture(GR_GL_TEXTURE_EXTERNAL, texID));
    if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) {
        GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
        return 0;
    }
    glEGLImageTargetTexture2D(GR_GL_TEXTURE_EXTERNAL, image);
    if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) {
        GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
        return 0;
    }
    return texID;
}

std::unique_ptr<sk_gpu_test::GLTestContext> EGLGLTestContext::makeNew() const {
    std::unique_ptr<sk_gpu_test::GLTestContext> ctx(new EGLGLTestContext(this->gl()->fStandard,
                                                                         nullptr));
    if (ctx) {
        ctx->makeCurrent();
    }
    return ctx;
}

void EGLGLTestContext::onPlatformMakeCurrent() const {
    if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
        SkDebugf("Could not set the context.\n");
    }
}

std::function<void()> EGLGLTestContext::onPlatformGetAutoContextRestore() const {
    if (eglGetCurrentContext() == fContext) {
        return nullptr;
    }
    return context_restorer();
}

void EGLGLTestContext::onPlatformSwapBuffers() const {
    if (!eglSwapBuffers(fDisplay, fSurface)) {
        SkDebugf("Could not complete eglSwapBuffers.\n");
    }
}

GrGLFuncPtr EGLGLTestContext::onPlatformGetProcAddress(const char* procName) const {
    return eglGetProcAddress(procName);
}

static bool supports_egl_extension(EGLDisplay display, const char* extension) {
    size_t extensionLength = strlen(extension);
    const char* extensionsStr = eglQueryString(display, EGL_EXTENSIONS);
    while (const char* match = strstr(extensionsStr, extension)) {
        // Ensure the string we found is its own extension, not a substring of a larger extension
        // (e.g. GL_ARB_occlusion_query / GL_ARB_occlusion_query2).
        if ((match == extensionsStr || match[-1] == ' ') &&
            (match[extensionLength] == ' ' || match[extensionLength] == '\0')) {
            return true;
        }
        extensionsStr = match + extensionLength;
    }
    return false;
}

std::unique_ptr<EGLFenceSync> EGLFenceSync::MakeIfSupported(EGLDisplay display) {
    if (!display || !supports_egl_extension(display, "EGL_KHR_fence_sync")) {
        return nullptr;
    }
    return std::unique_ptr<EGLFenceSync>(new EGLFenceSync(display));
}

EGLFenceSync::EGLFenceSync(EGLDisplay display)
    : fDisplay(display) {
    fEGLCreateSyncKHR = (PFNEGLCREATESYNCKHRPROC) eglGetProcAddress("eglCreateSyncKHR");
    fEGLClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC) eglGetProcAddress("eglClientWaitSyncKHR");
    fEGLDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC) eglGetProcAddress("eglDestroySyncKHR");
    SkASSERT(fEGLCreateSyncKHR && fEGLClientWaitSyncKHR && fEGLDestroySyncKHR);
}

sk_gpu_test::PlatformFence EGLFenceSync::insertFence() const {
    EGLSyncKHR eglsync = fEGLCreateSyncKHR(fDisplay, EGL_SYNC_FENCE_KHR, nullptr);
    return reinterpret_cast<sk_gpu_test::PlatformFence>(eglsync);
}

bool EGLFenceSync::waitFence(sk_gpu_test::PlatformFence platformFence) const {
    EGLSyncKHR eglsync = reinterpret_cast<EGLSyncKHR>(platformFence);
    return EGL_CONDITION_SATISFIED_KHR ==
            fEGLClientWaitSyncKHR(fDisplay,
                                  eglsync,
                                  EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
                                  EGL_FOREVER_KHR);
}

void EGLFenceSync::deleteFence(sk_gpu_test::PlatformFence platformFence) const {
    EGLSyncKHR eglsync = reinterpret_cast<EGLSyncKHR>(platformFence);
    fEGLDestroySyncKHR(fDisplay, eglsync);
}

GR_STATIC_ASSERT(sizeof(EGLSyncKHR) <= sizeof(sk_gpu_test::PlatformFence));

}  // anonymous namespace

namespace sk_gpu_test {
GLTestContext *CreatePlatformGLTestContext(GrGLStandard forcedGpuAPI,
                                           GLTestContext *shareContext) {
    EGLGLTestContext* eglShareContext = reinterpret_cast<EGLGLTestContext*>(shareContext);
    EGLGLTestContext *ctx = new EGLGLTestContext(forcedGpuAPI, eglShareContext);
    if (!ctx->isValid()) {
        delete ctx;
        return nullptr;
    }
    return ctx;
}
}  // namespace sk_gpu_test
<commit_msg>Revert "CreatePlatformGLTestContext_egl:  Try GLES 3, then GLES 2."<commit_after>
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "gl/GLTestContext.h"

#define GL_GLEXT_PROTOTYPES
#include <GLES2/gl2.h>

#include <EGL/egl.h>
#include <EGL/eglext.h>

#include "gl/GrGLDefines.h"
#include "gl/GrGLUtil.h"

namespace {

// TODO: Share this class with ANGLE if/when it gets support for EGL_KHR_fence_sync.
class EGLFenceSync : public sk_gpu_test::FenceSync {
public:
    static std::unique_ptr<EGLFenceSync> MakeIfSupported(EGLDisplay);

    sk_gpu_test::PlatformFence SK_WARN_UNUSED_RESULT insertFence() const override;
    bool waitFence(sk_gpu_test::PlatformFence fence) const override;
    void deleteFence(sk_gpu_test::PlatformFence fence) const override;

private:
    EGLFenceSync(EGLDisplay display);

    PFNEGLCREATESYNCKHRPROC       fEGLCreateSyncKHR;
    PFNEGLCLIENTWAITSYNCKHRPROC   fEGLClientWaitSyncKHR;
    PFNEGLDESTROYSYNCKHRPROC      fEGLDestroySyncKHR;

    EGLDisplay                    fDisplay;

    typedef sk_gpu_test::FenceSync INHERITED;
};

std::function<void()> context_restorer() {
    auto display = eglGetCurrentDisplay();
    auto dsurface = eglGetCurrentSurface(EGL_DRAW);
    auto rsurface = eglGetCurrentSurface(EGL_READ);
    auto context = eglGetCurrentContext();
    return [display, dsurface, rsurface, context] {
        eglMakeCurrent(display, dsurface, rsurface, context);
    };
}

class EGLGLTestContext : public sk_gpu_test::GLTestContext {
public:
    EGLGLTestContext(GrGLStandard forcedGpuAPI, EGLGLTestContext* shareContext);
    ~EGLGLTestContext() override;

    GrEGLImage texture2DToEGLImage(GrGLuint texID) const override;
    void destroyEGLImage(GrEGLImage) const override;
    GrGLuint eglImageToExternalTexture(GrEGLImage) const override;
    std::unique_ptr<sk_gpu_test::GLTestContext> makeNew() const override;

private:
    void destroyGLContext();

    void onPlatformMakeCurrent() const override;
    std::function<void()> onPlatformGetAutoContextRestore() const override;
    void onPlatformSwapBuffers() const override;
    GrGLFuncPtr onPlatformGetProcAddress(const char*) const override;

    EGLContext fContext;
    EGLDisplay fDisplay;
    EGLSurface fSurface;
};

EGLGLTestContext::EGLGLTestContext(GrGLStandard forcedGpuAPI, EGLGLTestContext* shareContext)
    : fContext(EGL_NO_CONTEXT)
    , fDisplay(EGL_NO_DISPLAY)
    , fSurface(EGL_NO_SURFACE) {

    EGLContext eglShareContext = shareContext ? shareContext->fContext : nullptr;

    static const EGLint kEGLContextAttribsForOpenGL[] = {
        EGL_NONE
    };

    static const EGLint kEGLContextAttribsForOpenGLES[] = {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE
    };

    static const struct {
        const EGLint* fContextAttribs;
        EGLenum fAPI;
        EGLint  fRenderableTypeBit;
        GrGLStandard fStandard;
    } kAPIs[] = {
        {   // OpenGL
            kEGLContextAttribsForOpenGL,
            EGL_OPENGL_API,
            EGL_OPENGL_BIT,
            kGL_GrGLStandard
        },
        {   // OpenGL ES. This seems to work for both ES2 and 3 (when available).
            kEGLContextAttribsForOpenGLES,
            EGL_OPENGL_ES_API,
            EGL_OPENGL_ES2_BIT,
            kGLES_GrGLStandard
        },
    };

    size_t apiLimit = SK_ARRAY_COUNT(kAPIs);
    size_t api = 0;
    if (forcedGpuAPI == kGL_GrGLStandard) {
        apiLimit = 1;
    } else if (forcedGpuAPI == kGLES_GrGLStandard) {
        api = 1;
    }
    SkASSERT(forcedGpuAPI == kNone_GrGLStandard || kAPIs[api].fStandard == forcedGpuAPI);

    sk_sp<const GrGLInterface> gl;

    for (; nullptr == gl.get() && api < apiLimit; ++api) {
        fDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);

        EGLint majorVersion;
        EGLint minorVersion;
        eglInitialize(fDisplay, &majorVersion, &minorVersion);

#if 0
        SkDebugf("VENDOR: %s\n", eglQueryString(fDisplay, EGL_VENDOR));
        SkDebugf("APIS: %s\n", eglQueryString(fDisplay, EGL_CLIENT_APIS));
        SkDebugf("VERSION: %s\n", eglQueryString(fDisplay, EGL_VERSION));
        SkDebugf("EXTENSIONS %s\n", eglQueryString(fDisplay, EGL_EXTENSIONS));
#endif

        if (!eglBindAPI(kAPIs[api].fAPI)) {
            continue;
        }

        EGLint numConfigs = 0;
        const EGLint configAttribs[] = {
            EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
            EGL_RENDERABLE_TYPE, kAPIs[api].fRenderableTypeBit,
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_ALPHA_SIZE, 8,
            EGL_NONE
        };

        EGLConfig surfaceConfig;
        if (!eglChooseConfig(fDisplay, configAttribs, &surfaceConfig, 1, &numConfigs)) {
            SkDebugf("eglChooseConfig failed. EGL Error: 0x%08x\n", eglGetError());
            continue;
        }

        if (0 == numConfigs) {
            SkDebugf("No suitable EGL config found.\n");
            continue;
        }

        fContext = eglCreateContext(fDisplay, surfaceConfig, eglShareContext,
                                    kAPIs[api].fContextAttribs);
        if (EGL_NO_CONTEXT == fContext) {
            SkDebugf("eglCreateContext failed.  EGL Error: 0x%08x\n", eglGetError());
            continue;
        }

        static const EGLint kSurfaceAttribs[] = {
            EGL_WIDTH, 1,
            EGL_HEIGHT, 1,
            EGL_NONE
        };

        fSurface = eglCreatePbufferSurface(fDisplay, surfaceConfig, kSurfaceAttribs);
        if (EGL_NO_SURFACE == fSurface) {
            SkDebugf("eglCreatePbufferSurface failed. EGL Error: 0x%08x\n", eglGetError());
            this->destroyGLContext();
            continue;
        }

        SkScopeExit restorer(context_restorer());
        if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
            SkDebugf("eglMakeCurrent failed.  EGL Error: 0x%08x\n", eglGetError());
            this->destroyGLContext();
            continue;
        }

        gl = GrGLMakeNativeInterface();
        if (!gl) {
            SkDebugf("Failed to create gl interface.\n");
            this->destroyGLContext();
            continue;
        }

        if (!gl->validate()) {
            SkDebugf("Failed to validate gl interface.\n");
            this->destroyGLContext();
            continue;
        }

        this->init(std::move(gl), EGLFenceSync::MakeIfSupported(fDisplay));
        break;
    }
}

EGLGLTestContext::~EGLGLTestContext() {
    this->teardown();
    this->destroyGLContext();
}

void EGLGLTestContext::destroyGLContext() {
    if (fDisplay) {
        if (fContext) {
            if (eglGetCurrentContext() == fContext) {
                // This will ensure that the context is immediately deleted.
                eglMakeCurrent(fDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
            }
            eglDestroyContext(fDisplay, fContext);
            fContext = EGL_NO_CONTEXT;
        }

        if (fSurface) {
            eglDestroySurface(fDisplay, fSurface);
            fSurface = EGL_NO_SURFACE;
        }

        //TODO should we close the display?
        fDisplay = EGL_NO_DISPLAY;
    }
}

GrEGLImage EGLGLTestContext::texture2DToEGLImage(GrGLuint texID) const {
    if (!this->gl()->hasExtension("EGL_KHR_gl_texture_2D_image")) {
        return GR_EGL_NO_IMAGE;
    }
    GrEGLImage img;
    GrEGLint attribs[] = { GR_EGL_GL_TEXTURE_LEVEL, 0, GR_EGL_NONE };
    GrEGLClientBuffer clientBuffer = reinterpret_cast<GrEGLClientBuffer>(texID);
    GR_GL_CALL_RET(this->gl(), img,
                   EGLCreateImage(fDisplay, fContext, GR_EGL_GL_TEXTURE_2D, clientBuffer, attribs));
    return img;
}

void EGLGLTestContext::destroyEGLImage(GrEGLImage image) const {
    GR_GL_CALL(this->gl(), EGLDestroyImage(fDisplay, image));
}

GrGLuint EGLGLTestContext::eglImageToExternalTexture(GrEGLImage image) const {
    GrGLClearErr(this->gl());
    if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) {
        return 0;
    }
    typedef GrGLvoid (*EGLImageTargetTexture2DProc)(GrGLenum, GrGLeglImage);

    EGLImageTargetTexture2DProc glEGLImageTargetTexture2D =
        (EGLImageTargetTexture2DProc) eglGetProcAddress("glEGLImageTargetTexture2DOES");
    if (!glEGLImageTargetTexture2D) {
        return 0;
    }
    GrGLuint texID;
    GR_GL_CALL(this->gl(), GenTextures(1, &texID));
    if (!texID) {
        return 0;
    }
    GR_GL_CALL_NOERRCHECK(this->gl(), BindTexture(GR_GL_TEXTURE_EXTERNAL, texID));
    if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) {
        GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
        return 0;
    }
    glEGLImageTargetTexture2D(GR_GL_TEXTURE_EXTERNAL, image);
    if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) {
        GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
        return 0;
    }
    return texID;
}

std::unique_ptr<sk_gpu_test::GLTestContext> EGLGLTestContext::makeNew() const {
    std::unique_ptr<sk_gpu_test::GLTestContext> ctx(new EGLGLTestContext(this->gl()->fStandard,
                                                                         nullptr));
    if (ctx) {
        ctx->makeCurrent();
    }
    return ctx;
}

void EGLGLTestContext::onPlatformMakeCurrent() const {
    if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
        SkDebugf("Could not set the context.\n");
    }
}

std::function<void()> EGLGLTestContext::onPlatformGetAutoContextRestore() const {
    if (eglGetCurrentContext() == fContext) {
        return nullptr;
    }
    return context_restorer();
}

void EGLGLTestContext::onPlatformSwapBuffers() const {
    if (!eglSwapBuffers(fDisplay, fSurface)) {
        SkDebugf("Could not complete eglSwapBuffers.\n");
    }
}

GrGLFuncPtr EGLGLTestContext::onPlatformGetProcAddress(const char* procName) const {
    return eglGetProcAddress(procName);
}

static bool supports_egl_extension(EGLDisplay display, const char* extension) {
    size_t extensionLength = strlen(extension);
    const char* extensionsStr = eglQueryString(display, EGL_EXTENSIONS);
    while (const char* match = strstr(extensionsStr, extension)) {
        // Ensure the string we found is its own extension, not a substring of a larger extension
        // (e.g. GL_ARB_occlusion_query / GL_ARB_occlusion_query2).
        if ((match == extensionsStr || match[-1] == ' ') &&
            (match[extensionLength] == ' ' || match[extensionLength] == '\0')) {
            return true;
        }
        extensionsStr = match + extensionLength;
    }
    return false;
}

std::unique_ptr<EGLFenceSync> EGLFenceSync::MakeIfSupported(EGLDisplay display) {
    if (!display || !supports_egl_extension(display, "EGL_KHR_fence_sync")) {
        return nullptr;
    }
    return std::unique_ptr<EGLFenceSync>(new EGLFenceSync(display));
}

EGLFenceSync::EGLFenceSync(EGLDisplay display)
    : fDisplay(display) {
    fEGLCreateSyncKHR = (PFNEGLCREATESYNCKHRPROC) eglGetProcAddress("eglCreateSyncKHR");
    fEGLClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC) eglGetProcAddress("eglClientWaitSyncKHR");
    fEGLDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC) eglGetProcAddress("eglDestroySyncKHR");
    SkASSERT(fEGLCreateSyncKHR && fEGLClientWaitSyncKHR && fEGLDestroySyncKHR);
}

sk_gpu_test::PlatformFence EGLFenceSync::insertFence() const {
    EGLSyncKHR eglsync = fEGLCreateSyncKHR(fDisplay, EGL_SYNC_FENCE_KHR, nullptr);
    return reinterpret_cast<sk_gpu_test::PlatformFence>(eglsync);
}

bool EGLFenceSync::waitFence(sk_gpu_test::PlatformFence platformFence) const {
    EGLSyncKHR eglsync = reinterpret_cast<EGLSyncKHR>(platformFence);
    return EGL_CONDITION_SATISFIED_KHR ==
            fEGLClientWaitSyncKHR(fDisplay,
                                  eglsync,
                                  EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
                                  EGL_FOREVER_KHR);
}

void EGLFenceSync::deleteFence(sk_gpu_test::PlatformFence platformFence) const {
    EGLSyncKHR eglsync = reinterpret_cast<EGLSyncKHR>(platformFence);
    fEGLDestroySyncKHR(fDisplay, eglsync);
}

GR_STATIC_ASSERT(sizeof(EGLSyncKHR) <= sizeof(sk_gpu_test::PlatformFence));

}  // anonymous namespace

namespace sk_gpu_test {
GLTestContext *CreatePlatformGLTestContext(GrGLStandard forcedGpuAPI,
                                           GLTestContext *shareContext) {
    EGLGLTestContext* eglShareContext = reinterpret_cast<EGLGLTestContext*>(shareContext);
    EGLGLTestContext *ctx = new EGLGLTestContext(forcedGpuAPI, eglShareContext);
    if (!ctx->isValid()) {
        delete ctx;
        return nullptr;
    }
    return ctx;
}
}  // namespace sk_gpu_test
<|endoftext|>


<commit_before>#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){
    ofSetLogLevel(OF_LOG_NOTICE);
     vidGrabber.setDesiredFrameRate(60);
    vidGrabber.initGrabber(640, 480);
    vidRecorder.setup("testMovie.mov", vidGrabber.width, vidGrabber.height, 60, JPEG_QUALITYSUPERB);
}

void testApp::exit() {
    vidRecorder.encodeVideo();
}

//--------------------------------------------------------------
void testApp::update(){
    vidGrabber.update();
    if(vidGrabber.isFrameNew()) {
        vidRecorder.addFrame(vidGrabber.getPixelsRef());
    }
    char title[256];
    sprintf(title,"%d/%d=%.2f, %.2f", vidRecorder.getNumFramesInQueue(), ofGetElapsedTimeMillis()/1000, vidRecorder.getNumFramesInQueue()/ofGetElapsedTimef(), ofGetFrameRate());
    ofSetWindowTitle(ofToString(title));
}

//--------------------------------------------------------------
void testApp::draw(){
    vidGrabber.draw(0,0);
}

//--------------------------------------------------------------
void testApp::keyPressed(int key){
    if(key=='c')
        vidRecorder.close();
}

//--------------------------------------------------------------
void testApp::keyReleased(int key){

}

//--------------------------------------------------------------
void testApp::mouseMoved(int x, int y ){

}

//--------------------------------------------------------------
void testApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::mouseReleased(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::windowResized(int w, int h){

}

//--------------------------------------------------------------
void testApp::gotMessage(ofMessage msg){

}

//--------------------------------------------------------------
void testApp::dragEvent(ofDragInfo dragInfo){

}
<commit_msg>updated to use ofImageQualityTypes in setup<commit_after>#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){
    ofSetLogLevel(OF_LOG_NOTICE);
     vidGrabber.setDesiredFrameRate(60);
    vidGrabber.initGrabber(640, 480);
    vidRecorder.setup("testMovie.mov", vidGrabber.getWidth(), vidGrabber.getHeight(), 60, OF_IMAGE_QUALITY_HIGH);

    ofSetWindowShape(640,480);
}

void testApp::exit() {
    vidRecorder.encodeVideo();
}

//--------------------------------------------------------------
void testApp::update(){
    vidGrabber.update();
    if(vidGrabber.isFrameNew()) {
        vidRecorder.addFrame(vidGrabber.getPixelsRef());
    }
}

//--------------------------------------------------------------
void testApp::draw(){
    vidGrabber.draw(0,0);
    char title[256];
    sprintf(title,"frames in queue: %d\nfps: %.2f", vidRecorder.getNumFramesInQueue(), ofGetFrameRate());
    ofDrawBitmapString(ofToString(title),20,20);
}

//--------------------------------------------------------------
void testApp::keyPressed(int key){
    if(key=='c')
        vidRecorder.close();
}

//--------------------------------------------------------------
void testApp::keyReleased(int key){

}

//--------------------------------------------------------------
void testApp::mouseMoved(int x, int y ){

}

//--------------------------------------------------------------
void testApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::mouseReleased(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::windowResized(int w, int h){

}

//--------------------------------------------------------------
void testApp::gotMessage(ofMessage msg){

}

//--------------------------------------------------------------
void testApp::dragEvent(ofDragInfo dragInfo){

}
<|endoftext|>


<commit_before>/*
 * RKP.cpp - All the Remote Keypad comms and display commands
 *
 * Created: 3/30/2014 10:04:10 PM
 *
 *   Aritech Alarm Panel Arduino Internet Enabled Keypad -  CS350 - CD34 - CD72 - CD91 and more
 *
 *   For Arduino (UNO or Leonardo) with added Ethernet Shield
 *
 *   See Circuit Diagram for wiring instructions
 *
 *   Author: Ozmo
 *
 *   See: http://www.boards.ie/vbulletin/showthread.php?p=88215184
 *
*/

//change network settings to yours
#include <Arduino.h>
#include "RKP.h"
#include "LOG.h"
#include "Websocket.h"
#include "SMTP.h"
#include "Config.h"

//Workaround if needed for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734
#ifdef PROGMEM
#undef PROGMEM
#define PROGMEM __attribute__((section(".progmem.data")))
#endif

#define nSerialBaudKP_RX 1953
#define ixMaxPanel 40	//40 bytes enough

const prog_uchar allmonths[] = {"JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"};
const prog_uchar alldays[] = {"SUNMONTUEWEDTHUFRISAT"};

String RKPClass::msKeyToSend="";
bool RKPClass::dateFlash=true;
bool RKPClass::mbIsPanelWarning=false;
bool RKPClass::mbIsPanelAlarm=false;

#define DISP_BUF_LEN 16+1+2		//16 characters - space - AW (will be followed by a Terminator 0)
byte RKPClass::dispBuffer[DISP_BUF_LEN+1];

//const char msgOff[] PROGMEM = "KeyPad OFF!";
//const char msgOn[] PROGMEM = "KeyPad ON!";

RKPClass::RKPClass()
{
	dispBuffer[16]='|';
	dispBuffer[DISP_BUF_LEN]=0;
}

void RKPClass::loop_PanelMon(void)
{
	static int msgbufLen = 0;
	static byte msgbuf[ixMaxPanel];
	static bool bReceivingPacketNow = false;

	static byte lastByte=-1;

	//Note - nothing here is to be blocking
	//- so sending emails must not block receiving serial logs

	//Software UART not great at receiving for some reason :/ - ok for sending.
	if (Serial1.available()==0)
		return;

	byte rx = Serial1.read();
	//Log((char)rx);

	if (bReceivingPacketNow == false)
	{
		if(lastByte == 0)
		{//last char was a zero - this might be our header...
			//It may be for us or not - but get whole message before testing so we stay in sync
			bReceivingPacketNow = true;
			msgbufLen=0;
			//not necessary to balnk - but good for debug
			for(int n=0;n<ixMaxPanel;n++)
				msgbuf[n]=0xFF;
			msgbuf[msgbufLen++]=rx;
		}
		lastByte = rx;
		return; //wait for byte#3 (0,id,..)
	}
	lastByte = rx;	//needed in case cs error

	msgbuf[msgbufLen++]=rx;
	if (rx!=0)
	{//wasn't end of packet - is buffer full?
		if (msgbufLen>=ixMaxPanel)
		{//packet never terminated - bytes lost :(
			Log("Buf overflow");
			bReceivingPacketNow = false;
			return;
		}
	}
	else
	{//End of the packet
		bReceivingPacketNow = false;

		byte idDev = (msgbuf[0] & 0xf0)>>4; //ID of remote that message is for 0x0-0xf are valid ids

		//Uncomment to Display all packets
		//LogHex(msgbuf,msgbufLen);Log(F("DevID:"));LogLn(idDev);

		byte cs = 0;
		for(int n=0;n<msgbufLen;n++)
		{
			byte rx = msgbuf[n];
			if (n<msgbufLen-2) //dont sum cs or terminator
				cs+=rx;
			//Log((char)rx);
		}
		if (cs != msgbuf[msgbufLen-2])
		{
			Log(F("CS Fail :( "));
			//LogHex(msgbuf,msgbufLen);
			Log(F("Dev:"));LogLn(idDev);
			return;
		}
		//LogLn(F(""));

		lastByte=-1; //good message - so waiting again for next zero

		{//Send Email if alarm lights come on
			bool bIsPanelWarning = (msgbuf[1] & 0x04) != 0;
			if (bIsPanelWarning == true && RKPClass::mbIsPanelWarning == false)
				SMTP::QueueEmail();
			RKPClass::mbIsPanelWarning = bIsPanelWarning;

			bool bIsPanelAlarm = (msgbuf[1] & 0x02) != 0;
			if (bIsPanelAlarm == true && RKPClass::mbIsPanelAlarm == false)
				SMTP::QueueEmail();
			RKPClass::mbIsPanelAlarm = bIsPanelAlarm;
		}

		//for us?
		if (idDev==RKP_ID)
		{
			DisplayScreen(msgbuf, msgbufLen); //try display what device 0 sees
			bool bAck = (msgbuf[0] & 0x04) != 0;	//required for good comms
			SendToPanel(RKP_ID, bAck);
			LogLn("-OK-");
		}
		else
		{
			//LogLn("-Not for us-");
		}
	}

}

void RKPClass::DisplayScreen( byte* msgbuf, int msgbufLen )
{
	byte bufix=0;
	//Force Screen clear each time
	//for(int m=0;m<DISP_BUF_LEN;m++)
	//	dispBuffer[m]='!';

	//Checksum so can see if changes and need update client
	static int previousCS =-1;

	int ixMsgbuf=2;//skip 2 header bytes
	msgbufLen-=2; //remove checksum and zero terminator

	while(ixMsgbuf<msgbufLen)
	{
		byte rx = msgbuf[ixMsgbuf++];
		if (rx>=0 && rx < 0x0f)
		{//Switch language - not implemented - ignore this command
		}
		else if (rx == 0x13)
		{//means will be no more text (but can be commands) - so do nothing?
		}
		else if (rx == 0x1b)
		{// 0x1b=Next character is a Non-Eng characters - not implemented - ignore this command
		}
		else if (rx>= 0x20 && rx <= 0x7F)
		{//Normal ASCII
			if (bufix < DISP_BUF_LEN)
				dispBuffer[bufix++]=(char)rx;
		}
		else if (rx>= 0x80 && rx <= 0x8F)
		{//Date in encoded format
			int b0=rx;
			int b1=msgbuf[ixMsgbuf++];
			int b2=msgbuf[ixMsgbuf++];
			int b3=msgbuf[ixMsgbuf++];

			byte nMonth= (b0 & 0x0f)-1;
			byte day = (b1 & (128+64+32))>> 5;
			byte date = (b1 & (31));
			byte h1=(b2 & 0xf0)>>4; if(h1==0x0A) h1=0;
			byte h2=(b2 & 0x0f); if(h2==0x0A) h2=0;
			byte m1=(b3 & 0xf0)>>4; if(m1==0x0A) m1=0;
			byte m2=(b3 & 0x0f); if(m2==0x0A) m2=0;

			memcpy_P(dispBuffer+0,alldays+(day*3),3);
			dispBuffer[3]=' ';
			dispBuffer[4]=('0'+(int)(date/10));
			dispBuffer[5]=('0'+(date%10));
			dispBuffer[6]=' ';

			memcpy_P(dispBuffer+7,allmonths+(nMonth*3),3);
			dispBuffer[10]=' ';
			dispBuffer[11]='0'+h1;
			dispBuffer[12]='0'+h2;
			//if (dateFlash)
			dispBuffer[13]= ':';
			//else
			//	buffer[13]= F(' ');
			//dateFlash=!dateFlash;
			//buffer[13]= ((millis()/500)&1) ==0? ':':' ';
			dispBuffer[14]='0'+m1;
			dispBuffer[15]='0'+m2;
			bufix=0;
		}
		else if (rx == 0x90)
		{//CLS
			bufix=0;
			for(int m=0;m<DISP_BUF_LEN;m++)
				dispBuffer[m]=' ';
		}
		else if (rx == 0x91)
		{//HOME Maybe??
			bufix=0;
		}
		else if (rx >= 0xA0 && rx <= 0xAf)
		{//MOVE cursor to position x
			bufix = (rx & 0x0f); //-1 gives us 2 *'s  but without -1 we go off screen at Login ***
		}
		else if (rx >= 0xB0 && rx <= 0xBF)
		{//{BLINK_N}"	Bxh Blink x chars starting at current cursor position
			//int nChars = (rx & 0x0f)-1;
			//if (dateFlash)
			//	buffer[i]= ':';
			//else
			//	buffer[i]= ' ';
			//dateFlash=!dateFlash;
		}
		else if (rx >= 0xC0 && rx <= 0xCf)
		{// Set position to x and clear all chars to right
			int i = (rx & 0x0f);
			if (i < DISP_BUF_LEN)
				bufix = i;
			for(int n=bufix;n<DISP_BUF_LEN;n++)
				dispBuffer[bufix++]=' ';
		}
		else if (rx>= 0xE0 && rx <= 0xFF)
		{// Special Characters Arrows and foreign chars
			int i = (rx & 0x0f);

			char c=0;
			if (i==4)	c= '*';
			else if (i==5)	c= '#';
			else if (i==7)	c= '>';

			if (c>0)
				if (bufix < DISP_BUF_LEN)
					dispBuffer[bufix++]=c;
		}
		else
		{//unknown command
			Log(F("{"));LogHex(rx);Log(F("}"));
		}

		//TODO: there are quite a few codes in Engineer menu to deal with flashing cursors and characters - cannot do easily in html
		//might look at later.
	}

	dispBuffer[16]='|';	//this may overwrite a char sometimes...
	dispBuffer[17]=(RKPClass::mbIsPanelAlarm)?'A':' ';
	dispBuffer[18]=(RKPClass::mbIsPanelWarning)?'W':' ';

	int cs=0;
	for(int n=0;n<DISP_BUF_LEN;n++)
		cs+=(char)dispBuffer[n];

	if (cs != previousCS)
	{
		////Display as hex
		LogHex(msgbuf, msgbufLen);
		////Display as characters
		for(int n=0;n<DISP_BUF_LEN;n++)	Log((char)dispBuffer[n]); LogLn(".");

		previousCS=cs;
		SendDisplayToBrowser();
	}

}

char RKPClass::NextKeyPress()
{
	if (RKPClass::msKeyToSend.length() == 0)
		return -1; //no keys
	char next = RKPClass::msKeyToSend[0];
	return next;
}

char RKPClass::PopKeyPress()
{
	char next = NextKeyPress();
	if (next!=0xFF)//RKPClass::mnKeyToSend=0xff;
		RKPClass::msKeyToSend = RKPClass::msKeyToSend.substring(1);
	return next;
}

void RKPClass::PushKey( char key )
{
	if (RKPClass::msKeyToSend.length()<10)
		RKPClass::msKeyToSend += key;
}

void RKPClass::SendToPanel( int id, bool bAck )
{
	//Log(F("Send: "));
	#define H1_LIDTAMP 1
	#define H1_BASE 2 //always set
	#define H1_CD3008 0 //keypad with buzzer
	#define H1_CD9038 4 //Keypad with 4 zones
	#define H1_CD9041 8 //Glassbreak Expander with no keypad - works on cd34 but not cd72
	static byte nH2Previous=-1;

	byte h1 = H1_BASE + H1_CD3008 + (id<<4); //kKKK (Capital K is an Extender enabled keypad)
	byte h2 = 0;

	if (bAck)
	//if (true)	//TODO: put back
	{
		//LogLn("Ack");
		char nKeyToSend = PopKeyPress();
		if (nKeyToSend!=0xff)
		{
			//nKeyToSend &= 0x20; //tolower
			int nNumb=0x0e;
			if (nKeyToSend >='1' && nKeyToSend <= '9')
			nNumb = (nKeyToSend-'0');
			else if (nKeyToSend == '0')	nNumb = 0x0a;
			else if (nKeyToSend == 'f'||nKeyToSend == '*')	nNumb = 0x0b;	//UP  (* for IPhone)
			else if (nKeyToSend == 'v'||nKeyToSend == '#')	nNumb = 0x0c;	//DOWN (# for IPhone)
			else if (nKeyToSend == 'p')	nNumb = 0x0d;	//UP + DOWN (Panic)
			else if (nKeyToSend == 'x'||nKeyToSend == ';'||nKeyToSend == 'n'||nKeyToSend == 'N')	nNumb = 0x0e;	//UP + 0 or X(reject) (WAIT on IPhone numpad)
			else if (nKeyToSend == 13||nKeyToSend == '+'||nKeyToSend == 'y'||nKeyToSend == 'Y')	nNumb = 0x0f;	//UP + 0 or *(accept) (+ on IPhone numpad)

			Log(F("Sent: ")); LogHex(nNumb);LogLn(F("."));

			h2 = nNumb<<4;
		}
		nH2Previous = h2;
	}
	else
	{
		Log(F("Resend: ")); LogHex(nH2Previous>>4);LogLn(F("!"));
		h2 = nH2Previous;
	}

	byte h3=0; byte h4=(byte)(h1+h2+h3);

	Serial1.write((byte)(h1));
	Serial1.write((byte)(h2));
	Serial1.write((byte)(h3));
	Serial1.write((byte)(h4));
	//LogHex(h1); LogHex(h2); LogHex(h3); LogHex(h4); LogLn(".");
}

void RKPClass::Init()
{
	Serial1.begin(nSerialBaudKP_RX, SERIAL_8N2);	//RKP Comms
}

void RKPClass::SendDisplayToBrowser()
{
	WebSocket::WebSocket_send((char*)dispBuffer,DISP_BUF_LEN);
}
<commit_msg>Update RKP.cpp<commit_after>/*
* SMTP.cpp
*
* Created: 4/5/2014 7:45:36 PM
* Author: Ambrose
*/
#include "limits.h"
#include "SMTP.h"
#include "Log.h"
#include "Config.h"

//for IPAddresss
//#include "w5100.h"
//#include "Ethernet.h"
#include "IPAddress.h"

//Workaround if needed for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734
#ifdef PROGMEM
#undef PROGMEM
#define PROGMEM __attribute__((section(".progmem.data")))
#endif

int SMTP::nEmailStage=-1;
EthernetClient SMTP::client;
//long SMTP::mDelay=-1;
unsigned long SMTP::mTimeout = LONG_MAX;
boolean SMTP::bWaitForResponse = false;

IPAddress SMTP::mSMTPServerIP;

// protected constructor
SMTP::SMTP()
{
}

void SMTP::Init( IPAddress smptServerIP )
{
	mSMTPServerIP = smptServerIP;
}

void SMTP::QueueEmail()
{
#ifdef sEmail
	LogLn(F("Sending Email."));
	nEmailStage=0;
#else
	LogLn(F("Email Disabled."));
#endif
}

void SMTP::SendEmailProcess()
{
#ifdef sEmail

	if(millis() > SMTP::mTimeout)
	{//we have taken longer than 10 secs to send our email - reset and resend
		LogLn(F("Email Reset."));
		mTimeout=LONG_MAX; //dont reset again
		nEmailStage=0;
		client.println(F("QUIT"));
	}

	if (SMTP::bWaitForResponse)
	{//We are awaiting a response
		if (SMTP::WaitForReplyLine())
			return;	//not yet - try next time
	}

	if (SMTP::nEmailStage==0)
	{//Connect
		SMTP::mTimeout = millis() + 10000; //10 sec to send...or will resend

		LogLn(F("--Start SendMail--"));

		Log(F("Cleaning buffers..{"));while(client.available()) Log(client.read()); LogLn("}");

		LogLn(F("Connecting..."));
		if(!client.connect(SMTP::mSMTPServerIP,25) /*|| !client.connected()*/)
		{
			LogLn(F("connection failed"));
			return;
		}
		LogLn(F("Confirmed Connected"));
		SMTP::bWaitForResponse=true;
		return;
	}

	if (nEmailStage==1)
	{
		LogLn(F("Sending helo"));
		SMTP::client.println("helo 1.2.3.4");
		SMTP::bWaitForResponse=true;
		return;
	}

	if (nEmailStage==2) //set to 20 to test retry
	{
		LogLn(F("From.."));
		SMTP::client.println("MAIL From: "sEmail);	//(sender)
		SMTP::bWaitForResponse=true;
		return;
	}

	if (nEmailStage==3)
	{//recipient address
		LogLn(F("To.."));
		SMTP::client.println("RCPT To: "sEmail);		//client.println("RCPT To: <ambrosec@gmail.com>");
		SMTP::bWaitForResponse=true;
		return;
	}

	if (nEmailStage==4)
	{
		LogLn(F("Sending DATA"));
		SMTP::client.println("DATA");
		SMTP::bWaitForResponse=true;
		return;
	}

	if (nEmailStage==5)
	{
		LogLn(F("Sending message"));
		SMTP::client.println("To: "sEmail);
		SMTP::client.println("From: TheHouse <" sEmail ">");
		SMTP::client.println("Subject: House Calling. Alarm.\r\n");

		{
			SMTP::client.println("The House Alarm has gone off\r\n");
			SMTP::client.println("EndLog");
		}

		SMTP::client.println(".");
		SMTP::bWaitForResponse=true;
		return;
	}

	if (nEmailStage==6)
	{
		SMTP::mTimeout = LONG_MAX; //cancel any timeout timer
		LogLn(F("Sending QUIT"));
		SMTP::client.println("QUIT");
		SMTP::bWaitForResponse=true;
		return;
	}

	if (nEmailStage==7)
	{
		SMTP::client.stop();
		SMTP::nEmailStage=-1;
		LogLn(F("disconnected"));
		return;
	}

#endif
}

//return False and inc stage if we get a response - true if we are waiting
boolean SMTP::WaitForReplyLine()
{
	if(client.connected() && client.available())
	{
		byte thisByte = client.read();
		//Log(thisByte);
		if (thisByte == '\n')
		{
			SMTP::nEmailStage++;
			SMTP::bWaitForResponse = false;
			return false;	//Good!
		}
	}
	return true;
}
<|endoftext|>


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

#if qPlatform_Windows
#include <windows.h>
#endif

#if qHasLibrary_ODBC
#include <sql.h>
#include <sqlext.h>
#endif

#include "../../Characters/CString/Utilities.h"
#include "../../Characters/Format.h"

#include "../Exception.h"

#include "ODBC.h"

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::Database;
using namespace Stroika::Foundation::Database::SQL::ODBC;
using namespace Debug;

#if qHasLibrary_ODBC

namespace {
    void ThrowIfSQLError_ (SQLRETURN r, const String& message)
    {
        if ((r != SQL_SUCCESS) and (r != SQL_SUCCESS_WITH_INFO)) [[UNLIKELY_ATTR]] {
            Execution::Throw (Exception{message});
        }
    }
}

/*
 ********************************************************************************
 *************************** ODBC::Connection::Rep_ *****************************
 ********************************************************************************
 */
struct Connection::Rep_ final : IRep {
    SQLHDBC      fConnectionHandle{nullptr};
    SQLHENV      fODBCEnvironmentHandle{nullptr};

    Rep_ (const Options& options)
    {
        TraceContextBumper ctx{"SQLite::Connection::Rep_::Rep_"};
        if (not options.fDSN) {
            Execution::Throw (Exception{L"DSN Required"sv});
        }
        try {
            ThrowIfSQLError_ (::SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &fODBCEnvironmentHandle), L"Error AllocHandle"sv);
            ThrowIfSQLError_ (::SQLSetEnvAttr (fODBCEnvironmentHandle, SQL_ATTR_ODBC_VERSION, reinterpret_cast<void*> (SQL_OV_ODBC3), 0), L"Error SetEnv"sv);
            ThrowIfSQLError_ (::SQLAllocHandle (SQL_HANDLE_DBC, fODBCEnvironmentHandle, &fConnectionHandle), L"Error AllocHDB"sv);

            ::SQLSetConnectAttr (fConnectionHandle, SQL_LOGIN_TIMEOUT, reinterpret_cast<SQLPOINTER*> (5), 0);
            {
                // See the docs on SQLConnect - the error handling needs to be VASTLY more complex. We need some mechanism to return
                // warnings to the caller (to be ignored or whatever).
                // And ONLY thorw exceptions on ERROR!
                SQLRETURN return_value = ::SQLConnect (
                    fConnectionHandle,
                    reinterpret_cast<SQLTCHAR*> (const_cast<TCHAR*> (options.fDSN->AsSDKString ().c_str ())),
                    SQL_NTS,
                    nullptr, SQL_NTS,
                    nullptr, SQL_NTS);
                if ((return_value != SQL_SUCCESS) && (return_value != SQL_SUCCESS_WITH_INFO)) {
                    // This logic for producing an error message completely sucks and is largely incorrect
                    wstring     errorString = L"Error SQLConnect: ";
                    SQLTCHAR    sqlState[6];
                    SQLINTEGER  errorCode;
                    SQLSMALLINT messageLength;
                    SQLTCHAR    errorMessage[1024];
                    DISABLE_COMPILER_MSC_WARNING_START (4267)
                    long errValue = ::SQLGetDiagRec (
                        SQL_HANDLE_DBC, fConnectionHandle, 1,
                        reinterpret_cast<SQLTCHAR*> (sqlState), &errorCode,
                        reinterpret_cast<SQLTCHAR*> (errorMessage),
                        NEltsOf (errorMessage), &messageLength);
                    DISABLE_COMPILER_MSC_WARNING_END (4267)
                    if (errValue == SQL_SUCCESS) {
                        // TCHAR isn't the same SQLTCHAR for 'ANSI' because for some crazy reason, they
                        // used unsigned char for SQLCHAR!
                        errorString += SDKString2Wide (reinterpret_cast<TCHAR*> (errorMessage));
                    }
                    else if (errValue == SQL_SUCCESS_WITH_INFO) {
                        errorString = L"Error message too long at";
                    }
                    else if (errValue == SQL_ERROR) {
                        errorString += L"RecNumber was negative or 0 or BufferLength was less tha 0";
                    }
                    else if (errValue == SQL_NO_DATA) {
                        errorString += L"SQL no data";
                    }
                    Execution::Throw (Exception{errorString});
                }
            }
        }
        catch (...) {
            if (fConnectionHandle != nullptr) {
                ::SQLFreeHandle (SQL_HANDLE_DBC, fConnectionHandle);
                fConnectionHandle = nullptr;
            }
            if (fODBCEnvironmentHandle != nullptr) {
                ::SQLFreeHandle (SQL_HANDLE_ENV, fODBCEnvironmentHandle);
                fODBCEnvironmentHandle = nullptr;
            }
        }
    }
    ~Rep_ ()
    {
        if (fConnectionHandle != nullptr) {
            ::SQLFreeHandle (SQL_HANDLE_DBC, fConnectionHandle);
        }
        if (fODBCEnvironmentHandle != nullptr) {
            ::SQLFreeHandle (SQL_HANDLE_ENV, fODBCEnvironmentHandle);
        }
    }
    virtual shared_ptr<const EngineProperties> GetEngineProperties () const override
    {
        AssertNotImplemented ();
        struct MyEngineProperties_ final : EngineProperties {
            virtual String GetEngineName () const override
            {
                return L"ODBC"sv;   // must indirect to connection to get more info (from dns at least? not clear)
            }
            virtual bool SupportsNestedTransactions () const override
            {
                return false;
            }
        };
        return make_shared<const MyEngineProperties_> ();   // dynamic info based on connection/dsn
    }
    virtual SQL::Statement mkStatement (const String& sql) override
    {
        Connection::Ptr conn = Connection::Ptr{dynamic_pointer_cast<Connection::IRep> (shared_from_this ())};
        return ODBC::Statement{conn, sql};
    }
    virtual SQL::Transaction mkTransaction () override
    {
        Connection::Ptr conn = Connection::Ptr{dynamic_pointer_cast<Connection::IRep> (shared_from_this ())};
        return ODBC::Transaction{conn};
    }
    virtual void Exec (const String& /*sql*/) override
    {
        AssertNotImplemented ();
    }
};

/*
 ********************************************************************************
 ************************* SQL::ODBC::Connection::Ptr ***************************
 ********************************************************************************
 */
SQL::ODBC::Connection::Ptr::Ptr (const shared_ptr<IRep>& src)
    : inherited{src}
{
}

/*
 ********************************************************************************
 ************************** SQL::ODBC::Connection *******************************
 ********************************************************************************
 */
auto SQL::ODBC::Connection::New (const Options& options) -> Ptr
{
    auto tmp = make_shared<Rep_> (options);
    Ptr  result{tmp};
    return result;
}

/*
 ********************************************************************************
 ******************************* SQLite::Statement ******************************
 ********************************************************************************
 */
struct Statement::MyRep_ : IRep {
    MyRep_ (const Connection::Ptr& db, const String& query)
        : fConnectionPtr_{db}
    {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
        TraceContextBumper ctx{"SQLite::Statement::MyRep_::CTOR", Stroika_Foundation_Debug_OptionalizeTraceArgs (L "db=%p, query='%s'", db, query.c_str ())};
#endif
        RequireNotNull (db);
#if qDebug
        _fSharedContext = fConnectionPtr_.GetSharedContext ();
#endif
        string                                                    queryUTF8 = query.AsUTF8 ();
        lock_guard<const Debug::AssertExternallySynchronizedLock> critSec{*this};
        AssertNotImplemented ();
    }
    ~MyRep_ ()
    {
        lock_guard<const Debug::AssertExternallySynchronizedLock> critSec{*this};
    }
    virtual String GetSQL (WhichSQLFlag whichSQL) const override
    {
        shared_lock<const Debug::AssertExternallySynchronizedLock> critSec{*this};
        AssertNotImplemented ();
        return String{};
    }
    virtual Sequence<ColumnDescription> GetColumns () const override
    {
        shared_lock<const Debug::AssertExternallySynchronizedLock> critSec{*this};
        AssertNotImplemented ();
        return Sequence<ColumnDescription>{};
    };
    virtual Sequence<ParameterDescription> GetParameters () const override
    {
        shared_lock<const Debug::AssertExternallySynchronizedLock> critSec{*this};
        return fParameters_;
    };
    virtual void Bind (unsigned int parameterIndex, const VariantValue& v) override
    {
        lock_guard<const Debug::AssertExternallySynchronizedLock> critSec{*this};
        fParameters_[parameterIndex].fValue = v;
        AssertNotImplemented ();
    }
    virtual void Bind (const String& parameterName, const VariantValue& v) override
    {
        Require (not parameterName.empty ());
        lock_guard<const Debug::AssertExternallySynchronizedLock> critSec{*this};
        AssertNotImplemented ();
        String pn = parameterName;
        if (pn[0] != ':') {
            pn = L":" + pn;
        }
        for (unsigned int i = 0; i < fParameters_.length (); ++i) {
            if (fParameters_[i].fName == pn) {
                Bind (i, v);
                return;
            }
        }
        DbgTrace (L"Statement::Bind: Parameter '%s' not found in list %s", parameterName.c_str (), Characters::ToString (fParameters_.Select<String> ([] (const auto& i) { return i.fName; })).c_str ());
        RequireNotReached (); // invalid paramter name provided
    }
    virtual void Reset () override
    {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
        TraceContextBumper ctx{"SQLite::Statement::MyRep_::Statement::Reset"};
#endif
        lock_guard<const Debug::AssertExternallySynchronizedLock> critSec{*this};
        AssertNotImplemented ();
    }
    virtual optional<Row> GetNextRow () override
    {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
        TraceContextBumper ctx{"SQLite::Statement::MyRep_::Statement::GetNextRow"};
#endif
        lock_guard<const Debug::AssertExternallySynchronizedLock> critSec{*this};
        AssertNotImplemented ();
        return nullopt;
    }

    Connection::Ptr                fConnectionPtr_;
    vector<ColumnDescription>      fColumns_;
    Sequence<ParameterDescription> fParameters_;
};

Statement::Statement (const Connection::Ptr& db, const String& query)
    : inherited{make_unique<MyRep_> (db, query)}
{
}

/*
 ********************************************************************************
 ******************************* SQLite::Transaction ****************************
 ********************************************************************************
 */
struct Transaction::MyRep_ : public SQL::Transaction::IRep {
    MyRep_ (const Connection::Ptr& db)
        : fConnectionPtr_{db}
    {
        AssertNotImplemented ();
    }
    virtual void Commit () override
    {
        Require (not fCompleted_);
        fCompleted_ = true;
        fConnectionPtr_->Exec (L"COMMIT;"sv);
    }
    virtual void Rollback () override
    {
        Require (not fCompleted_);
        fCompleted_ = true;
        fConnectionPtr_->Exec (L"ROLLBACK;"sv);
    }
    virtual Disposition GetDisposition () const override
    {
        // @todo record more info so we can report finer grained status ; try/catch in rollback/commit and dbgtraces
        return fCompleted_ ? Disposition::eCompleted : Disposition::eNone;
    }
    Connection::Ptr fConnectionPtr_;
    bool            fCompleted_{false};
};
Transaction::Transaction (const Connection::Ptr& db)
    : inherited{make_unique<MyRep_> (db)}
{
}

/*
 ********************************************************************************
 ******************************** LEGACY-DEPRECATED *****************************
 ********************************************************************************
 */
class Database::SQL::ODBC::DBConnection::Rep {
public:
    SQLHDBC      fConnectionHandle{nullptr};
    SQLHENV      fODBCEnvironmentHandle{nullptr};
    unsigned int fNestedTransactionCount{0};

public:
    Rep (const wstring& dsn)
    {
        try {
            ThrowIfSQLError_ (::SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &fODBCEnvironmentHandle), L"Error AllocHandle"sv);
            ThrowIfSQLError_ (::SQLSetEnvAttr (fODBCEnvironmentHandle, SQL_ATTR_ODBC_VERSION, reinterpret_cast<void*> (SQL_OV_ODBC3), 0), L"Error SetEnv"sv);
            ThrowIfSQLError_ (::SQLAllocHandle (SQL_HANDLE_DBC, fODBCEnvironmentHandle, &fConnectionHandle), L"Error AllocHDB"sv);

            ::SQLSetConnectAttr (fConnectionHandle, SQL_LOGIN_TIMEOUT, reinterpret_cast<SQLPOINTER*> (5), 0);
            {
                // See the docs on SQLConnect - the error handling needs to be VASTLY more complex. We need some mechanism to return
                // warnings to the caller (to be ignored or whatever).
                // And ONLY thorw exceptions on ERROR!
                SQLRETURN return_value = ::SQLConnect (
                    fConnectionHandle,
                    reinterpret_cast<SQLTCHAR*> (const_cast<TCHAR*> (ToSDKString (dsn).c_str ())),
                    SQL_NTS,
                    nullptr, SQL_NTS,
                    nullptr, SQL_NTS);
                if ((return_value != SQL_SUCCESS) && (return_value != SQL_SUCCESS_WITH_INFO)) {
                    // This logic for producing an error message completely sucks and is largely incorrect
                    wstring     errorString = L"Error SQLConnect: ";
                    SQLTCHAR    sqlState[6];
                    SQLINTEGER  errorCode;
                    SQLSMALLINT messageLength;
                    SQLTCHAR    errorMessage[1024];
                    DISABLE_COMPILER_MSC_WARNING_START (4267)
                    long errValue = ::SQLGetDiagRec (
                        SQL_HANDLE_DBC, fConnectionHandle, 1,
                        reinterpret_cast<SQLTCHAR*> (sqlState), &errorCode,
                        reinterpret_cast<SQLTCHAR*> (errorMessage),
                        NEltsOf (errorMessage), &messageLength);
                    DISABLE_COMPILER_MSC_WARNING_END (4267)
                    if (errValue == SQL_SUCCESS) {
                        // TCHAR isn't the same SQLTCHAR for 'ANSI' because for some crazy reason, they
                        // used unsigned char for SQLCHAR!
                        errorString += SDKString2Wide (reinterpret_cast<TCHAR*> (errorMessage));
                    }
                    else if (errValue == SQL_SUCCESS_WITH_INFO) {
                        errorString = L"Error message too long at";
                    }
                    else if (errValue == SQL_ERROR) {
                        errorString += L"RecNumber was negative or 0 or BufferLength was less tha 0";
                    }
                    else if (errValue == SQL_NO_DATA) {
                        errorString += L"SQL no data";
                    }
                    Execution::Throw (Exception{errorString});
                }
            }
        }
        catch (...) {
            if (fConnectionHandle != nullptr) {
                ::SQLFreeHandle (SQL_HANDLE_DBC, fConnectionHandle);
                fConnectionHandle = nullptr;
            }
            if (fODBCEnvironmentHandle != nullptr) {
                ::SQLFreeHandle (SQL_HANDLE_ENV, fODBCEnvironmentHandle);
                fODBCEnvironmentHandle = nullptr;
            }
        }
    }
    virtual ~Rep ()
    {
        if (fConnectionHandle != nullptr) {
            ::SQLFreeHandle (SQL_HANDLE_DBC, fConnectionHandle);
        }
        if (fODBCEnvironmentHandle != nullptr) {
            ::SQLFreeHandle (SQL_HANDLE_ENV, fODBCEnvironmentHandle);
        }
    }
};
Database::SQL::ODBC::DBConnection::DBConnection (const wstring& dsn)
    : fRep{make_shared<Rep> (dsn)}
{
}

unsigned int Database::SQL::ODBC::DBConnection::GetNestedTransactionCount () const
{
    return fRep->fNestedTransactionCount;
}
#endif
<commit_msg>cosemtic<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2021.  All rights reserved
 */
#include "../../StroikaPreComp.h"

#if qPlatform_Windows
#include <windows.h>
#endif

#if qHasLibrary_ODBC
#include <sql.h>
#include <sqlext.h>
#endif

#include "../../Characters/CString/Utilities.h"
#include "../../Characters/Format.h"

#include "../Exception.h"

#include "ODBC.h"

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::Database;
using namespace Stroika::Foundation::Database::SQL::ODBC;
using namespace Debug;

#if qHasLibrary_ODBC

namespace {
    void ThrowIfSQLError_ (SQLRETURN r, const String& message)
    {
        if ((r != SQL_SUCCESS) and (r != SQL_SUCCESS_WITH_INFO)) [[UNLIKELY_ATTR]] {
            Execution::Throw (Exception{message});
        }
    }
}

/*
 ********************************************************************************
 *************************** ODBC::Connection::Rep_ *****************************
 ********************************************************************************
 */
struct Connection::Rep_ final : IRep {
    SQLHDBC fConnectionHandle{nullptr};
    SQLHENV fODBCEnvironmentHandle{nullptr};

    Rep_ (const Options& options)
    {
        TraceContextBumper ctx{"SQLite::Connection::Rep_::Rep_"};
        if (not options.fDSN) {
            Execution::Throw (Exception{L"DSN Required"sv});
        }
        try {
            ThrowIfSQLError_ (::SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &fODBCEnvironmentHandle), L"Error AllocHandle"sv);
            ThrowIfSQLError_ (::SQLSetEnvAttr (fODBCEnvironmentHandle, SQL_ATTR_ODBC_VERSION, reinterpret_cast<void*> (SQL_OV_ODBC3), 0), L"Error SetEnv"sv);
            ThrowIfSQLError_ (::SQLAllocHandle (SQL_HANDLE_DBC, fODBCEnvironmentHandle, &fConnectionHandle), L"Error AllocHDB"sv);

            ::SQLSetConnectAttr (fConnectionHandle, SQL_LOGIN_TIMEOUT, reinterpret_cast<SQLPOINTER*> (5), 0);
            {
                // See the docs on SQLConnect - the error handling needs to be VASTLY more complex. We need some mechanism to return
                // warnings to the caller (to be ignored or whatever).
                // And ONLY thorw exceptions on ERROR!
                SQLRETURN return_value = ::SQLConnect (
                    fConnectionHandle,
                    reinterpret_cast<SQLTCHAR*> (const_cast<TCHAR*> (options.fDSN->AsSDKString ().c_str ())),
                    SQL_NTS,
                    nullptr, SQL_NTS,
                    nullptr, SQL_NTS);
                if ((return_value != SQL_SUCCESS) && (return_value != SQL_SUCCESS_WITH_INFO)) {
                    // This logic for producing an error message completely sucks and is largely incorrect
                    wstring     errorString = L"Error SQLConnect: ";
                    SQLTCHAR    sqlState[6];
                    SQLINTEGER  errorCode;
                    SQLSMALLINT messageLength;
                    SQLTCHAR    errorMessage[1024];
                    DISABLE_COMPILER_MSC_WARNING_START (4267)
                    long errValue = ::SQLGetDiagRec (
                        SQL_HANDLE_DBC, fConnectionHandle, 1,
                        reinterpret_cast<SQLTCHAR*> (sqlState), &errorCode,
                        reinterpret_cast<SQLTCHAR*> (errorMessage),
                        NEltsOf (errorMessage), &messageLength);
                    DISABLE_COMPILER_MSC_WARNING_END (4267)
                    if (errValue == SQL_SUCCESS) {
                        // TCHAR isn't the same SQLTCHAR for 'ANSI' because for some crazy reason, they
                        // used unsigned char for SQLCHAR!
                        errorString += SDKString2Wide (reinterpret_cast<TCHAR*> (errorMessage));
                    }
                    else if (errValue == SQL_SUCCESS_WITH_INFO) {
                        errorString = L"Error message too long at";
                    }
                    else if (errValue == SQL_ERROR) {
                        errorString += L"RecNumber was negative or 0 or BufferLength was less tha 0";
                    }
                    else if (errValue == SQL_NO_DATA) {
                        errorString += L"SQL no data";
                    }
                    Execution::Throw (Exception{errorString});
                }
            }
        }
        catch (...) {
            if (fConnectionHandle != nullptr) {
                ::SQLFreeHandle (SQL_HANDLE_DBC, fConnectionHandle);
                fConnectionHandle = nullptr;
            }
            if (fODBCEnvironmentHandle != nullptr) {
                ::SQLFreeHandle (SQL_HANDLE_ENV, fODBCEnvironmentHandle);
                fODBCEnvironmentHandle = nullptr;
            }
        }
    }
    ~Rep_ ()
    {
        if (fConnectionHandle != nullptr) {
            ::SQLFreeHandle (SQL_HANDLE_DBC, fConnectionHandle);
        }
        if (fODBCEnvironmentHandle != nullptr) {
            ::SQLFreeHandle (SQL_HANDLE_ENV, fODBCEnvironmentHandle);
        }
    }
    virtual shared_ptr<const EngineProperties> GetEngineProperties () const override
    {
        AssertNotImplemented ();
        struct MyEngineProperties_ final : EngineProperties {
            virtual String GetEngineName () const override
            {
                return L"ODBC"sv; // must indirect to connection to get more info (from dns at least? not clear)
            }
            virtual bool SupportsNestedTransactions () const override
            {
                return false;
            }
        };
        return make_shared<const MyEngineProperties_> (); // dynamic info based on connection/dsn
    }
    virtual SQL::Statement mkStatement (const String& sql) override
    {
        Connection::Ptr conn = Connection::Ptr{dynamic_pointer_cast<Connection::IRep> (shared_from_this ())};
        return ODBC::Statement{conn, sql};
    }
    virtual SQL::Transaction mkTransaction () override
    {
        Connection::Ptr conn = Connection::Ptr{dynamic_pointer_cast<Connection::IRep> (shared_from_this ())};
        return ODBC::Transaction{conn};
    }
    virtual void Exec (const String& /*sql*/) override
    {
        AssertNotImplemented ();
    }
};

/*
 ********************************************************************************
 ************************* SQL::ODBC::Connection::Ptr ***************************
 ********************************************************************************
 */
SQL::ODBC::Connection::Ptr::Ptr (const shared_ptr<IRep>& src)
    : inherited{src}
{
}

/*
 ********************************************************************************
 ************************** SQL::ODBC::Connection *******************************
 ********************************************************************************
 */
auto SQL::ODBC::Connection::New (const Options& options) -> Ptr
{
    auto tmp = make_shared<Rep_> (options);
    Ptr  result{tmp};
    return result;
}

/*
 ********************************************************************************
 ******************************* SQLite::Statement ******************************
 ********************************************************************************
 */
struct Statement::MyRep_ : IRep {
    MyRep_ (const Connection::Ptr& db, const String& query)
        : fConnectionPtr_{db}
    {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
        TraceContextBumper ctx{"SQLite::Statement::MyRep_::CTOR", Stroika_Foundation_Debug_OptionalizeTraceArgs (L "db=%p, query='%s'", db, query.c_str ())};
#endif
        RequireNotNull (db);
#if qDebug
        _fSharedContext = fConnectionPtr_.GetSharedContext ();
#endif
        string                                                    queryUTF8 = query.AsUTF8 ();
        lock_guard<const Debug::AssertExternallySynchronizedLock> critSec{*this};
        AssertNotImplemented ();
    }
    ~MyRep_ ()
    {
        lock_guard<const Debug::AssertExternallySynchronizedLock> critSec{*this};
    }
    virtual String GetSQL (WhichSQLFlag whichSQL) const override
    {
        shared_lock<const Debug::AssertExternallySynchronizedLock> critSec{*this};
        AssertNotImplemented ();
        return String{};
    }
    virtual Sequence<ColumnDescription> GetColumns () const override
    {
        shared_lock<const Debug::AssertExternallySynchronizedLock> critSec{*this};
        AssertNotImplemented ();
        return Sequence<ColumnDescription>{};
    };
    virtual Sequence<ParameterDescription> GetParameters () const override
    {
        shared_lock<const Debug::AssertExternallySynchronizedLock> critSec{*this};
        return fParameters_;
    };
    virtual void Bind (unsigned int parameterIndex, const VariantValue& v) override
    {
        lock_guard<const Debug::AssertExternallySynchronizedLock> critSec{*this};
        fParameters_[parameterIndex].fValue = v;
        AssertNotImplemented ();
    }
    virtual void Bind (const String& parameterName, const VariantValue& v) override
    {
        Require (not parameterName.empty ());
        lock_guard<const Debug::AssertExternallySynchronizedLock> critSec{*this};
        AssertNotImplemented ();
        String pn = parameterName;
        if (pn[0] != ':') {
            pn = L":" + pn;
        }
        for (unsigned int i = 0; i < fParameters_.length (); ++i) {
            if (fParameters_[i].fName == pn) {
                Bind (i, v);
                return;
            }
        }
        DbgTrace (L"Statement::Bind: Parameter '%s' not found in list %s", parameterName.c_str (), Characters::ToString (fParameters_.Select<String> ([] (const auto& i) { return i.fName; })).c_str ());
        RequireNotReached (); // invalid paramter name provided
    }
    virtual void Reset () override
    {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
        TraceContextBumper ctx{"SQLite::Statement::MyRep_::Statement::Reset"};
#endif
        lock_guard<const Debug::AssertExternallySynchronizedLock> critSec{*this};
        AssertNotImplemented ();
    }
    virtual optional<Row> GetNextRow () override
    {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
        TraceContextBumper ctx{"SQLite::Statement::MyRep_::Statement::GetNextRow"};
#endif
        lock_guard<const Debug::AssertExternallySynchronizedLock> critSec{*this};
        AssertNotImplemented ();
        return nullopt;
    }

    Connection::Ptr                fConnectionPtr_;
    vector<ColumnDescription>      fColumns_;
    Sequence<ParameterDescription> fParameters_;
};

Statement::Statement (const Connection::Ptr& db, const String& query)
    : inherited{make_unique<MyRep_> (db, query)}
{
}

/*
 ********************************************************************************
 ******************************* SQLite::Transaction ****************************
 ********************************************************************************
 */
struct Transaction::MyRep_ : public SQL::Transaction::IRep {
    MyRep_ (const Connection::Ptr& db)
        : fConnectionPtr_{db}
    {
        AssertNotImplemented ();
    }
    virtual void Commit () override
    {
        Require (not fCompleted_);
        fCompleted_ = true;
        fConnectionPtr_->Exec (L"COMMIT;"sv);
    }
    virtual void Rollback () override
    {
        Require (not fCompleted_);
        fCompleted_ = true;
        fConnectionPtr_->Exec (L"ROLLBACK;"sv);
    }
    virtual Disposition GetDisposition () const override
    {
        // @todo record more info so we can report finer grained status ; try/catch in rollback/commit and dbgtraces
        return fCompleted_ ? Disposition::eCompleted : Disposition::eNone;
    }
    Connection::Ptr fConnectionPtr_;
    bool            fCompleted_{false};
};
Transaction::Transaction (const Connection::Ptr& db)
    : inherited{make_unique<MyRep_> (db)}
{
}

/*
 ********************************************************************************
 ******************************** LEGACY-DEPRECATED *****************************
 ********************************************************************************
 */
class Database::SQL::ODBC::DBConnection::Rep {
public:
    SQLHDBC      fConnectionHandle{nullptr};
    SQLHENV      fODBCEnvironmentHandle{nullptr};
    unsigned int fNestedTransactionCount{0};

public:
    Rep (const wstring& dsn)
    {
        try {
            ThrowIfSQLError_ (::SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &fODBCEnvironmentHandle), L"Error AllocHandle"sv);
            ThrowIfSQLError_ (::SQLSetEnvAttr (fODBCEnvironmentHandle, SQL_ATTR_ODBC_VERSION, reinterpret_cast<void*> (SQL_OV_ODBC3), 0), L"Error SetEnv"sv);
            ThrowIfSQLError_ (::SQLAllocHandle (SQL_HANDLE_DBC, fODBCEnvironmentHandle, &fConnectionHandle), L"Error AllocHDB"sv);

            ::SQLSetConnectAttr (fConnectionHandle, SQL_LOGIN_TIMEOUT, reinterpret_cast<SQLPOINTER*> (5), 0);
            {
                // See the docs on SQLConnect - the error handling needs to be VASTLY more complex. We need some mechanism to return
                // warnings to the caller (to be ignored or whatever).
                // And ONLY thorw exceptions on ERROR!
                SQLRETURN return_value = ::SQLConnect (
                    fConnectionHandle,
                    reinterpret_cast<SQLTCHAR*> (const_cast<TCHAR*> (ToSDKString (dsn).c_str ())),
                    SQL_NTS,
                    nullptr, SQL_NTS,
                    nullptr, SQL_NTS);
                if ((return_value != SQL_SUCCESS) && (return_value != SQL_SUCCESS_WITH_INFO)) {
                    // This logic for producing an error message completely sucks and is largely incorrect
                    wstring     errorString = L"Error SQLConnect: ";
                    SQLTCHAR    sqlState[6];
                    SQLINTEGER  errorCode;
                    SQLSMALLINT messageLength;
                    SQLTCHAR    errorMessage[1024];
                    DISABLE_COMPILER_MSC_WARNING_START (4267)
                    long errValue = ::SQLGetDiagRec (
                        SQL_HANDLE_DBC, fConnectionHandle, 1,
                        reinterpret_cast<SQLTCHAR*> (sqlState), &errorCode,
                        reinterpret_cast<SQLTCHAR*> (errorMessage),
                        NEltsOf (errorMessage), &messageLength);
                    DISABLE_COMPILER_MSC_WARNING_END (4267)
                    if (errValue == SQL_SUCCESS) {
                        // TCHAR isn't the same SQLTCHAR for 'ANSI' because for some crazy reason, they
                        // used unsigned char for SQLCHAR!
                        errorString += SDKString2Wide (reinterpret_cast<TCHAR*> (errorMessage));
                    }
                    else if (errValue == SQL_SUCCESS_WITH_INFO) {
                        errorString = L"Error message too long at";
                    }
                    else if (errValue == SQL_ERROR) {
                        errorString += L"RecNumber was negative or 0 or BufferLength was less tha 0";
                    }
                    else if (errValue == SQL_NO_DATA) {
                        errorString += L"SQL no data";
                    }
                    Execution::Throw (Exception{errorString});
                }
            }
        }
        catch (...) {
            if (fConnectionHandle != nullptr) {
                ::SQLFreeHandle (SQL_HANDLE_DBC, fConnectionHandle);
                fConnectionHandle = nullptr;
            }
            if (fODBCEnvironmentHandle != nullptr) {
                ::SQLFreeHandle (SQL_HANDLE_ENV, fODBCEnvironmentHandle);
                fODBCEnvironmentHandle = nullptr;
            }
        }
    }
    virtual ~Rep ()
    {
        if (fConnectionHandle != nullptr) {
            ::SQLFreeHandle (SQL_HANDLE_DBC, fConnectionHandle);
        }
        if (fODBCEnvironmentHandle != nullptr) {
            ::SQLFreeHandle (SQL_HANDLE_ENV, fODBCEnvironmentHandle);
        }
    }
};
Database::SQL::ODBC::DBConnection::DBConnection (const wstring& dsn)
    : fRep{make_shared<Rep> (dsn)}
{
}

unsigned int Database::SQL::ODBC::DBConnection::GetNestedTransactionCount () const
{
    return fRep->fNestedTransactionCount;
}
#endif
<|endoftext|>