<commit_before>//===----------------------------------------------------------------------===//
//
//                         Peloton
//
// block_nested_loop_join_translator.cpp
//
// Identification:
// src/codegen/operator/block_nested_loop_join_translator.cpp
//
// Copyright (c) 2015-2018, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "codegen/operator/block_nested_loop_join_translator.h"

#include "codegen/compilation_context.h"
#include "codegen/function_builder.h"
#include "codegen/operator/projection_translator.h"
#include "codegen/proxy/sorter_proxy.h"
#include "planner/nested_loop_join_plan.h"

namespace peloton {
namespace codegen {

////////////////////////////////////////////////////////////////////////////////
///
/// This class implements a block-wise nested loop join. It does this by using
/// a buffer (in our case, a Sorter instance) into which tuples are buffered
/// from the left side. If this buffer is full, we call a second, generated
/// function that joins the buffer with all tuples from the right side. This
/// generated "joinBuffer" function implements the nested-loop portion. The
/// psuedocode for an INNER join would be:
///
/// function main():
///   Buffer b
///   for r in R:
///     b.insert(r)
///     if b.isFull():
///       call joinBuffer(b)
///       b.reset()
///
/// function joinBuffer(Buffer b):
///   for s in S:
///     for r in b:
///       if pred(r, s):
///         emit(r, s)
///
///
/// To facilitate this process, we **GENERATE** the "joinBuffer" function as an
/// auxiliary function. This function implements the logic for the right-side
/// query pipeline.
///
////////////////////////////////////////////////////////////////////////////////

BlockNestedLoopJoinTranslator::BlockNestedLoopJoinTranslator(
    const planner::NestedLoopJoinPlan &nlj_plan, CompilationContext &context,
    Pipeline &pipeline)
    : OperatorTranslator(context, pipeline),
      nlj_plan_(nlj_plan),
      left_pipeline_(this),
      max_buf_rows_(256) {
  PL_ASSERT(nlj_plan.GetChildrenSize() == 2 &&
            "NLJ must have exactly two children");

  // Prepare children
  context.Prepare(*nlj_plan.GetChild(0), left_pipeline_);
  context.Prepare(*nlj_plan.GetChild(1), pipeline);

  // Prepare join predicate (if one exists)
  auto *predicate = nlj_plan.GetPredicate();
  if (predicate != nullptr) {
    context.Prepare(*predicate);
  }

  // Prepare projection (if one exists)
  auto *projection = nlj_plan.GetProjInfo();
  if (projection != nullptr) {
    ProjectionTranslator::PrepareProjection(context, *projection);
  }

  // Allocate our sorter in runtime state
  auto &runtime_state = context.GetRuntimeState();
  buffer_id_ =
      runtime_state.RegisterState("buffer", SorterProxy::GetType(GetCodeGen()));

  // Collect all unique attributes from the left side
  for (const auto *ai : nlj_plan.GetJoinAIsLeft()) {
    unique_left_attributes_.push_back(ai);
  }
  for (const auto *ai : nlj_plan.GetLeftAttributes()) {
    if (std::find(unique_left_attributes_.begin(),
                  unique_left_attributes_.end(),
                  ai) == unique_left_attributes_.end()) {
      unique_left_attributes_.push_back(ai);
    }
  }

  // Construct the layout of tuples we store in our buffer
  std::vector<type::Type> left_input_desc;
  for (const auto *ai : unique_left_attributes_) {
    left_input_desc.push_back(ai->type);
  }

  buffer_ = Sorter{GetCodeGen(), left_input_desc};
}

void BlockNestedLoopJoinTranslator::InitializeState() {
  auto &codegen = GetCodeGen();
  auto *null_func = codegen.Null(
      proxy::TypeBuilder<util::Sorter::ComparisonFunction>::GetType(codegen));
  buffer_.Init(codegen, LoadStatePtr(buffer_id_), null_func);
}

void BlockNestedLoopJoinTranslator::DefineAuxiliaryFunctions() {
  const auto &right_producer = *GetPlan().GetChild(1);
  auto &compilation_context = GetCompilationContext();
  join_buffer_func_ = compilation_context.DeclareAuxiliaryProducer(
      right_producer, "joinBuffer");
}

void BlockNestedLoopJoinTranslator::TearDownState() {
  buffer_.Destroy(GetCodeGen(), LoadStatePtr(buffer_id_));
}

std::string BlockNestedLoopJoinTranslator::GetName() const {
  return "BlockNestedLoopJoin";
}

void BlockNestedLoopJoinTranslator::Produce() const {
  // Let the left child produce tuples we'll batch-process in Consume()
  GetCompilationContext().Produce(*GetPlan().GetChild(0));

  // Flush any remaining buffered tuples through the join
  auto &codegen = GetCodeGen();
  auto *num_tuples =
      buffer_.GetNumberOfStoredTuples(codegen, LoadStatePtr(buffer_id_));
  lang::If has_tuples{codegen,
                      codegen->CreateICmpUGT(num_tuples, codegen.Const32(0))};
  {
    // Flush remaining
    join_buffer_func_.Call(codegen);
  }
  has_tuples.EndIf();
}

bool BlockNestedLoopJoinTranslator::IsFromLeftChild(
    const Pipeline &pipeline) const {
  return pipeline.GetChild() == left_pipeline_.GetChild();
}

void BlockNestedLoopJoinTranslator::ConsumeFromLeft(
    UNUSED_ATTRIBUTE ConsumerContext &context, RowBatch::Row &row) const {
  auto &codegen = GetCodeGen();

  // Construct tuple
  std::vector<Value> tuple;
  for (const auto &left_ai : unique_left_attributes_) {
    tuple.push_back(row.DeriveValue(codegen, left_ai));
  }

  // Append tuple to buffer
  auto *buffer_ptr = LoadStatePtr(buffer_id_);
  buffer_.Append(codegen, buffer_ptr, tuple);

  // Check if we should process the filled buffer
  auto *buf_size = buffer_.GetNumberOfStoredTuples(codegen, buffer_ptr);
  auto *flush_buffer_cond =
      codegen->CreateICmpUGE(buf_size, codegen.Const32(max_buf_rows_));
  lang::If flush_buffer{codegen, flush_buffer_cond};
  {
    // Process and reset buffer
    join_buffer_func_.Call(codegen);
    buffer_.Reset(codegen, buffer_ptr);
  }
  flush_buffer.EndIf();
}

void BlockNestedLoopJoinTranslator::ConsumeFromRight(ConsumerContext &context,
                                                     RowBatch::Row &row) const {
  // At this point, we've buffered tuples from the left input side and are
  // receiving a new input tuple from the right side. Let's find all join
  // partners with the new input tuple.
  FindMatchesForRow(context, row);
}

void BlockNestedLoopJoinTranslator::Consume(ConsumerContext &ctx,
                                            RowBatch::Row &row) const {
  if (IsFromLeftChild(ctx.GetPipeline())) {
    ConsumeFromLeft(ctx, row);
  } else {
    ConsumeFromRight(ctx, row);
  }
}

namespace {

// This is the callback called for every tuple in the buffer. There's a bit of
// ceremony, but it's just a callback function.
class BufferedTupleCallback : public Sorter::IterateCallback {
 public:
  // Constructor
  BufferedTupleCallback(
      const planner::NestedLoopJoinPlan &plan,
      const std::vector<const planner::AttributeInfo *> &left_attributes,
      ConsumerContext &ctx, RowBatch::Row &right_row);

  // The callback invoked for each tuple in the sorter/buffer
  void ProcessEntry(CodeGen &codegen,
                    const std::vector<codegen::Value> &left_row) const override;

  void ProjectAndConsume() const;

 private:
  // The plan
  const planner::NestedLoopJoinPlan &plan_;
  // The attributes produced by the left child
  const std::vector<const planner::AttributeInfo *> &left_attributes_;
  // The consumer context
  ConsumerContext &ctx_;
  // The current "outer" row
  RowBatch::Row &right_row_;
};

inline BufferedTupleCallback::BufferedTupleCallback(
    const planner::NestedLoopJoinPlan &plan,
    const std::vector<const planner::AttributeInfo *> &left_attributes,
    ConsumerContext &ctx, RowBatch::Row &right_row)
    : plan_(plan),
      left_attributes_(left_attributes),
      ctx_(ctx),
      right_row_(right_row) {}

// This function is called for each tuple in the BNLJ buffer.
inline void BufferedTupleCallback::ProcessEntry(
    CodeGen &codegen, const std::vector<codegen::Value> &left_row) const {
  PL_ASSERT(left_row.size() == left_attributes_.size());

  // Add all the attributes from left tuple (from the sorter) into the row
  // coming from the right input side. We need to do this in order to evaluate
  // the predicate.
  for (size_t i = 0; i < left_attributes_.size(); i++) {
    right_row_.RegisterAttributeValue(left_attributes_[i], left_row[i]);
  }

  auto *predicate = plan_.GetPredicate();
  if (predicate == nullptr) {
    // No predicate, just apply projection and finish
    ProjectAndConsume();
  } else {
    // Check predicate before sending to parent
    const auto &valid = right_row_.DeriveValue(codegen, *predicate);
    lang::If valid_match{codegen, valid};
    {
      // Valid tuple
      ProjectAndConsume();
    }
    valid_match.EndIf();
  }
}

inline void BufferedTupleCallback::ProjectAndConsume() const {
  const auto *projection_info = plan_.GetProjInfo();
  std::vector<RowBatch::ExpressionAccess> derived_attribute_access;
  if (projection_info != nullptr) {
    ProjectionTranslator::AddNonTrivialAttributes(
        right_row_.GetBatch(), *projection_info, derived_attribute_access);
  }

  // That's it, let the parent process the row
  ctx_.Consume(right_row_);
}

}  // anonymous namespace

void BlockNestedLoopJoinTranslator::FindMatchesForRow(
    ConsumerContext &ctx, RowBatch::Row &row) const {
  BufferedTupleCallback callback{GetPlan(), unique_left_attributes_, ctx, row};
  buffer_.Iterate(GetCodeGen(), LoadStatePtr(buffer_id_), callback);
}

}  // namespace codegen
}  // namespace peloton
<commit_msg>Calculate number of rows to buffer based on configuration-based buffer size and input row size<commit_after>//===----------------------------------------------------------------------===//
//
//                         Peloton
//
// block_nested_loop_join_translator.cpp
//
// Identification:
// src/codegen/operator/block_nested_loop_join_translator.cpp
//
// Copyright (c) 2015-2018, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "codegen/operator/block_nested_loop_join_translator.h"

#include "codegen/compilation_context.h"
#include "codegen/function_builder.h"
#include "codegen/operator/projection_translator.h"
#include "codegen/proxy/sorter_proxy.h"
#include "planner/nested_loop_join_plan.h"
#include "settings/settings_manager.h"

