<commit_before>/**
 * Throughout these tests, you might see some notation indicating the state of
 * a placement allocator. General things to know:
 *
 * - R = active region structure in memory
 * - S = inactive region structure in the stack
 * - space = a free piece of memory
 *
 * I denote disjoint regions using brackets ([ and ]). I separate cells of
 * memory using vertical bars (|).
 */

#include "scoped-pass.hpp"
#include <analloc2/free-list>

using namespace analloc;
using namespace ansa;

template <size_t Capacity>
void TestAll();

template <size_t Capacity>
void TestSimplePlace();

template <size_t Capacity>
void TestOffsetPlace();

template <size_t Capacity>
void TestNormalAllocOverflow();

template <size_t Capacity>
void TestDoubleAllocOverflow();

template <size_t Capacity>
size_t ComputePreambleSize();

template <size_t Capacity>
size_t ComputeRegionSize();

int main() {
  TestAll<3>();
  TestAll<4>();
  TestAll<5>();
  TestAll<6>();
  TestAll<0x10>();
  return 0;
}

template <size_t Capacity>
void TestAll() {
  TestSimplePlace<Capacity>();
  TestOffsetPlace<Capacity>();
  TestNormalAllocOverflow<Capacity>();
  TestDoubleAllocOverflow<Capacity>();
}

template <size_t Capacity>
void TestSimplePlace() {
  ScopedPass pass("PlacedFreeList<", Capacity, ">::Place() [simple]");

  typedef PlacedFreeList<Capacity> Pfl;

  size_t regionSize = ComputeRegionSize<Capacity>();
  size_t preamble = ComputePreambleSize<Capacity>();

  // Create a buffer that gives everything the proper alignment.
  void * buffer;
  assert(!posix_memalign(&buffer, regionSize, preamble + regionSize * 3));
  uintptr_t start = (uintptr_t)buffer;
  uintptr_t addr;
  assert(!Pfl::Place(start, preamble));
  assert(!Pfl::Place(start, preamble + regionSize - 1));
  Pfl * pfl = Pfl::Place(start, preamble + regionSize);
  assert(!pfl->Alloc(addr, 1));
  assert(!pfl->Alloc(addr, 0));
  pfl = Pfl::Place(start, preamble + regionSize * 2);
  assert(!pfl->Alloc(addr, 1));
  assert(!pfl->Alloc(addr, 0));
  pfl = Pfl::Place(start, preamble + regionSize * 3);
  assert(!pfl->Alloc(addr, regionSize + 1));
  assert(pfl->Alloc(addr, regionSize));
  assert(addr == start + preamble + regionSize * 2);
  assert(!pfl->Alloc(addr, 1));

  free(buffer);
}

template <size_t Capacity>
void TestOffsetPlace() {
  ScopedPass pass("PlacedFreeList<", Capacity, ">::Place() [offset]");

  typedef PlacedFreeList<Capacity> Pfl;

  size_t objectAlign = sizeof(void *);
  size_t regionSize = (size_t)1 << Log2Ceil(sizeof(typename Pfl::FreeRegion));
  regionSize = Align2(regionSize, objectAlign);
  size_t instanceSize = Align2(sizeof(Pfl), objectAlign);
  size_t stackSize = Align2(sizeof(typename Pfl::StackType), objectAlign);
  size_t preamble = instanceSize + stackSize;
  size_t misalignment = (preamble + objectAlign) % regionSize;
  if (misalignment) {
    preamble += regionSize - misalignment;
  }

  // Create a buffer that gives everything the proper alignment.
  void * buffer;
  assert(!posix_memalign(&buffer, regionSize, objectAlign + preamble +
                         regionSize * 3));
  uintptr_t start = (uintptr_t)buffer + objectAlign;
  uintptr_t addr;
  assert(!Pfl::Place(start, preamble));
  assert(!Pfl::Place(start, preamble + regionSize - 1));
  Pfl * pfl = Pfl::Place(start, preamble + regionSize);
  assert(!pfl->Alloc(addr, 1));
  assert(!pfl->Alloc(addr, 0));
  pfl = Pfl::Place(start, preamble + regionSize * 2);
  assert(!pfl->Alloc(addr, 1));
  assert(!pfl->Alloc(addr, 0));
  pfl = Pfl::Place(start, preamble + regionSize * 3);
  assert(!pfl->Alloc(addr, regionSize + 1));
  assert(pfl->Alloc(addr, regionSize));
  assert(addr == start + preamble + regionSize * 2);
  assert(!pfl->Alloc(addr, 1));

  free(buffer);
}

template <size_t Capacity>
void TestNormalAllocOverflow() {

}

template <size_t Capacity>
void TestDoubleAllocOverflow() {
  // NOTE: most of the visual examples in this test make use of a large
  // Capacity to give a good depiction of what's going on. This test works for
  // Capacity >= 3, but the visual examples don't.

  ScopedPass pass("PlacedFreeList<", Capacity, ">::Alloc() [double overflow]");

  typedef PlacedFreeList<Capacity> Pfl;

  uintptr_t addr;

  size_t preambleSize = ComputePreambleSize<Capacity>();
  size_t regionSize = ComputeRegionSize<Capacity>();

  // Let R=regionSize, N=Capacity
  // We will have:
  // - one region of size R*(N+2)
  // - N-1 regions of size R*(N+3)
  // - Every region will be separated by R bytes
  // Total size needed = R*(N+2) + (N-1)*R*(N+3) + R*(N - 1)

  size_t headingSize = regionSize * (Capacity + 2);
  size_t blockSize = regionSize * (Capacity + 3);

  size_t totalSize = preambleSize + headingSize + blockSize * (Capacity - 1) +
      regionSize * (Capacity - 1);

  void * buffer;
  assert(!posix_memalign(&buffer, regionSize, totalSize));

  Pfl * pfl = Pfl::Place((uintptr_t)buffer, preambleSize + headingSize);
  assert(pfl != nullptr);
  assert(pfl->GetStackCount() == 1);

  uintptr_t start = (uintptr_t)buffer + preambleSize;

  // Currently, the allocator looks like this: [S|R| |...]

  // Add a bunch of blocks
  for (size_t i = 1; i < Capacity; ++i) {
    size_t offset = headingSize + i * regionSize + (i - 1) * blockSize;
    pfl->Dealloc(start + offset, blockSize);
    assert(pfl->GetStackCount() == 1);
  }

  // Currently, the allocator looks like this:
  // [R|R|...|S| ] [ |...] ... [ |...]

  // Each time we Alloc() another block, it moves a region structure to the
  // stack as follows:
  // [S|R|...|S| ]
  // [S|R|S|R...|S| ]
  // [S|R|S|S|R...|S| ]
  // Thus, after Capacity - 2 Alloc()s, the stack will contain Capacity - 1
  // items:
  // [S|R|S|S|S|...|R|S| ]

  for (size_t i = 1; i < Capacity - 1; ++i) {
    assert(pfl->GetStackCount() == i);
    size_t offset = headingSize + i * regionSize + (i - 1) * blockSize;
    assert(pfl->Alloc(addr, blockSize));
    assert(addr == start + offset);
    assert(pfl->GetStackCount() == i + 1);
  }

  // When we allocate the next block, the heading will at first look like this:
  // [S|R|S|S|...|S|S|S| ]
  // To address this, the stack will remove the last element from the stack:
  // [S|R|S|S|...|S|X|S| ]
  // Then, it will need a new region, so it will remove the next element from
  // the stack as well:
  // [S|R|S|S|...|R| |S| ]

  assert(pfl->Alloc(addr, blockSize));
  assert(addr == start + headingSize + regionSize * (Capacity - 1) +
                 blockSize * (Capacity - 2));
  assert(pfl->GetStackCount() == Capacity - 2);

  // The first free region is now located near the end of the heading.
  assert(pfl->Alloc(addr, regionSize));
  assert(addr == start + headingSize - (regionSize * 3));
}

template <size_t Capacity>
size_t ComputePreambleSize() {
  typedef PlacedFreeList<Capacity> Pfl;

  size_t objectAlign = sizeof(void *);
  size_t regionSize = ComputeRegionSize<Capacity>();
  size_t instanceSize = Align2(sizeof(Pfl), objectAlign);
  size_t stackSize = Align2(sizeof(typename Pfl::StackType), objectAlign);
  return Align2(instanceSize + stackSize, regionSize);
}

template <size_t Capacity>
size_t ComputeRegionSize() {
  typedef PlacedFreeList<Capacity> Pfl;
  size_t objectAlign = sizeof(void *);
  size_t regionSize = (size_t)1 << Log2Ceil(sizeof(typename Pfl::FreeRegion));
  return Align2(regionSize, objectAlign);
}
<commit_msg>both alloc overflow cases pass<commit_after>/**
 * Throughout these tests, you might see some notation indicating the state of
 * an allocator. General things to know:
 *
 * - R = active region structure in memory
 * - S = inactive region structure in the stack
 * - space = a free piece of memory
 *
 * I denote disjoint regions using brackets ([ and ]). I separate cells of
 * memory using vertical bars (|).
 *
 * Most of the time, I used this notation to figure out what a test should do.
 * A lot of the time, I assumed a large stack capacity so that I could see the
 * inductive nature of the test.
 */

#include "scoped-pass.hpp"
#include <analloc2/free-list>

using namespace analloc;
using namespace ansa;

template <size_t Capacity>
void TestAll();

template <size_t Capacity>
void TestSimplePlace();

template <size_t Capacity>
void TestOffsetPlace();

template <size_t Capacity>
void TestNormalAllocOverflow();

template <size_t Capacity>
void TestDoubleAllocOverflow();

template <size_t Capacity>
size_t ComputePreambleSize();

template <size_t Capacity>
size_t ComputeRegionSize();

int main() {
  TestAll<3>();
  TestAll<4>();
  TestAll<5>();
  TestAll<6>();
  TestAll<0x10>();
  return 0;
}

template <size_t Capacity>
void TestAll() {
  TestSimplePlace<Capacity>();
  TestOffsetPlace<Capacity>();
  TestNormalAllocOverflow<Capacity>();
  TestDoubleAllocOverflow<Capacity>();
}

template <size_t Capacity>
void TestSimplePlace() {
  ScopedPass pass("PlacedFreeList<", Capacity, ">::Place() [simple]");

  typedef PlacedFreeList<Capacity> Pfl;

  size_t regionSize = ComputeRegionSize<Capacity>();
  size_t preamble = ComputePreambleSize<Capacity>();

  // Create a buffer that gives everything the proper alignment.
  void * buffer;
  assert(!posix_memalign(&buffer, regionSize, preamble + regionSize * 3));
  uintptr_t start = (uintptr_t)buffer;
  uintptr_t addr;
  assert(!Pfl::Place(start, preamble));
  assert(!Pfl::Place(start, preamble + regionSize - 1));
  Pfl * pfl = Pfl::Place(start, preamble + regionSize);
  assert(!pfl->Alloc(addr, 1));
  assert(!pfl->Alloc(addr, 0));
  pfl = Pfl::Place(start, preamble + regionSize * 2);
  assert(!pfl->Alloc(addr, 1));
  assert(!pfl->Alloc(addr, 0));
  pfl = Pfl::Place(start, preamble + regionSize * 3);
  assert(!pfl->Alloc(addr, regionSize + 1));
  assert(pfl->Alloc(addr, regionSize));
  assert(addr == start + preamble + regionSize * 2);
  assert(!pfl->Alloc(addr, 1));

  free(buffer);
}

