<commit_before>/*******************************************************
 * Copyright (c) 2014, ArrayFire
 * All rights reserved.
 *
 * This file is distributed under 3-clause BSD license.
 * The complete license agreement can be obtained at:
 * http://arrayfire.com/licenses/BSD-3-Clause
 ********************************************************/

#include <gtest/gtest.h>
#include <arrayfire.h>
#include <af/dim4.hpp>
#include <af/defines.h>
#include <af/traits.hpp>
#include <vector>
#include <iostream>
#include <complex>
#include <string>
#include <testHelpers.hpp>

using std::vector;
using std::string;
using std::cout;
using std::endl;
using af::cfloat;
using af::cdouble;

template<typename T>
class Reorder : public ::testing::Test
{
    public:
        virtual void SetUp() {
            subMat0.push_back(af_make_seq(0, 4, 1));
            subMat0.push_back(af_make_seq(2, 6, 1));
            subMat0.push_back(af_make_seq(0, 2, 1));
        }
        vector<af_seq> subMat0;
};

// create a list of types to be tested
typedef ::testing::Types<float, double, cfloat, cdouble, int, unsigned int, char, unsigned char, short, ushort> TestTypes;

// register the type list
TYPED_TEST_CASE(Reorder, TestTypes);

template<typename T>
void reorderTest(string pTestFile, const unsigned resultIdx,
                 const uint x, const uint y, const uint z, const uint w,
                 bool isSubRef = false, const vector<af_seq> * seqv = NULL)
{
    if (noDoubleTests<T>()) return;

    vector<af::dim4> numDims;
    vector<vector<T> > in;
    vector<vector<T> > tests;
    readTests<T, T, int>(pTestFile,numDims,in,tests);

    af::dim4 idims = numDims[0];

    af_array inArray = 0;
    af_array outArray = 0;
    af_array tempArray = 0;

    if (isSubRef) {
        ASSERT_EQ(AF_SUCCESS, af_create_array(&tempArray, &(in[0].front()), idims.ndims(), idims.get(), (af_dtype) af::dtype_traits<T>::af_type));

        ASSERT_EQ(AF_SUCCESS, af_index(&inArray, tempArray, seqv->size(), &seqv->front()));
    } else {
        ASSERT_EQ(AF_SUCCESS, af_create_array(&inArray, &(in[0].front()), idims.ndims(), idims.get(), (af_dtype) af::dtype_traits<T>::af_type));
    }

    ASSERT_EQ(AF_SUCCESS, af_reorder(&outArray, inArray, x, y, z, w));

    // Get result
    T* outData = new T[tests[resultIdx].size()];
    ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray));

    // Compare result
    size_t nElems = tests[resultIdx].size();
    for (size_t elIter = 0; elIter < nElems; ++elIter) {
        ASSERT_EQ(tests[resultIdx][elIter], outData[elIter]) << "at: " << elIter << std::endl;
    }

    // Delete
    delete[] outData;

    if(inArray   != 0) af_release_array(inArray);
    if(outArray  != 0) af_release_array(outArray);
    if(tempArray != 0) af_release_array(tempArray);
}

#define REORDER_INIT(desc, file, resultIdx, x, y, z, w)                                        \
    TYPED_TEST(Reorder, desc)                                                                  \
    {                                                                                \
        reorderTest<TypeParam>(string(TEST_DIR"/reorder/"#file".test"), resultIdx, x, y, z, w);   \
    }

    REORDER_INIT(Reorder012, reorder, 0, 0, 1, 2, 3);
    REORDER_INIT(Reorder021, reorder, 1, 0, 2, 1, 3);
    REORDER_INIT(Reorder102, reorder, 2, 1, 0, 2, 3);
    REORDER_INIT(Reorder120, reorder, 3, 1, 2, 0, 3);
    REORDER_INIT(Reorder201, reorder, 4, 2, 0, 1, 3);
    REORDER_INIT(Reorder210, reorder, 5, 2, 1, 0, 3);

    REORDER_INIT(Reorder0123, reorder4d, 0, 0, 1, 2, 3);
    REORDER_INIT(Reorder0132, reorder4d, 1, 0, 1, 3, 2);
    REORDER_INIT(Reorder0213, reorder4d, 2, 0, 2, 1, 3);
    REORDER_INIT(Reorder0231, reorder4d, 3, 0, 2, 3, 1);
    REORDER_INIT(Reorder0312, reorder4d, 4, 0, 3, 1, 2);
    REORDER_INIT(Reorder0321, reorder4d, 5, 0, 3, 2, 1);

    REORDER_INIT(Reorder1023, reorder4d, 6, 1, 0, 2, 3);
    REORDER_INIT(Reorder1032, reorder4d, 7, 1, 0, 3, 2);
    REORDER_INIT(Reorder1203, reorder4d, 8, 1, 2, 0, 3);
    REORDER_INIT(Reorder1230, reorder4d, 9, 1, 2, 3, 0);
    REORDER_INIT(Reorder1302, reorder4d,10, 1, 3, 0, 2);
    REORDER_INIT(Reorder1320, reorder4d,11, 1, 3, 2, 0);

    REORDER_INIT(Reorder2103, reorder4d,12, 2, 1, 0, 3);
    REORDER_INIT(Reorder2130, reorder4d,13, 2, 1, 3, 0);
    REORDER_INIT(Reorder2013, reorder4d,14, 2, 0, 1, 3);
    REORDER_INIT(Reorder2031, reorder4d,15, 2, 0, 3, 1);
    REORDER_INIT(Reorder2310, reorder4d,16, 2, 3, 1, 0);
    REORDER_INIT(Reorder2301, reorder4d,17, 2, 3, 0, 1);

    REORDER_INIT(Reorder3120, reorder4d,18, 3, 1, 2, 0);
    REORDER_INIT(Reorder3102, reorder4d,19, 3, 1, 0, 2);
    REORDER_INIT(Reorder3210, reorder4d,20, 3, 2, 1, 0);
    REORDER_INIT(Reorder3201, reorder4d,21, 3, 2, 0, 1);
    REORDER_INIT(Reorder3012, reorder4d,22, 3, 0, 1, 2);
    REORDER_INIT(Reorder3021, reorder4d,23, 3, 0, 2, 1);

////////////////////////////////// CPP ///////////////////////////////////
//
TEST(Reorder, CPP)
{
    if (noDoubleTests<float>()) return;

    const unsigned resultIdx = 0;
    const unsigned x = 0;
    const unsigned y = 1;
    const unsigned z = 2;
    const unsigned w = 3;

    vector<af::dim4> numDims;
    vector<vector<float> > in;
    vector<vector<float> > tests;
    readTests<float, float, int>(string(TEST_DIR"/reorder/reorder4d.test"),numDims,in,tests);

    af::dim4 idims = numDims[0];

    af::array input(idims, &(in[0].front()));
    af::array output = af::reorder(input, x, y, z, w);

    // Get result
    float* outData = new float[tests[resultIdx].size()];
    output.host((void*)outData);

    // Compare result
    size_t nElems = tests[resultIdx].size();
    for (size_t elIter = 0; elIter < nElems; ++elIter) {
        ASSERT_EQ(tests[resultIdx][elIter], outData[elIter]) << "at: " << elIter << std::endl;
    }

    // Delete
    delete[] outData;
}

<commit_msg>Adding tests for reorder after tile<commit_after>/*******************************************************
 * Copyright (c) 2014, ArrayFire
 * All rights reserved.
 *
 * This file is distributed under 3-clause BSD license.
 * The complete license agreement can be obtained at:
 * http://arrayfire.com/licenses/BSD-3-Clause
 ********************************************************/

#include <gtest/gtest.h>
#include <arrayfire.h>
#include <af/dim4.hpp>
#include <af/defines.h>
#include <af/traits.hpp>
#include <vector>
#include <iostream>
#include <complex>
#include <string>
#include <testHelpers.hpp>

using std::vector;
using std::string;
using std::cout;
using std::endl;
using af::cfloat;
using af::cdouble;

template<typename T>
class Reorder : public ::testing::Test
{
    public:
        virtual void SetUp() {
            subMat0.push_back(af_make_seq(0, 4, 1));
            subMat0.push_back(af_make_seq(2, 6, 1));
            subMat0.push_back(af_make_seq(0, 2, 1));
        }
        vector<af_seq> subMat0;
};

// create a list of types to be tested
typedef ::testing::Types<float, double, cfloat, cdouble, int, unsigned int, char, unsigned char, short, ushort> TestTypes;

// register the type list
TYPED_TEST_CASE(Reorder, TestTypes);

template<typename T>
void reorderTest(string pTestFile, const unsigned resultIdx,
                 const uint x, const uint y, const uint z, const uint w,
                 bool isSubRef = false, const vector<af_seq> * seqv = NULL)
{
    if (noDoubleTests<T>()) return;

    vector<af::dim4> numDims;
    vector<vector<T> > in;
    vector<vector<T> > tests;
    readTests<T, T, int>(pTestFile,numDims,in,tests);

    af::dim4 idims = numDims[0];

    af_array inArray = 0;
    af_array outArray = 0;
    af_array tempArray = 0;

    if (isSubRef) {
        ASSERT_EQ(AF_SUCCESS, af_create_array(&tempArray, &(in[0].front()), idims.ndims(), idims.get(), (af_dtype) af::dtype_traits<T>::af_type));

        ASSERT_EQ(AF_SUCCESS, af_index(&inArray, tempArray, seqv->size(), &seqv->front()));
    } else {
        ASSERT_EQ(AF_SUCCESS, af_create_array(&inArray, &(in[0].front()), idims.ndims(), idims.get(), (af_dtype) af::dtype_traits<T>::af_type));
    }

    ASSERT_EQ(AF_SUCCESS, af_reorder(&outArray, inArray, x, y, z, w));

    // Get result
    T* outData = new T[tests[resultIdx].size()];
    ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray));

    // Compare result
    size_t nElems = tests[resultIdx].size();
    for (size_t elIter = 0; elIter < nElems; ++elIter) {
        ASSERT_EQ(tests[resultIdx][elIter], outData[elIter]) << "at: " << elIter << std::endl;
    }

    // Delete
    delete[] outData;

    if(inArray   != 0) af_release_array(inArray);
    if(outArray  != 0) af_release_array(outArray);
    if(tempArray != 0) af_release_array(tempArray);
}