namespace peloton {
namespace codegen {

////////////////////////////////////////////////////////////////////////////////
///
/// This class implements a block-wise nested loop join. It does this by using
/// a buffer (in our case, a Sorter instance) into which tuples are buffered
/// from the left side. If this buffer is full, we call a second, generated
/// function that joins the buffer with all tuples from the right side. This
/// generated "joinBuffer" function implements the nested-loop portion. The
/// psuedocode for an INNER join would be:
///
/// function main():
///   Buffer b
///   for r in R:
///     b.insert(r)
///     if b.isFull():
///       call joinBuffer(b)
///       b.reset()
///
/// function joinBuffer(Buffer b):
///   for s in S:
///     for r in b:
///       if pred(r, s):
///         emit(r, s)
///
///
/// To facilitate this process, we **GENERATE** the "joinBuffer" function as an
/// auxiliary function. This function implements the logic for the right-side
/// query pipeline.
///
////////////////////////////////////////////////////////////////////////////////

BlockNestedLoopJoinTranslator::BlockNestedLoopJoinTranslator(
    const planner::NestedLoopJoinPlan &nlj_plan, CompilationContext &context,
    Pipeline &pipeline)
    : OperatorTranslator(context, pipeline),
      nlj_plan_(nlj_plan),
      left_pipeline_(this) {
  PL_ASSERT(nlj_plan.GetChildrenSize() == 2 &&
            "NLJ must have exactly two children");

  // Prepare children
  context.Prepare(*nlj_plan.GetChild(0), left_pipeline_);
  context.Prepare(*nlj_plan.GetChild(1), pipeline);

  // Prepare join predicate (if one exists)
  auto *predicate = nlj_plan.GetPredicate();
  if (predicate != nullptr) {
    context.Prepare(*predicate);
  }

  // Prepare projection (if one exists)
  auto *projection = nlj_plan.GetProjInfo();
  if (projection != nullptr) {
    ProjectionTranslator::PrepareProjection(context, *projection);
  }

  // Collect all unique attributes from the left side
  for (const auto *ai : nlj_plan.GetJoinAIsLeft()) {
    unique_left_attributes_.push_back(ai);
  }
  for (const auto *ai : nlj_plan.GetLeftAttributes()) {
    if (std::find(unique_left_attributes_.begin(),
                  unique_left_attributes_.end(),
                  ai) == unique_left_attributes_.end()) {
      unique_left_attributes_.push_back(ai);
    }
  }

  // Construct the layout of tuples we store in our buffer
  std::vector<type::Type> left_input_desc;
  for (const auto *ai : unique_left_attributes_) {
    left_input_desc.push_back(ai->type);
  }

  // Allocate buffer instance in runtime state and configure its accessor
  auto &codegen = GetCodeGen();
  auto &runtime_state = context.GetRuntimeState();
  buffer_id_ =
      runtime_state.RegisterState("buffer", SorterProxy::GetType(codegen));
  buffer_ = Sorter{codegen, left_input_desc};

  // Determine the number of rows to buffer before flushing it through the join
  auto max_buffer_size = settings::SettingsManager::GetDouble(
      settings::SettingId::bnlj_buffer_size);
  auto row_size = buffer_.GetStorageFormat().GetStorageSize();
  max_buf_rows_ =
      static_cast<uint32_t>(std::max(1.0, max_buffer_size / row_size));

  LOG_DEBUG(
      "Buffer size: %.2lf bytes, row size: %u.0 bytes, max buffered rows: %u",
      max_buffer_size, row_size, max_buf_rows_);
}

void BlockNestedLoopJoinTranslator::InitializeState() {
  auto &codegen = GetCodeGen();
  auto *null_func = codegen.Null(
      proxy::TypeBuilder<util::Sorter::ComparisonFunction>::GetType(codegen));
  buffer_.Init(codegen, LoadStatePtr(buffer_id_), null_func);
}

void BlockNestedLoopJoinTranslator::DefineAuxiliaryFunctions() {
  const auto &right_producer = *GetPlan().GetChild(1);
  auto &compilation_context = GetCompilationContext();
  join_buffer_func_ = compilation_context.DeclareAuxiliaryProducer(
      right_producer, "joinBuffer");
}

void BlockNestedLoopJoinTranslator::TearDownState() {
  buffer_.Destroy(GetCodeGen(), LoadStatePtr(buffer_id_));
}

std::string BlockNestedLoopJoinTranslator::GetName() const {
  return "BlockNestedLoopJoin";
}

void BlockNestedLoopJoinTranslator::Produce() const {
  // Let the left child produce tuples we'll batch-process in Consume()
  GetCompilationContext().Produce(*GetPlan().GetChild(0));

  // Flush any remaining buffered tuples through the join
  auto &codegen = GetCodeGen();
  auto *num_tuples =
      buffer_.GetNumberOfStoredTuples(codegen, LoadStatePtr(buffer_id_));
  lang::If has_tuples{codegen,
                      codegen->CreateICmpUGT(num_tuples, codegen.Const32(0))};
  {
    // Flush remaining
    join_buffer_func_.Call(codegen);
  }
  has_tuples.EndIf();
}

bool BlockNestedLoopJoinTranslator::IsFromLeftChild(
    const Pipeline &pipeline) const {
  return pipeline.GetChild() == left_pipeline_.GetChild();
}

void BlockNestedLoopJoinTranslator::ConsumeFromLeft(
    UNUSED_ATTRIBUTE ConsumerContext &context, RowBatch::Row &row) const {
  auto &codegen = GetCodeGen();

  // Construct tuple
  std::vector<Value> tuple;
  for (const auto &left_ai : unique_left_attributes_) {
    tuple.push_back(row.DeriveValue(codegen, left_ai));
  }

  // Append tuple to buffer
  auto *buffer_ptr = LoadStatePtr(buffer_id_);
  buffer_.Append(codegen, buffer_ptr, tuple);

  // Check if we should process the filled buffer
  auto *buf_size = buffer_.GetNumberOfStoredTuples(codegen, buffer_ptr);
  auto *flush_buffer_cond =
      codegen->CreateICmpUGE(buf_size, codegen.Const32(max_buf_rows_));
  lang::If flush_buffer{codegen, flush_buffer_cond};
  {
    // Process and reset buffer
    join_buffer_func_.Call(codegen);
    buffer_.Reset(codegen, buffer_ptr);
  }
  flush_buffer.EndIf();
}

void BlockNestedLoopJoinTranslator::ConsumeFromRight(ConsumerContext &context,
                                                     RowBatch::Row &row) const {
  // At this point, we've buffered tuples from the left input side and are
  // receiving a new input tuple from the right side. Let's find all join
  // partners with the new input tuple.
  FindMatchesForRow(context, row);
}

void BlockNestedLoopJoinTranslator::Consume(ConsumerContext &ctx,
                                            RowBatch::Row &row) const {
  if (IsFromLeftChild(ctx.GetPipeline())) {
    ConsumeFromLeft(ctx, row);
  } else {
    ConsumeFromRight(ctx, row);
  }
}

namespace {

// This is the callback called for every tuple in the buffer. There's a bit of
// ceremony, but it's just a callback function.
class BufferedTupleCallback : public Sorter::IterateCallback {
 public:
  // Constructor
  BufferedTupleCallback(
      const planner::NestedLoopJoinPlan &plan,
      const std::vector<const planner::AttributeInfo *> &left_attributes,
      ConsumerContext &ctx, RowBatch::Row &right_row);

  // The callback invoked for each tuple in the sorter/buffer
  void ProcessEntry(CodeGen &codegen,
                    const std::vector<codegen::Value> &left_row) const override;

  void ProjectAndConsume() const;

 private:
  // The plan
  const planner::NestedLoopJoinPlan &plan_;
  // The attributes produced by the left child
  const std::vector<const planner::AttributeInfo *> &left_attributes_;
  // The consumer context
  ConsumerContext &ctx_;
  // The current "outer" row
  RowBatch::Row &right_row_;
};

inline BufferedTupleCallback::BufferedTupleCallback(
    const planner::NestedLoopJoinPlan &plan,
    const std::vector<const planner::AttributeInfo *> &left_attributes,
    ConsumerContext &ctx, RowBatch::Row &right_row)
    : plan_(plan),
      left_attributes_(left_attributes),
      ctx_(ctx),
      right_row_(right_row) {}

// This function is called for each tuple in the BNLJ buffer.
inline void BufferedTupleCallback::ProcessEntry(
    CodeGen &codegen, const std::vector<codegen::Value> &left_row) const {
  PL_ASSERT(left_row.size() == left_attributes_.size());

  // Add all the attributes from left tuple (from the sorter) into the row
  // coming from the right input side. We need to do this in order to evaluate
  // the predicate.
  for (size_t i = 0; i < left_attributes_.size(); i++) {
    right_row_.RegisterAttributeValue(left_attributes_[i], left_row[i]);
  }

  auto *predicate = plan_.GetPredicate();
  if (predicate == nullptr) {
    // No predicate, just apply projection and finish
    ProjectAndConsume();
  } else {
    // Check predicate before sending to parent
    const auto &valid = right_row_.DeriveValue(codegen, *predicate);
    lang::If valid_match{codegen, valid};
    {
      // Valid tuple
      ProjectAndConsume();
    }
    valid_match.EndIf();
  }
}

inline void BufferedTupleCallback::ProjectAndConsume() const {
  const auto *projection_info = plan_.GetProjInfo();
  std::vector<RowBatch::ExpressionAccess> derived_attribute_access;
  if (projection_info != nullptr) {
    ProjectionTranslator::AddNonTrivialAttributes(
        right_row_.GetBatch(), *projection_info, derived_attribute_access);
  }

  // That's it, let the parent process the row
  ctx_.Consume(right_row_);
}

}  // anonymous namespace

void BlockNestedLoopJoinTranslator::FindMatchesForRow(
    ConsumerContext &ctx, RowBatch::Row &row) const {
  BufferedTupleCallback callback{GetPlan(), unique_left_attributes_, ctx, row};
  buffer_.Iterate(GetCodeGen(), LoadStatePtr(buffer_id_), callback);
}

}  // namespace codegen
}  // namespace peloton
<|endoftext|>


<commit_before>#include "infer.hpp"
#include "ast.hpp"
#include "package.hpp"

#include <sstream>

namespace type {

  // try to open type `self` from signatures in `s`
  static mono open(const ref<state>& s, const mono& self);

  template<class Func>
  static void iter_rows(mono self, Func func) {
    assert(self.kind() == kind::row());
    self.match
      ([&](const app& self) {
        const auto e = extension::unpack(self);
        func(e.attr, e.head);
        iter_rows(e.tail, func);
      }, [](const mono& ) { });
  }

  struct let {
    const ::list<ast::bind> defs;
    const ast::expr body;

    static symbol fix() { return "__fix__"; }

    // rewrite let as non-recursive let + fix **when defining functions**
    static let rewrite(const ast::let& self) {
      using ::list;
      const list<ast::bind> defs = map(self.defs, [](ast::bind self) {
          return self.value.match<ast::bind>
          ([&](const ast::abs& abs) {
            return ast::bind{self.name,
                             ast::app{ast::var{fix()},
                                      ast::abs{self.name >>= list<ast::abs::arg>(),
                                               self.value}
                                      >>= list<ast::expr>()}};
          },
            [&](const ast::expr& expr) { return self; });
        });

      return {defs, *self.body};
    }
  };

  // rewrite app as nested unary applications
  static ast::app rewrite(const ast::app& self) {
    const ast::expr& init = *self.func;
    return foldl(init, self.args, [](ast::expr func, ast::expr arg) {
        return ast::app(func, arg >>= list<ast::expr>());
      }).cast<ast::app>();
  }