template <size_t Capacity>
void TestOffsetPlace() {
  ScopedPass pass("PlacedFreeList<", Capacity, ">::Place() [offset]");

  typedef PlacedFreeList<Capacity> Pfl;

  size_t objectAlign = sizeof(void *);
  size_t regionSize = (size_t)1 << Log2Ceil(sizeof(typename Pfl::FreeRegion));
  regionSize = Align2(regionSize, objectAlign);
  size_t instanceSize = Align2(sizeof(Pfl), objectAlign);
  size_t stackSize = Align2(sizeof(typename Pfl::StackType), objectAlign);
  size_t preamble = instanceSize + stackSize;
  size_t misalignment = (preamble + objectAlign) % regionSize;
  if (misalignment) {
    preamble += regionSize - misalignment;
  }

  // Create a buffer that gives everything the proper alignment.
  void * buffer;
  assert(!posix_memalign(&buffer, regionSize, objectAlign + preamble +
                         regionSize * 3));
  uintptr_t start = (uintptr_t)buffer + objectAlign;
  uintptr_t addr;
  assert(!Pfl::Place(start, preamble));
  assert(!Pfl::Place(start, preamble + regionSize - 1));
  Pfl * pfl = Pfl::Place(start, preamble + regionSize);
  assert(!pfl->Alloc(addr, 1));
  assert(!pfl->Alloc(addr, 0));
  pfl = Pfl::Place(start, preamble + regionSize * 2);
  assert(!pfl->Alloc(addr, 1));
  assert(!pfl->Alloc(addr, 0));
  pfl = Pfl::Place(start, preamble + regionSize * 3);
  assert(!pfl->Alloc(addr, regionSize + 1));
  assert(pfl->Alloc(addr, regionSize));
  assert(addr == start + preamble + regionSize * 2);
  assert(!pfl->Alloc(addr, 1));

  free(buffer);
}

template <size_t Capacity>
void TestNormalAllocOverflow() {
  ScopedPass pass("PlacedFreeList<", Capacity, ">::Alloc() [single overflow]");

  typedef PlacedFreeList<Capacity> Pfl;

  uintptr_t addr;

  size_t preambleSize = ComputePreambleSize<Capacity>();
  size_t regionSize = ComputeRegionSize<Capacity>();

  // Let R=regionSize, N=Capacity
  // We will have:
  // - one region of size R*N
  // - a region of size 5*R
  // - N-2 regions of size 2*R
  // - a region of size R
  // - a region of size 2*R
  // - Every region will be separated by R bytes

  size_t headingSize = Capacity * regionSize;
  size_t firstSize = 5 * regionSize;
  size_t blockSize = 2 * regionSize;
  size_t totalSize = preambleSize + headingSize + firstSize +
      blockSize * (Capacity - 1) + (Capacity + 2) * regionSize;

  void * buffer;
  assert(!posix_memalign(&buffer, regionSize, totalSize));

  Pfl * pfl = Pfl::Place((uintptr_t)buffer, preambleSize + headingSize);
  assert(pfl != nullptr);
  assert(pfl->GetStackCount() == 1);

  uintptr_t start = (uintptr_t)buffer + preambleSize;

  // Currently, the allocator looks like this: [S|R| |...]

  pfl->Dealloc(start + headingSize + regionSize * 3, regionSize * 3);

  // Now, the allocator looks like this:
  // [R|R|S| |...]    [ |...]

  // Now, deallocate enough more regions to saturate the heading
  for (size_t i = 0; i < Capacity - 3; ++i) {
    assert(pfl->GetStackCount() == 1);
    size_t offset = headingSize + regionSize * 6 + blockSize * i +
        regionSize * (i + 1);
    pfl->Dealloc(start + offset, blockSize);
  }
  // Ensure that the heading was saturated
  assert(pfl->GetStackCount() == 2);

  // The allocator now looks like this:
  // [R|S|R|...|S]    [ | | ] [ | ] ...

  // Now, we unsaturate the heading by deallocating another block
  size_t unsaturateOffset = headingSize + regionSize * 6 +
      blockSize * (Capacity - 3) + regionSize * (Capacity - 2);
  pfl->Dealloc(start + unsaturateOffset, blockSize);

  // The allocator now looks like this:
  // [R|R|R|...|S]    [ | | ] [ | ] ...

  // Add a small block to the allocator to fill up the heading altogether
  size_t smallBlockOffset = unsaturateOffset + regionSize * 3;
  pfl->Dealloc(start + smallBlockOffset, regionSize);

  // The allocator now looks like this:
  // [R|R|R|...|R]    [S| | ] [ | ] ... [ ]

  // Throw another block on the end.
  size_t lastBlockOffset = smallBlockOffset + regionSize * 2;
  assert(lastBlockOffset + regionSize * 2 == totalSize - preambleSize);
  pfl->Dealloc(start + lastBlockOffset, blockSize);

  // The allocator now looks like this:
  // [R|R|R|...|R]    [R|S| ] [ | ] ... [ ] [ | ]

  // Ninja the hell out of the allocator by adding a region below the first
  // region.
  pfl->Dealloc(start + headingSize + regionSize, 2 * regionSize);

  // The allocator now looks like this:
  // [R|R|R|...|R] [S| |R|R| ] [ | ] ... [ ] [ | ]

  // The next N-2 allocations will add to the stack
  for (size_t i = 0; i < Capacity - 2; ++i) {
    size_t offset = headingSize + regionSize * 6 + blockSize * i +
        regionSize * (i + 1);
    assert(pfl->Alloc(addr, blockSize));
    assert(addr == start + offset);
    assert(pfl->GetStackCount() == i + 2);
  }

  // The allocator now looks like this:
  // [R|S|...|S|R] [S| |R|R| ] [ ] [ | ]

  // Now, we'll deallocate the last block and go over the maximum stack size.
  assert(pfl->Alloc(addr, blockSize));
  assert(addr == start + lastBlockOffset);
  assert(pfl->GetStackCount() == Capacity - 1);

  // The allocator now looks like this:
  // [R|S|...|S|R] [S| | |R| ] [ ] [ | ]

  // We should now be able to allocate 2*R and find it in the first block
  assert(pfl->Alloc(addr, regionSize * 2));
  assert(addr == start + headingSize + regionSize * 2);
}

template <size_t Capacity>
void TestDoubleAllocOverflow() {
  ScopedPass pass("PlacedFreeList<", Capacity, ">::Alloc() [double overflow]");

  typedef PlacedFreeList<Capacity> Pfl;

  uintptr_t addr;

  size_t preambleSize = ComputePreambleSize<Capacity>();
  size_t regionSize = ComputeRegionSize<Capacity>();

  // Let R=regionSize, N=Capacity
  // We will have:
  // - one region of size R*(N+2)
  // - N-1 regions of size R*(N+3)
  // - Every region will be separated by R bytes
  // Total size needed = R*(N+2) + (N-1)*R*(N+3) + R*(N - 1)

  size_t headingSize = regionSize * (Capacity + 2);
  size_t blockSize = regionSize * (Capacity + 3);

  size_t totalSize = preambleSize + headingSize + blockSize * (Capacity - 1) +
      regionSize * (Capacity - 1);

  void * buffer;
  assert(!posix_memalign(&buffer, regionSize, totalSize));

  Pfl * pfl = Pfl::Place((uintptr_t)buffer, preambleSize + headingSize);
  assert(pfl != nullptr);
  assert(pfl->GetStackCount() == 1);

  uintptr_t start = (uintptr_t)buffer + preambleSize;

  // Currently, the allocator looks like this: [S|R| |...]

  // Add a bunch of blocks
  for (size_t i = 1; i < Capacity; ++i) {
    size_t offset = headingSize + i * regionSize + (i - 1) * blockSize;
    pfl->Dealloc(start + offset, blockSize);
    assert(pfl->GetStackCount() == 1);
  }

  // Currently, the allocator looks like this:
  // [R|R|...|S| ] [ |...] ... [ |...]

  // Each time we Alloc() another block, it moves a region structure to the
  // stack as follows:
  // [S|R|...|S| ]
  // [S|R|S|R...|S| ]
  // [S|R|S|S|R...|S| ]
  // Thus, after Capacity - 2 Alloc()s, the stack will contain Capacity - 1
  // items:
  // [S|R|S|S|S|...|R|S| ]

  for (size_t i = 1; i < Capacity - 1; ++i) {
    assert(pfl->GetStackCount() == i);
    size_t offset = headingSize + i * regionSize + (i - 1) * blockSize;
    assert(pfl->Alloc(addr, blockSize));
    assert(addr == start + offset);
    assert(pfl->GetStackCount() == i + 1);
  }

  // When we allocate the next block, the heading will at first look like this:
  // [S|R|S|S|...|S|S|S| ]
  // To address this, the stack will remove the last element from the stack:
  // [S|R|S|S|...|S|X|S| ]
  // Then, it will need a new region, so it will remove the next element from
  // the stack as well:
  // [S|R|S|S|...|R| |S| ]

  assert(pfl->Alloc(addr, blockSize));
  assert(addr == start + headingSize + regionSize * (Capacity - 1) +
                 blockSize * (Capacity - 2));
  assert(pfl->GetStackCount() == Capacity - 2);

  // The first free region is now located near the end of the heading.
  assert(pfl->Alloc(addr, regionSize));
  assert(addr == start + headingSize - (regionSize * 3));
}

template <size_t Capacity>
size_t ComputePreambleSize() {
  typedef PlacedFreeList<Capacity> Pfl;

  size_t objectAlign = sizeof(void *);
  size_t regionSize = ComputeRegionSize<Capacity>();
  size_t instanceSize = Align2(sizeof(Pfl), objectAlign);
  size_t stackSize = Align2(sizeof(typename Pfl::StackType), objectAlign);
  return Align2(instanceSize + stackSize, regionSize);
}

template <size_t Capacity>
size_t ComputeRegionSize() {
  typedef PlacedFreeList<Capacity> Pfl;
  size_t objectAlign = sizeof(void *);
  size_t regionSize = (size_t)1 << Log2Ceil(sizeof(typename Pfl::FreeRegion));
  return Align2(regionSize, objectAlign);
}
<|endoftext|>


<commit_before>// Author: Enrico Guiraud, Danilo Piparo CERN  12/2016

/*************************************************************************
 * Copyright (C) 1995-2016, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

/**
  \defgroup dataframe DataFrame
ROOT's RDataFrame allows to analyse data stored in TTrees with a high level interface.
*/

#ifndef ROOT_RDATAFRAME
#define ROOT_RDATAFRAME

#include <sstream> // std::ostringstream
#include <memory>
#include <ostream>
#include <string>
#include <vector>

#include "ROOT/RCutFlowReport.hxx"
#include "ROOT/RDFInterface.hxx"
#include "ROOT/RDFNodes.hxx"
#include "ROOT/RDFUtils.hxx"
#include "ROOT/RDataSource.hxx"
#include "ROOT/TypeTraits.hxx"
#include "ROOT/RStringView.hxx"
#include "RtypesCore.h"
#include "TTree.h"

class TDirectory;

namespace ROOT {
namespace RDFDetail = ROOT::Detail::RDF;
namespace RDFInternal = ROOT::Internal::RDF;
namespace TTraits = ROOT::TypeTraits;

class RDataFrame : public ROOT::RDF::RInterface<RDFDetail::RLoopManager> {
   using ColumnNames_t = RDFDetail::ColumnNames_t;
   using RDataSource = ROOT::RDF::RDataSource;

public:
   RDataFrame(std::string_view treeName, std::string_view filenameglob, const ColumnNames_t &defaultBranches = {});
   RDataFrame(std::string_view treename, const std::vector<std::string> &filenames,
              const ColumnNames_t &defaultBranches = {});
   RDataFrame(std::string_view treeName, ::TDirectory *dirPtr, const ColumnNames_t &defaultBranches = {});
   RDataFrame(TTree &tree, const ColumnNames_t &defaultBranches = {});
   RDataFrame(ULong64_t numEntries);
   RDataFrame(std::unique_ptr<RDataSource>, const ColumnNames_t &defaultBranches = {});
};

} // end NS ROOT

/// Print a RDataFrame at the prompt
namespace cling {
std::string printValue(ROOT::RDataFrame *tdf);
} // namespace cling

#endif // ROOT_TDATAFRAME
<commit_msg>[RDF] Make useful aliases public<commit_after>// Author: Enrico Guiraud, Danilo Piparo CERN  12/2016

/*************************************************************************
 * Copyright (C) 1995-2016, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

/**
  \defgroup dataframe DataFrame
ROOT's RDataFrame allows to analyse data stored in TTrees with a high level interface.
*/

#ifndef ROOT_RDATAFRAME
#define ROOT_RDATAFRAME

#include <sstream> // std::ostringstream
#include <memory>
#include <ostream>
#include <string>
#include <vector>