#define REORDER_INIT(desc, file, resultIdx, x, y, z, w)                                        \
    TYPED_TEST(Reorder, desc)                                                                  \
    {                                                                                \
        reorderTest<TypeParam>(string(TEST_DIR"/reorder/"#file".test"), resultIdx, x, y, z, w);   \
    }

    REORDER_INIT(Reorder012, reorder, 0, 0, 1, 2, 3);
    REORDER_INIT(Reorder021, reorder, 1, 0, 2, 1, 3);
    REORDER_INIT(Reorder102, reorder, 2, 1, 0, 2, 3);
    REORDER_INIT(Reorder120, reorder, 3, 1, 2, 0, 3);
    REORDER_INIT(Reorder201, reorder, 4, 2, 0, 1, 3);
    REORDER_INIT(Reorder210, reorder, 5, 2, 1, 0, 3);

    REORDER_INIT(Reorder0123, reorder4d, 0, 0, 1, 2, 3);
    REORDER_INIT(Reorder0132, reorder4d, 1, 0, 1, 3, 2);
    REORDER_INIT(Reorder0213, reorder4d, 2, 0, 2, 1, 3);
    REORDER_INIT(Reorder0231, reorder4d, 3, 0, 2, 3, 1);
    REORDER_INIT(Reorder0312, reorder4d, 4, 0, 3, 1, 2);
    REORDER_INIT(Reorder0321, reorder4d, 5, 0, 3, 2, 1);

    REORDER_INIT(Reorder1023, reorder4d, 6, 1, 0, 2, 3);
    REORDER_INIT(Reorder1032, reorder4d, 7, 1, 0, 3, 2);
    REORDER_INIT(Reorder1203, reorder4d, 8, 1, 2, 0, 3);
    REORDER_INIT(Reorder1230, reorder4d, 9, 1, 2, 3, 0);
    REORDER_INIT(Reorder1302, reorder4d,10, 1, 3, 0, 2);
    REORDER_INIT(Reorder1320, reorder4d,11, 1, 3, 2, 0);

    REORDER_INIT(Reorder2103, reorder4d,12, 2, 1, 0, 3);
    REORDER_INIT(Reorder2130, reorder4d,13, 2, 1, 3, 0);
    REORDER_INIT(Reorder2013, reorder4d,14, 2, 0, 1, 3);
    REORDER_INIT(Reorder2031, reorder4d,15, 2, 0, 3, 1);
    REORDER_INIT(Reorder2310, reorder4d,16, 2, 3, 1, 0);
    REORDER_INIT(Reorder2301, reorder4d,17, 2, 3, 0, 1);

    REORDER_INIT(Reorder3120, reorder4d,18, 3, 1, 2, 0);
    REORDER_INIT(Reorder3102, reorder4d,19, 3, 1, 0, 2);
    REORDER_INIT(Reorder3210, reorder4d,20, 3, 2, 1, 0);
    REORDER_INIT(Reorder3201, reorder4d,21, 3, 2, 0, 1);
    REORDER_INIT(Reorder3012, reorder4d,22, 3, 0, 1, 2);
    REORDER_INIT(Reorder3021, reorder4d,23, 3, 0, 2, 1);

////////////////////////////////// CPP ///////////////////////////////////
//
TEST(Reorder, CPP)
{
    const unsigned resultIdx = 0;
    const unsigned x = 0;
    const unsigned y = 1;
    const unsigned z = 2;
    const unsigned w = 3;

    vector<af::dim4> numDims;
    vector<vector<float> > in;
    vector<vector<float> > tests;
    readTests<float, float, int>(string(TEST_DIR"/reorder/reorder4d.test"),numDims,in,tests);

    af::dim4 idims = numDims[0];

    af::array input(idims, &(in[0].front()));
    af::array output = af::reorder(input, x, y, z, w);

    // Get result
    float* outData = new float[tests[resultIdx].size()];
    output.host((void*)outData);

    // Compare result
    size_t nElems = tests[resultIdx].size();
    for (size_t elIter = 0; elIter < nElems; ++elIter) {
        ASSERT_EQ(tests[resultIdx][elIter], outData[elIter]) << "at: " << elIter << std::endl;
    }

    // Delete
    delete[] outData;
}

TEST(Reorder, ISSUE_1777)
{
    const int m = 5;
    const int n = 4;
    const int k = 3;
    vector<float> h_input(m * n);

    for (int i = 0; i < m * n; i++) {
        h_input[i] = (float)(i);
    }

    af::array a(m, n, &h_input[0]);
    af::array a_t = af::tile(a, 1, 1, 3);
    af::array a_r = af::reorder(a_t, 0, 2, 1);

    vector<float> h_output(m * n * k);
    a_r.host((void *)&h_output[0]);
    for (int z = 0; z < n; z++) {
        for (int y = 0; y < k; y++) {
            for (int x = 0; x < m; x++) {
                ASSERT_EQ(h_output[z * k * m + y * m + x], h_input[z * m + x]);
            }
        }
    }
}
<|endoftext|>


<commit_before>#include <cassert>
#include <iostream>
#include <list>

#include <pqxx/pqxx>

#include <pqxx/compiler-internal.hxx>

using namespace PGSTD;
using namespace pqxx;

namespace
{
#ifndef PQXX_HAVE_DISTANCE
template<typename ITERATOR> size_t distance(ITERATOR begin, ITERATOR end)
{
  size_t d = 0;
  while (begin != end)
  {
    ++begin;
    ++d;
  }
  return d;
}
#endif // PQXX_HAVE_DISTANCE

void compare_results(string name, result lhs, result rhs)
{
  if (lhs != rhs)
    throw logic_error("Executing " + name + " as prepared statement "
	  "yields different results from direct execution");

  if (lhs.empty())
    throw logic_error("Results being compared are empty.  Not much point!");
}

string stringize(transaction_base &t, const string &arg)
{
  return "'" + t.esc(arg) + "'";
}
string stringize(transaction_base &t, const char arg[])
{
  return arg ? stringize(t,string(arg)) : "null";
}
string stringize(transaction_base &t, char arg[])
{
  return arg ? stringize(t, string(arg)) : "null";
}
template<typename T> string stringize(transaction_base &t, T i)
{
  return stringize(t, to_string(i));
}

// Substitute variables in raw query.  This is not likely to be very robust,
// but it should do for just this test.  The main shortcomings are escaping,
// and not knowing when to quote the variables.
// Note we do the replacement backwards (meaning forward_only iterators won't
// do!) to avoid substituting e.g. "$12" as "$1" first.
template<typename ITER> string subst(transaction_base &t,
	string q,
	ITER patbegin,
	ITER patend)
{
  int i = distance(patbegin, patend);
  for (ITER arg = patend; i > 0; --i)
  {
   --arg;
    const string marker = "$" + to_string(i),
	  var = stringize(t, *arg);
    const string::size_type msz = marker.size();
    while (q.find(marker) != string::npos) q.replace(q.find(marker),msz,var);
  }
  return q;
}

template<typename CNTNR> string subst(transaction_base &t,
	string q,
	const CNTNR &patterns)
{
  return subst(t, q, patterns.begin(), patterns.end());
}

} // namespace

// Test program for libpqxx.  Define and use prepared statements.
//
// Usage: test085
int main()
{
  try
  {
    /* A bit of nastiness in prepared statements: on 7.3.x backends we can't
     * compare pg_tables.tablename to a string.  We work around this by using
     * the LIKE operator.
     *
     * Later backend versions do not suffer from this problem.
     */
    const string QN_readpgtables = "ReadPGTables",
	  Q_readpgtables = "SELECT * FROM pg_tables",
	  QN_seetable = "SeeTable",
	  Q_seetable = Q_readpgtables + " WHERE tablename LIKE $1",
	  QN_seetables = "SeeTables",
	  Q_seetables = Q_seetable + " OR tablename LIKE $2";

    lazyconnection C;

    cout << "Preparing a simple statement..." << endl;
    C.prepare(QN_readpgtables, Q_readpgtables);
    nontransaction T(C, "test85");

    try
    {
      // See if a basic prepared statement works just like a regular query
      cout << "Basic correctness check on prepared statement..." << endl;
      compare_results(QN_readpgtables,
	T.prepared(QN_readpgtables).exec(),
	T.exec(Q_readpgtables));
    }
    catch (const exception &)
    {
      if (!C.supports(connection_base::cap_prepared_statements))
      {
        cout << "Backend version does not support prepared statements.  "
	        "Skipping."
	     << endl;
	return 0;
      }
      throw;
    }

    // Try prepare_now() on an already prepared statement
    C.prepare_now(QN_readpgtables);

    // Pro forma check: same thing but with name passed as C-style string
    compare_results(QN_readpgtables+"_char",
	T.prepared(QN_readpgtables.c_str()).exec(),
	T.exec(Q_readpgtables));

    cout << "Dropping prepared statement..." << endl;
    C.unprepare(QN_readpgtables);

    bool failed = true;
    try
    {
      disable_noticer d(C);
      C.prepare_now(QN_readpgtables);
      failed = false;
    }
    catch (const exception &e)
    {
      cout << "(Expected) " << e.what() << endl;
    }
    if (!failed)
      throw runtime_error("prepare_now() succeeded on dropped statement");

    // Just to try and confuse things, "unprepare" twice
    cout << "Testing error detection and handling..." << endl;
    try { C.unprepare(QN_readpgtables); }
    catch (const exception &e) { cout << "(Expected) " << e.what() << endl; }

    // Verify that attempt to execute unprepared statement fails
    bool failsOK = true;
    try { T.prepared(QN_readpgtables).exec(); failsOK = false; }
    catch (const exception &e) { cout << "(Expected) " << e.what() << endl; }
    if (!failsOK) throw logic_error("Execute unprepared statement didn't fail");

    // Re-prepare the same statement and test again
    C.prepare(QN_readpgtables, Q_readpgtables);
    C.prepare_now(QN_readpgtables);
    compare_results(QN_readpgtables+"_2",
	T.prepared(QN_readpgtables).exec(),
	T.exec(Q_readpgtables));

    // Double preparation of identical statement should be ignored...
    C.prepare(QN_readpgtables, Q_readpgtables);
    compare_results(QN_readpgtables+"_double",
	T.prepared(QN_readpgtables).exec(),
	T.exec(Q_readpgtables));

    // ...But a modified definition shouldn't
    try
    {
      failsOK = true;
      C.prepare(QN_readpgtables, Q_readpgtables + " ORDER BY tablename");
      failsOK = false;
    }
    catch (const exception &e)
    {
      cout << "(Expected) " << e.what() << endl;
    }
    if (!failsOK)
      throw logic_error("Bad redefinition of statement went unnoticed");

    cout << "Testing prepared statement with parameter..." << endl;

    C.prepare(QN_seetable, Q_seetable)("varchar", pqxx::prepare::treat_string);

    vector<string> args;
    args.push_back("pg_type");
    compare_results(QN_seetable+"_seq",
	T.prepared(QN_seetable)(args[0]).exec(),
	T.exec(subst(T,Q_seetable,args)));

    cout << "Testing prepared statement with 2 parameters..." << endl;

    C.prepare(QN_seetables, Q_seetables)
      ("varchar",pqxx::prepare::treat_string)
      ("varchar",pqxx::prepare::treat_string);
    args.push_back("pg_index");
    compare_results(QN_seetables+"_seq",
      T.prepared(QN_seetables)(args[0])(args[1]).exec(),
      T.exec(subst(T,Q_seetables,args)));

    cout << "Testing prepared statement with a null parameter..." << endl;
    vector<const char *> ptrs;
    ptrs.push_back(0);
    ptrs.push_back("pg_index");
    compare_results(QN_seetables+"_null1",
	T.prepared(QN_seetables)(ptrs[0])(ptrs[1]).exec(),
	T.exec(subst(T,Q_seetables,ptrs)));
    compare_results(QN_seetables+"_null2",
	T.prepared(QN_seetables)(ptrs[0])(ptrs[1]).exec(),
	T.prepared(QN_seetables)()(ptrs[1]).exec());
    compare_results(QN_seetables+"_null3",
	T.prepared(QN_seetables)(ptrs[0])(ptrs[1]).exec(),
	T.prepared(QN_seetables)("somestring",false)(ptrs[1]).exec());
    compare_results(QN_seetables+"_null4",
	T.prepared(QN_seetables)(ptrs[0])(ptrs[1]).exec(),
	T.prepared(QN_seetables)(42,false)(ptrs[1]).exec());
    compare_results(QN_seetables+"_null5",
	T.prepared(QN_seetables)(ptrs[0])(ptrs[1]).exec(),
	T.prepared(QN_seetables)(0,false)(ptrs[1]).exec());

    cout << "Testing wrong numbers of parameters..." << endl;
    try
    {
      failsOK = true;
      T.prepared(QN_seetables)()()("hi mom!").exec();
      failsOK = false;
    }
    catch (const exception &e)
    {
      cout << "(Expected) " << e.what() << endl;
    }
    if (!failsOK)
      throw logic_error("No error for too many parameters");
    try
    {
      failsOK = true;
      T.prepared(QN_seetables)("who, me?").exec();
      failsOK = false;
    }
    catch (const exception &e)
    {
      cout << "(Expected) " << e.what() << endl;
    }
    if (!failsOK)
      throw logic_error("No error for too few parameters");

    cout << "Done." << endl;
  }
  catch (const feature_not_supported &e)
  {
    cout << "Backend version does not support prepared statements.  Skipping."
         << endl;
    return 0;
  }
  catch (const sql_error &e)
  {
    cerr << "SQL error: " << e.what() << endl
         << "Query was: " << e.query() << endl;
    return 1;
  }
  catch (const exception &e)
  {
    // All exceptions thrown by libpqxx are derived from std::exception
    cerr << "Exception: " << e.what() << endl;
    return 2;
  }
  catch (...)
  {
    // This is really unexpected (see above)
    cerr << "Unhandled exception" << endl;
    return 100;
  }

  return 0;
}

<commit_msg>Put compiler-internal include in a better place.<commit_after>#include <cassert>
#include <iostream>
#include <list>

#include <pqxx/compiler-internal.hxx>

#include <pqxx/pqxx>

using namespace PGSTD;
using namespace pqxx;

namespace
{
#ifndef PQXX_HAVE_DISTANCE
template<typename ITERATOR> size_t distance(ITERATOR begin, ITERATOR end)
{
  size_t d = 0;
  while (begin != end)
  {
    ++begin;
    ++d;
  }
  return d;
}
#endif // PQXX_HAVE_DISTANCE

void compare_results(string name, result lhs, result rhs)
{
  if (lhs != rhs)
    throw logic_error("Executing " + name + " as prepared statement "
	  "yields different results from direct execution");

  if (lhs.empty())
    throw logic_error("Results being compared are empty.  Not much point!");
}

string stringize(transaction_base &t, const string &arg)
{
  return "'" + t.esc(arg) + "'";
}
string stringize(transaction_base &t, const char arg[])
{
  return arg ? stringize(t,string(arg)) : "null";
}
string stringize(transaction_base &t, char arg[])
{
  return arg ? stringize(t, string(arg)) : "null";
}
template<typename T> string stringize(transaction_base &t, T i)
{
  return stringize(t, to_string(i));
}

// Substitute variables in raw query.  This is not likely to be very robust,
// but it should do for just this test.  The main shortcomings are escaping,
// and not knowing when to quote the variables.
// Note we do the replacement backwards (meaning forward_only iterators won't
// do!) to avoid substituting e.g. "$12" as "$1" first.
template<typename ITER> string subst(transaction_base &t,
	string q,
	ITER patbegin,
	ITER patend)
{
  int i = distance(patbegin, patend);
  for (ITER arg = patend; i > 0; --i)
  {
   --arg;
    const string marker = "$" + to_string(i),
	  var = stringize(t, *arg);
    const string::size_type msz = marker.size();
    while (q.find(marker) != string::npos) q.replace(q.find(marker),msz,var);
  }
  return q;
}

template<typename CNTNR> string subst(transaction_base &t,
	string q,
	const CNTNR &patterns)
{
  return subst(t, q, patterns.begin(), patterns.end());
}

} // namespace

// Test program for libpqxx.  Define and use prepared statements.
//
// Usage: test085
int main()
{
  try
  {
    /* A bit of nastiness in prepared statements: on 7.3.x backends we can't
     * compare pg_tables.tablename to a string.  We work around this by using
     * the LIKE operator.
     *
     * Later backend versions do not suffer from this problem.
     */
    const string QN_readpgtables = "ReadPGTables",
	  Q_readpgtables = "SELECT * FROM pg_tables",
	  QN_seetable = "SeeTable",
	  Q_seetable = Q_readpgtables + " WHERE tablename LIKE $1",
	  QN_seetables = "SeeTables",
	  Q_seetables = Q_seetable + " OR tablename LIKE $2";

    lazyconnection C;

    cout << "Preparing a simple statement..." << endl;
    C.prepare(QN_readpgtables, Q_readpgtables);
    nontransaction T(C, "test85");

    try
    {
      // See if a basic prepared statement works just like a regular query
      cout << "Basic correctness check on prepared statement..." << endl;
      compare_results(QN_readpgtables,
	T.prepared(QN_readpgtables).exec(),
	T.exec(Q_readpgtables));
    }
    catch (const exception &)
    {
      if (!C.supports(connection_base::cap_prepared_statements))
      {
        cout << "Backend version does not support prepared statements.  "
	        "Skipping."
	     << endl;
	return 0;
      }
      throw;
    }

    // Try prepare_now() on an already prepared statement
    C.prepare_now(QN_readpgtables);

    // Pro forma check: same thing but with name passed as C-style string
    compare_results(QN_readpgtables+"_char",
	T.prepared(QN_readpgtables.c_str()).exec(),
	T.exec(Q_readpgtables));

    cout << "Dropping prepared statement..." << endl;
    C.unprepare(QN_readpgtables);

    bool failed = true;
    try
    {
      disable_noticer d(C);
      C.prepare_now(QN_readpgtables);
      failed = false;
    }
    catch (const exception &e)
    {
      cout << "(Expected) " << e.what() << endl;
    }
    if (!failed)
      throw runtime_error("prepare_now() succeeded on dropped statement");

    // Just to try and confuse things, "unprepare" twice
    cout << "Testing error detection and handling..." << endl;
    try { C.unprepare(QN_readpgtables); }
    catch (const exception &e) { cout << "(Expected) " << e.what() << endl; }

    // Verify that attempt to execute unprepared statement fails
    bool failsOK = true;
    try { T.prepared(QN_readpgtables).exec(); failsOK = false; }
    catch (const exception &e) { cout << "(Expected) " << e.what() << endl; }
    if (!failsOK) throw logic_error("Execute unprepared statement didn't fail");

    // Re-prepare the same statement and test again
    C.prepare(QN_readpgtables, Q_readpgtables);
    C.prepare_now(QN_readpgtables);
    compare_results(QN_readpgtables+"_2",
	T.prepared(QN_readpgtables).exec(),
	T.exec(Q_readpgtables));

    // Double preparation of identical statement should be ignored...
    C.prepare(QN_readpgtables, Q_readpgtables);
    compare_results(QN_readpgtables+"_double",
	T.prepared(QN_readpgtables).exec(),
	T.exec(Q_readpgtables));

    // ...But a modified definition shouldn't
    try
    {
      failsOK = true;
      C.prepare(QN_readpgtables, Q_readpgtables + " ORDER BY tablename");
      failsOK = false;
    }
    catch (const exception &e)
    {
      cout << "(Expected) " << e.what() << endl;
    }
    if (!failsOK)
      throw logic_error("Bad redefinition of statement went unnoticed");

    cout << "Testing prepared statement with parameter..." << endl;

    C.prepare(QN_seetable, Q_seetable)("varchar", pqxx::prepare::treat_string);

    vector<string> args;
    args.push_back("pg_type");
    compare_results(QN_seetable+"_seq",
	T.prepared(QN_seetable)(args[0]).exec(),
	T.exec(subst(T,Q_seetable,args)));

    cout << "Testing prepared statement with 2 parameters..." << endl;

    C.prepare(QN_seetables, Q_seetables)
      ("varchar",pqxx::prepare::treat_string)
      ("varchar",pqxx::prepare::treat_string);
    args.push_back("pg_index");
    compare_results(QN_seetables+"_seq",
      T.prepared(QN_seetables)(args[0])(args[1]).exec(),
      T.exec(subst(T,Q_seetables,args)));

    cout << "Testing prepared statement with a null parameter..." << endl;
    vector<const char *> ptrs;
    ptrs.push_back(0);
    ptrs.push_back("pg_index");
    compare_results(QN_seetables+"_null1",
	T.prepared(QN_seetables)(ptrs[0])(ptrs[1]).exec(),
	T.exec(subst(T,Q_seetables,ptrs)));
    compare_results(QN_seetables+"_null2",
	T.prepared(QN_seetables)(ptrs[0])(ptrs[1]).exec(),
	T.prepared(QN_seetables)()(ptrs[1]).exec());
    compare_results(QN_seetables+"_null3",
	T.prepared(QN_seetables)(ptrs[0])(ptrs[1]).exec(),
	T.prepared(QN_seetables)("somestring",false)(ptrs[1]).exec());
    compare_results(QN_seetables+"_null4",
	T.prepared(QN_seetables)(ptrs[0])(ptrs[1]).exec(),
	T.prepared(QN_seetables)(42,false)(ptrs[1]).exec());
    compare_results(QN_seetables+"_null5",
	T.prepared(QN_seetables)(ptrs[0])(ptrs[1]).exec(),
	T.prepared(QN_seetables)(0,false)(ptrs[1]).exec());

    cout << "Testing wrong numbers of parameters..." << endl;
    try
    {
      failsOK = true;
      T.prepared(QN_seetables)()()("hi mom!").exec();
      failsOK = false;
    }
    catch (const exception &e)
    {
      cout << "(Expected) " << e.what() << endl;
    }
    if (!failsOK)
      throw logic_error("No error for too many parameters");
    try
    {
      failsOK = true;
      T.prepared(QN_seetables)("who, me?").exec();
      failsOK = false;
    }
    catch (const exception &e)
    {
      cout << "(Expected) " << e.what() << endl;
    }
    if (!failsOK)
      throw logic_error("No error for too few parameters");

    cout << "Done." << endl;
  }
  catch (const feature_not_supported &e)
  {
    cout << "Backend version does not support prepared statements.  Skipping."
         << endl;
    return 0;
  }
  catch (const sql_error &e)
  {
    cerr << "SQL error: " << e.what() << endl
         << "Query was: " << e.query() << endl;
    return 1;
  }
  catch (const exception &e)
  {
    // All exceptions thrown by libpqxx are derived from std::exception
    cerr << "Exception: " << e.what() << endl;
    return 2;
  }
  catch (...)
  {
    // This is really unexpected (see above)
    cerr << "Unhandled exception" << endl;
    return 100;
  }

  return 0;
}

<|endoftext|>


<commit_before>#include <iostream>
#include <vpp/core/image2d.hh>
#include <vpp/core/pyramid.hh>

int main()
{
  using namespace vpp;

  image2d<vint1> img(4, 4);

  int s1[] = {
    1,2,3,4,
    5,6,7,8,
    9,10,11,12,
    13,14,15,16
  };

  int s2[] = {
    (1+2+5+6)/4, (3+4+7+8)/4,
    (9+10+13+14)/4, (11+12+15+16)/4
  };

  int i = 0;
  for (vint1& p : img) p[0] = s1[i++];

  pyramid2d<vint1> pyramid(img.domain(), 2, 2);

  assert(pyramid.factor() == 2);
  assert(pyramid.levels().size() == 2);
  assert(pyramid.levels()[0].domain() == img.domain());
  assert(pyramid.levels()[1].domain() == make_box2d(2, 2));

  pyramid.update(img);

  i = 0;
  for (const vint1& p : pyramid.levels()[1])
  {
    assert(p[0] == s2[i++]);
  }

}
<commit_msg>Remove the deprecated pyramid test.<commit_after>#include <iostream>
#include <vpp/core/image2d.hh>
#include <vpp/core/pyramid.hh>

int main()
{
  using namespace vpp;

  image2d<vint1> img(4, 4);

  // Todo.
}
<|endoftext|>


<commit_before>#include "keyword.hh"

#include <array>
#include <cassert>

using namespace std;

namespace {

struct Entry {
  Keyword k;
  const char* str;
};

static constexpr
array<Entry, static_cast<int>(Keyword::MAX)+1>
keyword_table{{
    { Keyword::not_keyword, "\0" },

    { Keyword::else_, "else" },
    { Keyword::r_arrow, "=>" },
    { Keyword::define, "define" },
    { Keyword::unquote, "unquote" },
    { Keyword::unquote_splicing, "unquote-splicing" },

    { Keyword::quote, "quote" },
    { Keyword::lambda, "lambda" },
    { Keyword::if_, "if" },
    { Keyword::set_, "set!" },
    { Keyword::begin, "begin" },
    { Keyword::cond, "cond" },
    { Keyword::and_, "and" },
    { Keyword::or_, "or" },
    { Keyword::case_, "case" },
    { Keyword::let, "let" },
    { Keyword::let_star, "let*" },
    { Keyword::letrec, "letrec" },
    { Keyword::do_, "do" },
    { Keyword::delay, "delay" },
    { Keyword::quasiquote, "quasiquote" },

    { Keyword::MAX, nullptr }
}};

} // namespace

const char* stringify(Keyword k){
  auto& e = keyword_table.at(static_cast<int>(k));
  assert(e.k == k);
  return e.str;
}

Keyword to_keyword(const std::string& s){
#if 0
  for(const auto& e : keyword_table){
    if(s == e.str)
      return e.k;
  }

  return Keyword::not_keyword;
#else
  if(s.empty())
    return Keyword::not_keyword;

  Keyword ret = Keyword::not_keyword;

  switch(s[0]){
  case '=':
    if(s.compare(1, string::npos, ">") == 0)
      ret = Keyword::r_arrow;
    break;

  case 'a':
    if(s.compare(1, string::npos, "nd") == 0)
      ret = Keyword::and_;
    break;

  case 'b':
    if(s.compare(1, string::npos, "egin") == 0)
      ret = Keyword::begin;
    break;

  case 'c':
    if(s.length() != 4) break;
    switch(s[1]){
    case 'a':
      if(s.compare(2, string::npos, "se") == 0)
        ret = Keyword::case_;
      break;
    case 'o':
      if(s.compare(2, string::npos, "nd") == 0)
        ret = Keyword::cond;
      break;
    }
    break;

  case 'd':
    if(s.length() < 2) break;
    switch(s[1]){
    case 'e':
      if(s.length() < 5) break;
      switch(s[2]){
      case 'f':
        if(s.compare(3, string::npos, "ine") == 0)
          ret = Keyword::define;
        break;
      case 'l':
        if(s.compare(3, string::npos, "ay") == 0)
          ret = Keyword::delay;
        break;
      }
      break;
    case 'o':
      if(s.length() == 2)
        ret = Keyword::do_;
      break;
    }
    break;

  case 'e':
    if(s.compare(1, string::npos, "lse") == 0)
      ret = Keyword::else_;
    break;

  case 'i':
    if(s.compare(1, string::npos, "f") == 0)
      ret = Keyword::if_;
    break;

  case 'l':
    if(s.length() < 3) break;
    switch(s[1]){
    case 'a':
      if(s.compare(2, string::npos, "mbda") == 0)
        ret = Keyword::lambda;
      break;
    case 'e':
      if(s[2] != 't') break;
      if(s.length() == 3)
        ret = Keyword::let;
      else if(s.compare(3, string::npos, "*") == 0)
        ret = Keyword::let_star;
      else if(s.compare(3, string::npos, "rec") == 0)
        ret = Keyword::letrec;
      break;
    }
    break;

  case 'o':
    if(s.compare(1, string::npos, "r") == 0)
      ret = Keyword::or_;
    break;

  case 'q':
    if(s.length() < 5 || s[1] != 'u') break;
    switch(s[2]){
    case 'a':
      if(s.compare(3, string::npos, "siquote") == 0)
        ret = Keyword::quasiquote;
      break;
    case 'o':
      if(s.compare(3, string::npos, "te") == 0)
        ret = Keyword::quote;
      break;
    }
    break;

  case 's':
    if(s.compare(1, string::npos, "et!") == 0)
      ret = Keyword::set_;
    break;

  case 'u': {
    auto cmp = s.compare(1, 7, "nquote");
    if(cmp == 0){
      ret = Keyword::unquote;
    }else if(cmp > 0 &&
             s.compare(7, string::npos, "-splicing") == 0)
      ret = Keyword::unquote_splicing;
    }
    break;
  }

  return ret;
#endif
}
<commit_msg>bitly cleanup<commit_after>#include "keyword.hh"

using namespace std;

const char* stringify(Keyword k){
  switch(k){
  case Keyword::else_:
    return "else";
  case Keyword::r_arrow:
    return "=>";
  case Keyword::define:
    return "define";
  case Keyword::unquote:
    return "unquote";
  case Keyword::unquote_splicing:
    return "unquote-splicing";
  case Keyword::quote:
    return "quote";
  case Keyword::lambda:
    return "lambda";
  case Keyword::if_:
    return "if";
  case Keyword::set_:
    return "set!";
  case Keyword::begin:
    return "begin";
  case Keyword::cond:
    return "cond";
  case Keyword::and_:
    return "and";
  case Keyword::or_:
    return "or";
  case Keyword::case_:
    return "case";
  case Keyword::let:
    return "let";
  case Keyword::let_star:
    return "let*";
  case Keyword::letrec:
    return "letrec";
  case Keyword::do_:
    return "do";
  case Keyword::delay:
    return "delay";
  case Keyword::quasiquote:
    return "quasiquote";
  default:
    return "(unknown keyword type)";
  }
}

Keyword to_keyword(const std::string& s){
  if(s.empty())
    return Keyword::not_keyword;

  Keyword ret = Keyword::not_keyword;

  switch(s[0]){
  case '=':
    if(s.compare(1, string::npos, ">") == 0)
      ret = Keyword::r_arrow;
    break;

  case 'a':
    if(s.compare(1, string::npos, "nd") == 0)
      ret = Keyword::and_;
    break;

  case 'b':
    if(s.compare(1, string::npos, "egin") == 0)
      ret = Keyword::begin;
    break;

  case 'c':
    if(s.length() != 4) break;
    switch(s[1]){
    case 'a':
      if(s.compare(2, string::npos, "se") == 0)
        ret = Keyword::case_;
      break;
    case 'o':
      if(s.compare(2, string::npos, "nd") == 0)
        ret = Keyword::cond;
      break;
    }
    break;

  case 'd':
    if(s.length() < 2) break;
    switch(s[1]){
    case 'e':
      if(s.length() < 5) break;
      switch(s[2]){
      case 'f':
        if(s.compare(3, string::npos, "ine") == 0)
          ret = Keyword::define;
        break;
      case 'l':
        if(s.compare(3, string::npos, "ay") == 0)
          ret = Keyword::delay;
        break;
      }
      break;
    case 'o':
      if(s.length() == 2)
        ret = Keyword::do_;
      break;
    }
    break;

  case 'e':
    if(s.compare(1, string::npos, "lse") == 0)
      ret = Keyword::else_;
    break;

  case 'i':
    if(s.compare(1, string::npos, "f") == 0)
      ret = Keyword::if_;
    break;

  case 'l':
    if(s.length() < 3) break;
    switch(s[1]){
    case 'a':
      if(s.compare(2, string::npos, "mbda") == 0)
        ret = Keyword::lambda;
      break;
    case 'e':
      if(s[2] != 't') break;
      if(s.length() == 3)
        ret = Keyword::let;
      else if(s.compare(3, string::npos, "*") == 0)
        ret = Keyword::let_star;
      else if(s.compare(3, string::npos, "rec") == 0)
        ret = Keyword::letrec;
      break;
    }
    break;

  case 'o':
    if(s.compare(1, string::npos, "r") == 0)
      ret = Keyword::or_;
    break;

  case 'q':
    if(s.length() < 5 || s[1] != 'u') break;
    switch(s[2]){
    case 'a':
      if(s.compare(3, string::npos, "siquote") == 0)
        ret = Keyword::quasiquote;
      break;
    case 'o':
      if(s.compare(3, string::npos, "te") == 0)
        ret = Keyword::quote;
      break;
    }
    break;

  case 's':
    if(s.compare(1, string::npos, "et!") == 0)
      ret = Keyword::set_;
    break;

  case 'u': {
    auto cmp = s.compare(1, 7, "nquote");
    if(cmp == 0){
      ret = Keyword::unquote;
    }else if(cmp > 0 &&
             s.compare(7, string::npos, "-splicing") == 0)
      ret = Keyword::unquote_splicing;
    }
    break;
  }

  return ret;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "server.hh"
#include "handler.hh"
#include "core/future-util.hh"
#include "core/circular_buffer.hh"
#include <seastar/core/metrics.hh>
#include "net/byteorder.hh"
#include "core/scattered_message.hh"
#include "log.hh"
#include <thrift/server/TServer.h>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/TProcessor.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/async/TAsyncProcessor.h>
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>
#include <bitset>
#include <limits>
#include <cctype>
#include <vector>
#include <boost/make_shared.hpp>

static logging::logger tlogger("thrift");

using namespace apache::thrift;
using namespace apache::thrift::transport;
using namespace apache::thrift::protocol;
using namespace apache::thrift::async;
using namespace ::cassandra;

using namespace std::chrono_literals;

class thrift_stats {
    seastar::metrics::metric_groups _metrics;
public:
    thrift_stats(thrift_server& server);
};

thrift_server::thrift_server(distributed<database>& db,
                             distributed<cql3::query_processor>& qp)
        : _stats(new thrift_stats(*this))
        , _handler_factory(create_handler_factory(db, qp).release())
        , _protocol_factory(new TBinaryProtocolFactoryT<TMemoryBuffer>())
        , _processor_factory(new CassandraAsyncProcessorFactory(_handler_factory)) {
}

thrift_server::~thrift_server() {
}

future<> thrift_server::stop() {
    std::for_each(_connections_list.begin(), _connections_list.end(), std::mem_fn(&connection::shutdown));
    return make_ready_future<>();
}

struct handler_deleter {
    CassandraCobSvIfFactory* hf;
    void operator()(CassandraCobSvIf* h) const {
        hf->releaseHandler(h);
    }
};

// thrift uses a shared_ptr to refer to the transport (= connection),
// while we do not, so we can't have connection inherit from TTransport.
struct thrift_server::connection::fake_transport : TTransport {
    fake_transport(thrift_server::connection* c) : conn(c) {}
    thrift_server::connection* conn;
};

thrift_server::connection::connection(thrift_server& server, connected_socket&& fd, socket_address addr)
        : _server(server), _fd(std::move(fd)), _read_buf(_fd.input())
        , _write_buf(_fd.output())
        , _transport(boost::make_shared<thrift_server::connection::fake_transport>(this))
        , _input(boost::make_shared<TMemoryBuffer>())
        , _output(boost::make_shared<TMemoryBuffer>())
        , _in_proto(_server._protocol_factory->getProtocol(_input))
        , _out_proto(_server._protocol_factory->getProtocol(_output))
        , _processor(_server._processor_factory->getProcessor({ _in_proto, _out_proto, _transport })) {
    ++_server._total_connections;
    ++_server._current_connections;
    _server._connections_list.push_back(*this);
}

thrift_server::connection::~connection() {
    if (is_linked()) {
        --_server._current_connections;
        _server._connections_list.erase(_server._connections_list.iterator_to(*this));
    }
}

thrift_server::connection::connection(connection&& other)
        : _server(other._server)
        , _fd(std::move(other._fd))
        , _read_buf(std::move(other._read_buf))
        , _write_buf(std::move(other._write_buf))
        , _transport(std::move(other._transport))
        , _input(std::move(other._input))
        , _output(std::move(other._output))
        , _in_proto(std::move(other._in_proto))
        , _out_proto(std::move(other._out_proto))
        , _processor(std::move(other._processor)) {
    if (other.is_linked()) {
        boost::intrusive::list<connection>::node_algorithms::init(this_ptr());
        boost::intrusive::list<connection>::node_algorithms::swap_nodes(other.this_ptr(), this_ptr());
    }
}

future<>
thrift_server::connection::process() {
    return do_until([this] { return _read_buf.eof(); },
            [this] { return process_one_request(); })
        .finally([this] {
            return _write_buf.close();
        });
}

future<>
thrift_server::connection::process_one_request() {
    _input->resetBuffer();
    _output->resetBuffer();
    return read().then([this] {
        ++_server._requests_served;
        auto ret = _processor_promise.get_future();
        // adapt from "continuation object style" to future/promise
        auto complete = [this] (bool success) mutable {
            // FIXME: look at success?
            write().forward_to(std::move(_processor_promise));
            _processor_promise = promise<>();
        };
        _processor->process(complete, _in_proto, _out_proto);
        return ret;
    });
}

future<>
thrift_server::connection::read() {
    return _read_buf.read_exactly(4).then([this] (temporary_buffer<char> size_buf) {
        if (size_buf.size() != 4) {
            return make_ready_future<>();
        }
        union {
            uint32_t n;
            char b[4];
        } data;
        std::copy_n(size_buf.get(), 4, data.b);
        auto n = ntohl(data.n);
        return _read_buf.read_exactly(n).then([this, n] (temporary_buffer<char> buf) {
            if (buf.size() != n) {
                // FIXME: exception perhaps?
                return;
            }
            _in_tmp = std::move(buf); // keep ownership of the data
            auto b = reinterpret_cast<uint8_t*>(_in_tmp.get_write());
            _input->resetBuffer(b, _in_tmp.size());
        });
    });
}

future<>
thrift_server::connection::write() {
    uint8_t* data;
    uint32_t len;
    _output->getBuffer(&data, &len);
    net::packed<uint32_t> plen = { net::hton(len) };
    return _write_buf.write(reinterpret_cast<char*>(&plen), 4).then([this, data, len] {
        // FIXME: zero-copy
        return _write_buf.write(reinterpret_cast<char*>(data), len);
    }).then([this] {
        return _write_buf.flush();
    });
}

void
thrift_server::connection::shutdown() {
    try {
        _fd.shutdown_input();
        _fd.shutdown_output();
    } catch (...) {
    }
}

future<>
thrift_server::listen(ipv4_addr addr, bool keepalive) {
    listen_options lo;
    lo.reuse_address = true;
    _listeners.push_back(engine().listen(make_ipv4_address(addr), lo));
    do_accepts(_listeners.size() - 1, keepalive);
    return make_ready_future<>();
}

void
thrift_server::do_accepts(int which, bool keepalive) {
    _listeners[which].accept().then([this, which, keepalive] (connected_socket fd, socket_address addr) mutable {
        fd.set_nodelay(true);
        fd.set_keepalive(keepalive);
        auto conn = new connection(*this, std::move(fd), addr);
        conn->process().then_wrapped([this, conn] (future<> f) {
            conn->shutdown();
            delete conn;
            try {
                f.get();
            } catch (std::exception& ex) {
                tlogger.debug("request error {}", ex.what());
            }
        });
        do_accepts(which, keepalive);
    }).handle_exception([this, which, keepalive] (auto ex) {
        tlogger.debug("accept failed {}", ex);
        maybe_retry_accept(which, keepalive, std::move(ex));
    });
}

void thrift_server::maybe_retry_accept(int which, bool keepalive, std::exception_ptr ex) {
    auto retry = [this, which, keepalive] {
        tlogger.debug("retrying accept after failure");
        do_accepts(which, keepalive);
    };
    auto retry_with_backoff = [&] {
        // FIXME: Consider using exponential backoff
        sleep(1ms).then([retry = std::move(retry)] { retry(); });
    };
    try {
        std::rethrow_exception(std::move(ex));
    } catch (const std::system_error& e) {
        switch (e.code().value()) {
            // FIXME: Don't retry for other fatal errors
            case EBADF:
                break;
            case ENFILE:
            case EMFILE:
            case ENOMEM:
                retry_with_backoff();
            default:
                retry();
        }
    } catch (const std::bad_alloc&) {
        retry_with_backoff();
    } catch (...) {
        retry();
    }
}

uint64_t
thrift_server::total_connections() const {
    return _total_connections;
}

uint64_t
thrift_server::current_connections() const {
    return _current_connections;
}

uint64_t
thrift_server::requests_served() const {
    return _requests_served;
}

thrift_stats::thrift_stats(thrift_server& server) {
    namespace sm = seastar::metrics;

    _metrics.add_group("thrift", {
        sm::make_derive("thrift-connections", [&server] { return server.total_connections(); },
                        sm::description("Rate of creation of new Thrift connections.")),

        sm::make_gauge("current_connections", [&server] { return server.current_connections(); },
                        sm::description("Holds a current number of opened Thrift connections.")),

        sm::make_derive("served", [&server] { return server.requests_served(); },
                        sm::description("Rate of serving Thrift requests.")),
    });
}

<commit_msg>thrift/server: Avoid manual memory management<commit_after>/*
 * Copyright (C) 2014 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "server.hh"
#include "handler.hh"
#include "core/future-util.hh"
#include "core/circular_buffer.hh"
#include <seastar/core/metrics.hh>
#include "net/byteorder.hh"
#include "core/scattered_message.hh"
#include "log.hh"
#include <thrift/server/TServer.h>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/TProcessor.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/async/TAsyncProcessor.h>
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>
#include <bitset>
#include <limits>
#include <cctype>
#include <vector>
#include <boost/make_shared.hpp>

static logging::logger tlogger("thrift");

using namespace apache::thrift;
using namespace apache::thrift::transport;
using namespace apache::thrift::protocol;
using namespace apache::thrift::async;
using namespace ::cassandra;

using namespace std::chrono_literals;

class thrift_stats {
    seastar::metrics::metric_groups _metrics;
public:
    thrift_stats(thrift_server& server);
};

thrift_server::thrift_server(distributed<database>& db,
                             distributed<cql3::query_processor>& qp)
        : _stats(new thrift_stats(*this))
        , _handler_factory(create_handler_factory(db, qp).release())
        , _protocol_factory(new TBinaryProtocolFactoryT<TMemoryBuffer>())
        , _processor_factory(new CassandraAsyncProcessorFactory(_handler_factory)) {
}

thrift_server::~thrift_server() {
}

future<> thrift_server::stop() {
    std::for_each(_connections_list.begin(), _connections_list.end(), std::mem_fn(&connection::shutdown));
    return make_ready_future<>();
}

struct handler_deleter {
    CassandraCobSvIfFactory* hf;
    void operator()(CassandraCobSvIf* h) const {
        hf->releaseHandler(h);
    }
};

// thrift uses a shared_ptr to refer to the transport (= connection),
// while we do not, so we can't have connection inherit from TTransport.
struct thrift_server::connection::fake_transport : TTransport {
    fake_transport(thrift_server::connection* c) : conn(c) {}
    thrift_server::connection* conn;
};

thrift_server::connection::connection(thrift_server& server, connected_socket&& fd, socket_address addr)
        : _server(server), _fd(std::move(fd)), _read_buf(_fd.input())
        , _write_buf(_fd.output())
        , _transport(boost::make_shared<thrift_server::connection::fake_transport>(this))
        , _input(boost::make_shared<TMemoryBuffer>())
        , _output(boost::make_shared<TMemoryBuffer>())
        , _in_proto(_server._protocol_factory->getProtocol(_input))
        , _out_proto(_server._protocol_factory->getProtocol(_output))
        , _processor(_server._processor_factory->getProcessor({ _in_proto, _out_proto, _transport })) {
    ++_server._total_connections;
    ++_server._current_connections;
    _server._connections_list.push_back(*this);
}

thrift_server::connection::~connection() {
    if (is_linked()) {
        --_server._current_connections;
        _server._connections_list.erase(_server._connections_list.iterator_to(*this));
    }
}

thrift_server::connection::connection(connection&& other)
        : _server(other._server)
        , _fd(std::move(other._fd))
        , _read_buf(std::move(other._read_buf))
        , _write_buf(std::move(other._write_buf))
        , _transport(std::move(other._transport))
        , _input(std::move(other._input))
        , _output(std::move(other._output))
        , _in_proto(std::move(other._in_proto))
        , _out_proto(std::move(other._out_proto))
        , _processor(std::move(other._processor)) {
    if (other.is_linked()) {
        boost::intrusive::list<connection>::node_algorithms::init(this_ptr());
        boost::intrusive::list<connection>::node_algorithms::swap_nodes(other.this_ptr(), this_ptr());
    }
}

future<>
thrift_server::connection::process() {
    return do_until([this] { return _read_buf.eof(); },
            [this] { return process_one_request(); })
        .finally([this] {
            return _write_buf.close();
        });
}

future<>
thrift_server::connection::process_one_request() {
    _input->resetBuffer();
    _output->resetBuffer();
    return read().then([this] {
        ++_server._requests_served;
        auto ret = _processor_promise.get_future();
        // adapt from "continuation object style" to future/promise
        auto complete = [this] (bool success) mutable {
            // FIXME: look at success?
            write().forward_to(std::move(_processor_promise));
            _processor_promise = promise<>();
        };
        _processor->process(complete, _in_proto, _out_proto);
        return ret;
    });
}

future<>
thrift_server::connection::read() {
    return _read_buf.read_exactly(4).then([this] (temporary_buffer<char> size_buf) {
        if (size_buf.size() != 4) {
            return make_ready_future<>();
        }
        union {
            uint32_t n;
            char b[4];
        } data;
        std::copy_n(size_buf.get(), 4, data.b);
        auto n = ntohl(data.n);
        return _read_buf.read_exactly(n).then([this, n] (temporary_buffer<char> buf) {
            if (buf.size() != n) {
                // FIXME: exception perhaps?
                return;
            }
            _in_tmp = std::move(buf); // keep ownership of the data
            auto b = reinterpret_cast<uint8_t*>(_in_tmp.get_write());
            _input->resetBuffer(b, _in_tmp.size());
        });
    });
}

future<>
thrift_server::connection::write() {
    uint8_t* data;
    uint32_t len;
    _output->getBuffer(&data, &len);
    net::packed<uint32_t> plen = { net::hton(len) };
    return _write_buf.write(reinterpret_cast<char*>(&plen), 4).then([this, data, len] {
        // FIXME: zero-copy
        return _write_buf.write(reinterpret_cast<char*>(data), len);
    }).then([this] {
        return _write_buf.flush();
    });
}

void
thrift_server::connection::shutdown() {
    try {
        _fd.shutdown_input();
        _fd.shutdown_output();
    } catch (...) {
    }
}

future<>
thrift_server::listen(ipv4_addr addr, bool keepalive) {
    listen_options lo;
    lo.reuse_address = true;
    _listeners.push_back(engine().listen(make_ipv4_address(addr), lo));
    do_accepts(_listeners.size() - 1, keepalive);
    return make_ready_future<>();
}

void
thrift_server::do_accepts(int which, bool keepalive) {
    _listeners[which].accept().then([this, which, keepalive] (connected_socket fd, socket_address addr) mutable {
        fd.set_nodelay(true);
        fd.set_keepalive(keepalive);
        do_with(connection(*this, std::move(fd), addr), [this] (auto& conn) {
            return conn.process().then_wrapped([this, &conn] (future<> f) {
                conn.shutdown();
                try {
                    f.get();
                } catch (std::exception& ex) {
                    tlogger.debug("request error {}", ex.what());
                }
            });
        });
        do_accepts(which, keepalive);
    }).handle_exception([this, which, keepalive] (auto ex) {
        tlogger.debug("accept failed {}", ex);
        maybe_retry_accept(which, keepalive, std::move(ex));
    });
}

void thrift_server::maybe_retry_accept(int which, bool keepalive, std::exception_ptr ex) {
    auto retry = [this, which, keepalive] {
        tlogger.debug("retrying accept after failure");
        do_accepts(which, keepalive);
    };
    auto retry_with_backoff = [&] {
        // FIXME: Consider using exponential backoff
        sleep(1ms).then([retry = std::move(retry)] { retry(); });
    };
    try {
        std::rethrow_exception(std::move(ex));
    } catch (const std::system_error& e) {
        switch (e.code().value()) {
            // FIXME: Don't retry for other fatal errors
            case EBADF:
                break;
            case ENFILE:
            case EMFILE:
            case ENOMEM:
                retry_with_backoff();
            default:
                retry();
        }
    } catch (const std::bad_alloc&) {
        retry_with_backoff();
    } catch (...) {
        retry();
    }
}

uint64_t
thrift_server::total_connections() const {
    return _total_connections;
}

uint64_t
thrift_server::current_connections() const {
    return _current_connections;
}

uint64_t
thrift_server::requests_served() const {
    return _requests_served;
}

thrift_stats::thrift_stats(thrift_server& server) {
    namespace sm = seastar::metrics;

    _metrics.add_group("thrift", {
        sm::make_derive("thrift-connections", [&server] { return server.total_connections(); },
                        sm::description("Rate of creation of new Thrift connections.")),

        sm::make_gauge("current_connections", [&server] { return server.current_connections(); },
                        sm::description("Holds a current number of opened Thrift connections.")),

        sm::make_derive("served", [&server] { return server.requests_served(); },
                        sm::description("Rate of serving Thrift requests.")),
    });
}

<|endoftext|>


<commit_before>#include <cassert>
#include <boost/cstdint.hpp>
#include <boost/integer/static_log2.hpp>

void fatal(const char*, ...);
extern "C" unsigned long hashbytes(unsigned char[], unsigned int);

class Tiles;

template<int Ntiles> class PackedTiles {
	friend class Tiles;

	enum {
		Nbits = boost::static_log2<Ntiles>::value,
		Nbytes = (int) ((Nbits / 8.0) * Ntiles),
	};

	unsigned char bytes[Nbytes];

public:

	void pack(Tiles::Tile ts[]) {
		fatal("Tiles::pack is unimplemented");
	}

	Tiles::Pos unpack(Tiles::Tile ts[]) {
		fatal("Tiles::unpack is unimplemented");
		return 0;
	}

	Tiles::Pos unpack_md(const unsigned int md[][Ntiles], Tiles::Tile ts[], Tiles::Cost *h) {
		*h = 0;
		fatal("Tiles::unpack_md is unimplemented");
		return 0;
	}

	unsigned long hash(const void*) {
		return hashbytes(bytes, sizeof(bytes));
	}

	bool eq(const void*, PackedTiles &o) const {
		return memcmp(bytes, o.bytes, sizeof(bytes)) == 0;
	}
};

template<> class PackedTiles<16> {
	friend class Tiles;

	enum { Ntiles = 16 };

	boost::uint64_t word;

public:

	void pack(Tiles::Tile ts[]) {
		word = 0;
		for (int i = 0; i < Ntiles; i++)
			word = (word << 4) | ts[i];
	}

	Tiles::Pos unpack(Tiles::Tile ts[]) {
		int b;
		boost::uint64_t w = word;
		for (int i = Ntiles - 1; i >= 0; i--) {
			Tiles::Tile t = w & 0xF;
			w >>= 4;
			ts[i] = t;
			if (t == 0)
				b = i;
		}
		return b;
	}

	Tiles::Pos unpack_md(const unsigned int md[][Ntiles], Tiles::Tile ts[], Tiles::Cost *hp) {
		int b = -1;
		Tiles::Cost h = 0;
		boost::uint64_t w = word;
		for (int i = Ntiles - 1; i >= 0; i--) {
			Tiles::Tile t = w & 0xF;
			w >>= 4;
			ts[i] = t;
			if (t == 0)
				b = i;
			else
				h += md[t][i];
		}
		*hp = h;
		return b;
	}

	unsigned long hash(const void*) {
		return word;
	}

	bool eq(const void*, const PackedTiles &o) const {
		return word == o.word;
	}
};
<commit_msg>tiles: support 8 puzzles.<commit_after>#include <cassert>
#include <boost/cstdint.hpp>
#include <boost/integer/static_log2.hpp>

void fatal(const char*, ...);
extern "C" unsigned long hashbytes(unsigned char[], unsigned int);

class Tiles;

template<int Ntiles> class PackedTiles {
	friend class Tiles;

	enum {
		Nbits = boost::static_log2<Ntiles>::value,
		Nbytes = (int) ((Nbits / 8.0) * Ntiles),
	};

	unsigned char bytes[Nbytes];

public:

	void pack(Tiles::Tile ts[]) {
		fatal("Tiles::pack is unimplemented");
	}

	Tiles::Pos unpack(Tiles::Tile ts[]) {
		fatal("Tiles::unpack is unimplemented");
		return 0;
	}

	Tiles::Pos unpack_md(const unsigned int md[][Ntiles], Tiles::Tile ts[], Tiles::Cost *h) {
		*h = 0;
		fatal("Tiles::unpack_md is unimplemented");
		return 0;
	}

	unsigned long hash(const void*) {
		return hashbytes(bytes, sizeof(bytes));
	}

	bool eq(const void*, PackedTiles &o) const {
		return memcmp(bytes, o.bytes, sizeof(bytes)) == 0;
	}
};

template<int Ntiles> class PackedTiles64 {
	friend class Tiles;

	boost::uint64_t word;

public:

	void pack(Tiles::Tile ts[]) {
		word = 0;
		for (int i = 0; i < Ntiles; i++)
			word = (word << 4) | ts[i];
	}

	Tiles::Pos unpack(Tiles::Tile ts[]) {
		int b;
		boost::uint64_t w = word;
		for (int i = Ntiles - 1; i >= 0; i--) {
			Tiles::Tile t = w & 0xF;
			w >>= 4;
			ts[i] = t;
			if (t == 0)
				b = i;
		}
		return b;
	}

	Tiles::Pos unpack_md(const unsigned int md[][Ntiles], Tiles::Tile ts[], Tiles::Cost *hp) {
		int b = -1;
		Tiles::Cost h = 0;
		boost::uint64_t w = word;
		for (int i = Ntiles - 1; i >= 0; i--) {
			Tiles::Tile t = w & 0xF;
			w >>= 4;
			ts[i] = t;
			if (t == 0)
				b = i;
			else
				h += md[t][i];
		}
		*hp = h;
		return b;
	}

	unsigned long hash(const void*) {
		return word;
	}

	bool eq(const void*, const PackedTiles64 &o) const {
		return word == o.word;
	}
};

template<> class PackedTiles<16> : public PackedTiles64<16> {
};

template<> class PackedTiles<9> : public PackedTiles64<9> {
};

<|endoftext|>


<commit_before>{
// .........................macro dirs.C............................
// This macro illustrates how to create a hierarchy of directories
// in a Root file.
// 10 directories called plane0, plane1, plane9 are created.
// Each plane directory contains 200 histograms.
//
// Run this macro (Note that the macro delete the TFile object at the end!)
// Connect the file again in read mode:
//   Root > TFile *top = new TFile("top.root");
// The hierarchy can be browsed by the Root browser as shown below
//   Root > TBrowser b;
//    click on the left pane on one of the plane directories.
//    this shows the list of all histograms in this directory.
//    Double click on one histogram to draw it (left mouse button).
//    Select different options with the right mouse button.
//
//  You can see the begin_html picture of the browser  end_html
//
//    Instead of using the browser, you can also do:
//   Root > top->cd();
//   Root > plane3->cd();
//   Root > h3_90N->Draw();

   gROOT->Reset();

    // create a new Root file
   TFile *top = new TFile("top.root","recreate");

   // create a subdirectory "tof" in this file
   TDirectory *cdtof = top->mkdir("tof");
   cdtof->cd();    // make the "tof" directory the current directory

   // create a new subdirectory for each plane
   const Int_t nplanes = 10;
   const Int_t ncounters = 100;
   char dirname[50];
   char hname[20];
   char htitle[80];
   Int_t i,j,k;
   TDirectory *cdplane[nplanes];
   TH1F *hn[nplanes][ncounters];
   TH1F *hs[nplanes][ncounters];
   for (i=0;i<nplanes;i++) {
      sprintf(dirname,"plane%d",i);
      cdplane[i] = cdtof->mkdir(dirname);
      cdplane[i]->cd();
      // create counter histograms
      for (j=0;j<ncounters;j++) {
         sprintf(hname,"h%d_%dN",i,j);
         sprintf(htitle,"hist for counter:%d in plane:%d North",j,i);
         hn[i][j] = new TH1F(hname,htitle,100,0,100);
         sprintf(hname,"h%d_%dS",i,j);
         sprintf(htitle,"hist for counter:%d in plane:%d South",j,i);
         hs[i][j] = new TH1F(hname,htitle,100,0,100);
      }
      cdtof->cd();    // change current directory to top
   }

     // .. fill histograms
   TRandom r;
   for (i=0;i<nplanes;i++) {
      cdplane[i]->cd();
      for (j=0;j<ncounters;j++) {
         for (k=0;k<100;k++) {
            hn[i][j]->Fill(100*r.Rndm(),i+j);
            hs[i][j]->Fill(100*r.Rndm(),i+j+k);
         }
      }
   }

     // save histogram hierarchy in the file
   top->Write();
   delete top;
}

<commit_msg>Fix a typo<commit_after>{
// .........................macro dirs.C............................
// This macro illustrates how to create a hierarchy of directories
// in a Root file.
// 10 directories called plane0, plane1, plane9 are created.
// Each plane directory contains 200 histograms.
//
// Run this macro (Note that the macro delete the TFile object at the end!)
// Connect the file again in read mode:
//   Root > TFile *top = new TFile("top.root");
// The hierarchy can be browsed by the Root browser as shown below
//   Root > TBrowser b;
//    click on the left pane on one of the plane directories.
//    this shows the list of all histograms in this directory.
//    Double click on one histogram to draw it (left mouse button).
//    Select different options with the right mouse button.
//
//  You can see the begin_html picture of the browser  end_html
//
//    Instead of using the browser, you can also do:
//   Root > tof->cd();
//   Root > plane3->cd();
//   Root > h3_90N->Draw();

   gROOT->Reset();

    // create a new Root file
   TFile *top = new TFile("top.root","recreate");

   // create a subdirectory "tof" in this file
   TDirectory *cdtof = top->mkdir("tof");
   cdtof->cd();    // make the "tof" directory the current directory

   // create a new subdirectory for each plane
   const Int_t nplanes = 10;
   const Int_t ncounters = 100;
   char dirname[50];
   char hname[20];
   char htitle[80];
   Int_t i,j,k;
   TDirectory *cdplane[nplanes];
   TH1F *hn[nplanes][ncounters];
   TH1F *hs[nplanes][ncounters];
   for (i=0;i<nplanes;i++) {
      sprintf(dirname,"plane%d",i);
      cdplane[i] = cdtof->mkdir(dirname);
      cdplane[i]->cd();
      // create counter histograms
      for (j=0;j<ncounters;j++) {
         sprintf(hname,"h%d_%dN",i,j);
         sprintf(htitle,"hist for counter:%d in plane:%d North",j,i);
         hn[i][j] = new TH1F(hname,htitle,100,0,100);
         sprintf(hname,"h%d_%dS",i,j);
         sprintf(htitle,"hist for counter:%d in plane:%d South",j,i);
         hs[i][j] = new TH1F(hname,htitle,100,0,100);
      }
      cdtof->cd();    // change current directory to top
   }

     // .. fill histograms
   TRandom r;
   for (i=0;i<nplanes;i++) {
      cdplane[i]->cd();
      for (j=0;j<ncounters;j++) {
         for (k=0;k<100;k++) {
            hn[i][j]->Fill(100*r.Rndm(),i+j);
            hs[i][j]->Fill(100*r.Rndm(),i+j+k);
         }
      }
   }

     // save histogram hierarchy in the file
   top->Write();
   delete top;
}

<|endoftext|>


<commit_before>/**\file
 * \ingroup example-programmes
 * \brief Matrix-esque terminal animation
 *
 * This is a terminal programme that uses libefgy's vt100 code to render a text
 * version of the matrix 'scrolling streams of text' animation. It's really
 * fairly simple but also kinda nice to see how the vt100 output is performing.
 *
 * \image html matrix.png "Screenshot of the programme running in Terminal.app"
 *
 * \copyright
 * This file is part of the libefgy project, which is released as open source
 * under the terms of an MIT/X11-style licence, described in the COPYING file.
 *
 * \see Project Documentation: https://ef.gy/documentation/libefgy
 * \see Project Source Code: https://github.com/ef-gy/libefgy
 * \see Licence Terms: https://github.com/ef-gy/libefgy/blob/master/COPYING
 */

#include <iostream>

#include <ef.gy/vt100.h>
#include <chrono>
#include <csignal>
#include <cmath>
#include <sched.h>
#include <random>

using namespace efgy;
using namespace std::chrono;

/**\brief Data and functions related to the matrix demo
 *
 * Contains global variables and classes used by the 'matrix' demo animation.
 * This is a separate namespace to keep things all neat, clean and tidy.
 */
namespace thematrix {
/**\brief VT100 output buffer
 *
 * Encapsulates stdio to automatically generate the terminal escape
 * sequences to write things at specific positions with specific colours.
 */
terminal::vt100<> output;

/**\brief Random number generator
 *
 * A seeded instance of the mersenne twister RNG; used to position the
 * streams and to add and remove glyphs randomly.
 */
std::mt19937 rng(1337);

/**\brief Current time
 *
 * The point in time when processing of the current streams started; used
 * to determine when streams should be updated.
 */
system_clock::time_point now;

/**\brief A stream of data
 *
 * A 'stream' is what I dubbed the individual columns of text in the
 * animation. These are generated randomly and they mutate randomly as
 * well.
 */
class stream {
public:
  /**\brief A single glyph in a stream
   *
   * To create the animation, each of the glyphs in the output needs
   * to be tagges with the time it was created, in order to know
   * which colour to draw the glyph in.
   */
  class cell {
  public:
    /**\brief Construct with glyph
     *
     * Constructs an instance of the class using the given
     * character and the current system time.
     *
     * \param[in] pCharacter A unicode code point.
     */
    cell(const unsigned long &pCharacter)
        : character(pCharacter), created(now) {}

    /**\brief The glyph
     *
     * This is the glyph to output; must be a valid unicode
     * glyph as the vt100 code will expect one to send to the
     * terminal.
     */
    unsigned long character;

    /**\brief When was this object created?
     *
     * Contains the time point of this object's creation; used
     * to determine the colour of the glyph in the output;
     */
    system_clock::time_point created;
  };

  /**\brief Construct with position
   *
   * Initialises an instance of the class given the line and column
   * of where the stream should appear.
   *
   * \param[in] pLine   The line at which to create the stream; must
   *                    be less than output.size()[1].
   * \param[in] pColumn The column at which to create the stream;
   *                    must be less than output.size()[0].
   */
  stream(const std::size_t &pLine, const std::size_t &pColumn)
      : line(pLine), column(pColumn), last(now), doDelete(false) {}

  /**\brief Line component of stream position
   *
   * This is the line at which the stream is rendered to the
   * terminal.
   */
  std::size_t line;

  /**\brief Column component of stream position
   *
   * This is the column at which the stream is rendered to the
   * terminal.
   */
  std::size_t column;

  /**\brief The stream contents to render
   *
   * A vector of glyphs and the time at which they were inserted into
   * the stream; this vector is placed vertically at the coordinates
   * given by line and column. Recent glyphs are rendered in white,
   * older ones in green.
   */
  std::vector<cell> data;

  /**\brief Time of last update
   *
   * Records the last time a glyph was inserted into or removed from
   * the stream; used to determine when to add new glyphs.
   */
  system_clock::time_point last;

  /**\brief Should this stream be deleted?
   *
   * Set to true when the upper part of the stream reaches the bottom
   * of the screen; the stream should then be deleted in the main
   * loop because it won't produce any output on the screen when this
   * happens.
   */
  bool doDelete;

  /**\brief Update and render stream
   *
   * Render the current contents of the stream to the screen; if it
   * so happens that enough time has passed since the last time this
   * method was called then this function will also randomly add or
   * remove glyphs, or move the stream further down.
   *
   * \returns 'true' if the update went smoothly; 'false' otherwise.
   *          There is currently no way this function can fail, so
   *          at the moment it will always return 'true'.
   */
  bool update(void) {
    if ((now - last) > std::chrono::milliseconds(10)) {
      last = now;
      switch (rng() % 3) {
      case 0:
        if (data.size() > 0) {
          data.erase(data.begin() + (rng() % data.size()));
          break;
        }
      default:
        data.push_back(cell(rng() % (1 << 7)));
      }
    }
    std::array<std::size_t, 2> s = output.size();
    if (data.size() > (s[1] / 2)) {
      if (line < s[1]) {
        if (rng() % 5 == 0) {
          output.target[line][column].content = ' ';
          line++;
        }
      } else {
        doDelete = true;
      }
      data.erase(data.begin());
    }
    int i = line;
    for (cell &d : data) {
      if (i >= s[1]) {
        break;
      }
      output.target[i][column].content = d.character;
      output.target[i][column].foregroundColour =
          (now - d.created) > std::chrono::milliseconds(120) ? 2 : 7;
      output.target[i][column].backgroundColour = 0;
      i++;
    }
    return true;
  }
};

/**\brief SIGINT handler
 *
 * Signal handler that calls exit() properly; the SIGINT handler is
 * replaced by this function. While SIGINT would ordinarily terminate the
 * programme anyway, it doesn't do so with a proper call to exit(), meaning
 * that destructors will not be called, further meaning that the terminal
 * can't be reset to a proper state by the terminal handling code, because
 * not using exit() properly means destructors won't run.
 */
void handle_interrupt(int signal) { exit(0); }

/**\brief Green-tinted post processing function
 *
 * A simple post-processing function for the vt100 code, which makes sure
 * that any output cell is always either white or green. This makes it
 * easier for the optimiser as it won't have to switch colours quite as
 * often as it otherwise might.
 */
terminal::cell<long> postProcess(const terminal::terminal<long> &t,
                                 const std::size_t &l, const std::size_t &c) {
  terminal::cell<long> rv = t.target[l][c];
  rv.content = rv.content == 0 ? ' ' : rv.content;
  rv.foregroundColour = rv.foregroundColour == 7 ? 7 : 2;
  rv.backgroundColour = 0;
  return rv;
}

/**\brief Alternative post processing function
 *
 * Adds some curviness to the output by modifying the input coordinates a
 * bit before creating the output. Kinda looks funky, but not really
 * matrix-y. You'll have to modify the main() function yourself if you want
 * to see this in action.
 */
terminal::cell<long> postProcessPolar(const terminal::terminal<long> &t,
                                      const std::size_t &pl,
                                      const std::size_t &pc) {
  double l = pl;
  double c = pc;
  std::array<std::size_t, 2> s = t.size();
  double hl = s[1] / 2;
  double hc = s[0] / 2;
  double loff = l - hl;
  double coff = c - hc;
  double r2 = loff * loff + coff * coff;
  double r = std::sqrt(r2);
  l = hl + loff + std::sin(r) * 1.0;
  c = hc + coff + std::cos(r) * 1.0;
  const std::size_t tl = (std::size_t)l < s[1] ? (std::size_t)l : (s[1] - 1);
  const std::size_t tc = (std::size_t)c < s[0] ? (std::size_t)c : (s[0] - 1);
  terminal::cell<long> rv = t.target[tl][tc];
  terminal::cell<long> cv = t.current[tl][tc];
  rv.content =
      rv.content == 0 ? (cv.content == 0 ? ' ' : cv.content) : rv.content;
  rv.foregroundColour = rv.foregroundColour == 7 ? 7 : 2;
  rv.backgroundColour = 0;
  return rv;
}
};

using namespace thematrix;

/**\brief Matrix demo main function
 *
 * Entry point for the matrix programme; resizes the output buffer to encompass
 * the whole terminal, sets up a SIGINT handler and then maintains a vector of
 * thematrix::stream objects at random positions.
 *
 * Use CTRL+C to terminate the programme.
 *
 * \note Command line arguments and the programme environment are ignored.
 *
 * \returns 0 for 'success', but this should never be reached as the SIGINT
 *          handler calls exit(), which is the only way to break out of the main
 *          loop.
 */
int main(int, char **) {
  output.resize(output.getOSDimensions());
  std::array<std::size_t, 2> s = output.size();

  std::size_t i;

  std::vector<stream> streams;

  std::signal(SIGINT, handle_interrupt);

  while (1) {
    i++;
    now = system_clock::now();

    if ((streams.size() <= 100) && (i % 50 == 0)) {
      std::size_t l = rng() % (s[1] / 3);
      std::size_t c = rng() % s[0];
      streams.push_back(stream(l, c));
    }

    for (stream &s : streams) {
      s.update();
    }

    for (unsigned int i = 0; i < streams.size(); i++) {
      if (streams[i].doDelete) {
        streams.erase(streams.begin() + i);
      }
    }

    if (output.flush(postProcess) == 0) {
      sched_yield();
    }
  }

  while ((i = output.flush(postProcess)) > 0)
    ;

  return 0;
}
<commit_msg>move the matrix demo main function into the matrix namespace.<commit_after>/**\file
 * \ingroup example-programmes
 * \brief Matrix-esque terminal animation
 *
 * This is a terminal programme that uses libefgy's vt100 code to render a text
 * version of the matrix 'scrolling streams of text' animation. It's really
 * fairly simple but also kinda nice to see how the vt100 output is performing.
 *
 * \image html matrix.png "Screenshot of the programme running in Terminal.app"
 *
 * \copyright
 * This file is part of the libefgy project, which is released as open source
 * under the terms of an MIT/X11-style licence, described in the COPYING file.
 *
 * \see Project Documentation: https://ef.gy/documentation/libefgy
 * \see Project Source Code: https://github.com/ef-gy/libefgy
 * \see Licence Terms: https://github.com/ef-gy/libefgy/blob/master/COPYING
 */

#include <iostream>

#include <ef.gy/vt100.h>
#include <chrono>
#include <csignal>
#include <cmath>
#include <sched.h>
#include <random>

using namespace efgy;
using namespace std::chrono;

/**\brief Data and functions related to the matrix demo
 *
 * Contains global variables and classes used by the 'matrix' demo animation.
 * This is a separate namespace to keep things all neat, clean and tidy.
 */
namespace thematrix {
/**\brief VT100 output buffer
 *
 * Encapsulates stdio to automatically generate the terminal escape
 * sequences to write things at specific positions with specific colours.
 */
terminal::vt100<> output;

/**\brief Random number generator
 *
 * A seeded instance of the mersenne twister RNG; used to position the
 * streams and to add and remove glyphs randomly.
 */
std::mt19937 rng(1337);

/**\brief Current time
 *
 * The point in time when processing of the current streams started; used
 * to determine when streams should be updated.
 */
system_clock::time_point now;

/**\brief A stream of data
 *
 * A 'stream' is what I dubbed the individual columns of text in the
 * animation. These are generated randomly and they mutate randomly as
 * well.
 */
class stream {
public:
  /**\brief A single glyph in a stream
   *
   * To create the animation, each of the glyphs in the output needs
   * to be tagges with the time it was created, in order to know
   * which colour to draw the glyph in.
   */
  class cell {
  public:
    /**\brief Construct with glyph
     *
     * Constructs an instance of the class using the given
     * character and the current system time.
     *
     * \param[in] pCharacter A unicode code point.
     */
    cell(const unsigned long &pCharacter)
        : character(pCharacter), created(now) {}

    /**\brief The glyph
     *
     * This is the glyph to output; must be a valid unicode
     * glyph as the vt100 code will expect one to send to the
     * terminal.
     */
    unsigned long character;

    /**\brief When was this object created?
     *
     * Contains the time point of this object's creation; used
     * to determine the colour of the glyph in the output;
     */
    system_clock::time_point created;
  };

  /**\brief Construct with position
   *
   * Initialises an instance of the class given the line and column
   * of where the stream should appear.
   *
   * \param[in] pLine   The line at which to create the stream; must
   *                    be less than output.size()[1].
   * \param[in] pColumn The column at which to create the stream;
   *                    must be less than output.size()[0].
   */
  stream(const std::size_t &pLine, const std::size_t &pColumn)
      : line(pLine), column(pColumn), last(now), doDelete(false) {}

  /**\brief Line component of stream position
   *
   * This is the line at which the stream is rendered to the
   * terminal.
   */
  std::size_t line;

  /**\brief Column component of stream position
   *
   * This is the column at which the stream is rendered to the
   * terminal.
   */
  std::size_t column;

  /**\brief The stream contents to render
   *
   * A vector of glyphs and the time at which they were inserted into
   * the stream; this vector is placed vertically at the coordinates
   * given by line and column. Recent glyphs are rendered in white,
   * older ones in green.
   */
  std::vector<cell> data;

  /**\brief Time of last update
   *
   * Records the last time a glyph was inserted into or removed from
   * the stream; used to determine when to add new glyphs.
   */
  system_clock::time_point last;

  /**\brief Should this stream be deleted?
   *
   * Set to true when the upper part of the stream reaches the bottom
   * of the screen; the stream should then be deleted in the main
   * loop because it won't produce any output on the screen when this
   * happens.
   */
  bool doDelete;

  /**\brief Update and render stream
   *
   * Render the current contents of the stream to the screen; if it
   * so happens that enough time has passed since the last time this
   * method was called then this function will also randomly add or
   * remove glyphs, or move the stream further down.
   *
   * \returns 'true' if the update went smoothly; 'false' otherwise.
   *          There is currently no way this function can fail, so
   *          at the moment it will always return 'true'.
   */
  bool update(void) {
    if ((now - last) > std::chrono::milliseconds(10)) {
      last = now;
      switch (rng() % 3) {
      case 0:
        if (data.size() > 0) {
          data.erase(data.begin() + (rng() % data.size()));
          break;
        }
      default:
        data.push_back(cell(rng() % (1 << 7)));
      }
    }
    std::array<std::size_t, 2> s = output.size();
    if (data.size() > (s[1] / 2)) {
      if (line < s[1]) {
        if (rng() % 5 == 0) {
          output.target[line][column].content = ' ';
          line++;
        }
      } else {
        doDelete = true;
      }
      data.erase(data.begin());
    }
    int i = line;
    for (cell &d : data) {
      if (i >= s[1]) {
        break;
      }
      output.target[i][column].content = d.character;
      output.target[i][column].foregroundColour =
          (now - d.created) > std::chrono::milliseconds(120) ? 2 : 7;
      output.target[i][column].backgroundColour = 0;
      i++;
    }
    return true;
  }
};

/**\brief SIGINT handler
 *
 * Signal handler that calls exit() properly; the SIGINT handler is
 * replaced by this function. While SIGINT would ordinarily terminate the
 * programme anyway, it doesn't do so with a proper call to exit(), meaning
 * that destructors will not be called, further meaning that the terminal
 * can't be reset to a proper state by the terminal handling code, because
 * not using exit() properly means destructors won't run.
 */
void handle_interrupt(int signal) { exit(0); }

/**\brief Green-tinted post processing function
 *
 * A simple post-processing function for the vt100 code, which makes sure
 * that any output cell is always either white or green. This makes it
 * easier for the optimiser as it won't have to switch colours quite as
 * often as it otherwise might.
 */
terminal::cell<long> postProcess(const terminal::terminal<long> &t,
                                 const std::size_t &l, const std::size_t &c) {
  terminal::cell<long> rv = t.target[l][c];
  rv.content = rv.content == 0 ? ' ' : rv.content;
  rv.foregroundColour = rv.foregroundColour == 7 ? 7 : 2;
  rv.backgroundColour = 0;
  return rv;
}

/**\brief Alternative post processing function
 *
 * Adds some curviness to the output by modifying the input coordinates a
 * bit before creating the output. Kinda looks funky, but not really
 * matrix-y. You'll have to modify the main() function yourself if you want
 * to see this in action.
 */
terminal::cell<long> postProcessPolar(const terminal::terminal<long> &t,
                                      const std::size_t &pl,
                                      const std::size_t &pc) {
  double l = pl;
  double c = pc;
  std::array<std::size_t, 2> s = t.size();
  double hl = s[1] / 2;
  double hc = s[0] / 2;
  double loff = l - hl;
  double coff = c - hc;
  double r2 = loff * loff + coff * coff;
  double r = std::sqrt(r2);
  l = hl + loff + std::sin(r) * 1.0;
  c = hc + coff + std::cos(r) * 1.0;
  const std::size_t tl = (std::size_t)l < s[1] ? (std::size_t)l : (s[1] - 1);
  const std::size_t tc = (std::size_t)c < s[0] ? (std::size_t)c : (s[0] - 1);
  terminal::cell<long> rv = t.target[tl][tc];
  terminal::cell<long> cv = t.current[tl][tc];
  rv.content =
      rv.content == 0 ? (cv.content == 0 ? ' ' : cv.content) : rv.content;
  rv.foregroundColour = rv.foregroundColour == 7 ? 7 : 2;
  rv.backgroundColour = 0;
  return rv;
}

/**\brief Matrix demo main function
 *
 * Entry point for the matrix programme; resizes the output buffer to encompass
 * the whole terminal, sets up a SIGINT handler and then maintains a vector of
 * thematrix::stream objects at random positions.
 *
 * Use CTRL+C to terminate the programme.
 *
 * \note Command line arguments and the programme environment are ignored.
 *
 * \returns 0 for 'success', but this should never be reached as the SIGINT
 *          handler calls exit(), which is the only way to break out of the main
 *          loop.
 */
extern "C" int main(int, char **) {
  output.resize(output.getOSDimensions());
  std::array<std::size_t, 2> s = output.size();

  std::size_t i;

  std::vector<stream> streams;

  std::signal(SIGINT, handle_interrupt);

  while (1) {
    i++;
    now = system_clock::now();

    if ((streams.size() <= 100) && (i % 50 == 0)) {
      std::size_t l = rng() % (s[1] / 3);
      std::size_t c = rng() % s[0];
      streams.push_back(stream(l, c));
    }

    for (stream &s : streams) {
      s.update();
    }

    for (unsigned int i = 0; i < streams.size(); i++) {
      if (streams[i].doDelete) {
        streams.erase(streams.begin() + i);
      }
    }

    if (output.flush(postProcess) == 0) {
      sched_yield();
    }
  }

  while ((i = output.flush(postProcess)) > 0)
    ;

  return 0;
}
}
<|endoftext|>


<commit_before>#include "tensorflow_types.hpp"

// TODO: Capture ... (named and un-named args) and forward to call
// TODO: py_object_convert (convert from Python to R). could be as.character,
//   as.matrix, as.logical, etc. Could also be done automatically or via
//   some sort of dynamic type annotation mechanism
// TODO: consider R6 wrapper (would allow custom $ functions)
// TODO: .DollarNames
// TODO: Globally available import function

using namespace Rcpp;

// https://docs.python.org/2/c-api/object.html

// [[Rcpp::export]]
void py_initialize() {
  ::Py_Initialize();
}

// [[Rcpp::export]]
void py_finalize() {
  ::Py_Finalize();
}

// wrap a PyObject in an XPtr
PyObjectPtr py_object_ptr(PyObject* object, bool decref = true) {
  PyObjectPtr ptr(object);
  ptr.attr("class") = "py_object";
  return ptr;
}

// get a string representing the last python error
std::string py_fetch_error() {
  std::ostringstream ostr;
  PyObject *pExcType , *pExcValue , *pExcTraceback;
  ::PyErr_Fetch(&pExcType , &pExcValue , &pExcTraceback) ;
  if (pExcType != NULL) {
    PyObject* pRepr = ::PyObject_Repr(pExcType ) ;
    ostr << PyString_AsString(pRepr) << " ";
    Py_DecRef(pRepr);
    Py_DecRef(pExcType);
  }
  if (pExcValue != NULL) {
    PyObject* pRepr = ::PyObject_Repr(pExcValue) ;
    ostr << ::PyString_AsString(pRepr);
    Py_DecRef(pRepr) ;
    Py_DecRef(pExcValue) ;
  }
  return ostr.str();
}

//' @export
// [[Rcpp::export]]
PyObjectPtr py_main_module() {
  PyObject* main = ::PyImport_AddModule("__main__");
  if (main == NULL)
    stop(py_fetch_error());
  return py_object_ptr(main, false);
}

//' @export
// [[Rcpp::export]]
PyObjectPtr py_run_string(const std::string& code)
{
  PyObjectPtr main = py_main_module();
  PyObject* dict = PyModule_GetDict(main);
  PyObject* res  = PyRun_StringFlags(code.c_str(), Py_file_input, dict, dict, NULL);
  if (res == NULL)
    stop(py_fetch_error());

  return py_object_ptr(res);
}

//' @export
// [[Rcpp::export]]
void py_run_file(const std::string& file)
{
  FILE* fp = ::fopen(file.c_str(), "r");
  if (fp)
    ::PyRun_SimpleFile(fp, file.c_str());
  else
    stop("Unable to read script file '%s' (does the file exist?)", file);
}

//' @export
// [[Rcpp::export]]
PyObjectPtr py_import(const std::string& module) {
  PyObject* pModule = ::PyImport_ImportModule(module.c_str());
  if (pModule == NULL)
    stop(py_fetch_error());

  return py_object_ptr(pModule);
}

//' @export
// [[Rcpp::export(print.py_object)]]
void py_object_print(PyObjectPtr pObject) {
  ::PyObject_Print(pObject.get(), stdout, Py_PRINT_RAW);
}

//' @export
// [[Rcpp::export]]
PyObjectPtr py_object_get_attr(PyObjectPtr pObject, const std::string& name) {
  PyObject* attr = ::PyObject_GetAttrString(pObject.get(), name.c_str());
  if (attr == NULL)
    stop(py_fetch_error());

  return py_object_ptr(attr);
}

//' @export
// [[Rcpp::export]]
bool py_object_is_callable(PyObjectPtr pObject) {
  return ::PyCallable_Check(pObject.get()) == 1;
}

//' @export
// [[Rcpp::export]]
PyObjectPtr py_object_call(PyObjectPtr pObject) {
  PyObject *args = PyTuple_New(0);
  PyObject *keywords = ::PyDict_New();
  PyObject* res = ::PyObject_Call(pObject.get(), args, keywords);
  ::Py_DecRef(args);
  ::Py_DecRef(keywords);
  if (res == NULL)
    stop(py_fetch_error());

  return py_object_ptr(res);
}
<commit_msg>improved error reporting<commit_after>#include "tensorflow_types.hpp"

// TODO: Capture ... (named and un-named args) and forward to call
// TODO: py_object_convert (convert from Python to R). could be as.character,
//   as.matrix, as.logical, etc. Could also be done automatically or via
//   some sort of dynamic type annotation mechanism
// TODO: consider R6 wrapper (would allow custom $ functions)
// TODO: .DollarNames
// TODO: Globally available import function

using namespace Rcpp;

// https://docs.python.org/2/c-api/object.html

// [[Rcpp::export]]
void py_initialize() {
  ::Py_Initialize();
}

// [[Rcpp::export]]
void py_finalize() {
  ::Py_Finalize();
}

// wrap a PyObject in an XPtr
PyObjectPtr py_object_ptr(PyObject* object, bool decref = true) {
  PyObjectPtr ptr(object);
  ptr.attr("class") = "py_object";
  return ptr;
}

// get a string representing the last python error
std::string py_fetch_error() {
  PyObject *pExcType , *pExcValue , *pExcTraceback;
  ::PyErr_Fetch(&pExcType , &pExcValue , &pExcTraceback) ;
  if (pExcValue != NULL) {
    std::ostringstream ostr;
    PyObject* pStr = ::PyObject_Str(pExcValue) ;
    ostr << ::PyString_AsString(pStr);
    Py_DecRef(pStr) ;
    Py_DecRef(pExcValue);
    return ostr.str();
  } else {
    return "<unknown error>";
  }
}

//' @export
// [[Rcpp::export]]
PyObjectPtr py_main_module() {
  PyObject* main = ::PyImport_AddModule("__main__");
  if (main == NULL)
    stop(py_fetch_error());
  return py_object_ptr(main, false);
}

//' @export
// [[Rcpp::export]]
PyObjectPtr py_run_string(const std::string& code)
{
  PyObjectPtr main = py_main_module();
  PyObject* dict = PyModule_GetDict(main);
  PyObject* res  = PyRun_StringFlags(code.c_str(), Py_file_input, dict, dict, NULL);
  if (res == NULL)
    stop(py_fetch_error());

  return py_object_ptr(res);
}

//' @export
// [[Rcpp::export]]
void py_run_file(const std::string& file)
{
  FILE* fp = ::fopen(file.c_str(), "r");
  if (fp)
    ::PyRun_SimpleFile(fp, file.c_str());
  else
    stop("Unable to read script file '%s' (does the file exist?)", file);
}

//' @export
// [[Rcpp::export]]
PyObjectPtr py_import(const std::string& module) {
  PyObject* pModule = ::PyImport_ImportModule(module.c_str());
  if (pModule == NULL)
    stop(py_fetch_error());

  return py_object_ptr(pModule);
}

//' @export
// [[Rcpp::export(print.py_object)]]
void py_object_print(PyObjectPtr pObject) {
  ::PyObject_Print(pObject.get(), stdout, Py_PRINT_RAW);
}

//' @export
// [[Rcpp::export]]
PyObjectPtr py_object_get_attr(PyObjectPtr pObject, const std::string& name) {
  PyObject* attr = ::PyObject_GetAttrString(pObject.get(), name.c_str());
  if (attr == NULL)
    stop(py_fetch_error());

  return py_object_ptr(attr);
}

//' @export
// [[Rcpp::export]]
bool py_object_is_callable(PyObjectPtr pObject) {
  return ::PyCallable_Check(pObject.get()) == 1;
}

//' @export
// [[Rcpp::export]]
PyObjectPtr py_object_call(PyObjectPtr pObject) {
  PyObject *args = PyTuple_New(0);
  PyObject *keywords = ::PyDict_New();
  PyObject* res = ::PyObject_Call(pObject.get(), args, keywords);
  ::Py_DecRef(args);
  ::Py_DecRef(keywords);
  if (res == NULL)
    stop(py_fetch_error());

  return py_object_ptr(res);
}
<|endoftext|>