  // reconstruct actual type from reified type: ... -> (type 'a) yields 'a
  // note: t must be substituted
  static mono reconstruct(ref<state> s, mono t) {
    // std::clog << "reconstructing: " << s->generalize(t) << std::endl;

    if(auto self = t.get<app>()) {
      // std::clog << "ctor: " << s->generalize((*self)->ctor) << std::endl;

      if((*self)->ctor == ty) {
        return (*self)->arg;
      }
    }

    auto from = s->fresh();
    auto to = s->fresh();

    s->unify(from >>= to, t);
    return reconstruct(s, s->substitute(to));
  }

  // obtain constructor for a monotype
  static cst constructor(mono t) {
    if(auto self = t.get<app>()) {
      return constructor((*self)->ctor);
    }

    if(auto self = t.get<cst>()) {
      return *self;
    }

    // TODO restrictive?
    throw error("constructor must be a constant");
  }

  // try to open type `self` from signatures in `s`
  static mono open(const ref<state>& s, const mono& self) {
    // obtain type constructor from argument type
    const cst c = constructor(s->substitute(self));

    try {
      auto sig = s->sigs->at(c);

      const mono inner = s->fresh();
      s->unify(self >>= inner, s->instantiate(sig));

      return inner;
    } catch(std::out_of_range&) {
      std::stringstream ss;
      ss << "constructor " << tool::quote(c->name.get()) << " has not associated signature";
      throw error(ss.str());
    }
  }

  ////////////////////////////////////////////////////////////////////////////////

  // error: unimplemented
  template<class T>
  static mono infer(const ref<state>& s, const T& self) {
    throw std::logic_error("infer unimplemented: " + tool::type_name(typeid(T)));
  }

  // var
  static mono infer(const ref<state>& s, const ast::var& self) {
    try {
      return s->instantiate(s->vars->find(self.name));
    } catch(std::out_of_range& e) {
      throw error("unbound variable " + tool::quote(self.name.get()));
    }
  }

  // initialize function scope with argument types
  static ref<state> function_scope(const ref<state>& s, const list<ast::abs::arg>& args) {
    auto sub = scope(s);

    for(auto arg: args) {
      const mono type = arg.match<mono>([&](symbol self) {
          return s->fresh();
        },
        [&](ast::abs::typed self) {
          // obtain reified type from annoatation
          const mono reified = infer(s, self.type);

          // TODO do we need gen/inst here?

          // extract concrete type from reified type
          const mono concrete = reconstruct(s, s->substitute(reified));

          return concrete;
        });

      sub->def(arg.name(), type);
    }

    return sub;
  }

  // abs
  static mono infer(const ref<state>& s, const ast::abs& self) {

    // function scope
    const auto sub = function_scope(s, self.args);

    const mono result = s->fresh();
    const mono sig = foldr(result, self.args, [&](auto arg, mono rhs) {
        const mono type = s->instantiate(sub->vars->find(arg.name()));
        return type >>= rhs;
      });

    // infer lambda body with augmented environment
    s->unify(result, infer(sub, *self.body));

    return result;
  }

  // app
  static mono infer(const ref<state>& s, const ast::app& self) {
    // normalize application as unary
    const ast::app rw = rewrite(self);
    assert(size(rw.args) == 1);

    // infer func/arg types for application
    const auto with_inferred = [&](auto cont) {
      const mono func = infer(s, *rw.func);
      const mono arg = infer(s, rw.args->head);

      return cont(func, arg);
    };

    // check if application works with given func/arg type
    const auto check = [&](mono func, mono arg) {
      const mono ret = s->fresh();
      s->unify(func , arg >>= ret);
      return ret;
    };

    // TODO find a less stupid way of trying all cases?
    try {
      // normal case
      return with_inferred([&](mono func, mono arg) {
          return check(func, arg);
        });

    } catch(error& e) {

      try {
        // open func type and retry
        return with_inferred([&](mono func, mono arg) {
            return check(open(s, func), arg);
          });
      }
      catch(error& ) {  }

      try {
        // open arg type and retry
        return with_inferred([&](mono func, mono arg) {
            return check(func, open(s, arg));
          });
      }
      catch(error& ) {  }

      try {
        // open both func and arg types and retry
        return with_inferred([&](mono func, mono arg) {
            return check(open(s, func), open(s, arg));
          });
      }
      catch(error& ) {  }

      throw e;
    }

  }

  // non-recursive let
  static mono infer(const ref<state>& s, const let& self) {
    auto sub = scope(s);

    for(ast::bind def : self.defs) {
      sub->vars->locals.emplace(def.name, s->generalize(infer(s, def.value)));
    }

    return infer(sub, self.body);
  }

  // recursive let
  static mono infer(const ref<state>& s, const ast::let& self) {
    auto sub = scope(s);

    const mono a = sub->fresh();
    sub->def(let::fix(), (a >>= a) >>= a);

    return infer(sub, let::rewrite(self));
  }

  // sel
  static mono infer(const ref<state>& s, const ast::sel& self) {
    const mono tail = s->fresh(kind::row());
    const mono head = s->fresh(kind::term());

    const mono row = ext(self.name)(head)(tail);
    return record(row) >>= head;
  }

  // record attrs
  static mono infer(const ref<state>& s, const list<ast::record::attr>& attrs) {
    const mono init = empty;
    return foldr(init, attrs, [&](ast::record::attr attr, mono tail) {
        return ext(attr.name)(infer(s, attr.value))(tail);
      });
  }

  // record
  static mono infer(const ref<state>& s, const ast::record& self) {
    return record(infer(s, self.attrs));
  }

  // make
  static mono infer(const ref<state>& s, const ast::make& self) {
    // get signature type
    const poly sig = s->vars->find(self.type);

    auto sub = scope(s);

    const mono outer = s->fresh();
    const mono inner = sub->fresh();

    // instantiate signature at sub level prevents generalization of
    // contravariant side (variables only appearing in the covariant side will
    // be generalized)
    s->unify(ty(outer) >>= ty(inner), sub->instantiate(sig));

    // vanilla covariant type
    const poly reference = sub->generalize(inner);

    // build provided type
    const mono init = empty;
    const mono provided =
      record(foldr(init, self.attrs,
                   [&](const ast::record::attr attr, mono tail) {
                     return row(attr.name, infer(sub, attr.value)) |= tail;
                   }));

    // now also unify inner with provided type
    s->unify(inner, provided);

    // now generalize the provided type
    const poly gen = sub->generalize(inner);

    // generalization check: quantified variables in reference/gen should
    // substitute to the same variables
    std::set<var> quantified;
    for(const var& v : gen.forall) {
      assert(sub->substitute(v) == v);
      quantified.insert(v);
    }

    // make sure all reference quantified references substitute to quantified
    // variables
    for(const var& v : reference.forall) {
      const mono vs = sub->substitute(v);
      if(auto u = vs.get<var>()) {
        auto it = quantified.find(*u);
        if(it != quantified.end()) {
          continue;
        }
      }

      std::stringstream ss;
      logger(ss) << "failed to generalize " << gen
                 << " as " << reference;

      throw error(ss.str());
    }

    return outer;
  }

  // cond
  static mono infer(const ref<state>& s, const ast::cond& self) {
    const mono test = infer(s, *self.test);
    s->unify(test, boolean);

    const mono conseq = infer(s, *self.conseq);
    const mono alt = infer(s, *self.alt);

    const mono result = s->fresh();
    s->unify(result, conseq);
    s->unify(result, alt);

    return result;
  }

  // def
  static mono infer(const ref<state>& s, const ast::def& self) {
    const mono value =
      infer(s, ast::let(ast::bind{self.name, *self.value} >>= list<ast::bind>(),
                        ast::var{self.name}));
    try {
      s->def(self.name, value);
      return io(unit);
    } catch(std::runtime_error& e) {
      throw error(e.what());
    }
  }

  // use
  static mono infer(const ref<state>& s, const ast::use& self) {
    // infer value type
    const mono value = infer(s, *self.env);

    // make sure value type is a record
    const mono row = s->fresh(kind::row());
    s->unify(value, record(row));

    auto sub = scope(s);

    // fill sub scope with record contents
    iter_rows(s->substitute(row), [&](symbol attr, mono t) {
        // TODO generalization issue?
        sub->def(attr, t);
      });

    return infer(sub, *self.body);
  }

  // import
  static mono infer(const ref<state>& s, const ast::import& self) {
    auto it = s->vars->locals.find(self.package);
    if(it != s->vars->locals.end()) {
      throw error("variable " + tool::quote(self.package.get()) + " already defined");
    }

    const package pkg = package::import(self.package);

    s->vars->locals.emplace(self.package, pkg.sig());
    return io(unit);
  }

  // module
  static mono infer(const ref<state>& s, const ast::module& self) {

    // module scope
    const auto sub = function_scope(s, self.args);

    const mono result = s->fresh();
    const mono sig = foldr(result, self.args, [&](auto arg, mono rhs) {
        const mono type = s->instantiate(sub->vars->find(arg.name()));
        return type >>= rhs;
      });

    // infer type for module attributes in module scope
    const mono attrs = infer(sub, self.attrs);

    iter_rows(attrs, [&](symbol attr, mono reified) {
        // make sure each attribute has a reified type
        // const mono type = reconstruct(sub, reified);
      });

    // TODO compute kind from signature

    // TODO create a new type constructor with given name/computed kind

    // in the meantime, return signature
    s->unify(result, attrs);
    return sig;
  }

  // lit
  static mono infer(const ref<state>&, const ast::lit<::boolean>& self) {
    return boolean;
  }

  static mono infer(const ref<state>&, const ast::lit<::integer>& self) {
    return integer;
  }

  static mono infer(const ref<state>&, const ast::lit<::real>& self) {
    return real;
  }

  ////////////////////////////////////////////////////////////////////////////////

  struct infer_visitor {
    using type = mono;

    template<class T>
    mono operator()(const T& self, const ref<state>& s) const {
      return infer(s, self);
    }

  };

  mono infer(const ref<state>& s, const ast::expr& self) {
    return self.visit(infer_visitor(), s);
  }

}
<commit_msg>whoops<commit_after>#include "infer.hpp"
#include "ast.hpp"
#include "package.hpp"

#include <sstream>

namespace type {

  // try to open type `self` from signatures in `s`
  static mono open(const ref<state>& s, const mono& self);

  template<class Func>
  static void iter_rows(mono self, Func func) {
    assert(self.kind() == kind::row());
    self.match
      ([&](const app& self) {
        const auto e = extension::unpack(self);
        func(e.attr, e.head);
        iter_rows(e.tail, func);
      }, [](const mono& ) { });
  }

  struct let {
    const ::list<ast::bind> defs;
    const ast::expr body;

    static symbol fix() { return "__fix__"; }

    // rewrite let as non-recursive let + fix **when defining functions**
    static let rewrite(const ast::let& self) {
      using ::list;
      const list<ast::bind> defs = map(self.defs, [](ast::bind self) {
          return self.value.match<ast::bind>
          ([&](const ast::abs& abs) {
            return ast::bind{self.name,
                             ast::app{ast::var{fix()},
                                      ast::abs{self.name >>= list<ast::abs::arg>(),
                                               self.value}
                                      >>= list<ast::expr>()}};
          },
            [&](const ast::expr& expr) { return self; });
        });

      return {defs, *self.body};
    }
  };

  // rewrite app as nested unary applications
  static ast::app rewrite(const ast::app& self) {
    const ast::expr& init = *self.func;
    return foldl(init, self.args, [](ast::expr func, ast::expr arg) {
        return ast::app(func, arg >>= list<ast::expr>());
      }).cast<ast::app>();
  }