#include "ROOT/RCutFlowReport.hxx"
#include "ROOT/RDFInterface.hxx"
#include "ROOT/RDFNodes.hxx"
#include "ROOT/RDFUtils.hxx"
#include "ROOT/RDataSource.hxx"
#include "ROOT/TypeTraits.hxx"
#include "ROOT/RStringView.hxx"
#include "RtypesCore.h"
#include "TTree.h"

class TDirectory;

namespace ROOT {
namespace RDFDetail = ROOT::Detail::RDF;
namespace RDFInternal = ROOT::Internal::RDF;
namespace TTraits = ROOT::TypeTraits;

class RDataFrame : public ROOT::RDF::RInterface<RDFDetail::RLoopManager> {

public:
   using ColumnNames_t = RDFDetail::ColumnNames_t;
   using RDataSource = ROOT::RDF::RDataSource;
   RDataFrame(std::string_view treeName, std::string_view filenameglob, const ColumnNames_t &defaultBranches = {});
   RDataFrame(std::string_view treename, const std::vector<std::string> &filenames,
              const ColumnNames_t &defaultBranches = {});
   RDataFrame(std::string_view treeName, ::TDirectory *dirPtr, const ColumnNames_t &defaultBranches = {});
   RDataFrame(TTree &tree, const ColumnNames_t &defaultBranches = {});
   RDataFrame(ULong64_t numEntries);
   RDataFrame(std::unique_ptr<RDataSource>, const ColumnNames_t &defaultBranches = {});
};

} // end NS ROOT

/// Print a RDataFrame at the prompt
namespace cling {
std::string printValue(ROOT::RDataFrame *tdf);
} // namespace cling

#endif // ROOT_TDATAFRAME
<|endoftext|>


<commit_before>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "utils/Filename.h"
#include "utils/Url.h"
#include "medialibrary/filesystem/Errors.h"

#ifdef _WIN32
# include <algorithm>
# define DIR_SEPARATOR "\\/"
#else
# define DIR_SEPARATOR '/'
#endif

namespace medialibrary
{

namespace utils
{

namespace file
{

std::string extension( const std::string& fileName )
{
    auto pos = fileName.find_last_of( '.' );
    if ( pos == std::string::npos )
        return {};
    return fileName.substr( pos + 1 );
}

std::string stripExtension( const std::string& fileName )
{
    auto pos = fileName.find_last_of( '.' );
    if ( pos == std::string::npos )
        return fileName;
    return fileName.substr( 0, pos );
}

std::string directory( const std::string& filePath )
{
    auto pos = filePath.find_last_of( DIR_SEPARATOR );
    if ( pos == std::string::npos )
        return {};
    return filePath.substr( 0, pos + 1 );
}

std::string directoryName( const std::string& directoryPath )
{
    auto pos = directoryPath.find_last_of( DIR_SEPARATOR );
    if ( pos == std::string::npos )
        return directoryPath;
    if ( pos == 0 )
        return directoryPath.substr( 1 );
    if ( pos != directoryPath.size() - 1 )
        return directoryPath.substr( pos + 1 );
    auto res = directoryPath;
    res.pop_back();
    pos = res.find_last_of( DIR_SEPARATOR );
    return res.substr( pos + 1 );
}

std::string parentDirectory( const std::string& path )
{
    auto pos = path.find_last_of( DIR_SEPARATOR );
    if ( pos == path.length() - 1 )
        pos = path.find_last_of( DIR_SEPARATOR, pos - 1 );
    if ( pos == std::string::npos )
        return {};
    return path.substr( 0, pos + 1 );
}

std::string fileName(const std::string& filePath)
{
    auto pos = filePath.find_last_of( DIR_SEPARATOR );
    if ( pos == std::string::npos )
        return filePath;
    return filePath.substr( pos + 1 );
}

std::string firstFolder( const std::string& path )
{
    size_t offset = path.find_first_not_of( DIR_SEPARATOR );
    auto pos = path.find_first_of( DIR_SEPARATOR, offset );
    if ( pos == std::string::npos )
        return {};
    return path.substr( offset, pos - offset );
}

std::string removePath( const std::string& fullPath, const std::string& toRemove )
{
    if ( toRemove.length() == 0 || toRemove.length() > fullPath.length() )
        return fullPath;
    auto pos = fullPath.find( toRemove );
    if ( pos == std::string::npos )
        return fullPath;
    pos += toRemove.length();
    // Skip over potentially duplicated DIR_SEPARATOR
    while ( pos < fullPath.length() &&
            ( fullPath[pos] == '/'
#ifdef _WIN32
                || fullPath[pos] == '\\'
#endif
            ) )
        pos++;
    if ( pos >= fullPath.length() )
        return {};
    return fullPath.substr( pos );
}

std::string toFolderPath( std::string path )
{
#ifdef _WIN32
    if ( *path.crbegin() == '\\' )
        *path.rbegin() = '/';
    else
#endif
    if ( *path.crbegin() != '/' )
        path += '/';
    return path;
}

#ifndef _WIN32

std::string toMrl( const std::string& path )
{
    return "file://" + utils::url::encode( path );
}

#else

std::string toMrl( const std::string& path )
{
    auto normalized = path;
    std::replace( begin( normalized ), end( normalized ), '\\', '/' );
    if ( isalpha( normalized[0] ) )
        normalized = "/" + normalized;
    return std::string{ "file://" } +
            utils::url::encode( normalized );
}

#endif

std::stack<std::string> splitPath( const std::string& path, bool isDirectory )
{
    std::stack<std::string> res;
    std::string currPath = isDirectory ? utils::file::toFolderPath( path )
                                       : utils::file::directory( path );
    auto firstFolder = utils::file::firstFolder( path );
    if ( isDirectory == false )
        res.push( utils::file::fileName( path ) );
    do
    {
        res.push( utils::file::directoryName( currPath ) );
        currPath = utils::file::parentDirectory( currPath );
    } while ( res.top() != firstFolder );
    return res;
}

}

}

}
<commit_msg>utils: Filename: Fix variable shadowing outer function<commit_after>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "utils/Filename.h"
#include "utils/Url.h"
#include "medialibrary/filesystem/Errors.h"

#ifdef _WIN32
# include <algorithm>
# define DIR_SEPARATOR "\\/"
#else
# define DIR_SEPARATOR '/'
#endif

namespace medialibrary
{

namespace utils
{

namespace file
{

std::string extension( const std::string& fileName )
{
    auto pos = fileName.find_last_of( '.' );
    if ( pos == std::string::npos )
        return {};
    return fileName.substr( pos + 1 );
}

std::string stripExtension( const std::string& fileName )
{
    auto pos = fileName.find_last_of( '.' );
    if ( pos == std::string::npos )
        return fileName;
    return fileName.substr( 0, pos );
}

std::string directory( const std::string& filePath )
{
    auto pos = filePath.find_last_of( DIR_SEPARATOR );
    if ( pos == std::string::npos )
        return {};
    return filePath.substr( 0, pos + 1 );
}

std::string directoryName( const std::string& directoryPath )
{
    auto pos = directoryPath.find_last_of( DIR_SEPARATOR );
    if ( pos == std::string::npos )
        return directoryPath;
    if ( pos == 0 )
        return directoryPath.substr( 1 );
    if ( pos != directoryPath.size() - 1 )
        return directoryPath.substr( pos + 1 );
    auto res = directoryPath;
    res.pop_back();
    pos = res.find_last_of( DIR_SEPARATOR );
    return res.substr( pos + 1 );
}

std::string parentDirectory( const std::string& path )
{
    auto pos = path.find_last_of( DIR_SEPARATOR );
    if ( pos == path.length() - 1 )
        pos = path.find_last_of( DIR_SEPARATOR, pos - 1 );
    if ( pos == std::string::npos )
        return {};
    return path.substr( 0, pos + 1 );
}

std::string fileName(const std::string& filePath)
{
    auto pos = filePath.find_last_of( DIR_SEPARATOR );
    if ( pos == std::string::npos )
        return filePath;
    return filePath.substr( pos + 1 );
}

std::string firstFolder( const std::string& path )
{
    size_t offset = path.find_first_not_of( DIR_SEPARATOR );
    auto pos = path.find_first_of( DIR_SEPARATOR, offset );
    if ( pos == std::string::npos )
        return {};
    return path.substr( offset, pos - offset );
}

std::string removePath( const std::string& fullPath, const std::string& toRemove )
{
    if ( toRemove.length() == 0 || toRemove.length() > fullPath.length() )
        return fullPath;
    auto pos = fullPath.find( toRemove );
    if ( pos == std::string::npos )
        return fullPath;
    pos += toRemove.length();
    // Skip over potentially duplicated DIR_SEPARATOR
    while ( pos < fullPath.length() &&
            ( fullPath[pos] == '/'
#ifdef _WIN32
                || fullPath[pos] == '\\'
#endif
            ) )
        pos++;
    if ( pos >= fullPath.length() )
        return {};
    return fullPath.substr( pos );
}

std::string toFolderPath( std::string path )
{
#ifdef _WIN32
    if ( *path.crbegin() == '\\' )
        *path.rbegin() = '/';
    else
#endif
    if ( *path.crbegin() != '/' )
        path += '/';
    return path;
}

#ifndef _WIN32

std::string toMrl( const std::string& path )
{
    return "file://" + utils::url::encode( path );
}

#else

std::string toMrl( const std::string& path )
{
    auto normalized = path;
    std::replace( begin( normalized ), end( normalized ), '\\', '/' );
    if ( isalpha( normalized[0] ) )
        normalized = "/" + normalized;
    return std::string{ "file://" } +
            utils::url::encode( normalized );
}

#endif

std::stack<std::string> splitPath( const std::string& path, bool isDirectory )
{
    std::stack<std::string> res;
    std::string currPath = isDirectory ? utils::file::toFolderPath( path )
                                       : utils::file::directory( path );
    auto ff = utils::file::firstFolder( path );
    if ( isDirectory == false )
        res.push( utils::file::fileName( path ) );
    do
    {
        res.push( utils::file::directoryName( currPath ) );
        currPath = utils::file::parentDirectory( currPath );
    } while ( res.top() != ff );
    return res;
}

}

}

}
<|endoftext|>


<commit_before>#include "Stdafx.h"
#include "SceneTextRegionExtractor.h"

using namespace std;
using namespace cv;

namespace ImageQuality
{
	IList<GetRegionsResult^>^ SceneTextRegionExtractor::SimpleWatermark(array<byte>^ buffer)
	{
		Mat img = ReadImage(buffer);
		List<GetRegionsResult^>^ list = gcnew List<GetRegionsResult^>(4);

		GetRegionsResult^ red = DetectAndRotate(img, Scalar(0, 0, 200), Scalar(50, 50, 255));
		if (red != nullptr)
			list->Add(red);

		GetRegionsResult^ yellow = DetectAndRotate(img, Scalar(0, 225, 225), Scalar(50, 255, 255));
		if (yellow != nullptr)
			list->Add(yellow);

		GetRegionsResult^ black = DetectAndRotate(img, Scalar(0, 0, 0), Scalar(25, 25, 25));
		if (black != nullptr)
			list->Add(black);

		GetRegionsResult^ white = DetectAndRotate(img, Scalar(200, 200, 200), Scalar(255, 255, 255));
		if (white != nullptr)
			list->Add(white);

		return list;
	}