  // reconstruct actual type from reified type: ... -> (type 'a) yields 'a
  // note: t must be substituted
  static mono reconstruct(ref<state> s, mono t) {
    // std::clog << "reconstructing: " << s->generalize(t) << std::endl;

    if(auto self = t.get<app>()) {
      // std::clog << "ctor: " << s->generalize((*self)->ctor) << std::endl;

      if((*self)->ctor == ty) {
        return (*self)->arg;
      }
    }

    auto from = s->fresh();
    auto to = s->fresh();

    s->unify(from >>= to, t);
    return reconstruct(s, s->substitute(to));
  }

  // obtain constructor for a monotype
  static cst constructor(mono t) {
    if(auto self = t.get<app>()) {
      return constructor((*self)->ctor);
    }

    if(auto self = t.get<cst>()) {
      return *self;
    }

    // TODO restrictive?
    throw error("constructor must be a constant");
  }

  // try to open type `self` from signatures in `s`
  static mono open(const ref<state>& s, const mono& self) {
    // obtain type constructor from argument type
    const cst c = constructor(s->substitute(self));

    try {
      auto sig = s->sigs->at(c);

      const mono inner = s->fresh();
      s->unify(self >>= inner, s->instantiate(sig));

      return inner;
    } catch(std::out_of_range&) {
      std::stringstream ss;
      ss << "constructor " << tool::quote(c->name.get()) << " has not associated signature";
      throw error(ss.str());
    }
  }

  ////////////////////////////////////////////////////////////////////////////////

  // error: unimplemented
  template<class T>
  static mono infer(const ref<state>& s, const T& self) {
    throw std::logic_error("infer unimplemented: " + tool::type_name(typeid(T)));
  }

  // var
  static mono infer(const ref<state>& s, const ast::var& self) {
    try {
      return s->instantiate(s->vars->find(self.name));
    } catch(std::out_of_range& e) {
      throw error("unbound variable " + tool::quote(self.name.get()));
    }
  }

  // initialize function scope with argument types
  static ref<state> function_scope(const ref<state>& s, const list<ast::abs::arg>& args) {
    auto sub = scope(s);

    for(auto arg: args) {
      const mono type = arg.match<mono>([&](symbol self) {
          return s->fresh();
        },
        [&](ast::abs::typed self) {
          // obtain reified type from annoatation
          const mono reified = infer(s, self.type);

          // TODO do we need gen/inst here?

          // extract concrete type from reified type
          const mono concrete = reconstruct(s, s->substitute(reified));

          return concrete;
        });

      sub->def(arg.name(), type);
    }

    return sub;
  }

  // abs
  static mono infer(const ref<state>& s, const ast::abs& self) {

    // function scope
    const auto sub = function_scope(s, self.args);

    const mono result = s->fresh();
    const mono sig = foldr(result, self.args, [&](auto arg, mono rhs) {
        const mono type = s->instantiate(sub->vars->find(arg.name()));
        return type >>= rhs;
      });

    // infer lambda body with augmented environment
    s->unify(result, infer(sub, *self.body));

    return sig;
  }

  // app
  static mono infer(const ref<state>& s, const ast::app& self) {
    // normalize application as unary
    const ast::app rw = rewrite(self);
    assert(size(rw.args) == 1);

    // infer func/arg types for application
    const auto with_inferred = [&](auto cont) {
      const mono func = infer(s, *rw.func);
      const mono arg = infer(s, rw.args->head);

      return cont(func, arg);
    };

    // check if application works with given func/arg type
    const auto check = [&](mono func, mono arg) {
      const mono ret = s->fresh();
      s->unify(func , arg >>= ret);
      return ret;
    };

    // TODO find a less stupid way of trying all cases?
    try {
      // normal case
      return with_inferred([&](mono func, mono arg) {
          return check(func, arg);
        });

    } catch(error& e) {

      try {
        // open func type and retry
        return with_inferred([&](mono func, mono arg) {
            return check(open(s, func), arg);
          });
      }
      catch(error& ) {  }

      try {
        // open arg type and retry
        return with_inferred([&](mono func, mono arg) {
            return check(func, open(s, arg));
          });
      }
      catch(error& ) {  }

      try {
        // open both func and arg types and retry
        return with_inferred([&](mono func, mono arg) {
            return check(open(s, func), open(s, arg));
          });
      }
      catch(error& ) {  }

      throw e;
    }

  }

  // non-recursive let
  static mono infer(const ref<state>& s, const let& self) {
    auto sub = scope(s);

    for(ast::bind def : self.defs) {
      sub->vars->locals.emplace(def.name, s->generalize(infer(s, def.value)));
    }

    return infer(sub, self.body);
  }

  // recursive let
  static mono infer(const ref<state>& s, const ast::let& self) {
    auto sub = scope(s);

    const mono a = sub->fresh();
    sub->def(let::fix(), (a >>= a) >>= a);

    return infer(sub, let::rewrite(self));
  }

  // sel
  static mono infer(const ref<state>& s, const ast::sel& self) {
    const mono tail = s->fresh(kind::row());
    const mono head = s->fresh(kind::term());

    const mono row = ext(self.name)(head)(tail);
    return record(row) >>= head;
  }

  // record attrs
  static mono infer(const ref<state>& s, const list<ast::record::attr>& attrs) {
    const mono init = empty;
    return foldr(init, attrs, [&](ast::record::attr attr, mono tail) {
        return ext(attr.name)(infer(s, attr.value))(tail);
      });
  }

  // record
  static mono infer(const ref<state>& s, const ast::record& self) {
    return record(infer(s, self.attrs));
  }

  // make
  static mono infer(const ref<state>& s, const ast::make& self) {
    // get signature type
    const poly sig = s->vars->find(self.type);

    auto sub = scope(s);

    const mono outer = s->fresh();
    const mono inner = sub->fresh();

    // instantiate signature at sub level prevents generalization of
    // contravariant side (variables only appearing in the covariant side will
    // be generalized)
    s->unify(ty(outer) >>= ty(inner), sub->instantiate(sig));

    // vanilla covariant type
    const poly reference = sub->generalize(inner);

    // build provided type
    const mono init = empty;
    const mono provided =
      record(foldr(init, self.attrs,
                   [&](const ast::record::attr attr, mono tail) {
                     return row(attr.name, infer(sub, attr.value)) |= tail;
                   }));

    // now also unify inner with provided type
    s->unify(inner, provided);

    // now generalize the provided type
    const poly gen = sub->generalize(inner);

    // generalization check: quantified variables in reference/gen should
    // substitute to the same variables
    std::set<var> quantified;
    for(const var& v : gen.forall) {
      assert(sub->substitute(v) == v);
      quantified.insert(v);
    }

    // make sure all reference quantified references substitute to quantified
    // variables
    for(const var& v : reference.forall) {
      const mono vs = sub->substitute(v);
      if(auto u = vs.get<var>()) {
        auto it = quantified.find(*u);
        if(it != quantified.end()) {
          continue;
        }
      }

      std::stringstream ss;
      logger(ss) << "failed to generalize " << gen
                 << " as " << reference;

      throw error(ss.str());
    }

    return outer;
  }

  // cond
  static mono infer(const ref<state>& s, const ast::cond& self) {
    const mono test = infer(s, *self.test);
    s->unify(test, boolean);

    const mono conseq = infer(s, *self.conseq);
    const mono alt = infer(s, *self.alt);

    const mono result = s->fresh();
    s->unify(result, conseq);
    s->unify(result, alt);

    return result;
  }

  // def
  static mono infer(const ref<state>& s, const ast::def& self) {
    const mono value =
      infer(s, ast::let(ast::bind{self.name, *self.value} >>= list<ast::bind>(),
                        ast::var{self.name}));
    try {
      s->def(self.name, value);
      return io(unit);
    } catch(std::runtime_error& e) {
      throw error(e.what());
    }
  }

  // use
  static mono infer(const ref<state>& s, const ast::use& self) {
    // infer value type
    const mono value = infer(s, *self.env);

    // make sure value type is a record
    const mono row = s->fresh(kind::row());
    s->unify(value, record(row));

    auto sub = scope(s);

    // fill sub scope with record contents
    iter_rows(s->substitute(row), [&](symbol attr, mono t) {
        // TODO generalization issue?
        sub->def(attr, t);
      });

    return infer(sub, *self.body);
  }

  // import
  static mono infer(const ref<state>& s, const ast::import& self) {
    auto it = s->vars->locals.find(self.package);
    if(it != s->vars->locals.end()) {
      throw error("variable " + tool::quote(self.package.get()) + " already defined");
    }

    const package pkg = package::import(self.package);

    s->vars->locals.emplace(self.package, pkg.sig());
    return io(unit);
  }

  // module
  static mono infer(const ref<state>& s, const ast::module& self) {

    // module scope
    const auto sub = function_scope(s, self.args);

    const mono result = s->fresh();
    const mono sig = foldr(result, self.args, [&](auto arg, mono rhs) {
        const mono type = s->instantiate(sub->vars->find(arg.name()));
        return type >>= rhs;
      });

    // infer type for module attributes in module scope
    const mono attrs = infer(sub, self.attrs);

    iter_rows(attrs, [&](symbol attr, mono reified) {
        // make sure each attribute has a reified type
        // const mono type = reconstruct(sub, reified);
      });

    // TODO compute kind from signature

    // TODO create a new type constructor with given name/computed kind

    // in the meantime, return signature
    s->unify(result, attrs);
    return sig;
  }

  // lit
  static mono infer(const ref<state>&, const ast::lit<::boolean>& self) {
    return boolean;
  }

  static mono infer(const ref<state>&, const ast::lit<::integer>& self) {
    return integer;
  }

  static mono infer(const ref<state>&, const ast::lit<::real>& self) {
    return real;
  }

  ////////////////////////////////////////////////////////////////////////////////

  struct infer_visitor {
    using type = mono;

    template<class T>
    mono operator()(const T& self, const ref<state>& s) const {
      return infer(s, self);
    }

  };

  mono infer(const ref<state>& s, const ast::expr& self) {
    return self.visit(infer_visitor(), s);
  }

}
<|endoftext|>


<commit_before>#pragma once

#include "manipulator/details/base.hpp"
#include "manipulator/details/types.hpp"
#include "time_utility.hpp"
#include <json/json.hpp>
#include <unordered_set>
#include <vector>

namespace krbn {
namespace manipulator {
namespace details {
class basic final : public base {
public:
  class manipulated_original_event final {
  public:
    manipulated_original_event(device_id device_id,
                               const event_queue::queued_event::event& original_event,
                               const std::unordered_set<modifier_flag> from_mandatory_modifiers,
                               uint64_t key_down_time_stamp) : device_id_(device_id),
                                                               original_event_(original_event),
                                                               from_mandatory_modifiers_(from_mandatory_modifiers),
                                                               key_down_time_stamp_(key_down_time_stamp),
                                                               alone_(true) {
    }

    device_id get_device_id(void) const {
      return device_id_;
    }

    const event_queue::queued_event::event& get_original_event(void) const {
      return original_event_;
    }

    const std::unordered_set<modifier_flag>& get_from_mandatory_modifiers(void) const {
      return from_mandatory_modifiers_;
    }

    uint64_t get_key_down_time_stamp(void) const {
      return key_down_time_stamp_;
    }

    bool get_alone(void) const {
      return alone_;
    }

    void unset_alone(void) {
      alone_ = false;
    }

    bool operator==(const manipulated_original_event& other) const {
      // Do not compare `from_mandatory_modifiers_`.
      return get_device_id() == other.get_device_id() &&
             get_original_event() == other.get_original_event();
    }

  private:
    device_id device_id_;
    event_queue::queued_event::event original_event_;
    std::unordered_set<modifier_flag> from_mandatory_modifiers_;
    uint64_t key_down_time_stamp_;
    bool alone_;
  };

  basic(const nlohmann::json& json,
        const core_configuration::profile::complex_modifications::parameters& parameters) : base(),
                                                                                parameters_(parameters),
                                                                                from_(json.find("from") != std::end(json) ? json["from"] : nlohmann::json()) {
    {
      const std::string key = "to";
      if (json.find(key) != std::end(json) && json[key].is_array()) {
        for (const auto& j : json[key]) {
          to_.emplace_back(j);
        }
      }
    }
    {
      const std::string key = "to_if_alone";
      if (json.find(key) != std::end(json) && json[key].is_array()) {
        for (const auto& j : json[key]) {
          to_if_alone_.emplace_back(j);
        }
      }
    }
  }

  basic(const from_event_definition& from,
        const to_event_definition& to) : from_(from),
                                         to_({to}) {
  }

  virtual ~basic(void) {
  }

  virtual void manipulate(event_queue::queued_event& front_input_event,
                          const event_queue& input_event_queue,
                          event_queue& output_event_queue) {
    unset_alone_if_needed(front_input_event.get_event(),
                          front_input_event.get_event_type());

    bool is_target = false;

    if (auto key_code = front_input_event.get_event().get_key_code()) {
      if (from_.get_key_code() == key_code) {
        is_target = true;
      }
    }
    if (auto pointing_button = front_input_event.get_event().get_pointing_button()) {
      if (from_.get_pointing_button() == pointing_button) {
        is_target = true;
      }
    }

    if (!front_input_event.get_valid()) {
      return;
    }

    if (is_target) {
      std::unordered_set<modifier_flag> from_mandatory_modifiers;
      uint64_t key_down_time_stamp = 0;
      bool alone = false;

      if (front_input_event.get_event_type() == event_type::key_down) {

        if (!valid_) {
          is_target = false;
        }

        if (auto modifiers = from_.test_modifiers(output_event_queue.get_modifier_flag_manager())) {
          from_mandatory_modifiers = *modifiers;
        } else {
          is_target = false;
        }

        if (!condition_manager_.is_fulfilled(output_event_queue.get_manipulator_environment())) {
          is_target = false;
        }

        if (is_target) {
          manipulated_original_events_.emplace_back(front_input_event.get_device_id(),
                                                    front_input_event.get_original_event(),
                                                    from_mandatory_modifiers,
                                                    front_input_event.get_time_stamp());
        }

      } else {
        // event_type::key_up

        // Check original_event in order to determine the correspond key_down is manipulated.

        auto it = std::find_if(std::begin(manipulated_original_events_),
                               std::end(manipulated_original_events_),
                               [&](const auto& manipulated_original_event) {
                                 return manipulated_original_event.get_device_id() == front_input_event.get_device_id() &&
                                        manipulated_original_event.get_original_event() == front_input_event.get_original_event();
                               });
        if (it != std::end(manipulated_original_events_)) {
          from_mandatory_modifiers = it->get_from_mandatory_modifiers();
          key_down_time_stamp = it->get_key_down_time_stamp();
          alone = it->get_alone();
          manipulated_original_events_.erase(it);
        } else {
          is_target = false;
        }
      }

      if (is_target) {
        front_input_event.set_valid(false);

        uint64_t time_stamp_delay = 0;

        // Release from_mandatory_modifiers

        if (front_input_event.get_event_type() == event_type::key_down) {
          for (const auto& m : from_mandatory_modifiers) {
            if (auto key_code = types::get_key_code(m)) {
              event_queue::queued_event event(front_input_event.get_device_id(),
                                              front_input_event.get_time_stamp() + time_stamp_delay++,
                                              event_queue::queued_event::event(*key_code),
                                              event_type::key_up,
                                              front_input_event.get_original_event(),
                                              true);
              output_event_queue.push_back_event(event);
            }
          }
        }

        // Send events

        for (size_t i = 0; i < to_.size(); ++i) {
          if (auto event = to_[i].to_event()) {
            if (front_input_event.get_event_type() == event_type::key_down) {
              enqueue_to_modifiers(to_[i],
                                   event_type::key_down,
                                   front_input_event,
                                   !preserve_to_modifiers_down(),
                                   time_stamp_delay,
                                   output_event_queue);

              output_event_queue.emplace_back_event(front_input_event.get_device_id(),
                                                    front_input_event.get_time_stamp() + time_stamp_delay++,
                                                    *event,
                                                    event_type::key_down,
                                                    front_input_event.get_original_event());

              if (i != to_.size() - 1) {
                output_event_queue.emplace_back_event(front_input_event.get_device_id(),
                                                      front_input_event.get_time_stamp() + time_stamp_delay++,
                                                      *event,
                                                      event_type::key_up,
                                                      front_input_event.get_original_event());
              }

              if (i != to_.size() - 1 || !preserve_to_modifiers_down()) {
                enqueue_to_modifiers(to_[i],
                                     event_type::key_up,
                                     front_input_event,
                                     !preserve_to_modifiers_down(),
                                     time_stamp_delay,
                                     output_event_queue);
              }

            } else {
              // event_type::key_up

              if (i == to_.size() - 1) {
                output_event_queue.emplace_back_event(front_input_event.get_device_id(),
                                                      front_input_event.get_time_stamp() + time_stamp_delay++,
                                                      *event,
                                                      event_type::key_up,
                                                      front_input_event.get_original_event());

                if (preserve_to_modifiers_down()) {
                  enqueue_to_modifiers(to_[i],
                                       event_type::key_up,
                                       front_input_event,
                                       !preserve_to_modifiers_down(),
                                       time_stamp_delay,
                                       output_event_queue);
                }

                uint64_t nanoseconds = time_utility::absolute_to_nano(front_input_event.get_time_stamp() - key_down_time_stamp);
                if (alone &&
                    nanoseconds < parameters_.get_basic_to_if_alone_timeout_milliseconds() * NSEC_PER_MSEC) {
                  send_to_if_alone(front_input_event,
                                   time_stamp_delay,
                                   output_event_queue);
                }
              }
            }
          }
        }

        // Restore from_mandatory_modifiers

        if ((front_input_event.get_event_type() == event_type::key_down && !preserve_from_mandatory_modifiers_up()) ||
            (front_input_event.get_event_type() == event_type::key_up && preserve_from_mandatory_modifiers_up())) {
          for (const auto& m : from_mandatory_modifiers) {
            if (auto key_code = types::get_key_code(m)) {
              event_queue::queued_event event(front_input_event.get_device_id(),
                                              front_input_event.get_time_stamp() + time_stamp_delay++,
                                              event_queue::queued_event::event(*key_code),
                                              event_type::key_down,
                                              front_input_event.get_original_event(),
                                              true);
              output_event_queue.push_back_event(event);
            }
          }
        }

        if (time_stamp_delay > 0) {
          output_event_queue.increase_time_stamp_delay(time_stamp_delay - 1);
        }
      }
    }
  }

  virtual bool active(void) const {
    return !manipulated_original_events_.empty();
  }

  virtual void handle_device_ungrabbed_event(device_id device_id,
                                             const event_queue& output_event_queue,
                                             uint64_t time_stamp) {
    manipulated_original_events_.erase(std::remove_if(std::begin(manipulated_original_events_),
                                                      std::end(manipulated_original_events_),
                                                      [&](const auto& e) {
                                                        return e.get_device_id() == device_id;
                                                      }),
                                       std::end(manipulated_original_events_));
  }

  virtual void handle_event_from_ignored_device(const event_queue::queued_event& front_input_event,
                                                event_queue& output_event_queue) {
    unset_alone_if_needed(front_input_event.get_original_event(),
                          front_input_event.get_event_type());
  }

  const from_event_definition& get_from(void) const {
    return from_;
  }

  const std::vector<to_event_definition>& get_to(void) const {
    return to_;
  }

  void enqueue_to_modifiers(const to_event_definition& to,
                            event_type event_type,
                            const event_queue::queued_event& front_input_event,
                            bool lazy,
                            uint64_t& time_stamp_delay,
                            event_queue& output_event_queue) {
    for (const auto& modifier : to.get_modifiers()) {
      // `event_definition::get_modifiers` might return two modifier_flags.
      // (eg. `modifier_flag::left_shift` and `modifier_flag::right_shift` for `modifier::shift`.)
      // We use the first modifier_flag.

      auto modifier_flags = event_definition::get_modifier_flags(modifier);
      if (!modifier_flags.empty()) {
        auto modifier_flag = modifier_flags.front();
        if (auto key_code = types::get_key_code(modifier_flag)) {
          output_event_queue.emplace_back_event(front_input_event.get_device_id(),
                                                front_input_event.get_time_stamp() + time_stamp_delay++,
                                                event_queue::queued_event::event(*key_code),
                                                event_type,
                                                front_input_event.get_original_event(),
                                                !preserve_to_modifiers_down());
        }
      }
    }
  }

private:
  bool preserve_from_mandatory_modifiers_up(void) const {
    if (to_.empty()) {
      return false;
    }

    if (auto event = to_.back().to_event()) {
      if (auto key_code = event->get_key_code()) {
        if (types::get_modifier_flag(*key_code) != modifier_flag::zero) {
          return true;
        }
      }
    }

    return false;
  }

  bool preserve_to_modifiers_down(void) const {
    return preserve_from_mandatory_modifiers_up();
  }

  void send_to_if_alone(const event_queue::queued_event& front_input_event,
                        uint64_t& time_stamp_delay,
                        event_queue& output_event_queue) {
    for (const auto& to : to_if_alone_) {
      if (auto event = to.to_event()) {
        enqueue_to_modifiers(to,
                             event_type::key_down,
                             front_input_event,
                             true,
                             time_stamp_delay,
                             output_event_queue);

        output_event_queue.emplace_back_event(front_input_event.get_device_id(),
                                              front_input_event.get_time_stamp() + time_stamp_delay++,
                                              *event,
                                              event_type::key_down,
                                              front_input_event.get_original_event());

        output_event_queue.emplace_back_event(front_input_event.get_device_id(),
                                              front_input_event.get_time_stamp() + time_stamp_delay++,
                                              *event,
                                              event_type::key_up,
                                              front_input_event.get_original_event());

        enqueue_to_modifiers(to,
                             event_type::key_up,
                             front_input_event,
                             true,
                             time_stamp_delay,
                             output_event_queue);
      }
    }
  }

  void unset_alone_if_needed(const event_queue::queued_event::event& event,
                             event_type event_type) {
    if (event.get_type() == event_queue::queued_event::event::type::key_code ||
        event.get_type() == event_queue::queued_event::event::type::pointing_button) {
      if (event_type == event_type::key_down) {
        goto run;
      }
    }
    if (event.get_type() == event_queue::queued_event::event::type::pointing_vertical_wheel ||
        event.get_type() == event_queue::queued_event::event::type::pointing_horizontal_wheel) {
      if (auto integer_value = event.get_integer_value()) {
        if (*integer_value != 0) {
          goto run;
        }
      }
    }

    return;

  run:
    for (auto& e : manipulated_original_events_) {
      e.unset_alone();
    }
  }