	GetRegionsResult^ SceneTextRegionExtractor::DetectAndRotate(Mat img, Scalar lower, Scalar upper)
	{
		Mat output;
		inRange(img, lower, upper, output);

		Mat morphKernel = getStructuringElement(MORPH_ELLIPSE, Size(2, 2));
		morphologyEx(output, output, MORPH_CLOSE, morphKernel);

		Mat contourImg = output.clone();

		vector<vector<Point>> contours;
		vector<Vec4i> hierarchy;
		findContours(contourImg, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

		if (!hierarchy.empty())
		{
			vector<float> angles;
			for (int idx = 0; idx >= 0; idx = hierarchy[idx][0])
			{
				RotatedRect box = minAreaRect(contours[idx]);

				float area = box.size.area();
				if (box.size.width > MinWidth && box.size.height > MinHeight)
				{
					float angle = box.angle;
					if (angle < -45)
						angle += 90;
					angles.push_back(angle);
				}
			}

			if (!angles.empty())
			{
				Nullable<float> bestAngle = FindBestAngle(angles);
				if (bestAngle.HasValue)
				{
					float angle = (bestAngle.Value > -18 && bestAngle.Value < 18) ? 0 : bestAngle.Value;

					int len = max(output.cols, output.rows);
					Point2f pt(len / 2., len / 2.);
					Mat r = getRotationMatrix2D(pt, angle, 1);
					warpAffine(output, output, r, Size(len, len));

					cvtColor(output, output, CV_GRAY2BGR);

					array<byte>^ b = ToByteArray(output, ".tiff");
					return GetRegions(b);
				}
			}
		}

		return nullptr;
	}

	GetRegionsResult^ SceneTextRegionExtractor::GetRegions(array<byte>^ buffer)
	{
		List<Region^>^ list = gcnew List<Region^>(5);
		Mat image = ReadImage(buffer);

		Mat tiff;
		cvtColor(image, tiff, CV_BGR2GRAY);
		threshold(tiff, tiff, 0, 255, THRESH_BINARY | THRESH_OTSU);

		Mat gray;
		cvtColor(image, gray, CV_BGR2GRAY);

		Mat scaled = gray;
		pyrDown(scaled, scaled);
		pyrUp(scaled, scaled);

		Mat grad;
		Mat morphKernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
		morphologyEx(scaled, grad, MORPH_GRADIENT, morphKernel);

		Mat bw;
		threshold(grad, bw, 32, 255, THRESH_BINARY | THRESH_OTSU);

		Mat connected;
		morphKernel = getStructuringElement(MORPH_RECT, Size(8, 1));
		morphologyEx(bw, connected, MORPH_CLOSE, morphKernel);

		Mat mask = Mat::zeros(bw.size(), CV_8UC1);
		vector<vector<Point>> contours;
		vector<Vec4i> hierarchy;
		findContours(connected, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

		if (!hierarchy.empty())
		{
			vector<Rect> regionRects;
			for (int idx = 0; idx >= 0; idx = hierarchy[idx][0])
			{
				Rect rect = boundingRect(contours[idx]);
				if (rect.height > 400)
					continue;

				Mat maskROI(mask, rect);
				maskROI = Scalar(0, 0, 0);

				drawContours(mask, contours, idx, Scalar(255, 255, 255), CV_FILLED);
				double r = (double)countNonZero(maskROI) / (rect.width*rect.height);

				if (r > .45 && rect.width >= MinWidth / 2 && rect.height >= MinHeight)
				{
					Mat roi(tiff, rect);
					Mat local = roi.clone();

					if (HorizontalHeuristics(local))
					{
						regionRects.push_back(rect);
					}
					else
					{
						rectangle(image, rect, Scalar(0, 255, 255), 1);
					}
				}
			}

			if (!regionRects.empty())
			{
				vector<Rect> mergedRects = MergeRects(regionRects, 10); //letters
				mergedRects = MergeRects(mergedRects, 15); //words

				if (!mergedRects.empty())
				{
					for each (Rect rect in mergedRects)
					{
						Mat roi(tiff, rect);
						Mat local = roi.clone();

						if (rect.width >= MinWidth * 3 && rect.height > MinHeight && VerticalHeuristics(local))
						{
							rectangle(image, rect, Scalar(0, 255, 0), 2);

							//imshow("roi", roi);
							//waitKey(0);

							Region^ region = gcnew Region(rect, ToByteArray(local, ".tiff"));
							list->Add(region);
						}
						else
						{
							rectangle(image, rect, Scalar(0, 0, 255), 1);
						}
					}
				}
			}
		}

		//imshow("debug", image);
		//waitKey(0);

		GetRegionsResult^ result = gcnew GetRegionsResult(list, ToByteArray(image, ".jpg"));
		return result;
	}

	bool sortRects(Rect left, Rect right)
	{
		return (left.x < right.x);
	}

	vector<Rect> SceneTextRegionExtractor::MergeRects(vector<Rect> rects, int expand)
	{
		if (rects.empty())
			return rects;

		sort(rects.begin(), rects.end(), sortRects);

		List<int> indexes;
		vector<Rect> merged;
		for (int i = 0; i < rects.size(); i++)
		{
			if (!indexes.Contains(i))
			{
				Rect local = rects[i];
				for (int j = i + 1; j < rects.size(); j++)
				{
					Rect temp(local.x, local.y, local.width + expand, local.height);
					Rect temp2(rects[j].x - expand, rects[j].y, rects[j].width, rects[j].height);

					Rect interset = (temp & temp2);
					if (interset != temp && interset != temp2)
					{
						temp = Rect(local.x, local.y + local.height / 2 - 4, local.width + expand, 8);
						temp2 = Rect(rects[j].x - expand, rects[j].y + rects[j].height / 2 - 4, rects[j].width, 8);

						interset = (temp & temp2);
						if (interset.area() > 0)
						{
							local = local | rects[j];
							indexes.Add(j);
						}
					}
				}
				merged.push_back(local);
			}
		}
		return merged;
	}

	bool SceneTextRegionExtractor::HorizontalHeuristics(Mat roi)
	{
		if (NeedsInverse(roi))
		{
			bitwise_not(roi, roi);
		}

		vector<double> horVals;
		for (int x = 0; x < roi.rows; x++)
		{
			Mat row = roi.row(x);
			double c = (roi.cols - countNonZero(row)) / (double)roi.cols;
			horVals.push_back(c);
		}

		vector<vector<double>> horDivided = HeuristicSplit(horVals);

		if (horDivided.empty() || horDivided.size() > 2 ||
			horVals.empty() || horVals[0] > 0.05 || horVals[horVals.size() - 1] > 0.05)
		{
			/*Mat horMask = Mat(Size(100, horVals.size()), CV_8UC1, Scalar(255));
			for (int i = 0; i < horVals.size(); i++)
			{
				int x = (int)(horVals[i] * 100);
				for (int j = 0; j < x; j++)
				{
					horMask.at<uchar>(Point(j, i)) = 0;
				}
			}

			imshow("roi", roi);
			imshow("horizontal_mask", horMask);
			waitKey(0);*/

			return false;
		}

		return true;
	}

	bool SceneTextRegionExtractor::VerticalHeuristics(Mat roi)
	{
		if (NeedsInverse(roi))
		{
			bitwise_not(roi, roi);
		}

		vector<double> verVals;
		for (int x = 0; x < roi.cols; x++)
		{
			Mat col = roi.col(x);
			double c = (roi.rows - countNonZero(col)) / (double)roi.rows;
			verVals.push_back(c);
		}

		vector<vector<double>> vertDivided = HeuristicSplit(verVals);

		if (vertDivided.empty() || vertDivided.size() < 2) //Text has spaces between chars
		{
			/*Mat vertMask = Mat(Size(verVals.size(), 100), CV_8UC1, Scalar(255));
			for (int i = 0; i < verVals.size(); i++)
			{
				int x = (int)(verVals[i] * 100);
				for (int j = 0; j < x; j++)
				{
					vertMask.at<uchar>(Point(i, 99 - j)) = 0;
				}
			}

			imshow("roi", roi);
			imshow("vertical_mask", vertMask);
			waitKey(0);*/

			return false;
		}

		return true;
	}

	bool SceneTextRegionExtractor::NeedsInverse(Mat roi)
	{
		int v1 = roi.at<uchar>(0, 0);
		int v2 = roi.at<uchar>(roi.rows - 1, roi.cols - 1);
		int v3 = roi.at<uchar>(0, roi.cols - 1);
		int v4 = roi.at<uchar>(roi.rows - 1, 0);

		if ((v1 + v2 + v3 + v4) / 4 < 128)
		{
			return true;
		}
		return false;
	}

	vector<vector<double>> SceneTextRegionExtractor::HeuristicSplit(vector<double> values)
	{
		vector<vector<double>> divided;
		vector<double> current;
		for (int i = 0; i < values.size(); i++)
		{
			if (values[i] == 0)
			{
				if (!current.empty())
				{
					divided.push_back(current);
					current.clear();
				}
			}
			else
			{
				current.push_back(values[i]);
			}
		}

		if (divided.empty() && !current.empty())
			divided.push_back(values);

		return divided;
	}

	Nullable<float> SceneTextRegionExtractor::FindBestAngle(vector<float> angles)
	{
		float bestAngle;
		int bestAngleCount = 1;
		bool found = false;

		if (!angles.empty())
		{
			for (int i = 0; i < angles.size() - 1; i++)
			{
				float angle = angles[i];
				int count = 1;
				for (int j = i + 1; j < angles.size(); j++)
				{
					if (i == j) continue;

					if (abs(angles[i] - angles[j]) < 10)
					{
						if (std::abs(angles[j]) < std::abs(angles[i]))
						{
							angle = angles[j];
						}
						count++;
					}
				}

				if (count > bestAngleCount && count >= angles.size() * 0.6)
				{
					bestAngle = angle;
					bestAngleCount = count;
					found = true;
				}
			}
		}
		return found ? bestAngle : Nullable<float>();
	}

	Mat SceneTextRegionExtractor::ReadImage(array<byte>^ buffer)
	{
		pin_ptr<byte> px = &buffer[0];
		Mat datax(1, buffer->Length, CV_8U, (void*)px, CV_AUTO_STEP);
		return imdecode(datax, CV_LOAD_IMAGE_COLOR);
	}

	array<byte>^ SceneTextRegionExtractor::ToByteArray(Mat image, const std::string& extension)
	{
		vector<byte> buffer;
		imencode(extension, image, buffer);

		array<byte>^ bytes = gcnew array<byte>(buffer.size());

		UnmanagedMemoryStream^ sourceStream = gcnew UnmanagedMemoryStream(buffer.data(), buffer.size());
		sourceStream->Read(bytes, 0, buffer.size());

		return bytes;
	}

	void SceneTextRegionExtractor::WriteToStream(const std::string& extension, Mat image, Stream^ stream)
	{
		vector<byte> buffer;
		imencode(extension, image, buffer);

		UnmanagedMemoryStream^ sourceStream = gcnew UnmanagedMemoryStream(buffer.data(), buffer.size());
		sourceStream->CopyTo(stream);
	}
}<commit_msg>Extra heuristic<commit_after>#include "Stdafx.h"
#include "SceneTextRegionExtractor.h"

using namespace std;
using namespace cv;

namespace ImageQuality
{
	IList<GetRegionsResult^>^ SceneTextRegionExtractor::SimpleWatermark(array<byte>^ buffer)
	{
		Mat img = ReadImage(buffer);
		List<GetRegionsResult^>^ list = gcnew List<GetRegionsResult^>(4);

		GetRegionsResult^ red = DetectAndRotate(img, Scalar(0, 0, 200), Scalar(50, 50, 255));
		if (red != nullptr)
			list->Add(red);

		GetRegionsResult^ yellow = DetectAndRotate(img, Scalar(0, 225, 225), Scalar(50, 255, 255));
		if (yellow != nullptr)
			list->Add(yellow);

		GetRegionsResult^ black = DetectAndRotate(img, Scalar(0, 0, 0), Scalar(25, 25, 25));
		if (black != nullptr)
			list->Add(black);

		GetRegionsResult^ white = DetectAndRotate(img, Scalar(200, 200, 200), Scalar(255, 255, 255));
		if (white != nullptr)
			list->Add(white);

		return list;
	}

	GetRegionsResult^ SceneTextRegionExtractor::DetectAndRotate(Mat img, Scalar lower, Scalar upper)
	{
		Mat output;
		inRange(img, lower, upper, output);

		Mat morphKernel = getStructuringElement(MORPH_ELLIPSE, Size(2, 2));
		morphologyEx(output, output, MORPH_CLOSE, morphKernel);

		Mat contourImg = output.clone();

		vector<vector<Point>> contours;
		vector<Vec4i> hierarchy;
		findContours(contourImg, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

		if (!hierarchy.empty())
		{
			vector<float> angles;
			for (int idx = 0; idx >= 0; idx = hierarchy[idx][0])
			{
				RotatedRect box = minAreaRect(contours[idx]);

				float area = box.size.area();
				if (box.size.width > MinWidth && box.size.height > MinHeight)
				{
					float angle = box.angle;
					if (angle < -45)
						angle += 90;
					angles.push_back(angle);
				}
			}

			if (!angles.empty())
			{
				Nullable<float> bestAngle = FindBestAngle(angles);
				if (bestAngle.HasValue)
				{
					float angle = (bestAngle.Value > -18 && bestAngle.Value < 18) ? 0 : bestAngle.Value;

					int len = max(output.cols, output.rows);
					Point2f pt(len / 2., len / 2.);
					Mat r = getRotationMatrix2D(pt, angle, 1);
					warpAffine(output, output, r, Size(len, len));

					cvtColor(output, output, CV_GRAY2BGR);

					array<byte>^ b = ToByteArray(output, ".tiff");
					return GetRegions(b);
				}
			}
		}

		return nullptr;
	}

	GetRegionsResult^ SceneTextRegionExtractor::GetRegions(array<byte>^ buffer)
	{
		List<Region^>^ list = gcnew List<Region^>(5);
		Mat image = ReadImage(buffer);

		Mat tiff;
		cvtColor(image, tiff, CV_BGR2GRAY);
		threshold(tiff, tiff, 0, 255, THRESH_BINARY | THRESH_OTSU);

		Mat gray;
		cvtColor(image, gray, CV_BGR2GRAY);

		Mat scaled = gray;
		pyrDown(scaled, scaled);
		pyrUp(scaled, scaled);

		Mat grad;
		Mat morphKernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
		morphologyEx(scaled, grad, MORPH_GRADIENT, morphKernel);

		Mat bw;
		threshold(grad, bw, 32, 255, THRESH_BINARY | THRESH_OTSU);

		Mat connected;
		morphKernel = getStructuringElement(MORPH_RECT, Size(8, 1));
		morphologyEx(bw, connected, MORPH_CLOSE, morphKernel);

		Mat mask = Mat::zeros(bw.size(), CV_8UC1);
		vector<vector<Point>> contours;
		vector<Vec4i> hierarchy;
		findContours(connected, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

		if (!hierarchy.empty())
		{
			vector<Rect> regionRects;
			for (int idx = 0; idx >= 0; idx = hierarchy[idx][0])
			{
				Rect rect = boundingRect(contours[idx]);
				if (rect.height > 400)
					continue;

				Mat maskROI(mask, rect);
				maskROI = Scalar(0, 0, 0);

				drawContours(mask, contours, idx, Scalar(255, 255, 255), CV_FILLED);
				double r = (double)countNonZero(maskROI) / (rect.width*rect.height);

				if (r > .45 && rect.width >= MinWidth / 2 && rect.height >= MinHeight)
				{
					Mat roi(tiff, rect);
					Mat local = roi.clone();

					if (HorizontalHeuristics(local))
					{
						regionRects.push_back(rect);
					}
					else
					{
						rectangle(image, rect, Scalar(0, 255, 255), 1);
					}
				}
			}

			if (!regionRects.empty())
			{
				vector<Rect> mergedRects = MergeRects(regionRects, 10); //letters
				mergedRects = MergeRects(mergedRects, 15); //words

				if (!mergedRects.empty())
				{
					for each (Rect rect in mergedRects)
					{
						Mat roi(tiff, rect);
						Mat local = roi.clone();

						if (rect.width >= MinWidth * 3 && rect.height > MinHeight && VerticalHeuristics(local))
						{
							rectangle(image, rect, Scalar(0, 255, 0), 2);

							//imshow("roi", roi);
							//waitKey(0);

							Region^ region = gcnew Region(rect, ToByteArray(local, ".tiff"));
							list->Add(region);
						}
						else
						{
							rectangle(image, rect, Scalar(0, 0, 255), 1);
						}
					}
				}
			}
		}

		//imshow("debug", image);
		//waitKey(0);

		GetRegionsResult^ result = gcnew GetRegionsResult(list, ToByteArray(image, ".jpg"));
		return result;
	}

	bool sortRects(Rect left, Rect right)
	{
		return (left.x < right.x);
	}

	vector<Rect> SceneTextRegionExtractor::MergeRects(vector<Rect> rects, int expand)
	{
		if (rects.empty())
			return rects;

		sort(rects.begin(), rects.end(), sortRects);

		List<int> indexes;
		vector<Rect> merged;
		for (int i = 0; i < rects.size(); i++)
		{
			if (!indexes.Contains(i))
			{
				Rect local = rects[i];
				for (int j = i + 1; j < rects.size(); j++)
				{
					Rect temp(local.x, local.y, local.width + expand, local.height);
					Rect temp2(rects[j].x - expand, rects[j].y, rects[j].width, rects[j].height);

					Rect interset = (temp & temp2);
					if (interset != temp && interset != temp2)
					{
						temp = Rect(local.x, local.y + local.height / 2 - 4, local.width + expand, 8);
						temp2 = Rect(rects[j].x - expand, rects[j].y + rects[j].height / 2 - 4, rects[j].width, 8);

						interset = (temp & temp2);
						if (interset.area() > 0)
						{
							local = local | rects[j];
							indexes.Add(j);
						}
					}
				}
				merged.push_back(local);
			}
		}
		return merged;
	}

	bool SceneTextRegionExtractor::HorizontalHeuristics(Mat roi)
	{
		if (NeedsInverse(roi))
		{
			bitwise_not(roi, roi);
		}

		vector<double> horVals;
		for (int x = 0; x < roi.rows; x++)
		{
			Mat row = roi.row(x);
			double c = (roi.cols - countNonZero(row)) / (double)roi.cols;
			horVals.push_back(c);
		}

		vector<vector<double>> horDivided = HeuristicSplit(horVals);

		if (horDivided.empty() || horDivided.size() > 2 ||
			horVals.empty() || horVals[0] > 0.05 || horVals[horVals.size() - 1] > 0.05)
		{
			/*Mat horMask = Mat(Size(100, horVals.size()), CV_8UC1, Scalar(255));
			for (int i = 0; i < horVals.size(); i++)
			{
				int x = (int)(horVals[i] * 100);
				for (int j = 0; j < x; j++)
				{
					horMask.at<uchar>(Point(j, i)) = 0;
				}
			}

			imshow("roi", roi);
			imshow("horizontal_mask", horMask);
			waitKey(0);*/

			return false;
		}

		return true;
	}

	bool SceneTextRegionExtractor::VerticalHeuristics(Mat roi)
	{
		if (NeedsInverse(roi))
		{
			bitwise_not(roi, roi);
		}

		vector<double> verVals;
		for (int x = 0; x < roi.cols; x++)
		{
			Mat col = roi.col(x);
			double c = (roi.rows - countNonZero(col)) / (double)roi.rows;
			verVals.push_back(c);
		}

		vector<vector<double>> vertDivided = HeuristicSplit(verVals);

		if (vertDivided.empty() || vertDivided.size() < 2) //Text has spaces between chars
		{
			/*Mat vertMask = Mat(Size(verVals.size(), 100), CV_8UC1, Scalar(255));
			for (int i = 0; i < verVals.size(); i++)
			{
				int x = (int)(verVals[i] * 100);
				for (int j = 0; j < x; j++)
				{
					vertMask.at<uchar>(Point(i, 99 - j)) = 0;
				}
			}

			imshow("roi", roi);
			imshow("vertical_mask", vertMask);
			waitKey(0);*/

			return false;
		}

		Mat clone = roi.clone();
		bitwise_not(clone, clone);

		vector<vector<Point>> contours;
		vector<Vec4i> hierarchy;
		findContours(clone, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

		Mat t = Mat::zeros(roi.size(), CV_8UC1);
		for (int idx = 0; idx >= 0; idx = hierarchy[idx][0])
		{
			Rect br = boundingRect(contours[idx]);
			if (br.area() > roi.size().area() * 0.5)
			{
				return false;
			}
		}

		return true;
	}

	bool SceneTextRegionExtractor::NeedsInverse(Mat roi)
	{
		int v1 = roi.at<uchar>(0, 0);
		int v2 = roi.at<uchar>(roi.rows - 1, roi.cols - 1);
		int v3 = roi.at<uchar>(0, roi.cols - 1);
		int v4 = roi.at<uchar>(roi.rows - 1, 0);

		if ((v1 + v2 + v3 + v4) / 4 < 128)
		{
			return true;
		}
		return false;
	}

	vector<vector<double>> SceneTextRegionExtractor::HeuristicSplit(vector<double> values)
	{
		vector<vector<double>> divided;
		vector<double> current;
		for (int i = 0; i < values.size(); i++)
		{
			if (values[i] == 0)
			{
				if (!current.empty())
				{
					divided.push_back(current);
					current.clear();
				}
			}
			else
			{
				current.push_back(values[i]);
			}
		}

		if (divided.empty() && !current.empty())
			divided.push_back(values);

		return divided;
	}

	Nullable<float> SceneTextRegionExtractor::FindBestAngle(vector<float> angles)
	{
		float bestAngle;
		int bestAngleCount = 1;
		bool found = false;

		if (!angles.empty())
		{
			for (int i = 0; i < angles.size() - 1; i++)
			{
				float angle = angles[i];
				int count = 1;
				for (int j = i + 1; j < angles.size(); j++)
				{
					if (i == j) continue;

					if (abs(angles[i] - angles[j]) < 10)
					{
						if (std::abs(angles[j]) < std::abs(angles[i]))
						{
							angle = angles[j];
						}
						count++;
					}
				}

				if (count > bestAngleCount && count >= angles.size() * 0.6)
				{
					bestAngle = angle;
					bestAngleCount = count;
					found = true;
				}
			}
		}
		return found ? bestAngle : Nullable<float>();
	}

	Mat SceneTextRegionExtractor::ReadImage(array<byte>^ buffer)
	{
		pin_ptr<byte> px = &buffer[0];
		Mat datax(1, buffer->Length, CV_8U, (void*)px, CV_AUTO_STEP);
		return imdecode(datax, CV_LOAD_IMAGE_COLOR);
	}

	array<byte>^ SceneTextRegionExtractor::ToByteArray(Mat image, const std::string& extension)
	{
		vector<byte> buffer;
		imencode(extension, image, buffer);

		array<byte>^ bytes = gcnew array<byte>(buffer.size());

		UnmanagedMemoryStream^ sourceStream = gcnew UnmanagedMemoryStream(buffer.data(), buffer.size());
		sourceStream->Read(bytes, 0, buffer.size());

		return bytes;
	}

	void SceneTextRegionExtractor::WriteToStream(const std::string& extension, Mat image, Stream^ stream)
	{
		vector<byte> buffer;
		imencode(extension, image, buffer);

		UnmanagedMemoryStream^ sourceStream = gcnew UnmanagedMemoryStream(buffer.data(), buffer.size());
		sourceStream->CopyTo(stream);
	}
}<|endoftext|>


<commit_before>#include "anovabayesianform.h"
#include "ui_anovabayesianform.h"

AnovaBayesianForm::AnovaBayesianForm(QWidget *parent) :
	AnalysisForm("AnovaBayesianForm", parent),
	ui(new Ui::AnovaBayesianForm)
{
	ui->setupUi(this);

	ui->listAvailableFields->setModel(&_availableVariablesModel);

	_dependentListModel = new TableModelVariablesAssigned(this);
	_dependentListModel->setVariableTypesSuggested(Column::ColumnTypeScale | Column::ColumnTypeOrdinal);
	_dependentListModel->setSource(&_availableVariablesModel);
	ui->dependent->setModel(_dependentListModel);

	_fixedFactorsListModel = new TableModelVariablesAssigned(this);
	_fixedFactorsListModel->setSource(&_availableVariablesModel);
	_fixedFactorsListModel->setVariableTypesSuggested(Column::ColumnTypeNominal | Column::ColumnTypeOrdinal);
	ui->fixedFactors->setModel(_fixedFactorsListModel);

	_randomFactorsListModel = new TableModelVariablesAssigned(this);
	_randomFactorsListModel->setSource(&_availableVariablesModel);
	_randomFactorsListModel->setVariableTypesSuggested(Column::ColumnTypeNominal | Column::ColumnTypeOrdinal);
	ui->randomFactors->setModel(_randomFactorsListModel);

	ui->buttonAssignDependent->setSourceAndTarget(ui->listAvailableFields, ui->dependent);
	ui->buttonAssignFixed->setSourceAndTarget(ui->listAvailableFields, ui->fixedFactors);
	ui->buttonAssignRandom->setSourceAndTarget(ui->listAvailableFields, ui->randomFactors);

	connect(_fixedFactorsListModel, SIGNAL(assignmentsChanged()), this, SLOT(factorsChanged()));
	connect(_randomFactorsListModel, SIGNAL(assignmentsChanged()), this, SLOT(factorsChanged()));

	_anovaModel = new TableModelAnovaModel(this);
	ui->modelTerms->setModel(_anovaModel);
	ui->modelTerms->hide();

#ifdef QT_NO_DEBUG
	// temporary hides until the appropriate R code is implemented

	ui->posteriorDistributions->hide();
	ui->posteriorEstimates->hide();
	ui->widgetModel->hide();

#else
	ui->posteriorDistributions->setStyleSheet("background-color: pink;");
	ui->posteriorEstimates->setStyleSheet("background-color: pink;");
	ui->widgetModel->setStyleSheet("background-color: pink;");
#endif
}

AnovaBayesianForm::~AnovaBayesianForm()
{
	delete ui;
}

void AnovaBayesianForm::factorsChanged()
{
	Terms factors;

	factors.add(_fixedFactorsListModel->assigned());
	factors.add(_randomFactorsListModel->assigned());

	_anovaModel->setVariables(factors);
}

<commit_msg>B ANOVA: re-enabled custom models in the UI<commit_after>#include "anovabayesianform.h"
#include "ui_anovabayesianform.h"

AnovaBayesianForm::AnovaBayesianForm(QWidget *parent) :
	AnalysisForm("AnovaBayesianForm", parent),
	ui(new Ui::AnovaBayesianForm)
{
	ui->setupUi(this);

	ui->listAvailableFields->setModel(&_availableVariablesModel);

	_dependentListModel = new TableModelVariablesAssigned(this);
	_dependentListModel->setVariableTypesSuggested(Column::ColumnTypeScale | Column::ColumnTypeOrdinal);
	_dependentListModel->setSource(&_availableVariablesModel);
	ui->dependent->setModel(_dependentListModel);

	_fixedFactorsListModel = new TableModelVariablesAssigned(this);
	_fixedFactorsListModel->setSource(&_availableVariablesModel);
	_fixedFactorsListModel->setVariableTypesSuggested(Column::ColumnTypeNominal | Column::ColumnTypeOrdinal);
	ui->fixedFactors->setModel(_fixedFactorsListModel);

	_randomFactorsListModel = new TableModelVariablesAssigned(this);
	_randomFactorsListModel->setSource(&_availableVariablesModel);
	_randomFactorsListModel->setVariableTypesSuggested(Column::ColumnTypeNominal | Column::ColumnTypeOrdinal);
	ui->randomFactors->setModel(_randomFactorsListModel);

	ui->buttonAssignDependent->setSourceAndTarget(ui->listAvailableFields, ui->dependent);
	ui->buttonAssignFixed->setSourceAndTarget(ui->listAvailableFields, ui->fixedFactors);
	ui->buttonAssignRandom->setSourceAndTarget(ui->listAvailableFields, ui->randomFactors);

	connect(_fixedFactorsListModel, SIGNAL(assignmentsChanged()), this, SLOT(factorsChanged()));
	connect(_randomFactorsListModel, SIGNAL(assignmentsChanged()), this, SLOT(factorsChanged()));

	_anovaModel = new TableModelAnovaModel(this);
	ui->modelTerms->setModel(_anovaModel);
	ui->modelTerms->hide();

#ifdef QT_NO_DEBUG
	// temporary hides until the appropriate R code is implemented

	ui->posteriorDistributions->hide();
	ui->posteriorEstimates->hide();

#else
	ui->posteriorDistributions->setStyleSheet("background-color: pink;");
	ui->posteriorEstimates->setStyleSheet("background-color: pink;");
#endif
}

AnovaBayesianForm::~AnovaBayesianForm()
{
	delete ui;
}

void AnovaBayesianForm::factorsChanged()
{
	Terms factors;

	factors.add(_fixedFactorsListModel->assigned());
	factors.add(_randomFactorsListModel->assigned());

	_anovaModel->setVariables(factors);
}

<|endoftext|>


<commit_before>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

#include "Arduino.h"
#include "TheThingsUno.h"

void TheThingsUno::init(Stream& modemStream, Stream& debugStream) {
  this->modemStream = &modemStream;
  this->debugStream = &debugStream;
}

String TheThingsUno::readLine(int waitTime) {
  unsigned long start = millis();
  while (millis() < start + waitTime) {
    String line = modemStream->readStringUntil('\n');
    if (line.length() >= 0) {
      debugPrintLn("Read " + line);
      return line.subString(0, line.length() - 1); // TODO: Check this
    }
  }
  return "";
}

bool TheThingsUno::waitForOK(int waitTime) {
  String line = readLine(waitTime);
  if (!line) {
    debugPrintLn("Wait for OK time-out expired");
    return false;
  }

  if (line != "ok") {
    debugPrintLn("Response is not OK");
    return false;
  }

  return true;
}

String TheThingsUno::readValue(String cmd) {
  debugPrintLn("Reading value " + cmd);

  modemStream->println(cmd);

  return readLine();
}

bool TheThingsUno::sendCommand(String cmd, int waitTime) {
  debugPrintLn("Sending: " + cmd);

  modemStream->println(cmd);

  return waitForOK(waitTime);
}

bool TheThingsUno::sendCommand(String cmd, String value, int waitTime) {
  debugPrintLn("Sending: " + cmd + " with value " + value);

  modemStream->print(cmd + " ");
  for (int i = 0; i < value.length(); i++)
    modemStream->print(String(value.charAt(i), HEX));
  modemStream->println();

  return waitForOK(waitTime);
}

bool TheThingsUno::sendCommand(String cmd, const byte* buffer, int length, int waitTime) {
  debugPrintLn("Sending: " + cmd + " with " + String(length) + " bytes");

  modemStream->print(cmd + " ");
  for (int i = 0; i < length; i++)
    modemStream->print(String(buffer[i], HEX));
  modemStream->println();

  return waitForOK(waitTime);
}

void TheThingsUno::reset(bool adr, int fsb) {
  if (!sendCommand("sys reset")) {
    debugPrintLn("Failed to reset");
    return;
  }

  String version = readLine();
  if (version == "") {
    debugPrintLn("Failed to read version");
    return;
  }

  model = "RN2903"; // TODO: Read model from version
  debugPrintLn("Version is " + version + ", model is " + model);

  sendCommand("mac set adr " + String(adr ? "on" : "off"));

  if (model == "RN2483")
    sendCommand("mac set pwridx " + String(PWRIDX_868));
  else if (model == "RN2903") {
    sendCommand("mac set pwridx " + String(PWRIDX_915));
    enableFsbChannels(fsb);
  }
}

bool TheThingsUno::enableFsbChannels(int fsb) {
  if (fsb == 0)
    return true;

  int chLow, chHigh;
  switch (fsb) {
    case 7:
      chLow = 48;
      chHigh = 55;
      break;
    default:
      debugPrintLn("Invalid FSB");
      return false;
  }

  for (int i = 0; i < 72; i++) {
    String command = "mac set ch status " + String(i);
    if (i == 70 || chLow <= i && i <= chHigh)
      sendCommand("mac set ch status " + String(i) + " on", "Enable channel " + String(i));
    else
      sendCommand("mac set ch status " + String(i) + " off", "Disable channel " + String(i));
  }
  return true;
}

bool TheThingsUno::personalize(const byte devAddr[4], const byte nwkSKey[16], const byte appSKey[16]) {
  sendCommand("mac set devaddr", devAddr, 4);
  sendCommand("mac set nwkskey", nwkSKey, 16);
  sendCommand("mac set appskey", appSKey, 16);
  sendCommand("mac join abp");

  if (readLine() != "accepted") {
    debugPrintLn("Personalize not accepted");
    return false;
  }

  debugPrintLn("Personalize accepted. Status: " + readValue("mac get status"));
  return true;
}

bool TheThingsUno::join(const byte appEui[8], const byte appKey[16]) {
  // TODO: Check that mac deveui is set to sys hweui by default, otherwise put that in here
  sendCommand("mac set appeui", appEui, 8);
  sendCommand("mac set appkey", appKey, 16);
  sendCommand("mac join otaa");

  if (readLine() != "accepted") {
    debugPrintLn("Join not accepted");
    return false;
  }

  debugPrintLn("Join accepted. Status: " + readValue("mac get status"));
  return true;
}

void TheThingsUno::sendBytes(const byte* buffer, int length, int port, bool confirm) {
  debugPrintLn("Sending buffer of " + String(length) + " bytes");
  sendCommand("mac tx " + String(confirm ? "cnf " : "uncnf ") + " " + String(port), buffer, length);
}

void TheThingsUno::sendString(String message, int port, bool confirm) {
  debugPrintLn("Sending string " + message);
  sendCommand("mac tx " + String(confirm ? "cnf " : "uncnf ") + " " + String(port), message);
}

#ifdef DEBUG
void TheThingsUno::debugStatus() {
  debugPrintLn("EUI: " + readValue("sys get hweui"));
  debugPrintLn("Battery: " + readValue("sys get vdd"));
  debugPrintLn("AppEUI: " + readValue("mac get appeui"));
  debugPrintLn("DevEUI: " + readValue("mac get deveui"));
  debugPrintLn("DevAddr: " + readValue("mac get devaddr"));
  debugPrintLn("NwkSKey: " + readValue("mac get nwkskey"));
  debugPrintLn("AppSKey: " + readValue("mac get appskey"));

  if (this->model == "RN2483") {
    // TODO: Test this
    debugPrintLn("Channel: " + readValue("mac get ch"));
    debugPrintLn("Band: " + readValue("mac get band"));
  }

  debugPrintLn("Data Rate: " + readValue("mac get dr"));
  debugPrintLn("RX Delay 1: " + readValue("mac get rxdelay1"));
  debugPrintLn("RX Delay 2: " + readValue("mac get rxdelay2"));
}
#endif
<commit_msg>Build fix and error reporting<commit_after>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

#include "Arduino.h"
#include "TheThingsUno.h"

void TheThingsUno::init(Stream& modemStream, Stream& debugStream) {
  this->modemStream = &modemStream;
  this->debugStream = &debugStream;
}

String TheThingsUno::readLine(int waitTime) {
  unsigned long start = millis();
  while (millis() < start + waitTime) {
    String line = modemStream->readStringUntil('\n');
    if (line.length() >= 0) {
      debugPrintLn("Read " + line);
      return line.substring(0, line.length() - 1); // TODO: Check this
    }
  }
  return "";
}

bool TheThingsUno::waitForOK(int waitTime) {
  String line = readLine(waitTime);
  if (!line) {
    debugPrintLn("Wait for OK time-out expired");
    return false;
  }

  if (line != "ok") {
    debugPrintLn("Response is not OK: " + line);
    return false;
  }

  return true;
}

String TheThingsUno::readValue(String cmd) {
  debugPrintLn("Reading value " + cmd);

  modemStream->println(cmd);

  return readLine();
}

bool TheThingsUno::sendCommand(String cmd, int waitTime) {
  debugPrintLn("Sending: " + cmd);

  modemStream->println(cmd);

  return waitForOK(waitTime);
}

bool TheThingsUno::sendCommand(String cmd, String value, int waitTime) {
  debugPrintLn("Sending: " + cmd + " with value " + value);

  modemStream->print(cmd + " ");
  for (int i = 0; i < value.length(); i++)
    modemStream->print(String(value.charAt(i), HEX));
  modemStream->println();

  return waitForOK(waitTime);
}

bool TheThingsUno::sendCommand(String cmd, const byte* buffer, int length, int waitTime) {
  debugPrintLn("Sending: " + cmd + " with " + String(length) + " bytes");

  modemStream->print(cmd + " ");
  for (int i = 0; i < length; i++)
    modemStream->print(String(buffer[i], HEX));
  modemStream->println();

  return waitForOK(waitTime);
}

void TheThingsUno::reset(bool adr, int fsb) {
  if (!sendCommand("sys reset")) {
    debugPrintLn("Failed to reset");
    return;
  }

  String version = readLine();
  if (version == "") {
    debugPrintLn("Failed to read version");
    return;
  }

  model = "RN2903"; // TODO: Read model from version
  debugPrintLn("Version is " + version + ", model is " + model);

  sendCommand("mac set adr " + String(adr ? "on" : "off"));

  if (model == "RN2483")
    sendCommand("mac set pwridx " + String(PWRIDX_868));
  else if (model == "RN2903") {
    sendCommand("mac set pwridx " + String(PWRIDX_915));
    enableFsbChannels(fsb);
  }
}

bool TheThingsUno::enableFsbChannels(int fsb) {
  if (fsb == 0)
    return true;

  int chLow, chHigh;
  switch (fsb) {
    case 7:
      chLow = 48;
      chHigh = 55;
      break;
    default:
      debugPrintLn("Invalid FSB");
      return false;
  }

  for (int i = 0; i < 72; i++) {
    String command = "mac set ch status " + String(i);
    if (i == 70 || chLow <= i && i <= chHigh)
      sendCommand("mac set ch status " + String(i) + " on", "Enable channel " + String(i));
    else
      sendCommand("mac set ch status " + String(i) + " off", "Disable channel " + String(i));
  }
  return true;
}

bool TheThingsUno::personalize(const byte devAddr[4], const byte nwkSKey[16], const byte appSKey[16]) {
  sendCommand("mac set devaddr", devAddr, 4);
  sendCommand("mac set nwkskey", nwkSKey, 16);
  sendCommand("mac set appskey", appSKey, 16);
  sendCommand("mac join abp");

  if (readLine() != "accepted") {
    debugPrintLn("Personalize not accepted");
    return false;
  }

  debugPrintLn("Personalize accepted. Status: " + readValue("mac get status"));
  return true;
}

bool TheThingsUno::join(const byte appEui[8], const byte appKey[16]) {
  // TODO: Check that mac deveui is set to sys hweui by default, otherwise put that in here
  sendCommand("mac set appeui", appEui, 8);
  sendCommand("mac set appkey", appKey, 16);
  sendCommand("mac join otaa");

  if (readLine() != "accepted") {
    debugPrintLn("Join not accepted");
    return false;
  }

  debugPrintLn("Join accepted. Status: " + readValue("mac get status"));
  return true;
}

void TheThingsUno::sendBytes(const byte* buffer, int length, int port, bool confirm) {
  debugPrintLn("Sending buffer of " + String(length) + " bytes");
  sendCommand("mac tx " + String(confirm ? "cnf " : "uncnf ") + " " + String(port), buffer, length);
}

void TheThingsUno::sendString(String message, int port, bool confirm) {
  debugPrintLn("Sending string " + message);
  sendCommand("mac tx " + String(confirm ? "cnf " : "uncnf ") + " " + String(port), message);
}

#ifdef DEBUG
void TheThingsUno::debugStatus() {
  debugPrintLn("EUI: " + readValue("sys get hweui"));
  debugPrintLn("Battery: " + readValue("sys get vdd"));
  debugPrintLn("AppEUI: " + readValue("mac get appeui"));
  debugPrintLn("DevEUI: " + readValue("mac get deveui"));
  debugPrintLn("DevAddr: " + readValue("mac get devaddr"));
  debugPrintLn("NwkSKey: " + readValue("mac get nwkskey"));
  debugPrintLn("AppSKey: " + readValue("mac get appskey"));

  if (this->model == "RN2483") {
    // TODO: Test this
    debugPrintLn("Channel: " + readValue("mac get ch"));
    debugPrintLn("Band: " + readValue("mac get band"));
  }

  debugPrintLn("Data Rate: " + readValue("mac get dr"));
  debugPrintLn("RX Delay 1: " + readValue("mac get rxdelay1"));
  debugPrintLn("RX Delay 2: " + readValue("mac get rxdelay2"));
}
#endif
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, Roland Bock
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "TabSample.h"
#include <sqlpp11/sqlpp11.h>
#include <sqlpp11/sqlite3/sqlite3.h>

#include <sqlite3.h>
#include <iostream>
#include <vector>

SQLPP_ALIAS_PROVIDER(left);

namespace sql = sqlpp::sqlite3;
int main()
{
	sql::connection_config config;
 	config.path_to_database = ":memory:";
	config.flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
	config.debug = true;

	sql::connection db(config);
	db.execute(R"(CREATE TABLE tab_sample (
		alpha bigint(20) DEFAULT NULL,
			beta bool DEFAULT NULL,
			gamma varchar(255) DEFAULT NULL
			))");
	db.execute(R"(CREATE TABLE tab_foo (
		omega bigint(20) DEFAULT NULL
			))");

	TabSample tab;
	// clear the table
	db(remove_from(tab).where(true));

	// explicit all_of(tab)
	for(const auto& row : db(select(all_of(tab)).from(tab).where(true)))
	{
		std::cerr << "row.alpha: " << row.alpha << ", row.beta: " << row.beta << ", row.gamma: " << row.gamma <<  std::endl;
	};
	std::cerr << __FILE__ << ": " << __LINE__ << std::endl;
	// selecting a table implicitly expands to all_of(tab)
	for(const auto& row : db(select(all_of(tab)).from(tab).where(true)))
	{
		std::cerr << "row.alpha: " << row.alpha << ", row.beta: " << row.beta << ", row.gamma: " << row.gamma <<  std::endl;
	};
	// selecting two multicolumns
	for(const auto& row : db(select(multi_column(tab.alpha, tab.beta, tab.gamma).as(left), multi_column(all_of(tab)).as(tab)).from(tab).where(true)))
	{
		std::cerr << "row.left.alpha: " << row.left.alpha << ", row.left.beta: " << row.left.beta << ", row.left.gamma: " << row.left.gamma <<  std::endl;
		std::cerr << "row.tabSample.alpha: " << row.tabSample.alpha << ", row.tabSample.beta: " << row.tabSample.beta << ", row.tabSample.gamma: " << row.tabSample.gamma <<  std::endl;
	};

	// insert
	std::cerr << "no of required columns: " << TabSample::_required_insert_columns::size::value << std::endl;
	db(insert_into(tab).default_values());
	db(insert_into(tab).set(tab.gamma = true));
	auto di = dynamic_insert_into(db, tab).dynamic_set(tab.gamma = true);
	di.insert_list.add(tab.beta = "");
	db(di);

	// update
	db(update(tab).set(tab.gamma = false).where(tab.alpha.in(1)));
	db(update(tab).set(tab.gamma = false).where(tab.alpha.in(sqlpp::value_list(std::vector<int>{1, 2, 3, 4}))));

	// remove
	db(remove_from(tab).where(tab.alpha == tab.alpha + 3));

	decltype(db(select(all_of(tab)))) result;
	result = db(select(all_of(tab)).from(tab).where(true));
	std::cerr << "Accessing a field directly from the result (using the current row): " << result.begin()->alpha << std::endl;
	std::cerr << "Can do that again, no problem: " << result.begin()->alpha << std::endl;

	auto tx = start_transaction(db);
	TabFoo foo;
	for (const auto& row : db(
				select(all_of(tab), select(max(foo.omega)).from(foo).where(foo.omega > tab.alpha)).from(tab).where(true)))
	{
		int x = row.alpha;
		int a = row.max;
		std::cout << x << ", " << a << std::endl;
	}
	tx.commit();

	for (const auto& row : db(select(tab.alpha).from(tab.join(foo).on(tab.alpha == foo.omega)).where(true)))
	{
		std::cerr << row.alpha << std::endl;
	}

	for (const auto& row : db(select(tab.alpha).from(tab.left_outer_join(foo).on(tab.alpha == foo.omega)).where(true)))
	{
		std::cerr << row.alpha << std::endl;
	}

	auto ps = db.prepare(select(all_of(tab)).from(tab).where(tab.alpha != parameter(tab.alpha) and tab.beta != parameter(tab.beta) and tab.gamma != parameter(tab.gamma)));
	ps.params.alpha = 7;
	ps.params.beta = "wurzelbrunft";
	ps.params.gamma = true;
	for (const auto& row: db(ps))
	{
		std::cerr << "bound result: alpha: " << row.alpha << std::endl;
		std::cerr << "bound result: beta: " << row.beta << std::endl;
		std::cerr << "bound result: gamma: " << row.gamma << std::endl;
	}

	std::cerr << "--------" << std::endl;
	ps.params.gamma = "false";
	for (const auto& row: db(ps))
	{
		std::cerr << "bound result: alpha: " << row.alpha << std::endl;
		std::cerr << "bound result: beta: " << row.beta << std::endl;
		std::cerr << "bound result: gamma: " << row.gamma << std::endl;
	}

	std::cerr << "--------" << std::endl;
	ps.params.beta = "kaesekuchen";
	for (const auto& row: db(ps))
	{
		std::cerr << "bound result: alpha: " << row.alpha << std::endl;
		std::cerr << "bound result: beta: " << row.beta << std::endl;
		std::cerr << "bound result: gamma: " << row.gamma << std::endl;
	}

	auto pi = db.prepare(insert_into(tab).set(tab.beta = parameter(tab.beta), tab.gamma = true));
	pi.params.beta = "prepared cake";
	std::cerr << "Inserted: " << db(pi) << std::endl;

	auto pu = db.prepare(update(tab).set(tab.gamma = parameter(tab.gamma)).where(tab.beta == "prepared cake"));
	pu.params.gamma = false;
	std::cerr << "Updated: " << db(pu) << std::endl;

	auto pr = db.prepare(remove_from(tab).where(tab.beta != parameter(tab.beta)));
	pr.params.beta = "prepared cake";
	std::cerr << "Deleted lines: " << db(pr) << std::endl;

  // Check that a prepared select is default-constructible
  {
    auto s = select(all_of(tab)).from(tab).where((tab.beta.like(parameter(tab.beta)) and tab.alpha == parameter(tab.alpha)) or tab.gamma != parameter(tab.gamma));
    using P = decltype(db.prepare(s));
    P p; // You must not use this one yet!
		p = db.prepare(s);
  }

	return 0;
}
<commit_msg>Added test for eval<commit_after>/*
 * Copyright (c) 2013, Roland Bock
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "TabSample.h"
#include <sqlpp11/sqlpp11.h>
#include <sqlpp11/sqlite3/sqlite3.h>

#include <sqlite3.h>
#include <iostream>
#include <vector>

SQLPP_ALIAS_PROVIDER(left);

namespace sql = sqlpp::sqlite3;
int main()
{
	sql::connection_config config;
 	config.path_to_database = ":memory:";
	config.flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
	config.debug = true;

	sql::connection db(config);
	db.execute(R"(CREATE TABLE tab_sample (
		alpha bigint(20) DEFAULT NULL,
			beta bool DEFAULT NULL,
			gamma varchar(255) DEFAULT NULL
			))");
	db.execute(R"(CREATE TABLE tab_foo (
		omega bigint(20) DEFAULT NULL
			))");

	TabSample tab;
	// clear the table
	db(remove_from(tab).where(true));

	// explicit all_of(tab)
	for(const auto& row : db(select(all_of(tab)).from(tab).where(true)))
	{
		std::cerr << "row.alpha: " << row.alpha << ", row.beta: " << row.beta << ", row.gamma: " << row.gamma <<  std::endl;
	};
	std::cerr << __FILE__ << ": " << __LINE__ << std::endl;
	// selecting a table implicitly expands to all_of(tab)
	for(const auto& row : db(select(all_of(tab)).from(tab).where(true)))
	{
		std::cerr << "row.alpha: " << row.alpha << ", row.beta: " << row.beta << ", row.gamma: " << row.gamma <<  std::endl;
	};
	// selecting two multicolumns
	for(const auto& row : db(select(multi_column(tab.alpha, tab.beta, tab.gamma).as(left), multi_column(all_of(tab)).as(tab)).from(tab).where(true)))
	{
		std::cerr << "row.left.alpha: " << row.left.alpha << ", row.left.beta: " << row.left.beta << ", row.left.gamma: " << row.left.gamma <<  std::endl;
		std::cerr << "row.tabSample.alpha: " << row.tabSample.alpha << ", row.tabSample.beta: " << row.tabSample.beta << ", row.tabSample.gamma: " << row.tabSample.gamma <<  std::endl;
	};

	// insert
	std::cerr << "no of required columns: " << TabSample::_required_insert_columns::size::value << std::endl;
	db(insert_into(tab).default_values());
	db(insert_into(tab).set(tab.gamma = true));
	auto di = dynamic_insert_into(db, tab).dynamic_set(tab.gamma = true);
	di.insert_list.add(tab.beta = "");
	db(di);

	// update
	db(update(tab).set(tab.gamma = false).where(tab.alpha.in(1)));
	db(update(tab).set(tab.gamma = false).where(tab.alpha.in(sqlpp::value_list(std::vector<int>{1, 2, 3, 4}))));

	// remove
	db(remove_from(tab).where(tab.alpha == tab.alpha + 3));

	decltype(db(select(all_of(tab)))) result;
	result = db(select(all_of(tab)).from(tab).where(true));
	std::cerr << "Accessing a field directly from the result (using the current row): " << result.begin()->alpha << std::endl;
	std::cerr << "Can do that again, no problem: " << result.begin()->alpha << std::endl;

	auto tx = start_transaction(db);
	TabFoo foo;
	for (const auto& row : db(
				select(all_of(tab), select(max(foo.omega)).from(foo).where(foo.omega > tab.alpha)).from(tab).where(true)))
	{
		int x = row.alpha;
		int a = row.max;
		std::cout << x << ", " << a << std::endl;
	}
	tx.commit();

	for (const auto& row : db(select(tab.alpha).from(tab.join(foo).on(tab.alpha == foo.omega)).where(true)))
	{
		std::cerr << row.alpha << std::endl;
	}

	for (const auto& row : db(select(tab.alpha).from(tab.left_outer_join(foo).on(tab.alpha == foo.omega)).where(true)))
	{
		std::cerr << row.alpha << std::endl;
	}

	auto ps = db.prepare(select(all_of(tab)).from(tab).where(tab.alpha != parameter(tab.alpha) and tab.beta != parameter(tab.beta) and tab.gamma != parameter(tab.gamma)));
	ps.params.alpha = 7;
	ps.params.beta = "wurzelbrunft";
	ps.params.gamma = true;
	for (const auto& row: db(ps))
	{
		std::cerr << "bound result: alpha: " << row.alpha << std::endl;
		std::cerr << "bound result: beta: " << row.beta << std::endl;
		std::cerr << "bound result: gamma: " << row.gamma << std::endl;
	}

	std::cerr << "--------" << std::endl;
	ps.params.alpha = sqlpp::eval<sqlpp::integer>(db, "last_insert_rowid()");
	ps.params.gamma = "false";
	for (const auto& row: db(ps))
	{
		std::cerr << "bound result: alpha: " << row.alpha << std::endl;
		std::cerr << "bound result: beta: " << row.beta << std::endl;
		std::cerr << "bound result: gamma: " << row.gamma << std::endl;
	}

	std::cerr << "--------" << std::endl;
	ps.params.beta = "kaesekuchen";
	for (const auto& row: db(ps))
	{
		std::cerr << "bound result: alpha: " << row.alpha << std::endl;
		std::cerr << "bound result: beta: " << row.beta << std::endl;
		std::cerr << "bound result: gamma: " << row.gamma << std::endl;
	}

	auto pi = db.prepare(insert_into(tab).set(tab.beta = parameter(tab.beta), tab.gamma = true));
	pi.params.beta = "prepared cake";
	std::cerr << "Inserted: " << db(pi) << std::endl;

	auto pu = db.prepare(update(tab).set(tab.gamma = parameter(tab.gamma)).where(tab.beta == "prepared cake"));
	pu.params.gamma = false;
	std::cerr << "Updated: " << db(pu) << std::endl;

	auto pr = db.prepare(remove_from(tab).where(tab.beta != parameter(tab.beta)));
	pr.params.beta = "prepared cake";
	std::cerr << "Deleted lines: " << db(pr) << std::endl;

  // Check that a prepared select is default-constructible
  {
    auto s = select(all_of(tab)).from(tab).where((tab.beta.like(parameter(tab.beta)) and tab.alpha == parameter(tab.alpha)) or tab.gamma != parameter(tab.gamma));
    using P = decltype(db.prepare(s));
    P p; // You must not use this one yet!
		p = db.prepare(s);
  }

	return 0;
}
<|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2013, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file PipeFlow.hxx
 * Implementation of the pipe dispatcher flow.
 *
 * @author Balazs Racz
 * @date 8 Dec 2013
 */

#ifndef _utils_PipeFlow_hxx_
#define _utils_PipeFlow_hxx_

#include "utils/pipe.hxx"
#include "executor/Dispatcher.hxx"

class PipeBuffer;
class PipeMember;

//! A packet of data to be written to the pipe device.
struct PipeBuffer : public QueueMember
{
    // The data to write.
    const void* data;
    // Number of bytes to write.
    size_t size;
    // Pipe member to skip during this write.
    PipeMember* skipMember;
    // Notifies the caller when the buffer is no longer needed.
    Notifiable* done;
};

/**
   An interface class for channels where we forward data from a pipe.

   Various different pipe receivers will implement this interface.
 */
class PipeMember : public HandlerBase
{
public:
    virtual ~PipeMember()
    {
    }
    /**
       Writes bytes to the device.

       @param buf is the source buffer from whch to write bytes.

       @param count is the number of bytes to write. count is a multiple of the
    parent pipe's unit, otherwise implementations are allowed to drop data or
    die.

       Blocks until the write is complete (that is, all data is enqueued in a
    buffer which will drain as the output device's speed
    allows). Implementations may want to use a lock inside to avoid writes from
    multiple sources being interleaved.
    */
    virtual void write(const void* buf, size_t count) = 0;

    virtual void async_write(const void* buf, size_t count, Notifiable* done)
    {
        write(buf, count);
        done->Notify();
    };
};

/*
static const int PIPE_BUFFER_COUNT = 5;
allBuffers_.reset(new PipeBuffer[PIPE_BUFFER_COUNT]);
for (int i = 0; i < PIPE_BUFFER_COUNT; ++i)
{
    empty_buffers()->TypedRelease(&allBuffers_[i]);
}
*/

/** Implementation of the core of an asynchronous pipe. Handles taking incoming
 * packets from the allocator queue, and passing them to pipe members. */
class PipeFlow : public DispatchFlow<uint32_t>
{
public:
    PipeFlow(Executor* e) : DispatchFlow<uint32_t>(e)
    {
        StartFlowAt(ST(wait_for_buffer));
    }

    ~PipeFlow()
    {
        StartFlowAt(ST(NotStarted));
        executor()->WaitUntilEmpty();
    }

    TypedAllocator<PipeBuffer>* full_buffers()
    {
        return &fullBufferAllocator_;
    }

    //! Adds a specific handler.
    void RegisterMember(PipeMember* handler)
    {
        RegisterHandler(0, 0, handler);
    }

    //! Removes a specific instance of a handler from this IF.
    void UnregisterMember(PipeMember* handler)
    {
        UnregisterHandler(0, 0, handler);
    }

protected:
    virtual void CheckNotStartedState()
    {
    }

private:
    ControlFlowAction wait_for_buffer()
    {
        return Allocate(&fullBufferAllocator_, ST(start_flow));
    }

    ControlFlowAction start_flow()
    {
        currentBuffer_ = GetTypedAllocationResult(&fullBufferAllocator_);
        IncomingMessage(0);
        return WaitForNotification();
    }

    virtual bool OnFlowFinished()
    {
        currentBuffer_->done->Notify();
        currentBuffer_ = nullptr;
        StartFlowAt(ST(wait_for_buffer));
        return false;
    }

    virtual void CallCurrentHandler(HandlerBase* b_handler, Notifiable* done)
    {
        PipeMember* member = static_cast<PipeMember*>(b_handler);
        if (member == currentBuffer_->skipMember)
        {
            done->Notify();
            return;
        }
        member->async_write(currentBuffer_->data, currentBuffer_->size, done);
    }

    PipeBuffer* currentBuffer_;
    TypedAllocator<PipeBuffer> fullBufferAllocator_;
};

#endif // _utils_PipeFlow_hxx_
<commit_msg>Switches pipe flow to use negative matching dispatcher instead of skipping the actual calls. Skipping the actual calls was leaking allocated objects.<commit_after>/** \copyright
 * Copyright (c) 2013, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file PipeFlow.hxx
 * Implementation of the pipe dispatcher flow.
 *
 * @author Balazs Racz
 * @date 8 Dec 2013
 */

#ifndef _utils_PipeFlow_hxx_
#define _utils_PipeFlow_hxx_

#include "utils/pipe.hxx"
#include "executor/Dispatcher.hxx"

class PipeBuffer;
class PipeMember;

//! A packet of data to be written to the pipe device.
struct PipeBuffer : public QueueMember
{
    // The data to write.
    const void* data;
    // Number of bytes to write.
    size_t size;
    // Pipe member to skip during this write.
    PipeMember* skipMember;
    // Notifies the caller when the buffer is no longer needed.
    Notifiable* done;
};

/**
   An interface class for channels where we forward data from a pipe.

   Various different pipe receivers will implement this interface.
 */
class PipeMember : public HandlerBase
{
public:
    virtual ~PipeMember()
    {
    }
    /**
       Writes bytes to the device.

       @param buf is the source buffer from whch to write bytes.

       @param count is the number of bytes to write. count is a multiple of the
    parent pipe's unit, otherwise implementations are allowed to drop data or
    die.

       Blocks until the write is complete (that is, all data is enqueued in a
    buffer which will drain as the output device's speed
    allows). Implementations may want to use a lock inside to avoid writes from
    multiple sources being interleaved.
    */
    virtual void write(const void* buf, size_t count) = 0;

    virtual void async_write(const void* buf, size_t count, Notifiable* done)
    {
        write(buf, count);
        done->Notify();
    }
};

/*
static const int PIPE_BUFFER_COUNT = 5;
allBuffers_.reset(new PipeBuffer[PIPE_BUFFER_COUNT]);
for (int i = 0; i < PIPE_BUFFER_COUNT; ++i)
{
    empty_buffers()->TypedRelease(&allBuffers_[i]);
}
*/

/** Implementation of the core of an asynchronous pipe. Handles taking incoming
 * packets from the allocator queue, and passing them to pipe members. */
class PipeFlow : public DispatchFlow<uint32_t>
{
public:
    PipeFlow(Executor* e) : DispatchFlow<uint32_t>(e)
    {
        negateMatch_ = true;
        StartFlowAt(ST(wait_for_buffer));
    }

    ~PipeFlow()
    {
        StartFlowAt(ST(NotStarted));
        executor()->WaitUntilEmpty();
    }

    TypedAllocator<PipeBuffer>* full_buffers()
    {
        return &fullBufferAllocator_;
    }

    //! Adds a specific handler.
    void RegisterMember(PipeMember* handler)
    {
        RegisterHandler(reinterpret_cast<uint32_t>(handler), 0xffffffffU, handler);
    }

    //! Removes a specific instance of a handler from this IF.
    void UnregisterMember(PipeMember* handler)
    {
        UnregisterHandler(reinterpret_cast<uint32_t>(handler), 0xffffffffU, handler);
    }

protected:
    virtual void CheckNotStartedState()
    {
    }

private:
    ControlFlowAction wait_for_buffer()
    {
        return Allocate(&fullBufferAllocator_, ST(start_flow));
    }

    ControlFlowAction start_flow()
    {
        currentBuffer_ = GetTypedAllocationResult(&fullBufferAllocator_);
        IncomingMessage(reinterpret_cast<uint32_t>(currentBuffer_->skipMember));
        return WaitForNotification();
    }

    virtual bool OnFlowFinished()
    {
        currentBuffer_->done->Notify();
        currentBuffer_ = nullptr;
        StartFlowAt(ST(wait_for_buffer));
        return false;
    }

    virtual void CallCurrentHandler(HandlerBase* b_handler, Notifiable* done)
    {
        PipeMember* member = static_cast<PipeMember*>(b_handler);
        member->async_write(currentBuffer_->data, currentBuffer_->size, done);
    }

    PipeBuffer* currentBuffer_;
    TypedAllocator<PipeBuffer> fullBufferAllocator_;
};

#endif // _utils_PipeFlow_hxx_
<|endoftext|>