  core_configuration::profile::complex_modifications::parameters parameters_;

  from_event_definition from_;
  std::vector<to_event_definition> to_;
  std::vector<to_event_definition> to_if_alone_;

  std::vector<manipulated_original_event> manipulated_original_events_;
};
} // namespace details
} // namespace manipulator
} // namespace krbn
<commit_msg>add error log<commit_after>#pragma once

#include "manipulator/details/base.hpp"
#include "manipulator/details/types.hpp"
#include "time_utility.hpp"
#include <json/json.hpp>
#include <unordered_set>
#include <vector>

namespace krbn {
namespace manipulator {
namespace details {
class basic final : public base {
public:
  class manipulated_original_event final {
  public:
    manipulated_original_event(device_id device_id,
                               const event_queue::queued_event::event& original_event,
                               const std::unordered_set<modifier_flag> from_mandatory_modifiers,
                               uint64_t key_down_time_stamp) : device_id_(device_id),
                                                               original_event_(original_event),
                                                               from_mandatory_modifiers_(from_mandatory_modifiers),
                                                               key_down_time_stamp_(key_down_time_stamp),
                                                               alone_(true) {
    }

    device_id get_device_id(void) const {
      return device_id_;
    }

    const event_queue::queued_event::event& get_original_event(void) const {
      return original_event_;
    }

    const std::unordered_set<modifier_flag>& get_from_mandatory_modifiers(void) const {
      return from_mandatory_modifiers_;
    }

    uint64_t get_key_down_time_stamp(void) const {
      return key_down_time_stamp_;
    }

    bool get_alone(void) const {
      return alone_;
    }

    void unset_alone(void) {
      alone_ = false;
    }

    bool operator==(const manipulated_original_event& other) const {
      // Do not compare `from_mandatory_modifiers_`.
      return get_device_id() == other.get_device_id() &&
             get_original_event() == other.get_original_event();
    }

  private:
    device_id device_id_;
    event_queue::queued_event::event original_event_;
    std::unordered_set<modifier_flag> from_mandatory_modifiers_;
    uint64_t key_down_time_stamp_;
    bool alone_;
  };

  basic(const nlohmann::json& json,
        const core_configuration::profile::complex_modifications::parameters& parameters) : base(),
                                                                                parameters_(parameters),
                                                                                from_(json.find("from") != std::end(json) ? json["from"] : nlohmann::json()) {
    {
      const std::string key = "to";
      if (json.find(key) != std::end(json)) {
        if (json[key].is_array()) {
          for (const auto& j : json[key]) {
            to_.emplace_back(j);
          }
        } else {
          logger::get_logger().error("`to` should be array: {0}", json.dump());
        }
      }
    }
    {
      const std::string key = "to_if_alone";
      if (json.find(key) != std::end(json)) {
        if (json[key].is_array()) {
          for (const auto& j : json[key]) {
            to_if_alone_.emplace_back(j);
          }
        } else {
          logger::get_logger().error("`to_if_alone` should be array: {0}", json.dump());
        }
      }
    }
  }

  basic(const from_event_definition& from,
        const to_event_definition& to) : from_(from),
                                         to_({to}) {
  }

  virtual ~basic(void) {
  }

  virtual void manipulate(event_queue::queued_event& front_input_event,
                          const event_queue& input_event_queue,
                          event_queue& output_event_queue) {
    unset_alone_if_needed(front_input_event.get_event(),
                          front_input_event.get_event_type());

    bool is_target = false;

    if (auto key_code = front_input_event.get_event().get_key_code()) {
      if (from_.get_key_code() == key_code) {
        is_target = true;
      }
    }
    if (auto pointing_button = front_input_event.get_event().get_pointing_button()) {
      if (from_.get_pointing_button() == pointing_button) {
        is_target = true;
      }
    }

    if (!front_input_event.get_valid()) {
      return;
    }

    if (is_target) {
      std::unordered_set<modifier_flag> from_mandatory_modifiers;
      uint64_t key_down_time_stamp = 0;
      bool alone = false;

      if (front_input_event.get_event_type() == event_type::key_down) {

        if (!valid_) {
          is_target = false;
        }

        if (auto modifiers = from_.test_modifiers(output_event_queue.get_modifier_flag_manager())) {
          from_mandatory_modifiers = *modifiers;
        } else {
          is_target = false;
        }

        if (!condition_manager_.is_fulfilled(output_event_queue.get_manipulator_environment())) {
          is_target = false;
        }

        if (is_target) {
          manipulated_original_events_.emplace_back(front_input_event.get_device_id(),
                                                    front_input_event.get_original_event(),
                                                    from_mandatory_modifiers,
                                                    front_input_event.get_time_stamp());
        }

      } else {
        // event_type::key_up

        // Check original_event in order to determine the correspond key_down is manipulated.

        auto it = std::find_if(std::begin(manipulated_original_events_),
                               std::end(manipulated_original_events_),
                               [&](const auto& manipulated_original_event) {
                                 return manipulated_original_event.get_device_id() == front_input_event.get_device_id() &&
                                        manipulated_original_event.get_original_event() == front_input_event.get_original_event();
                               });
        if (it != std::end(manipulated_original_events_)) {
          from_mandatory_modifiers = it->get_from_mandatory_modifiers();
          key_down_time_stamp = it->get_key_down_time_stamp();
          alone = it->get_alone();
          manipulated_original_events_.erase(it);
        } else {
          is_target = false;
        }
      }

      if (is_target) {
        front_input_event.set_valid(false);

        uint64_t time_stamp_delay = 0;

        // Release from_mandatory_modifiers

        if (front_input_event.get_event_type() == event_type::key_down) {
          for (const auto& m : from_mandatory_modifiers) {
            if (auto key_code = types::get_key_code(m)) {
              event_queue::queued_event event(front_input_event.get_device_id(),
                                              front_input_event.get_time_stamp() + time_stamp_delay++,
                                              event_queue::queued_event::event(*key_code),
                                              event_type::key_up,
                                              front_input_event.get_original_event(),
                                              true);
              output_event_queue.push_back_event(event);
            }
          }
        }

        // Send events

        for (size_t i = 0; i < to_.size(); ++i) {
          if (auto event = to_[i].to_event()) {
            if (front_input_event.get_event_type() == event_type::key_down) {
              enqueue_to_modifiers(to_[i],
                                   event_type::key_down,
                                   front_input_event,
                                   !preserve_to_modifiers_down(),
                                   time_stamp_delay,
                                   output_event_queue);

              output_event_queue.emplace_back_event(front_input_event.get_device_id(),
                                                    front_input_event.get_time_stamp() + time_stamp_delay++,
                                                    *event,
                                                    event_type::key_down,
                                                    front_input_event.get_original_event());

              if (i != to_.size() - 1) {
                output_event_queue.emplace_back_event(front_input_event.get_device_id(),
                                                      front_input_event.get_time_stamp() + time_stamp_delay++,
                                                      *event,
                                                      event_type::key_up,
                                                      front_input_event.get_original_event());
              }

              if (i != to_.size() - 1 || !preserve_to_modifiers_down()) {
                enqueue_to_modifiers(to_[i],
                                     event_type::key_up,
                                     front_input_event,
                                     !preserve_to_modifiers_down(),
                                     time_stamp_delay,
                                     output_event_queue);
              }

            } else {
              // event_type::key_up

              if (i == to_.size() - 1) {
                output_event_queue.emplace_back_event(front_input_event.get_device_id(),
                                                      front_input_event.get_time_stamp() + time_stamp_delay++,
                                                      *event,
                                                      event_type::key_up,
                                                      front_input_event.get_original_event());

                if (preserve_to_modifiers_down()) {
                  enqueue_to_modifiers(to_[i],
                                       event_type::key_up,
                                       front_input_event,
                                       !preserve_to_modifiers_down(),
                                       time_stamp_delay,
                                       output_event_queue);
                }

                uint64_t nanoseconds = time_utility::absolute_to_nano(front_input_event.get_time_stamp() - key_down_time_stamp);
                if (alone &&
                    nanoseconds < parameters_.get_basic_to_if_alone_timeout_milliseconds() * NSEC_PER_MSEC) {
                  send_to_if_alone(front_input_event,
                                   time_stamp_delay,
                                   output_event_queue);
                }
              }
            }
          }
        }

        // Restore from_mandatory_modifiers

        if ((front_input_event.get_event_type() == event_type::key_down && !preserve_from_mandatory_modifiers_up()) ||
            (front_input_event.get_event_type() == event_type::key_up && preserve_from_mandatory_modifiers_up())) {
          for (const auto& m : from_mandatory_modifiers) {
            if (auto key_code = types::get_key_code(m)) {
              event_queue::queued_event event(front_input_event.get_device_id(),
                                              front_input_event.get_time_stamp() + time_stamp_delay++,
                                              event_queue::queued_event::event(*key_code),
                                              event_type::key_down,
                                              front_input_event.get_original_event(),
                                              true);
              output_event_queue.push_back_event(event);
            }
          }
        }

        if (time_stamp_delay > 0) {
          output_event_queue.increase_time_stamp_delay(time_stamp_delay - 1);
        }
      }
    }
  }

  virtual bool active(void) const {
    return !manipulated_original_events_.empty();
  }

  virtual void handle_device_ungrabbed_event(device_id device_id,
                                             const event_queue& output_event_queue,
                                             uint64_t time_stamp) {
    manipulated_original_events_.erase(std::remove_if(std::begin(manipulated_original_events_),
                                                      std::end(manipulated_original_events_),
                                                      [&](const auto& e) {
                                                        return e.get_device_id() == device_id;
                                                      }),
                                       std::end(manipulated_original_events_));
  }

  virtual void handle_event_from_ignored_device(const event_queue::queued_event& front_input_event,
                                                event_queue& output_event_queue) {
    unset_alone_if_needed(front_input_event.get_original_event(),
                          front_input_event.get_event_type());
  }

  const from_event_definition& get_from(void) const {
    return from_;
  }

  const std::vector<to_event_definition>& get_to(void) const {
    return to_;
  }

  void enqueue_to_modifiers(const to_event_definition& to,
                            event_type event_type,
                            const event_queue::queued_event& front_input_event,
                            bool lazy,
                            uint64_t& time_stamp_delay,
                            event_queue& output_event_queue) {
    for (const auto& modifier : to.get_modifiers()) {
      // `event_definition::get_modifiers` might return two modifier_flags.
      // (eg. `modifier_flag::left_shift` and `modifier_flag::right_shift` for `modifier::shift`.)
      // We use the first modifier_flag.

      auto modifier_flags = event_definition::get_modifier_flags(modifier);
      if (!modifier_flags.empty()) {
        auto modifier_flag = modifier_flags.front();
        if (auto key_code = types::get_key_code(modifier_flag)) {
          output_event_queue.emplace_back_event(front_input_event.get_device_id(),
                                                front_input_event.get_time_stamp() + time_stamp_delay++,
                                                event_queue::queued_event::event(*key_code),
                                                event_type,
                                                front_input_event.get_original_event(),
                                                !preserve_to_modifiers_down());
        }
      }
    }
  }

private:
  bool preserve_from_mandatory_modifiers_up(void) const {
    if (to_.empty()) {
      return false;
    }

    if (auto event = to_.back().to_event()) {
      if (auto key_code = event->get_key_code()) {
        if (types::get_modifier_flag(*key_code) != modifier_flag::zero) {
          return true;
        }
      }
    }

    return false;
  }

  bool preserve_to_modifiers_down(void) const {
    return preserve_from_mandatory_modifiers_up();
  }

  void send_to_if_alone(const event_queue::queued_event& front_input_event,
                        uint64_t& time_stamp_delay,
                        event_queue& output_event_queue) {
    for (const auto& to : to_if_alone_) {
      if (auto event = to.to_event()) {
        enqueue_to_modifiers(to,
                             event_type::key_down,
                             front_input_event,
                             true,
                             time_stamp_delay,
                             output_event_queue);

        output_event_queue.emplace_back_event(front_input_event.get_device_id(),
                                              front_input_event.get_time_stamp() + time_stamp_delay++,
                                              *event,
                                              event_type::key_down,
                                              front_input_event.get_original_event());

        output_event_queue.emplace_back_event(front_input_event.get_device_id(),
                                              front_input_event.get_time_stamp() + time_stamp_delay++,
                                              *event,
                                              event_type::key_up,
                                              front_input_event.get_original_event());

        enqueue_to_modifiers(to,
                             event_type::key_up,
                             front_input_event,
                             true,
                             time_stamp_delay,
                             output_event_queue);
      }
    }
  }

  void unset_alone_if_needed(const event_queue::queued_event::event& event,
                             event_type event_type) {
    if (event.get_type() == event_queue::queued_event::event::type::key_code ||
        event.get_type() == event_queue::queued_event::event::type::pointing_button) {
      if (event_type == event_type::key_down) {
        goto run;
      }
    }
    if (event.get_type() == event_queue::queued_event::event::type::pointing_vertical_wheel ||
        event.get_type() == event_queue::queued_event::event::type::pointing_horizontal_wheel) {
      if (auto integer_value = event.get_integer_value()) {
        if (*integer_value != 0) {
          goto run;
        }
      }
    }

    return;

  run:
    for (auto& e : manipulated_original_events_) {
      e.unset_alone();
    }
  }

  core_configuration::profile::complex_modifications::parameters parameters_;

  from_event_definition from_;
  std::vector<to_event_definition> to_;
  std::vector<to_event_definition> to_if_alone_;

  std::vector<manipulated_original_event> manipulated_original_events_;
};
} // namespace details
} // namespace manipulator
} // namespace krbn
<|endoftext|>


<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 <af/dim4.hpp>
#include <Array.hpp>
#include <map>
#include <iostream>
#include <stdexcept>
#include <copy.hpp>
#include <JIT/Node.hpp>
#include <kernel_headers/jit.hpp>
#include <program.hpp>
#include <dispatch.hpp>
#include <err_opencl.hpp>

namespace opencl
{

using JIT::Node;

using cl::Buffer;
using cl::Program;
using cl::Kernel;
using cl::make_kernel;
using cl::EnqueueArgs;
using cl::NDRange;
using std::string;
using std::stringstream;

static string getFuncName(Node *node, bool is_linear)
{
    stringstream funcName;

    if (is_linear) {
        funcName << "KL_";
    } else {
        funcName << "KG_";
    }

    node->genKerName(funcName, false);
    funcName << "_";
    node->genKerName(funcName, true);
    return funcName.str();
}

static string getKernelString(string funcName, Node *node, bool is_linear)
{
    stringstream kerStream;
    int id = node->setId(0) - 1;

    kerStream << "#pragma OPENCL EXTENSION cl_khr_fp64 : enable" << "\n" << "\n";
    kerStream << "__kernel void" << "\n";

    kerStream << funcName;
    kerStream << "(" << "\n";

    node->genParams(kerStream);
    kerStream << "__global " << node->getTypeStr() << " *out, KParam oInfo," << "\n";
    kerStream << "uint groups_0, uint groups_1)" << "\n";

    kerStream << "{" << "\n" << "\n";

    if (!is_linear) {

        kerStream << "uint id2 = get_group_id(0) / groups_0;" << "\n";
        kerStream << "uint id3 = get_group_id(1) / groups_1;" << "\n";
        kerStream << "uint groupId_0 = get_group_id(0) - id2 * groups_0;" << "\n";
        kerStream << "uint groupId_1 = get_group_id(1) - id3 * groups_1;" << "\n";
        kerStream << "uint id1 = get_local_id(1) + groupId_1 * get_local_size(1);" << "\n";
        kerStream << "uint id0 = get_local_id(0) + groupId_0 * get_local_size(0);" << "\n";
        kerStream << "\n";

        kerStream << "bool cond = " << "\n";
        kerStream << "id0 < oInfo.dims[0] && " << "\n";
        kerStream << "id1 < oInfo.dims[1] && " << "\n";
        kerStream << "id2 < oInfo.dims[2] && " << "\n";
        kerStream << "id3 < oInfo.dims[3];" << "\n" << "\n";

        kerStream << "if (!cond) return;" << "\n" << "\n";

        kerStream << "int idx = ";
        kerStream << "oInfo.strides[3] * id3 + oInfo.strides[2] * id2 + ";
        kerStream << "oInfo.strides[1] * id1 + id0 + oInfo.offset;" << "\n" << "\n";

    } else {

        kerStream << "uint groupId  = get_group_id(1) * get_num_groups(0) + get_group_id(0);" << "\n";
        kerStream << "uint threadId = get_local_id(0);" << "\n";
        kerStream << "int idx = groupId * get_local_size(0) * get_local_size(1) + threadId;" << "\n";
        kerStream << "if (idx >= oInfo.dims[3] * oInfo.strides[3]) return;" << "\n";
    }

    node->genOffsets(kerStream, is_linear);
    node->genFuncs(kerStream, is_linear);
    kerStream << "\n";

    kerStream << "out[idx] = val"
           << id << ";"  << "\n";

    kerStream << "}" << "\n";

    return kerStream.str();
}

static Kernel getKernel(Node *node, bool is_linear)
{

    string funcName = getFuncName(node, is_linear);

    typedef struct {
        Program prog;
        Kernel ker;
    } kc_entry_t;

    typedef std::map<string, kc_entry_t> kc_t;
    static kc_t kernelCaches[DeviceManager::MAX_DEVICES];
    int device = getActiveDeviceId();

    kc_t::iterator idx = kernelCaches[device].find(funcName);
    kc_entry_t entry;

    if (idx == kernelCaches[device].end()) {
        string jit_ker = getKernelString(funcName, node, is_linear);

        const char *ker_strs[] = {jit_cl, jit_ker.c_str()};
        const int ker_lens[] = {jit_cl_len, (int)jit_ker.size()};

        buildProgram(entry.prog, 2, ker_strs, ker_lens, string(""));
        entry.ker = Kernel(entry.prog, funcName.c_str());

        kernelCaches[device][funcName] = entry;
    } else {
        entry = idx->second;
    }

    return entry.ker;
}

void evalNodes(Param &out, Node *node)
{
    try {

        bool is_linear = node->isLinear();
        Kernel ker = getKernel(node, is_linear);

        uint local_0 = 1;
        uint local_1 = 1;
        uint global_0 = 1;
        uint global_1 = 1;
        uint groups_0 = 1;
        uint groups_1 = 1;

        if (is_linear) {
            local_0 = 256;
            uint out_elements = out.info.dims[3] * out.info.strides[3];
            uint groups = divup(out_elements, local_0);

            global_1 = divup(groups,     1000) * local_1;
            global_0 = divup(groups, global_1) * local_0;

        } else {
            local_0 = 32;
            local_1 = 8;

            groups_0 = divup(out.info.dims[0], local_0);
            groups_1 = divup(out.info.dims[1], local_1);

            global_0 = groups_0 * local_0 * out.info.dims[2];
            global_1 = groups_1 * local_1 * out.info.dims[3];
        }

        NDRange local(local_0, local_1);
        NDRange global(global_0, global_1);

        int args = node->setArgs(ker, 0);
        ker.setArg(args + 0, out.data);
        ker.setArg(args + 1, out.info);
        ker.setArg(args + 2, groups_0);
        ker.setArg(args + 3, groups_1);

        getQueue().enqueueNDRangeKernel(ker, cl::NullRange, global, local);

    } catch (cl::Error ex) {
        CL_TO_AF_ERROR(ex);
    }

}

}
<commit_msg>correctly adding USE_DOUBLE to OpenCL JIT<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 <af/dim4.hpp>
#include <Array.hpp>
#include <map>
#include <iostream>
#include <stdexcept>
#include <copy.hpp>
#include <JIT/Node.hpp>
#include <kernel_headers/jit.hpp>
#include <program.hpp>
#include <dispatch.hpp>
#include <err_opencl.hpp>

namespace opencl
{

using JIT::Node;

using cl::Buffer;
using cl::Program;
using cl::Kernel;
using cl::make_kernel;
using cl::EnqueueArgs;
using cl::NDRange;
using std::string;
using std::stringstream;

static string getFuncName(Node *node, bool is_linear, bool *is_double)
{
    stringstream funcName;

    if (is_linear) {
        funcName << "KL_";
    } else {
        funcName << "KG_";
    }

    node->genKerName(funcName, false);
    funcName << "_";

    stringstream inNames;
    node->genKerName(inNames, true);

    string inStr = inNames.str();
    string dblChars = "dDzZ";

    size_t loc = inStr.find_first_of(dblChars);
    *is_double = (loc != std::string::npos);

    funcName << inStr;
    return funcName.str();
}

static string getKernelString(string funcName, Node *node, bool is_linear)
{
    stringstream kerStream;
    int id = node->setId(0) - 1;

    kerStream << "__kernel void" << "\n";

    kerStream << funcName;
    kerStream << "(" << "\n";

    node->genParams(kerStream);
    kerStream << "__global " << node->getTypeStr() << " *out, KParam oInfo," << "\n";
    kerStream << "uint groups_0, uint groups_1)" << "\n";

    kerStream << "{" << "\n" << "\n";

    if (!is_linear) {

        kerStream << "uint id2 = get_group_id(0) / groups_0;" << "\n";
        kerStream << "uint id3 = get_group_id(1) / groups_1;" << "\n";
        kerStream << "uint groupId_0 = get_group_id(0) - id2 * groups_0;" << "\n";
        kerStream << "uint groupId_1 = get_group_id(1) - id3 * groups_1;" << "\n";
        kerStream << "uint id1 = get_local_id(1) + groupId_1 * get_local_size(1);" << "\n";
        kerStream << "uint id0 = get_local_id(0) + groupId_0 * get_local_size(0);" << "\n";
        kerStream << "\n";

        kerStream << "bool cond = " << "\n";
        kerStream << "id0 < oInfo.dims[0] && " << "\n";
        kerStream << "id1 < oInfo.dims[1] && " << "\n";
        kerStream << "id2 < oInfo.dims[2] && " << "\n";
        kerStream << "id3 < oInfo.dims[3];" << "\n" << "\n";

        kerStream << "if (!cond) return;" << "\n" << "\n";

        kerStream << "int idx = ";
        kerStream << "oInfo.strides[3] * id3 + oInfo.strides[2] * id2 + ";
        kerStream << "oInfo.strides[1] * id1 + id0 + oInfo.offset;" << "\n" << "\n";

    } else {

        kerStream << "uint groupId  = get_group_id(1) * get_num_groups(0) + get_group_id(0);" << "\n";
        kerStream << "uint threadId = get_local_id(0);" << "\n";
        kerStream << "int idx = groupId * get_local_size(0) * get_local_size(1) + threadId;" << "\n";
        kerStream << "if (idx >= oInfo.dims[3] * oInfo.strides[3]) return;" << "\n";
    }

    node->genOffsets(kerStream, is_linear);
    node->genFuncs(kerStream, is_linear);
    kerStream << "\n";

    kerStream << "out[idx] = val"
           << id << ";"  << "\n";

    kerStream << "}" << "\n";

    return kerStream.str();
}

static Kernel getKernel(Node *node, bool is_linear)
{

    bool is_dbl = false;
    string funcName = getFuncName(node, is_linear, &is_dbl);

    typedef struct {
        Program prog;
        Kernel ker;
    } kc_entry_t;

    typedef std::map<string, kc_entry_t> kc_t;
    static kc_t kernelCaches[DeviceManager::MAX_DEVICES];
    int device = getActiveDeviceId();

    kc_t::iterator idx = kernelCaches[device].find(funcName);
    kc_entry_t entry;

    if (idx == kernelCaches[device].end()) {
        string jit_ker = getKernelString(funcName, node, is_linear);

        const char *ker_strs[] = {jit_cl, jit_ker.c_str()};
        const int ker_lens[] = {jit_cl_len, (int)jit_ker.size()};

        buildProgram(entry.prog, 2, ker_strs, ker_lens, is_dbl ? string(" -D USE_DOUBLE") :  string(""));
        entry.ker = Kernel(entry.prog, funcName.c_str());

        kernelCaches[device][funcName] = entry;
    } else {
        entry = idx->second;
    }

    return entry.ker;
}

void evalNodes(Param &out, Node *node)
{
    try {

        bool is_linear = node->isLinear();
        Kernel ker = getKernel(node, is_linear);

        uint local_0 = 1;
        uint local_1 = 1;
        uint global_0 = 1;
        uint global_1 = 1;
        uint groups_0 = 1;
        uint groups_1 = 1;

        if (is_linear) {
            local_0 = 256;
            uint out_elements = out.info.dims[3] * out.info.strides[3];
            uint groups = divup(out_elements, local_0);

            global_1 = divup(groups,     1000) * local_1;
            global_0 = divup(groups, global_1) * local_0;

        } else {
            local_0 = 32;
            local_1 = 8;

            groups_0 = divup(out.info.dims[0], local_0);
            groups_1 = divup(out.info.dims[1], local_1);

            global_0 = groups_0 * local_0 * out.info.dims[2];
            global_1 = groups_1 * local_1 * out.info.dims[3];
        }

        NDRange local(local_0, local_1);
        NDRange global(global_0, global_1);

        int args = node->setArgs(ker, 0);
        ker.setArg(args + 0, out.data);
        ker.setArg(args + 1, out.info);
        ker.setArg(args + 2, groups_0);
        ker.setArg(args + 3, groups_1);

        getQueue().enqueueNDRangeKernel(ker, cl::NullRange, global, local);

    } catch (cl::Error ex) {
        CL_TO_AF_ERROR(ex);
    }

}

}
<|endoftext|>


<commit_before>#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include <roomba_clean_actions/basic_cleanAction.h>
#include <roomba_serial/SendButton.h>
#include <roomba_serial/SetMode.h>
#include <roomba_serial/Sensors.h>
#include <ctime>

class basic_cleanAction {
protected:
  ros::NodeHandle nh_;
  ros::ServiceClient client = nh_.serviceClient<roomba_serial::Sensors>("GetSensors");
  actionlib::SimpleActionServer<roomba_clean_actions::basic_cleanAction> cleanserv_;
  std::string action_name_;
  roomba_clean_actions::basic_cleanFeedback feedback_;
  roomba_clean_actions::basic_cleanResult result_;
  ros::Publisher button_pub = nh_.advertise<roomba_serial::SendButton>("BUTTON_OUT", 100);
  ros::Publisher mode_pub = nh_.advertise<roomba_serial::SetMode>("MODE_CHANGES", 100);

public:
  basic_cleanAction(std::string name) :
  cleanserv_(nh_, name, boost::bind(&basic_cleanAction::executeCB, this, _1), false), action_name_(name) {
    cleanserv_.start();
    ROS_INFO("Clean server started.");
  }

  ~basic_cleanAction(void) {
  }

  void executeCB(const roomba_clean_actions::basic_cleanGoalConstPtr &goal) {
    ROS_INFO("Got goal of %d seconds", goal->seconds);
    ros::Rate r(1);
    roomba_serial::SendButton dock;
    dock.buttoncode = 10;
    roomba_serial::Sensors sensorServer;
    int distance = 0;
    float charge;
    sensorServer.request.request = 0;
    bool success = true;
    time_t base = time(NULL);
    time_t curr = base;
    roomba_serial::SetMode mode;
    roomba_serial::SendButton button;
    mode.modecode = 2; // Safe mode, to start cleaning cycle
    button.buttoncode = 3; // Max length cycle
    bool running = false;
    while(!running) {
      mode_pub.publish(mode);
      button_pub.publish(button);
      while(!client.call(sensorServer)) {r.sleep();}
      running = (sensorServer.response.current < -150);
      ROS_INFO("Current out of battery is %d mA", sensorServer.response.current);
      r.sleep();
      r.sleep();
      r.sleep();
      r.sleep();
      r.sleep();
    }
    while(curr < base + goal->seconds) {
      if(cleanserv_.isPreemptRequested() || !ros::ok()) {
        mode_pub.publish(dock);
        ROS_INFO("%s: Preempted, now docking", action_name_.c_str());
        cleanserv_.setPreempted();
        success = false;
        break;
      }
      curr = time(NULL);
      if(client.call(sensorServer)) {
        distance = distance + sensorServer.response.distance;
        charge = (float) (sensorServer.response.charge / sensorServer.response.capacity);
      }
      else {
        ROS_INFO("%s: Didn't get sensor response, sending stale data.", action_name_.c_str());
      }
      feedback_.seconds = (uint16_t) (curr - base);
      feedback_.millimeters = distance;
      feedback_.battery_charge = charge;
      cleanserv_.publishFeedback(feedback_);
      r.sleep();
    }
    button_pub.publish(dock);
    bool charging = false;
    while(!charging) {
      while(!client.call(sensorServer)) {r.sleep();}
      charging = (sensorServer.response.current > 0);
      feedback_.millimeters = distance + sensorServer.response.distance;
      feedback_.battery_charge = (float) (sensorServer.response.charge / sensorServer.response.capacity);
      feedback_.seconds = time(NULL) - base;
      cleanserv_.publishFeedback(feedback_);
      r.sleep();
    }
    result_.seconds = feedback_.seconds;
    result_.millimeters = feedback_.millimeters;
    result_.battery_charge = feedback_.battery_charge;
    cleanserv_.setSucceeded(result_);
  }
};

int main(int argc, char** argv) {
  ros::init(argc, argv, "basic_clean");
  basic_cleanAction bc(ros::this_node::getName());
  ros::spin();
}
<commit_msg>Pre-empt should now work even when waiting for clean to start. Removed some logging as well.<commit_after>#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include <roomba_clean_actions/basic_cleanAction.h>
#include <roomba_serial/SendButton.h>
#include <roomba_serial/SetMode.h>
#include <roomba_serial/Sensors.h>
#include <ctime>

class basic_cleanAction {
protected:
  ros::NodeHandle nh_;
  ros::ServiceClient client = nh_.serviceClient<roomba_serial::Sensors>("GetSensors");
  actionlib::SimpleActionServer<roomba_clean_actions::basic_cleanAction> cleanserv_;
  std::string action_name_;
  roomba_clean_actions::basic_cleanFeedback feedback_;
  roomba_clean_actions::basic_cleanResult result_;
  ros::Publisher button_pub = nh_.advertise<roomba_serial::SendButton>("BUTTON_OUT", 100);
  ros::Publisher mode_pub = nh_.advertise<roomba_serial::SetMode>("MODE_CHANGES", 100);

public:
  basic_cleanAction(std::string name) :
  cleanserv_(nh_, name, boost::bind(&basic_cleanAction::executeCB, this, _1), false), action_name_(name) {
    cleanserv_.start();
    ROS_INFO("Clean server started.");
  }

  ~basic_cleanAction(void) {
  }

  void executeCB(const roomba_clean_actions::basic_cleanGoalConstPtr &goal) {
    ROS_INFO("Got goal of %d seconds", goal->seconds);
    ros::Rate r(1);
    roomba_serial::SendButton dock;
    dock.buttoncode = 10;
    roomba_serial::Sensors sensorServer;
    int distance = 0;
    float charge;
    sensorServer.request.request = 0;
    bool success = true;
    time_t base = time(NULL);
    time_t curr = base;
    roomba_serial::SetMode mode;
    roomba_serial::SendButton button;
    mode.modecode = 2; // Safe mode, to start cleaning cycle
    button.buttoncode = 3; // Max length cycle
    bool running = false;
    while(!running) {
      if(cleanserv_.isPreemptRequested() || !ros::ok()) {
        mode_pub.publish(dock);
        ROS_INFO("%s: Preempted, now docking", action_name_.c_str());
        cleanserv_.setPreempted();
        success = false;
        break;
      }
      mode_pub.publish(mode);
      button_pub.publish(button);
      while(!client.call(sensorServer)) {r.sleep();}
      running = (sensorServer.response.current < -150);
      //ROS_INFO("Current out of battery is %d mA", sensorServer.response.current);
      r.sleep();
      r.sleep();
      r.sleep();
      r.sleep();
      r.sleep();
    }
    while(curr < base + goal->seconds) {
      if(cleanserv_.isPreemptRequested() || !ros::ok()) {
        mode_pub.publish(dock);
        ROS_INFO("%s: Preempted, now docking", action_name_.c_str());
        cleanserv_.setPreempted();
        success = false;
        break;
      }
      curr = time(NULL);
      if(client.call(sensorServer)) {
        distance = distance + sensorServer.response.distance;
        charge = (float) (sensorServer.response.charge / sensorServer.response.capacity);
      }
      else {
        ROS_INFO("%s: Didn't get sensor response, sending stale data.", action_name_.c_str());
      }
      feedback_.seconds = (uint16_t) (curr - base);
      feedback_.millimeters = distance;
      feedback_.battery_charge = charge;
      cleanserv_.publishFeedback(feedback_);
      r.sleep();
    }
    button_pub.publish(dock);
    bool charging = false;
    while(!charging) {
      while(!client.call(sensorServer)) {r.sleep();}
      charging = (sensorServer.response.current > 0);
      feedback_.millimeters = distance + sensorServer.response.distance;
      feedback_.battery_charge = (float) (sensorServer.response.charge / sensorServer.response.capacity);
      feedback_.seconds = time(NULL) - base;
      cleanserv_.publishFeedback(feedback_);
      r.sleep();
    }
    result_.seconds = feedback_.seconds;
    result_.millimeters = feedback_.millimeters;
    result_.battery_charge = feedback_.battery_charge;
    cleanserv_.setSucceeded(result_);
  }
};

int main(int argc, char** argv) {
  ros::init(argc, argv, "basic_clean");
  basic_cleanAction bc(ros::this_node::getName());
  ros::spin();
}
<|endoftext|>