<commit_before>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Michael Hackstein
////////////////////////////////////////////////////////////////////////////////

#include "Basics/JsonHelper.h"
#include "Aql/Ast.h"
#include "Aql/ExecutionPlan.h"
#include "Aql/TraversalConditionFinder.h"
#include "Aql/TraversalNode.h"

using namespace arangodb::aql;
using EN = arangodb::aql::ExecutionNode;

static bool checkPathVariableAccessFeasible(CalculationNode const* cn,
                                            TraversalNode* tn,
                                            Variable const* var,
                                            bool& conditionIsImpossible,
                                            Ast* ast) {
  auto node = cn->expression()->node();

  if (node->containsNodeType(NODE_TYPE_OPERATOR_BINARY_OR) ||
      node->containsNodeType(NODE_TYPE_OPERATOR_BINARY_IN) ||
      node->containsNodeType(NODE_TYPE_OPERATOR_BINARY_NIN)) {
    return false;
  }

  std::vector<AstNode const*> currentPath;
  std::vector<std::vector<AstNode const*>> paths;

  node->findVariableAccess(currentPath, paths, var);

  for (auto const& onePath : paths) {
    size_t len = onePath.size();
    bool isEdgeAccess = false;

    for (auto const & node : onePath) {
      if (node->type == NODE_TYPE_FCALL) {
        //
        // we currently don't know how to execute functions in the
        // traversal (-> TraverserExpression::recursiveCheck
        return false;
      }
    }

    if (onePath[len - 2]->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
      isEdgeAccess = strcmp(onePath[len - 2]->getStringValue(), "edges") == 0;

      if (!isEdgeAccess &&
          strcmp(onePath[len - 2]->getStringValue(), "vertices") != 0) {
        /* We can't catch all cases in which this error would occur, so we don't
           throw here.
           std::string message("TRAVERSAL: path only knows 'edges' and
           'vertices', not ");
           message += onePath[len - 2]->getStringValue();
           THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_QUERY_PARSE, message);
        */
        return false;
      }
    }

    // we now need to check for p.edges[n] whether n is >= 0
    if (onePath[len - 3]->type == NODE_TYPE_INDEXED_ACCESS) {
      auto indexAccessNode = onePath[len - 3]->getMember(1);
      if ((indexAccessNode->type != NODE_TYPE_VALUE) ||
          (indexAccessNode->value.type != VALUE_TYPE_INT) ||
          (indexAccessNode->value.value._int < 0)) {
        return false;
      }

      conditionIsImpossible =
          !tn->isInRange(indexAccessNode->value.value._int, isEdgeAccess);

    } else if ((onePath[len - 3]->type == NODE_TYPE_ITERATOR) &&
               (onePath[len - 4]->type == NODE_TYPE_EXPANSION)) {
      // we now need to check for p.edges[*] which becomes a fancy structure
      return false;
    } else {
      return false;
    }
  }

  return true;
}

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

static bool extractSimplePathAccesses(AstNode const* node, TraversalNode* tn,
                                      Ast* ast) {
  std::vector<AstNode const*> currentPath;
  std::vector<std::vector<AstNode const*>> paths;
  std::vector<std::vector<AstNode const*>> clonePath;

  node->findVariableAccess(currentPath, paths, tn->pathOutVariable());

  for (auto const& onePath : paths) {
    size_t len = onePath.size();
    bool isEdgeAccess = false;
    size_t attrAccessTo = 0;

    if (onePath[len - 2]->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
      isEdgeAccess = strcmp(onePath[len - 2]->getStringValue(), "edges") == 0;
    }
    // we now need to check for p.edges[n] whether n is >= 0
    if (onePath[len - 3]->type == NODE_TYPE_INDEXED_ACCESS) {
      auto indexAccessNode = onePath[len - 3]->getMember(1);
      attrAccessTo = indexAccessNode->value.value._int;
    }

    AstNode const* compareNode = nullptr;
    AstNode const* accessNodeBranch = nullptr;

    for (auto const& oneNode : onePath) {
      if (compareNode != nullptr && accessNodeBranch == nullptr) {
        accessNodeBranch = oneNode;
      }

      if ((oneNode->type == NODE_TYPE_OPERATOR_BINARY_EQ) ||
          (oneNode->type == NODE_TYPE_OPERATOR_BINARY_NE) ||
          (oneNode->type == NODE_TYPE_OPERATOR_BINARY_LT) ||
          (oneNode->type == NODE_TYPE_OPERATOR_BINARY_LE) ||
          (oneNode->type == NODE_TYPE_OPERATOR_BINARY_GT) ||
          (oneNode->type == NODE_TYPE_OPERATOR_BINARY_GE)
          //  || As long as we need to twist the access, this is impossible:
          // (oneNode->type == NODE_TYPE_OPERATOR_BINARY_IN ) ||
          // (oneNode->type == NODE_TYPE_OPERATOR_BINARY_NIN))
          ) {
        compareNode = oneNode;
      }
    }

    if (compareNode != nullptr) {
      AstNode const* pathAccessNode;
      AstNode* filterByNode;
      bool flipOperator = false;

      if (compareNode->getMember(0) == accessNodeBranch) {
        pathAccessNode = accessNodeBranch;
        filterByNode = compareNode->getMember(1);
      } else {
        flipOperator = (compareNode->type == NODE_TYPE_OPERATOR_BINARY_LT) ||
                       (compareNode->type == NODE_TYPE_OPERATOR_BINARY_LE) ||
                       (compareNode->type == NODE_TYPE_OPERATOR_BINARY_GT) ||
                       (compareNode->type == NODE_TYPE_OPERATOR_BINARY_GE);

        pathAccessNode = accessNodeBranch;
        filterByNode = compareNode->getMember(0);
      }

      // TODO: warning: Called C++ object pointer is null
      if (accessNodeBranch->isSimple() && filterByNode->isDeterministic()) {
        currentPath.clear();
        clonePath.clear();
        filterByNode->findVariableAccess(currentPath, clonePath,
                                         tn->pathOutVariable());
        if (!clonePath.empty()) {
          // Path variable access on the RHS? can't do that.
          continue;
        }
        AstNode* newNode = pathAccessNode->clone(ast);

        // since we just copied one path, we should only find one.
        currentPath.clear();
        clonePath.clear();
        newNode->findVariableAccess(currentPath, clonePath,
                                    tn->pathOutVariable());
        if (clonePath.size() != 1) {
          continue;
        }
        auto len = clonePath[0].size();
        if (len < 4) {
          continue;
        }

        AstNode* firstRefNode = (AstNode*)clonePath[0][len - 4];
        TRI_ASSERT(firstRefNode->type == NODE_TYPE_ATTRIBUTE_ACCESS);

        // replace the path variable access by a variable access to edge/vertex
        // (then current to the iteration)
        auto varRefNode = new AstNode(NODE_TYPE_REFERENCE);
        try {
          ast->query()->addNode(varRefNode);
        } catch (...) {
          // prevent leak
          delete varRefNode;
          throw;
        }
        varRefNode->setData(isEdgeAccess ? tn->edgeOutVariable()
                                         : tn->vertexOutVariable());
        firstRefNode->changeMember(0, varRefNode);

        auto expressionOperator = compareNode->type;
        if (flipOperator) {
          if (expressionOperator == NODE_TYPE_OPERATOR_BINARY_LT) {
            expressionOperator = NODE_TYPE_OPERATOR_BINARY_GT;
          } else if (expressionOperator == NODE_TYPE_OPERATOR_BINARY_LE) {
            expressionOperator = NODE_TYPE_OPERATOR_BINARY_GE;
          } else if (expressionOperator == NODE_TYPE_OPERATOR_BINARY_GT) {
            expressionOperator = NODE_TYPE_OPERATOR_BINARY_LT;
          } else if (expressionOperator == NODE_TYPE_OPERATOR_BINARY_GE) {
            expressionOperator = NODE_TYPE_OPERATOR_BINARY_LE;
          }
        }
        tn->storeSimpleExpression(isEdgeAccess, attrAccessTo,
                                  expressionOperator, newNode, filterByNode);
      }
    }
  }

  return true;
}

bool TraversalConditionFinder::before(ExecutionNode* en) {
  if (!_variableDefinitions.empty() && en->canThrow()) {
    // we already found a FILTER and
    // something that can throw is not safe to optimize
    _filters.clear();
    return true;
  }

  switch (en->getType()) {
    case EN::ENUMERATE_LIST:
    case EN::COLLECT:
    case EN::SCATTER:
    case EN::DISTRIBUTE:
    case EN::GATHER:
    case EN::REMOTE:
    case EN::SUBQUERY:
    case EN::INDEX:
    case EN::INSERT:
    case EN::REMOVE:
    case EN::REPLACE:
    case EN::UPDATE:
    case EN::UPSERT:
    case EN::RETURN:
    case EN::SORT:
    case EN::ENUMERATE_COLLECTION:
    case EN::LIMIT:
      // in these cases we simply ignore the intermediate nodes, note
      // that we have taken care of nodes that could throw exceptions
      // above.
      break;

    case EN::SINGLETON:
    case EN::NORESULTS:
    case EN::ILLEGAL:
      // in all these cases we better abort
      return true;

    case EN::FILTER: {
      std::vector<Variable const*>&& invars = en->getVariablesUsedHere();
      TRI_ASSERT(invars.size() == 1);
      // register which variable is used in a FILTER
      _filters.emplace(invars[0]->id, en);
      break;
    }

    case EN::CALCULATION: {
      auto outvars = en->getVariablesSetHere();
      TRI_ASSERT(outvars.size() == 1);

      _variableDefinitions.emplace(outvars[0]->id,
                                   static_cast<CalculationNode const*>(en));
      TRI_IF_FAILURE("ConditionFinder::variableDefinition") {
        THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
      }
      break;
    }

    case EN::TRAVERSAL: {
      auto node = static_cast<TraversalNode*>(en);

      auto condition = std::make_unique<Condition>(_plan->getAst());

      bool foundCondition = false;
      auto const& varsValidInTraversal = node->getVarsValid();
      std::unordered_set<Variable const*> varsUsedByCondition;

      bool conditionIsImpossible = false;

      for (auto& it : _variableDefinitions) {
        auto f = _filters.find(it.first);
        if (f != _filters.end()) {
          // a variable used in a FILTER
          auto outVar = node->getVariablesSetHere();
          if (outVar.size() != 1 || outVar[0]->id == f->first) {
            // now we know, this filter is used for our traversal node.
            auto cn = it.second;

            // check whether variables that are not in scope of the condition
            // are used:
            varsUsedByCondition.clear();
            Ast::getReferencedVariables(cn->expression()->node(),
                                        varsUsedByCondition);
            bool unknownVariableFound = false;
            for (auto const& conditionVar : varsUsedByCondition) {
              bool found = false;
              for (auto const& traversalKnownVar : varsValidInTraversal) {
                if (conditionVar->id == traversalKnownVar->id) {
                  found = true;
                  break;
                }
              }
              if (!found) {
                unknownVariableFound = true;
                break;
              }
            }
            if (unknownVariableFound) {
              continue;
            }

            for (auto const& conditionVar : varsUsedByCondition) {
              // check whether conditionVar is one of those we emit
              int variableType = node->checkIsOutVariable(conditionVar->id);
              if (variableType >= 0) {
                if ((variableType == 2) &&
                    checkPathVariableAccessFeasible(cn, node, conditionVar,
                                                    conditionIsImpossible,
                                                    _plan->getAst())) {
                  condition->andCombine(
                      it.second->expression()->node()->clone(_plan->getAst()));
                  foundCondition = true;
                }
                if (conditionIsImpossible) break;
              }
            }
          }
        }
        if (conditionIsImpossible) break;
      }

      if (!conditionIsImpossible) {
        conditionIsImpossible = !node->isRangeValid();
      }

      // TODO: we can't execute if we condition->normalize(_plan); in
      // generateCodeNode
      if (!conditionIsImpossible) {
        // right now we're not clever enough to find impossible conditions...
        conditionIsImpossible = (foundCondition && condition->isEmpty());
      }

      if (conditionIsImpossible) {
        // condition is always false
        for (auto const& x : node->getParents()) {
          auto noRes = new NoResultsNode(_plan, _plan->nextId());
          _plan->registerNode(noRes);
          _plan->insertDependency(x, noRes);
          *_planAltered = true;
        }
        break;
      }
      if (foundCondition) {
        condition->normalize();
        TRI_IF_FAILURE("ConditionFinder::normalizePlan") {
          THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
        }
        extractSimplePathAccesses(condition->root(), node, _plan->getAst());
        node->setCondition(condition.release());
        *_planAltered = true;
      }

      break;
    }
  }
  return false;
}

bool TraversalConditionFinder::enterSubquery(ExecutionNode*, ExecutionNode*) {
  return false;
}
<commit_msg>Added an additional nullptr check.<commit_after>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Michael Hackstein
////////////////////////////////////////////////////////////////////////////////

#include "Basics/JsonHelper.h"
#include "Aql/Ast.h"
#include "Aql/ExecutionPlan.h"
#include "Aql/TraversalConditionFinder.h"
#include "Aql/TraversalNode.h"

using namespace arangodb::aql;
using EN = arangodb::aql::ExecutionNode;

static bool checkPathVariableAccessFeasible(CalculationNode const* cn,
                                            TraversalNode* tn,
                                            Variable const* var,
                                            bool& conditionIsImpossible,
                                            Ast* ast) {
  auto node = cn->expression()->node();

  if (node->containsNodeType(NODE_TYPE_OPERATOR_BINARY_OR) ||
      node->containsNodeType(NODE_TYPE_OPERATOR_BINARY_IN) ||
      node->containsNodeType(NODE_TYPE_OPERATOR_BINARY_NIN)) {
    return false;
  }

  std::vector<AstNode const*> currentPath;
  std::vector<std::vector<AstNode const*>> paths;

  node->findVariableAccess(currentPath, paths, var);

  for (auto const& onePath : paths) {
    size_t len = onePath.size();
    bool isEdgeAccess = false;

    for (auto const & node : onePath) {
      if (node->type == NODE_TYPE_FCALL) {
        //
        // we currently don't know how to execute functions in the
        // traversal (-> TraverserExpression::recursiveCheck
        return false;
      }
    }

    if (onePath[len - 2]->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
      isEdgeAccess = strcmp(onePath[len - 2]->getStringValue(), "edges") == 0;

      if (!isEdgeAccess &&
          strcmp(onePath[len - 2]->getStringValue(), "vertices") != 0) {
        /* We can't catch all cases in which this error would occur, so we don't
           throw here.
           std::string message("TRAVERSAL: path only knows 'edges' and
           'vertices', not ");
           message += onePath[len - 2]->getStringValue();
           THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_QUERY_PARSE, message);
        */
        return false;
      }
    }

    // we now need to check for p.edges[n] whether n is >= 0
    if (onePath[len - 3]->type == NODE_TYPE_INDEXED_ACCESS) {
      auto indexAccessNode = onePath[len - 3]->getMember(1);
      if ((indexAccessNode->type != NODE_TYPE_VALUE) ||
          (indexAccessNode->value.type != VALUE_TYPE_INT) ||
          (indexAccessNode->value.value._int < 0)) {
        return false;
      }

      conditionIsImpossible =
          !tn->isInRange(indexAccessNode->value.value._int, isEdgeAccess);

    } else if ((onePath[len - 3]->type == NODE_TYPE_ITERATOR) &&
               (onePath[len - 4]->type == NODE_TYPE_EXPANSION)) {
      // we now need to check for p.edges[*] which becomes a fancy structure
      return false;
    } else {
      return false;
    }
  }

  return true;
}

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

static bool extractSimplePathAccesses(AstNode const* node, TraversalNode* tn,
                                      Ast* ast) {
  std::vector<AstNode const*> currentPath;
  std::vector<std::vector<AstNode const*>> paths;
  std::vector<std::vector<AstNode const*>> clonePath;

  node->findVariableAccess(currentPath, paths, tn->pathOutVariable());

  for (auto const& onePath : paths) {
    size_t len = onePath.size();
    bool isEdgeAccess = false;
    size_t attrAccessTo = 0;

    if (onePath[len - 2]->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
      isEdgeAccess = strcmp(onePath[len - 2]->getStringValue(), "edges") == 0;
    }
    // we now need to check for p.edges[n] whether n is >= 0
    if (onePath[len - 3]->type == NODE_TYPE_INDEXED_ACCESS) {
      auto indexAccessNode = onePath[len - 3]->getMember(1);
      attrAccessTo = indexAccessNode->value.value._int;
    }

    AstNode const* compareNode = nullptr;
    AstNode const* accessNodeBranch = nullptr;

    for (auto const& oneNode : onePath) {
      if (compareNode != nullptr && accessNodeBranch == nullptr) {
        accessNodeBranch = oneNode;
      }

      if ((oneNode->type == NODE_TYPE_OPERATOR_BINARY_EQ) ||
          (oneNode->type == NODE_TYPE_OPERATOR_BINARY_NE) ||
          (oneNode->type == NODE_TYPE_OPERATOR_BINARY_LT) ||
          (oneNode->type == NODE_TYPE_OPERATOR_BINARY_LE) ||
          (oneNode->type == NODE_TYPE_OPERATOR_BINARY_GT) ||
          (oneNode->type == NODE_TYPE_OPERATOR_BINARY_GE)
          //  || As long as we need to twist the access, this is impossible:
          // (oneNode->type == NODE_TYPE_OPERATOR_BINARY_IN ) ||
          // (oneNode->type == NODE_TYPE_OPERATOR_BINARY_NIN))
          ) {
        compareNode = oneNode;
      }
    }

    if (compareNode != nullptr) {
      AstNode const* pathAccessNode;
      AstNode* filterByNode;
      bool flipOperator = false;

      if (compareNode->getMember(0) == accessNodeBranch) {
        pathAccessNode = accessNodeBranch;
        filterByNode = compareNode->getMember(1);
      } else {
        flipOperator = (compareNode->type == NODE_TYPE_OPERATOR_BINARY_LT) ||
                       (compareNode->type == NODE_TYPE_OPERATOR_BINARY_LE) ||
                       (compareNode->type == NODE_TYPE_OPERATOR_BINARY_GT) ||
                       (compareNode->type == NODE_TYPE_OPERATOR_BINARY_GE);

        pathAccessNode = accessNodeBranch;
        filterByNode = compareNode->getMember(0);
      }

      // Hacki: I do not think that the nullptr check can ever fail because of
      // the structure of onePath
      if (accessNodeBranch != nullptr && accessNodeBranch->isSimple() &&
          filterByNode->isDeterministic()) {
        currentPath.clear();
        clonePath.clear();
        filterByNode->findVariableAccess(currentPath, clonePath,
                                         tn->pathOutVariable());
        if (!clonePath.empty()) {
          // Path variable access on the RHS? can't do that.
          continue;
        }
        AstNode* newNode = pathAccessNode->clone(ast);

        // since we just copied one path, we should only find one.
        currentPath.clear();
        clonePath.clear();
        newNode->findVariableAccess(currentPath, clonePath,
                                    tn->pathOutVariable());
        if (clonePath.size() != 1) {
          continue;
        }
        auto len = clonePath[0].size();
        if (len < 4) {
          continue;
        }

        AstNode* firstRefNode = (AstNode*)clonePath[0][len - 4];
        TRI_ASSERT(firstRefNode->type == NODE_TYPE_ATTRIBUTE_ACCESS);

        // replace the path variable access by a variable access to edge/vertex
        // (then current to the iteration)
        auto varRefNode = new AstNode(NODE_TYPE_REFERENCE);
        try {
          ast->query()->addNode(varRefNode);
        } catch (...) {
          // prevent leak
          delete varRefNode;
          throw;
        }
        varRefNode->setData(isEdgeAccess ? tn->edgeOutVariable()
                                         : tn->vertexOutVariable());
        firstRefNode->changeMember(0, varRefNode);

        auto expressionOperator = compareNode->type;
        if (flipOperator) {
          if (expressionOperator == NODE_TYPE_OPERATOR_BINARY_LT) {
            expressionOperator = NODE_TYPE_OPERATOR_BINARY_GT;
          } else if (expressionOperator == NODE_TYPE_OPERATOR_BINARY_LE) {
            expressionOperator = NODE_TYPE_OPERATOR_BINARY_GE;
          } else if (expressionOperator == NODE_TYPE_OPERATOR_BINARY_GT) {
            expressionOperator = NODE_TYPE_OPERATOR_BINARY_LT;
          } else if (expressionOperator == NODE_TYPE_OPERATOR_BINARY_GE) {
            expressionOperator = NODE_TYPE_OPERATOR_BINARY_LE;
          }
        }
        tn->storeSimpleExpression(isEdgeAccess, attrAccessTo,
                                  expressionOperator, newNode, filterByNode);
      }
    }
  }

  return true;
}

bool TraversalConditionFinder::before(ExecutionNode* en) {
  if (!_variableDefinitions.empty() && en->canThrow()) {
    // we already found a FILTER and
    // something that can throw is not safe to optimize
    _filters.clear();
    return true;
  }

  switch (en->getType()) {
    case EN::ENUMERATE_LIST:
    case EN::COLLECT:
    case EN::SCATTER:
    case EN::DISTRIBUTE:
    case EN::GATHER:
    case EN::REMOTE:
    case EN::SUBQUERY:
    case EN::INDEX:
    case EN::INSERT:
    case EN::REMOVE:
    case EN::REPLACE:
    case EN::UPDATE:
    case EN::UPSERT:
    case EN::RETURN:
    case EN::SORT:
    case EN::ENUMERATE_COLLECTION:
    case EN::LIMIT:
      // in these cases we simply ignore the intermediate nodes, note
      // that we have taken care of nodes that could throw exceptions
      // above.
      break;

    case EN::SINGLETON:
    case EN::NORESULTS:
    case EN::ILLEGAL:
      // in all these cases we better abort
      return true;

    case EN::FILTER: {
      std::vector<Variable const*>&& invars = en->getVariablesUsedHere();
      TRI_ASSERT(invars.size() == 1);
      // register which variable is used in a FILTER
      _filters.emplace(invars[0]->id, en);
      break;
    }

    case EN::CALCULATION: {
      auto outvars = en->getVariablesSetHere();
      TRI_ASSERT(outvars.size() == 1);

      _variableDefinitions.emplace(outvars[0]->id,
                                   static_cast<CalculationNode const*>(en));
      TRI_IF_FAILURE("ConditionFinder::variableDefinition") {
        THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
      }
      break;
    }

    case EN::TRAVERSAL: {
      auto node = static_cast<TraversalNode*>(en);

      auto condition = std::make_unique<Condition>(_plan->getAst());

      bool foundCondition = false;
      auto const& varsValidInTraversal = node->getVarsValid();
      std::unordered_set<Variable const*> varsUsedByCondition;

      bool conditionIsImpossible = false;

      for (auto& it : _variableDefinitions) {
        auto f = _filters.find(it.first);
        if (f != _filters.end()) {
          // a variable used in a FILTER
          auto outVar = node->getVariablesSetHere();
          if (outVar.size() != 1 || outVar[0]->id == f->first) {
            // now we know, this filter is used for our traversal node.
            auto cn = it.second;

            // check whether variables that are not in scope of the condition
            // are used:
            varsUsedByCondition.clear();
            Ast::getReferencedVariables(cn->expression()->node(),
                                        varsUsedByCondition);
            bool unknownVariableFound = false;
            for (auto const& conditionVar : varsUsedByCondition) {
              bool found = false;
              for (auto const& traversalKnownVar : varsValidInTraversal) {
                if (conditionVar->id == traversalKnownVar->id) {
                  found = true;
                  break;
                }
              }
              if (!found) {
                unknownVariableFound = true;
                break;
              }
            }
            if (unknownVariableFound) {
              continue;
            }

            for (auto const& conditionVar : varsUsedByCondition) {
              // check whether conditionVar is one of those we emit
              int variableType = node->checkIsOutVariable(conditionVar->id);
              if (variableType >= 0) {
                if ((variableType == 2) &&
                    checkPathVariableAccessFeasible(cn, node, conditionVar,
                                                    conditionIsImpossible,
                                                    _plan->getAst())) {
                  condition->andCombine(
                      it.second->expression()->node()->clone(_plan->getAst()));
                  foundCondition = true;
                }
                if (conditionIsImpossible) break;
              }
            }
          }
        }
        if (conditionIsImpossible) break;
      }

      if (!conditionIsImpossible) {
        conditionIsImpossible = !node->isRangeValid();
      }

      // TODO: we can't execute if we condition->normalize(_plan); in
      // generateCodeNode
      if (!conditionIsImpossible) {
        // right now we're not clever enough to find impossible conditions...
        conditionIsImpossible = (foundCondition && condition->isEmpty());
      }

      if (conditionIsImpossible) {
        // condition is always false
        for (auto const& x : node->getParents()) {
          auto noRes = new NoResultsNode(_plan, _plan->nextId());
          _plan->registerNode(noRes);
          _plan->insertDependency(x, noRes);
          *_planAltered = true;
        }
        break;
      }
      if (foundCondition) {
        condition->normalize();
        TRI_IF_FAILURE("ConditionFinder::normalizePlan") {
          THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
        }
        extractSimplePathAccesses(condition->root(), node, _plan->getAst());
        node->setCondition(condition.release());
        *_planAltered = true;
      }

      break;
    }
  }
  return false;
}

bool TraversalConditionFinder::enterSubquery(ExecutionNode*, ExecutionNode*) {
  return false;
}
<|endoftext|>


<commit_before>#include "DistributeDriver.h"
#include "MasterManagerBase.h"
#include "DistributeRequestHooker.h"
#include "RequestLog.h"

#include <util/scheduler.h>
#include <util/driver/Reader.h>
#include <util/driver/Writer.h>
#include <util/driver/writers/JsonWriter.h>
#include <util/driver/readers/JsonReader.h>
#include <util/driver/Keys.h>
#include <boost/bind.hpp>
#include <glog/logging.h>

using namespace izenelib::driver;

namespace sf1r
{

DistributeDriver::DistributeDriver()
{
    async_task_worker_ = boost::thread(&DistributeDriver::run, this);
    // only one write task can exist in distribute system.
    asyncWriteTasks_.resize(1);
}

void DistributeDriver::stop()
{
    async_task_worker_.interrupt();
    async_task_worker_.join();
    asyncWriteTasks_.clear();
}

void DistributeDriver::run()
{
    try
    {
        while(true)
        {
            boost::function<bool()> task;
            asyncWriteTasks_.pop(task);
            task();
            boost::this_thread::interruption_point();
        }
    }
    catch (boost::thread_interrupted&)
    {
        return;
    }
}

void DistributeDriver::init(const RouterPtr& router)
{
    router_ = router;
    MasterManagerBase::get()->setCallback(boost::bind(&DistributeDriver::on_new_req_available, this));
}

static bool callHandler(izenelib::driver::Router::handler_ptr handler,
    Request::kCallType calltype, const std::string& packed_data,
    Request& request)
{
    try
    {
        Response response;
        response.setSuccess(true);
        static Poller tmp_poller;
        // prepare request
        handler->invoke(request,
            response,
            tmp_poller);

        LOG(INFO) << "write request send in DistributeDriver success.";
        return true;
    }
    catch(const std::exception& e)
    {
        LOG(ERROR) << "call request handler exception: " << e.what();
    }
    return false;
}

bool DistributeDriver::handleRequest(const std::string& reqjsondata, const std::string& packed_data, Request::kCallType calltype)
{
    static izenelib::driver::JsonReader reader;
    Value requestValue;
    if(reader.read(reqjsondata, requestValue))
    {
        if (requestValue.type() != Value::kObjectType)
        {
            LOG(ERROR) << "read request data type error: Malformed request: require an object as input.";
            return false;
        }
        Request request;
        request.assignTmp(requestValue);
        izenelib::driver::Router::handler_ptr handler = router_->find(
            request.controller(),
            request.action()
            );
        if (!handler)
        {
            LOG(ERROR) << "Handler not found for the request : " << request.controller() <<
                "," << request.action();
            return false;
        }
        if (!ReqLogMgr::isWriteRequest(request.controller(), request.action()))
        {
            LOG(ERROR) << "=== Wrong, not a write request in DistributeDriver!!";
            return false;
        }
        try
        {
            DistributeRequestHooker::get()->setHook(calltype, packed_data);
            request.setCallType(calltype);
            if (calltype == Request::FromLog || calltype == Request::FromDistribute)
            {
                if (!asyncWriteTasks_.empty())
                {
                    LOG(ERROR) << "another write task is running in async_task_worker_!!";
                    return false;
                }
                // redo log must process the request one by one, so sync needed.
                return callHandler(handler, calltype, packed_data, request);
            }
            else
            {
                if (!async_task_worker_.interruption_requested())
                {
                    asyncWriteTasks_.push(boost::bind(&callHandler,
                            handler, calltype, packed_data, request));
                }
            }
            return true;
        }
        catch (const std::exception& e)
        {
            LOG(ERROR) << "process request exception: " << e.what();
        }
    }
    else
    {
        // malformed request
        LOG(WARNING) << "read request data error: " << reader.errorMessages();
    }
    return false;
}

bool DistributeDriver::handleReqFromLog(int reqtype, const std::string& reqjsondata, const std::string& packed_data)
{
    if ((ReqLogType)reqtype == Req_CronJob)
    {
	    LOG(INFO) << "got a cron job request in log." << reqjsondata;
	    DistributeRequestHooker::get()->setHook(Request::FromLog, packed_data);
	    DistributeRequestHooker::get()->hookCurrentReq(packed_data);
            DistributeRequestHooker::get()->processLocalBegin();
	    bool ret = izenelib::util::Scheduler::runJobImmediatly(reqjsondata, Request::FromLog, true);
	    if (!ret)
	    {
		    LOG(ERROR) << "cron job start failed";
	    }
	    return ret;
    }
    return handleRequest(reqjsondata, packed_data, Request::FromLog);
}

bool DistributeDriver::handleReqFromPrimary(int reqtype, const std::string& reqjsondata, const std::string& packed_data)
{
    if ((ReqLogType)reqtype == Req_CronJob)
    {
	DistributeRequestHooker::get()->setHook(Request::FromPrimaryWorker, packed_data);
	DistributeRequestHooker::get()->hookCurrentReq(packed_data);
	DistributeRequestHooker::get()->processLocalBegin();
        LOG(INFO) << "got a cron job request from primary." << reqjsondata;
        return izenelib::util::Scheduler::runJobImmediatly(reqjsondata, Request::FromPrimaryWorker);
    }

    return handleRequest(reqjsondata, packed_data, Request::FromPrimaryWorker);
}

bool DistributeDriver::on_new_req_available()
{
    if (!MasterManagerBase::get()->prepareWriteReq())
    {
        LOG(WARNING) << "prepare new request failed. maybe some other primary master prepared first. ";
        return false;
    }
    while(true)
    {
        std::string reqdata;
	std::string reqtype;
        if(!MasterManagerBase::get()->popWriteReq(reqdata, reqtype))
        {
            LOG(INFO) << "no more request.";
            break;
        }

        if(reqtype.empty() && !handleRequest(reqdata, reqdata, Request::FromDistribute) )
        {
            LOG(WARNING) << "one write request failed to deliver :" << reqdata;
            // a write request failed to deliver means the worker did not
            // started to handle this request, so the request is ignored, and
            // continue to deliver next write request in the queue.
        }
	else if (reqtype == "cron")
	{
	    LOG(INFO) << "got a cron job request from queue." << reqdata;
            DistributeRequestHooker::get()->setHook(Request::FromDistribute, reqdata);
            DistributeRequestHooker::get()->hookCurrentReq(reqdata);
            DistributeRequestHooker::get()->processLocalBegin();
	    bool ret = izenelib::util::Scheduler::runJobImmediatly(reqdata, Request::FromDistribute);
            if (!ret)
            {
		LOG(ERROR) << "cron job start failed";
	    }
	    else
	    {
		    break;
	    }
	}
        else
        {
            // a write request deliver success from master to worker.
            // this mean the worker has started to process this request,
            // may be not finish writing, so we break here to wait the finished
            // event from primary worker.
            break;
        }
    }
    return true;
}

}
<commit_msg>run job in async if not from log<commit_after>#include "DistributeDriver.h"
#include "MasterManagerBase.h"
#include "DistributeRequestHooker.h"
#include "RequestLog.h"

#include <util/scheduler.h>
#include <util/driver/Reader.h>
#include <util/driver/Writer.h>
#include <util/driver/writers/JsonWriter.h>
#include <util/driver/readers/JsonReader.h>
#include <util/driver/Keys.h>
#include <boost/bind.hpp>
#include <glog/logging.h>

using namespace izenelib::driver;

namespace sf1r
{

DistributeDriver::DistributeDriver()
{
    async_task_worker_ = boost::thread(&DistributeDriver::run, this);
    // only one write task can exist in distribute system.
    asyncWriteTasks_.resize(1);
}

void DistributeDriver::stop()
{
    async_task_worker_.interrupt();
    async_task_worker_.join();
    asyncWriteTasks_.clear();
}

void DistributeDriver::run()
{
    try
    {
        while(true)
        {
            boost::function<bool()> task;
            asyncWriteTasks_.pop(task);
            task();
            boost::this_thread::interruption_point();
        }
    }
    catch (boost::thread_interrupted&)
    {
        return;
    }
}

void DistributeDriver::init(const RouterPtr& router)
{
    router_ = router;
    MasterManagerBase::get()->setCallback(boost::bind(&DistributeDriver::on_new_req_available, this));
}

static bool callCronJob(Request::kCallType calltype, const std::string& jobname, const std::string& packed_data)
{
	DistributeRequestHooker::get()->hookCurrentReq(packed_data);
	DistributeRequestHooker::get()->processLocalBegin();
	LOG(INFO) << "got a cron job request." << jobname;

	bool ret = izenelib::util::Scheduler::runJobImmediatly(jobname, calltype, calltype == Request::FromLog);
	if (!ret)
	{
		LOG(ERROR) << "start cron job failed." << jobname;
	}
	return ret;
}

static bool callHandler(izenelib::driver::Router::handler_ptr handler,
    Request::kCallType calltype, const std::string& packed_data,
    Request& request)
{
    try
    {
        Response response;
        response.setSuccess(true);
        static Poller tmp_poller;
        // prepare request
        handler->invoke(request,
            response,
            tmp_poller);

        LOG(INFO) << "write request send in DistributeDriver success.";
        return true;
    }
    catch(const std::exception& e)
    {
        LOG(ERROR) << "call request handler exception: " << e.what();
    }
    return false;
}

bool DistributeDriver::handleRequest(const std::string& reqjsondata, const std::string& packed_data, Request::kCallType calltype)
{
    static izenelib::driver::JsonReader reader;
    Value requestValue;
    if(reader.read(reqjsondata, requestValue))
    {
        if (requestValue.type() != Value::kObjectType)
        {
            LOG(ERROR) << "read request data type error: Malformed request: require an object as input.";
            return false;
        }
        Request request;
        request.assignTmp(requestValue);
        izenelib::driver::Router::handler_ptr handler = router_->find(
            request.controller(),
            request.action()
            );
        if (!handler)
        {
            LOG(ERROR) << "Handler not found for the request : " << request.controller() <<
                "," << request.action();
            return false;
        }
        if (!ReqLogMgr::isWriteRequest(request.controller(), request.action()))
        {
            LOG(ERROR) << "=== Wrong, not a write request in DistributeDriver!!";
            return false;
        }
        try
        {
            DistributeRequestHooker::get()->setHook(calltype, packed_data);
            request.setCallType(calltype);
            if (calltype == Request::FromLog || calltype == Request::FromDistribute)
            {
                if (!asyncWriteTasks_.empty())
                {
                    LOG(ERROR) << "another write task is running in async_task_worker_!!";
                    return false;
                }
                // redo log must process the request one by one, so sync needed.
                return callHandler(handler, calltype, packed_data, request);
            }
            else
            {
                if (!async_task_worker_.interruption_requested())
                {
                    asyncWriteTasks_.push(boost::bind(&callHandler,
                            handler, calltype, packed_data, request));
                }
            }
            return true;
        }
        catch (const std::exception& e)
        {
            LOG(ERROR) << "process request exception: " << e.what();
        }
    }
    else
    {
        // malformed request
        LOG(WARNING) << "read request data error: " << reader.errorMessages();
    }
    return false;
}

bool DistributeDriver::handleReqFromLog(int reqtype, const std::string& reqjsondata, const std::string& packed_data)
{
    if ((ReqLogType)reqtype == Req_CronJob)
    {
	    LOG(INFO) << "got a cron job request in log." << reqjsondata;
	    DistributeRequestHooker::get()->setHook(Request::FromLog, packed_data);
	    return callCronJob(Request::FromLog, reqjsondata, packed_data);
    }
    return handleRequest(reqjsondata, packed_data, Request::FromLog);
}

bool DistributeDriver::handleReqFromPrimary(int reqtype, const std::string& reqjsondata, const std::string& packed_data)
{
    if ((ReqLogType)reqtype == Req_CronJob)
    {
	DistributeRequestHooker::get()->setHook(Request::FromPrimaryWorker, packed_data);
	if (!async_task_worker_.interruption_requested())
	{
		asyncWriteTasks_.push(boost::bind(&callCronJob,
					Request::FromPrimaryWorker, reqjsondata, packed_data));
	}
        return true;
    }

    return handleRequest(reqjsondata, packed_data, Request::FromPrimaryWorker);
}

bool DistributeDriver::on_new_req_available()
{
    if (!MasterManagerBase::get()->prepareWriteReq())
    {
        LOG(WARNING) << "prepare new request failed. maybe some other primary master prepared first. ";
        return false;
    }
    while(true)
    {
        std::string reqdata;
	std::string reqtype;
        if(!MasterManagerBase::get()->popWriteReq(reqdata, reqtype))
        {
            LOG(INFO) << "no more request.";
            break;
        }

        if(reqtype.empty() && !handleRequest(reqdata, reqdata, Request::FromDistribute) )
        {
            LOG(WARNING) << "one write request failed to deliver :" << reqdata;
            // a write request failed to deliver means the worker did not
            // started to handle this request, so the request is ignored, and
            // continue to deliver next write request in the queue.
        }
	else if (reqtype == "cron")
	{
	    LOG(INFO) << "got a cron job request from queue." << reqdata;
            DistributeRequestHooker::get()->setHook(Request::FromDistribute, reqdata);
            DistributeRequestHooker::get()->hookCurrentReq(reqdata);
            DistributeRequestHooker::get()->processLocalBegin();
	    bool ret = izenelib::util::Scheduler::runJobImmediatly(reqdata, Request::FromDistribute);
            if (!ret)
            {
		LOG(ERROR) << "cron job start failed";
	    }
	    else
	    {
		    break;
	    }
	}
        else
        {
            // a write request deliver success from master to worker.
            // this mean the worker has started to process this request,
            // may be not finish writing, so we break here to wait the finished
            // event from primary worker.
            break;
        }
    }
    return true;
}

}
<|endoftext|>


<commit_before>#include "message.h"
#include <yuni/core/system/console/console.h>

using namespace Yuni;

#ifndef YUNI_OS_WINDOWS
#define SEP " \u205E "
#else
#define SEP " : "
#endif

namespace Nany
{
namespace Logs
{

	Message& Message::createEntry(Level level)
	{
		Message* entry = new Message{level};

		ThreadingPolicy::MutexLocker locker{*this};
		entry->origins = origins;
		entries.push_back(entry);
		return *entry;
	}

	void Message::appendEntry(const Message::Ptr& entry)
	{
		if (!!entry)
		{
			if (not (entry->entries.empty() and entry->level == Level::none))
			{
				ThreadingPolicy::MutexLocker locker{*this};
				entries.push_back(entry);
				hasErrors &= entry->hasErrors;
			}
		}
	}

	namespace // anonymous
	{

		template<bool unify>
		static void printMessage(nyconsole_t& out, const Message& message, uint32_t indent, String& xx)
		{
			Message::ThreadingPolicy::MutexLocker locker{message};

			// which output ?
			nyconsole_output_t omode = (unify or (message.level != Level::error and message.level != Level::warning))
				? nycout : nycerr;
			// function writer
			auto wrfn = (omode == nycout) ? out.write_stdout : out.write_stderr;
			// print method
			auto print = [&](const AnyString& s) { wrfn(out.internal, s.c_str(), s.size()); };

			if (message.level != Level::none)
			{
				switch (message.level)
				{
					case Level::error:
					{
						out.set_color(out.internal, omode, nyc_red);
						print("   error" SEP);
						out.set_color(out.internal, omode, nyc_none);
						break;
					}
					case Level::warning:
					{
						out.set_color(out.internal, omode, nyc_yellow);
						print(" warning" SEP);
						out.set_color(out.internal, omode, nyc_none);
						break;
					}
					case Level::info:
					{
						if (message.section.empty())
						{
							out.set_color(out.internal, omode, nyc_none);
							print("        " SEP);
						}
						else
						{
							out.set_color(out.internal, omode, nyc_lightblue);
							xx = "        ";
							xx.overwriteRight(message.section);
							xx << SEP;
							print(xx);
							out.set_color(out.internal, omode, nyc_none);
						}
						break;
					}
					case Level::hint:
					case Level::suggest:
					{
						out.set_color(out.internal, omode, nyc_none);
						print("        " SEP);
						out.set_color(out.internal, omode, nyc_none);
						break;
					}
					case Level::success:
					{
						out.set_color(out.internal, omode, nyc_green);
						#ifndef YUNI_OS_WINDOWS
						print("      \u2713 " SEP);
						#else
						print("      ok" SEP);
						#endif
						out.set_color(out.internal, omode, nyc_none);
						break;
					}
					case Level::trace:
					{
						out.set_color(out.internal, omode, nyc_purple);
						print("      ::");
						out.set_color(out.internal, omode, nyc_none);
						print(SEP);
						break;
					}
					case Level::verbose:
					{
						out.set_color(out.internal, omode, nyc_green);
						print("      ::");
						out.set_color(out.internal, omode, nyc_none);
						print(SEP);
						break;
					}
					case Level::ICE:
					{
						out.set_color(out.internal, omode, nyc_red);
						print("     ICE" SEP);
						out.set_color(out.internal, omode, nyc_none);
						break;
					}
					case Level::none:
					{
						// unreachable - cf condition above
						break;
					}
				}

				if (indent)
				{
					xx.clear();
					for (uint32_t i = indent; i--; )
						xx << "    ";
					print(xx);
				}

				if (not message.prefix.empty())
				{
					out.set_color(out.internal, omode, nyc_white);
					print(message.prefix);
					out.set_color(out.internal, omode, nyc_none);
				}

				if (message.level == Level::suggest)
				{
					out.set_color(out.internal, omode, nyc_lightblue);
					print("suggest: ");
					out.set_color(out.internal, omode, nyc_none);
				}
				else if (message.level == Level::hint)
				{
					out.set_color(out.internal, omode, nyc_lightblue);
					print("hint: ");
					out.set_color(out.internal, omode, nyc_none);
				}

				if (not message.origins.location.target.empty())
				{
					print("{");
					print(message.origins.location.target);
					print("} ");
				}

				if (not message.origins.location.filename.empty())
				{
					out.set_color(out.internal, omode, nyc_white);
					xx = message.origins.location.filename;

					if (message.origins.location.pos.line > 0)
					{
						xx << ':';
						xx << message.origins.location.pos.line;
						if (message.origins.location.pos.offset != 0)
						{
							xx << ':';
							xx << message.origins.location.pos.offset;
							if (message.origins.location.pos.offsetEnd != 0)
							{
								xx << '-';
								xx << message.origins.location.pos.offsetEnd;
							}
						}
						print(xx);
					}
					xx << ": ";
					print(xx);
					out.set_color(out.internal, omode, nyc_none);
				}

				if (not message.message.empty())
				{
					String msg{message.message};
					msg.trimRight(" \t\r\n");
					msg.replace("\t", "    "); // tabs

					auto firstLF = msg.find('\n');
					if (not (firstLF < message.message.size()))
					{
						print(msg);
					}
					else
					{
						xx.clear() << "\n        " SEP;
						for (uint i = indent; i--; )
							xx.write("    ", 4);

						bool addLF = false;
						msg.words("\n", [&](const AnyString& word) -> bool
						{
							if (addLF)
								print(xx);
							print(word);
							addLF = true;
							return true;
						});
					}
				}

				print("\n");
				++indent;
			}

			for (auto& ptr: message.entries)
				printMessage<unify>(out, *ptr, indent, xx);
		}

	} // anonymous namespace

	void Message::print(nyconsole_t& out, bool unify)
	{
		assert(out.set_color);
		if (out.set_color and out.write_stderr and out.write_stdout)
		{
			String tmp;
			tmp.reserve(1024);
			if (unify)
				printMessage<true>(out, *this, 0, tmp);
			else
				printMessage<false>(out, *this, 0, tmp);
		}
	}

} // namespace Logs
} // namespace Nany
<commit_msg>err reporting: the location was printed twice<commit_after>#include "message.h"
#include <yuni/core/system/console/console.h>

using namespace Yuni;

#ifndef YUNI_OS_WINDOWS
#define SEP " \u205E "
#else
#define SEP " : "
#endif

namespace Nany
{
namespace Logs
{

	Message& Message::createEntry(Level level)
	{
		Message* entry = new Message{level};

		ThreadingPolicy::MutexLocker locker{*this};
		entry->origins = origins;
		entries.push_back(entry);
		return *entry;
	}

	void Message::appendEntry(const Message::Ptr& entry)
	{
		if (!!entry)
		{
			if (not (entry->entries.empty() and entry->level == Level::none))
			{
				ThreadingPolicy::MutexLocker locker{*this};
				entries.push_back(entry);
				hasErrors &= entry->hasErrors;
			}
		}
	}

	namespace // anonymous
	{

		template<bool unify>
		static void printMessage(nyconsole_t& out, const Message& message, uint32_t indent, String& xx)
		{
			Message::ThreadingPolicy::MutexLocker locker{message};

			// which output ?
			nyconsole_output_t omode = (unify or (message.level != Level::error and message.level != Level::warning))
				? nycout : nycerr;
			// function writer
			auto wrfn = (omode == nycout) ? out.write_stdout : out.write_stderr;
			// print method
			auto print = [&](const AnyString& s) { wrfn(out.internal, s.c_str(), s.size()); };

			if (message.level != Level::none)
			{
				switch (message.level)
				{
					case Level::error:
					{
						out.set_color(out.internal, omode, nyc_red);
						print("   error" SEP);
						out.set_color(out.internal, omode, nyc_none);
						break;
					}
					case Level::warning:
					{
						out.set_color(out.internal, omode, nyc_yellow);
						print(" warning" SEP);
						out.set_color(out.internal, omode, nyc_none);
						break;
					}
					case Level::info:
					{
						if (message.section.empty())
						{
							out.set_color(out.internal, omode, nyc_none);
							print("        " SEP);
						}
						else
						{
							out.set_color(out.internal, omode, nyc_lightblue);
							xx = "        ";
							xx.overwriteRight(message.section);
							xx << SEP;
							print(xx);
							out.set_color(out.internal, omode, nyc_none);
						}
						break;
					}
					case Level::hint:
					case Level::suggest:
					{
						out.set_color(out.internal, omode, nyc_none);
						print("        " SEP);
						out.set_color(out.internal, omode, nyc_none);
						break;
					}
					case Level::success:
					{
						out.set_color(out.internal, omode, nyc_green);
						#ifndef YUNI_OS_WINDOWS
						print("      \u2713 " SEP);
						#else
						print("      ok" SEP);
						#endif
						out.set_color(out.internal, omode, nyc_none);
						break;
					}
					case Level::trace:
					{
						out.set_color(out.internal, omode, nyc_purple);
						print("      ::");
						out.set_color(out.internal, omode, nyc_none);
						print(SEP);
						break;
					}
					case Level::verbose:
					{
						out.set_color(out.internal, omode, nyc_green);
						print("      ::");
						out.set_color(out.internal, omode, nyc_none);
						print(SEP);
						break;
					}
					case Level::ICE:
					{
						out.set_color(out.internal, omode, nyc_red);
						print("     ICE" SEP);
						out.set_color(out.internal, omode, nyc_none);
						break;
					}
					case Level::none:
					{
						// unreachable - cf condition above
						break;
					}
				}

				if (indent)
				{
					xx.clear();
					for (uint32_t i = indent; i--; )
						xx << "    ";
					print(xx);
				}

				if (not message.prefix.empty())
				{
					out.set_color(out.internal, omode, nyc_white);
					print(message.prefix);
					out.set_color(out.internal, omode, nyc_none);
				}

				if (message.level == Level::suggest)
				{
					out.set_color(out.internal, omode, nyc_lightblue);
					print("suggest: ");
					out.set_color(out.internal, omode, nyc_none);
				}
				else if (message.level == Level::hint)
				{
					out.set_color(out.internal, omode, nyc_lightblue);
					print("hint: ");
					out.set_color(out.internal, omode, nyc_none);
				}

				if (not message.origins.location.target.empty())
				{
					print("{");
					print(message.origins.location.target);
					print("} ");
				}

				if (not message.origins.location.filename.empty())
				{
					out.set_color(out.internal, omode, nyc_white);
					xx = message.origins.location.filename;

					if (message.origins.location.pos.line > 0)
					{
						xx << ':';
						xx << message.origins.location.pos.line;
						if (message.origins.location.pos.offset != 0)
						{
							xx << ':';
							xx << message.origins.location.pos.offset;
							if (message.origins.location.pos.offsetEnd != 0)
							{
								xx << '-';
								xx << message.origins.location.pos.offsetEnd;
							}
						}
					}
					xx << ": ";
					print(xx);
					out.set_color(out.internal, omode, nyc_none);
				}

				if (not message.message.empty())
				{
					String msg{message.message};
					msg.trimRight(" \t\r\n");
					msg.replace("\t", "    "); // tabs

					auto firstLF = msg.find('\n');
					if (not (firstLF < message.message.size()))
					{
						print(msg);
					}
					else
					{
						xx.clear() << "\n        " SEP;
						for (uint i = indent; i--; )
							xx.write("    ", 4);

						bool addLF = false;
						msg.words("\n", [&](const AnyString& word) -> bool
						{
							if (addLF)
								print(xx);
							print(word);
							addLF = true;
							return true;
						});
					}
				}

				print("\n");
				++indent;
			}

			for (auto& ptr: message.entries)
				printMessage<unify>(out, *ptr, indent, xx);
		}

	} // anonymous namespace

	void Message::print(nyconsole_t& out, bool unify)
	{
		assert(out.set_color);
		if (out.set_color and out.write_stderr and out.write_stdout)
		{
			String tmp;
			tmp.reserve(1024);
			if (unify)
				printMessage<true>(out, *this, 0, tmp);
			else
				printMessage<false>(out, *this, 0, tmp);
		}
	}

} // namespace Logs
} // namespace Nany
<|endoftext|>


<commit_before>#include "al.h"
#include "alc.h"
#include <stdio.h>
#include <string>
#include <math.h>
#include <ctime>

#ifndef M_PI
#define M_PI atan(1.0)*4
#endif

#define CHANNELS 2
#define SAMPLE_RATE 48000
#define BITS 16
#define T_FRAMES 2400
#define NUM_BUFFERS 2

using namespace std;

//This will automatically assume 16-bit audio. It will not work otherwise, will fix this later.
void fillSine(ALvoid *bufferData, short channels, unsigned long frames, int sampleRate, float frequency, short amplitude)
{
	double time = 0;
	short *data = (short*)bufferData, val;

	for(unsigned long i = 0; i < frames; i++)
	{
		time += 1 /(float)sampleRate;
		val = (short)(amplitude * sin(2 * M_PI * frequency * time));

		for(short j = 0; j < channels; j++)
		{
			*data++ = val;
		}
	}
}

void fillSaw(ALvoid *bufferData, short channels, unsigned long frames, int sampleRate, float frequency, short amplitude)
{

}

int main()
{
	ALCenum error;
	ALCdevice *device;
	device = alcOpenDevice(NULL);
	if(!device)
		printf("Device not found!\n");

	ALboolean enumeration;
	enumeration = alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT");
	if (enumeration == AL_FALSE)
		printf("no enumeration supported!\n");

	ALCcontext *context;
	context = alcCreateContext(device, NULL);
	if (!alcMakeContextCurrent(context))
		printf("Context creation failed!");

	ALfloat listenerOri[] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f };
	alListener3f(AL_POSITION, 0, 0, 1.0f);
	alListener3f(AL_VELOCITY, 0, 0, 0);
	alListenerfv(AL_ORIENTATION, listenerOri);

	ALuint source;
	alGenSources(1, &source);
	alSourcef(source, AL_PITCH, 1);
	alSourcef(source, AL_GAIN, 1);
	alSource3f(source, AL_POSITION, 0, 0, 0);
	alSource3f(source, AL_VELOCITY, 0, 0, 0);
	alSourcei(source, AL_LOOPING, AL_FALSE);

	ALuint *buffer = (ALuint*)malloc(sizeof(ALuint)*NUM_BUFFERS);
	alGenBuffers(NUM_BUFFERS, buffer);

	ALsizei dataSize = T_FRAMES * CHANNELS * (BITS/8);
	ALvoid *bufferData = (ALvoid *)malloc(dataSize);
	float mPitch = 20;
	fillSine(bufferData, CHANNELS, T_FRAMES, SAMPLE_RATE, mPitch, 25000);

	for(int i = 0; i < NUM_BUFFERS; i++)
		alBufferData(buffer[i], AL_FORMAT_STEREO16, bufferData, dataSize, SAMPLE_RATE);
	alSourceQueueBuffers(source, NUM_BUFFERS, buffer);
	alSourcePlay(source);

	clock_t start, current;
	start = clock();
	current = clock();

	short buffID = 0, totalBuffers = 0;
	while(true)
	{
		ALint processed;
		alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
		while(processed--)
		{
			alSourceUnqueueBuffers(source, 1, &buffer[buffID]);
			fillSine(bufferData, CHANNELS, T_FRAMES, SAMPLE_RATE, mPitch, 25000);
			printf("Refilled the sine data into buffer: %d, Pitch is %f\n", buffID, mPitch);
			alBufferData(buffer[buffID], AL_FORMAT_STEREO16, bufferData, dataSize, SAMPLE_RATE);
			alSourceQueueBuffers(source, 1, &buffer[buffID]);

			buffID++; totalBuffers++;
			mPitch += totalBuffers;
			if(buffID == NUM_BUFFERS)
				buffID = 0;
		}
	}

	// cleanup context
	alDeleteSources(1, &source);
	alDeleteBuffers(NUM_BUFFERS, buffer);
	device = alcGetContextsDevice(context);
	alcMakeContextCurrent(NULL);
	alcDestroyContext(context);
	alcCloseDevice(device);

	return 0;
}<commit_msg>saw added<commit_after>#include "al.h"
#include "alc.h"
#include <stdio.h>
#include <string>
#include <math.h>
#include <ctime>

#ifndef M_PI
#define M_PI atan(1.0)*4
#endif

#define CHANNELS 2
#define SAMPLE_RATE 48000
#define BITS 16
#define T_FRAMES 2400
#define NUM_BUFFERS 2

using namespace std;

//This will automatically assume 16-bit audio. It will not work otherwise, will fix this later.
void fillSine(ALvoid *bufferData, short channels, unsigned long frames, int sampleRate, float frequency, short amplitude)
{
	double time = 0;
	short *data = (short*)bufferData, val;

	for(unsigned long i = 0; i < frames; i++)
	{
		time += 1 /(float)sampleRate;
		val = (short)(amplitude * sin(2 * M_PI * frequency * time));

		for(short j = 0; j < channels; j++)
		{
			*data++ = val;
		}
	}
}

void fillSaw(ALvoid *bufferData, short channels, unsigned long frames, int sampleRate, float frequency, short amplitude)
{
	double time = 0;
	short *data = (short*)bufferData;
		double val;

	for (unsigned long i = 0; i < frames; i++)
	{
		time += 1 / (double)sampleRate;
		if (time >= 1 / frequency)
			time -= 1 / frequency;

		val = (2 * amplitude* frequency * time - amplitude);
		//printf("the value is %lf. \n", val);

		for (short j = 0; j < channels; j++)
		{
			*data++ = (short)val;
		}
	}
}

int main()
{
	ALCenum error;
	ALCdevice *device;
	device = alcOpenDevice(NULL);
	if(!device)
		printf("Device not found!\n");

	ALboolean enumeration;
	enumeration = alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT");
	if (enumeration == AL_FALSE)
		printf("no enumeration supported!\n");

	ALCcontext *context;
	context = alcCreateContext(device, NULL);
	if (!alcMakeContextCurrent(context))
		printf("Context creation failed!");

	ALfloat listenerOri[] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f };
	alListener3f(AL_POSITION, 0, 0, 1.0f);
	alListener3f(AL_VELOCITY, 0, 0, 0);
	alListenerfv(AL_ORIENTATION, listenerOri);

	ALuint source;
	alGenSources(1, &source);
	alSourcef(source, AL_PITCH, 1);
	alSourcef(source, AL_GAIN, 1);
	alSource3f(source, AL_POSITION, 0, 0, 0);
	alSource3f(source, AL_VELOCITY, 0, 0, 0);
	alSourcei(source, AL_LOOPING, AL_FALSE);

	ALuint *buffer = (ALuint*)malloc(sizeof(ALuint)*NUM_BUFFERS);
	alGenBuffers(NUM_BUFFERS, buffer);

	ALsizei dataSize = T_FRAMES * CHANNELS * (BITS/8);
	ALvoid *bufferData = (ALvoid *)malloc(dataSize);
	float mPitch = 440;
	fillSaw(bufferData, CHANNELS, T_FRAMES, SAMPLE_RATE, mPitch, 25000);

	for(int i = 0; i < NUM_BUFFERS; i++)
		alBufferData(buffer[i], AL_FORMAT_STEREO16, bufferData, dataSize, SAMPLE_RATE);
	alSourceQueueBuffers(source, NUM_BUFFERS, buffer);
	alSourcePlay(source);

	clock_t start, current;
	start = clock();
	current = clock();

	short buffID = 0, totalBuffers = 0;
	while(true)
	{
		ALint processed;
		alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
		while(processed--)
		{
			alSourceUnqueueBuffers(source, 1, &buffer[buffID]);
			fillSaw(bufferData, CHANNELS, T_FRAMES, SAMPLE_RATE, mPitch, 25000);
			printf("Refilled the sine data into buffer: %d, Pitch is %f\n", buffID, mPitch);
			alBufferData(buffer[buffID], AL_FORMAT_STEREO16, bufferData, dataSize, SAMPLE_RATE);
			alSourceQueueBuffers(source, 1, &buffer[buffID]);

			buffID++; totalBuffers++;
			//mPitch += totalBuffers;
			if(buffID == NUM_BUFFERS)
				buffID = 0;
		}
	}

	// cleanup context
	alDeleteSources(1, &source);
	alDeleteBuffers(NUM_BUFFERS, buffer);
	device = alcGetContextsDevice(context);
	alcMakeContextCurrent(NULL);
	alcDestroyContext(context);
	alcCloseDevice(device);

	return 0;
}<|endoftext|>


<commit_before>/* Luwra
 * Minimal-overhead Lua wrapper for C++
 *
 * Copyright (C) 2015, Ole Krüger <ole@vprsm.de>
 */

#ifndef LUWRA_TYPES_H_
#define LUWRA_TYPES_H_

#include "common.hpp"

#include <utility>
#include <tuple>
#include <string>
#include <type_traits>
#include <limits>

LUWRA_NS_BEGIN

/* Lua types */
using Integer = lua_Integer;
using Number = lua_Number;
using State = lua_State;
using CFunction = lua_CFunction;

/**
 * A value on the stack
 */
template <typename T>
struct Value {
	static_assert(sizeof(T) == -1, "You must not use an unspecialized version of Value");

	/**
	 * Retrieve the value at position `n`.
	 */
	static
	T read(State*, int);

	/**
	 * push the value onto the stack.
	 */
	static
	int push(State*, T);
};

/**
 * Convenient wrapped for `Value<T>::push`.
 */
template <typename T> static inline
int push(State* state, T value) {
	return Value<T>::push(state, value);
}

/**
 * Define a template specialization of `Value` for `type` with a `retrf(State*, int)` which
 * extracts it from the stack and a `pushf(State*, type)` which pushes the value on the stack again.
 * This assumes that only one value will be pushed onto the stack.
 */
#define LUWRA_DEF_VALUE(type, retrf, pushf)                          \
	template <>                                                      \
	struct Value<type> {                                             \
		static inline                                                \
		type read(State* state, int n) {                             \
			return retrf(state, n);                                  \
		}                                                            \
                                                                     \
		static inline                                                \
		int push(State* state, type value) {                         \
			pushf(state, value);                                     \
			return 1;                                                \
		}                                                            \
	}

#ifndef luaL_checkboolean
	/**
	 * Check if the value at index `n` is a boolean and retrieve its value.
	 */
	#define luaL_checkboolean(state, n) \
		(luaL_checktype(state, n, LUA_TBOOLEAN), lua_toboolean(state, n))
#endif

#ifndef luaL_checkcfunction
	/**
	 * Check if the value at index `n` is a C function and retrieve it.
	 */
	#define luaL_checkcfunction(state, n) \
		(luaL_checktype(state, n, LUA_TCFUNCTION), lua_toboolean(state, n))
#endif

#ifndef luaL_pushstdstring
	/**
	 * push a `std::string` as string onto the stack.
	 */
	#define luaL_pushstdstring(state, stdstring) \
		(lua_pushstring(state, stdstring.c_str()))
#endif

namespace internal {
	template <typename>
	struct NumericTransportValue;

	// Transport unit `Integer`
	template <>
	struct NumericTransportValue<Integer> {
		static inline
		Integer read(State* state, int index) {
			return luaL_checkinteger(state, index);
		}

		static inline
		int push(State* state, Integer value) {
			lua_pushinteger(state, value);
			return 1;
		}
	};

	// Transport unit `Number`
	template <>
	struct NumericTransportValue<Number> {
		static inline
		Number read(State* state, int index) {
			return luaL_checknumber(state, index);
		}

		static inline
		int push(State* state, Number value) {
			lua_pushnumber(state, value);
			return 1;
		}
	};

	// Base for `Value<I>` specializations which uses `B` as transport unit, where `I` is smaller
	// than `B`.
	template <typename I, typename B>
	struct NumericContainedValueBase {
		static constexpr
		bool qualifies =
			// TODO: Remove warning about comparsion between signed and unsigned integers
			std::numeric_limits<I>::max() <= std::numeric_limits<B>::max()
			&& std::numeric_limits<I>::min() >= std::numeric_limits<B>::min();

		static inline
		I read(State* state, int index) {
			return
				std::max<B>(
					std::numeric_limits<I>::min(),
					std::min<B>(
						std::numeric_limits<I>::max(),
						NumericTransportValue<B>::read(state, index)
					)
				);
		}

		static inline
		int push(State* state, I value) {
			NumericTransportValue<B>::push(state, static_cast<B>(value));
			return 1;
		}
	};

	// Base for `Value<I>` specializations which uses `B` as transport unit, where `I` is bigger
	// than `B`.
	template <typename I, typename B>
	struct NumericTruncatingValueBase {
		static inline
		I read(State* state, int index) {
			return static_cast<I>(NumericTransportValue<B>::read(state, index));
		}

		static inline
		int push(State*, I) {
			static_assert(
				sizeof(I) == -1,
				"You must not use 'Value<I>::push' specializations which inherit from NumericTruncatingValueBase"
			);

			return -1;
		}
	};

	// Base for `Value<I>` specializations which uses `B` as transport unit
	template <typename I, typename B>
	using NumericValueBase =
		typename std::conditional<
			std::is_same<I, B>::value,
			NumericTransportValue<B>,
			typename std::conditional<
				NumericContainedValueBase<I, B>::qualifies,
				NumericContainedValueBase<I, B>,
				NumericTruncatingValueBase<I, B>
			>::type
		>::type;
}

/**
 * Define an integral type which will be transported via `base`.
 */
#define LUWRA_DEF_NUMERIC(base, type) \
	template <> \
	struct Value<type>: internal::NumericValueBase<type, base> {};

// Lua-dependent types
LUWRA_DEF_NUMERIC(Number, float)
LUWRA_DEF_NUMERIC(Number, double)
LUWRA_DEF_NUMERIC(Number, long double)

// Integral types
LUWRA_DEF_NUMERIC(Integer, signed   short)
LUWRA_DEF_NUMERIC(Integer, unsigned short)
LUWRA_DEF_NUMERIC(Integer, signed   int)
LUWRA_DEF_NUMERIC(Integer, unsigned int)
LUWRA_DEF_NUMERIC(Integer, signed   long int)
LUWRA_DEF_NUMERIC(Integer, unsigned long int)
LUWRA_DEF_NUMERIC(Integer, signed   long long int)
LUWRA_DEF_NUMERIC(Integer, unsigned long long int)

// C/C++ types
LUWRA_DEF_VALUE(bool,        luaL_checkboolean, lua_pushboolean);
LUWRA_DEF_VALUE(const char*, luaL_checkstring,  lua_pushstring);
LUWRA_DEF_VALUE(std::string, luaL_checkstring,  luaL_pushstdstring);

// Do not export these macros
#undef LUWRA_DEF_VALUE
#undef LUWRA_DEF_NUMERIC

template <>
struct Value<CFunction> {
	static inline
	int push(State* state, CFunction fun) {
		lua_pushcfunction(state, fun);
		return 1;
	}
};

/**
 * An arbitrary value on an execution stack.
 * Note: this value is only available as long as it exists on its originating stack.
 */
struct Arbitrary {
	State* state;
	int index;
};

template <>
struct Value<Arbitrary> {
	static inline
	Arbitrary read(State* state, int index) {
		if (index < 0)
			index = lua_gettop(state) + (index + 1);

		return Arbitrary {state, index};
	}

	static inline
	int push(State* state, const Arbitrary& value) {
		lua_pushvalue(value.state, value.index);

		if (value.state != state)
			lua_xmove(value.state, state, 1);

		return 1;
	}
};

namespace internal {
	template <typename>
	struct Stackpusher;

	template <size_t I>
	struct Stackpusher<std::index_sequence<I>> {
		template <typename T> static inline
		int push(State* state, const T& package) {
			return push(state, std::get<I>(package));
		}
	};

	template <size_t I, size_t... Is>
	struct Stackpusher<std::index_sequence<I, Is...>> {
		template <typename T> static inline
		int push(State* state, const T& package) {
			int r = push(state, std::get<I>(package));
			return std::max(0, r) + Stackpusher<std::index_sequence<Is...>>::push(state, package);
		}
	};
}

/**
 * Allows you to use multiple return values.
 */
template <typename... A>
struct Value<std::tuple<A...>> {
	static inline
	int push(State* state, const std::tuple<A...>& value) {
		return internal::Stackpusher<std::make_index_sequence<sizeof...(A)>>::push(state, value);
	}
};

LUWRA_NS_END

#endif
<commit_msg>Use lowest() instead of min() from std::numeric_limits<commit_after>/* Luwra
 * Minimal-overhead Lua wrapper for C++
 *
 * Copyright (C) 2015, Ole Krüger <ole@vprsm.de>
 */

#ifndef LUWRA_TYPES_H_
#define LUWRA_TYPES_H_

#include "common.hpp"

#include <utility>
#include <tuple>
#include <string>
#include <type_traits>
#include <limits>

LUWRA_NS_BEGIN

/* Lua types */
using Integer = lua_Integer;
using Number = lua_Number;
using State = lua_State;
using CFunction = lua_CFunction;

/**
 * A value on the stack
 */
template <typename T>
struct Value {
	static_assert(sizeof(T) == -1, "You must not use an unspecialized version of Value");

	/**
	 * Retrieve the value at position `n`.
	 */
	static
	T read(State*, int);

	/**
	 * push the value onto the stack.
	 */
	static
	int push(State*, T);
};

/**
 * Convenient wrapped for `Value<T>::push`.
 */
template <typename T> static inline
int push(State* state, T value) {
	return Value<T>::push(state, value);
}

/**
 * Define a template specialization of `Value` for `type` with a `retrf(State*, int)` which
 * extracts it from the stack and a `pushf(State*, type)` which pushes the value on the stack again.
 * This assumes that only one value will be pushed onto the stack.
 */
#define LUWRA_DEF_VALUE(type, retrf, pushf)                          \
	template <>                                                      \
	struct Value<type> {                                             \
		static inline                                                \
		type read(State* state, int n) {                             \
			return retrf(state, n);                                  \
		}                                                            \
                                                                     \
		static inline                                                \
		int push(State* state, type value) {                         \
			pushf(state, value);                                     \
			return 1;                                                \
		}                                                            \
	}

#ifndef luaL_checkboolean
	/**
	 * Check if the value at index `n` is a boolean and retrieve its value.
	 */
	#define luaL_checkboolean(state, n) \
		(luaL_checktype(state, n, LUA_TBOOLEAN), lua_toboolean(state, n))
#endif

#ifndef luaL_checkcfunction
	/**
	 * Check if the value at index `n` is a C function and retrieve it.
	 */
	#define luaL_checkcfunction(state, n) \
		(luaL_checktype(state, n, LUA_TCFUNCTION), lua_toboolean(state, n))
#endif

#ifndef luaL_pushstdstring
	/**
	 * push a `std::string` as string onto the stack.
	 */
	#define luaL_pushstdstring(state, stdstring) \
		(lua_pushstring(state, stdstring.c_str()))
#endif

namespace internal {
	template <typename>
	struct NumericTransportValue;

	// Transport unit `Integer`
	template <>
	struct NumericTransportValue<Integer> {
		static inline
		Integer read(State* state, int index) {
			return luaL_checkinteger(state, index);
		}

		static inline
		int push(State* state, Integer value) {
			lua_pushinteger(state, value);
			return 1;
		}
	};

	// Transport unit `Number`
	template <>
	struct NumericTransportValue<Number> {
		static inline
		Number read(State* state, int index) {
			return luaL_checknumber(state, index);
		}

		static inline
		int push(State* state, Number value) {
			lua_pushnumber(state, value);
			return 1;
		}
	};

	// Base for `Value<I>` specializations which uses `B` as transport unit, where `I` is smaller
	// than `B`.
	template <typename I, typename B>
	struct NumericContainedValueBase {
		static constexpr
		bool qualifies =
			// TODO: Remove warning about comparsion between signed and unsigned integers
			std::numeric_limits<I>::max() <= std::numeric_limits<B>::max()
			&& std::numeric_limits<I>::lowest() >= std::numeric_limits<B>::lowest();

		static inline
		I read(State* state, int index) {
			return
				std::max<B>(
					std::numeric_limits<I>::lowest(),
					std::min<B>(
						std::numeric_limits<I>::max(),
						NumericTransportValue<B>::read(state, index)
					)
				);
		}

		static inline
		int push(State* state, I value) {
			NumericTransportValue<B>::push(state, static_cast<B>(value));
			return 1;
		}
	};

	// Base for `Value<I>` specializations which uses `B` as transport unit, where `I` is bigger
	// than `B`.
	template <typename I, typename B>
	struct NumericTruncatingValueBase {
		static inline
		I read(State* state, int index) {
			return static_cast<I>(NumericTransportValue<B>::read(state, index));
		}

		static inline
		int push(State*, I) {
			static_assert(
				sizeof(I) == -1,
				"You must not use 'Value<I>::push' specializations which inherit from NumericTruncatingValueBase"
			);

			return -1;
		}
	};

	// Base for `Value<I>` specializations which uses `B` as transport unit
	template <typename I, typename B>
	using NumericValueBase =
		typename std::conditional<
			std::is_same<I, B>::value,
			NumericTransportValue<B>,
			typename std::conditional<
				NumericContainedValueBase<I, B>::qualifies,
				NumericContainedValueBase<I, B>,
				NumericTruncatingValueBase<I, B>
			>::type
		>::type;
}

/**
 * Define an integral type which will be transported via `base`.
 */
#define LUWRA_DEF_NUMERIC(base, type) \
	template <> \
	struct Value<type>: internal::NumericValueBase<type, base> {};

// Lua-dependent types
LUWRA_DEF_NUMERIC(Number, float)
LUWRA_DEF_NUMERIC(Number, double)
LUWRA_DEF_NUMERIC(Number, long double)

// Integral types
LUWRA_DEF_NUMERIC(Integer, signed   short)
LUWRA_DEF_NUMERIC(Integer, unsigned short)
LUWRA_DEF_NUMERIC(Integer, signed   int)
LUWRA_DEF_NUMERIC(Integer, unsigned int)
LUWRA_DEF_NUMERIC(Integer, signed   long int)
LUWRA_DEF_NUMERIC(Integer, unsigned long int)
LUWRA_DEF_NUMERIC(Integer, signed   long long int)
LUWRA_DEF_NUMERIC(Integer, unsigned long long int)

// C/C++ types
LUWRA_DEF_VALUE(bool,        luaL_checkboolean, lua_pushboolean);
LUWRA_DEF_VALUE(const char*, luaL_checkstring,  lua_pushstring);
LUWRA_DEF_VALUE(std::string, luaL_checkstring,  luaL_pushstdstring);

// Do not export these macros
#undef LUWRA_DEF_VALUE
#undef LUWRA_DEF_NUMERIC

template <>
struct Value<CFunction> {
	static inline
	int push(State* state, CFunction fun) {
		lua_pushcfunction(state, fun);
		return 1;
	}
};

/**
 * An arbitrary value on an execution stack.
 * Note: this value is only available as long as it exists on its originating stack.
 */
struct Arbitrary {
	State* state;
	int index;
};

template <>
struct Value<Arbitrary> {
	static inline
	Arbitrary read(State* state, int index) {
		if (index < 0)
			index = lua_gettop(state) + (index + 1);

		return Arbitrary {state, index};
	}

	static inline
	int push(State* state, const Arbitrary& value) {
		lua_pushvalue(value.state, value.index);

		if (value.state != state)
			lua_xmove(value.state, state, 1);

		return 1;
	}
};

namespace internal {
	template <typename>
	struct Stackpusher;

	template <size_t I>
	struct Stackpusher<std::index_sequence<I>> {
		template <typename T> static inline
		int push(State* state, const T& package) {
			return push(state, std::get<I>(package));
		}
	};

	template <size_t I, size_t... Is>
	struct Stackpusher<std::index_sequence<I, Is...>> {
		template <typename T> static inline
		int push(State* state, const T& package) {
			int r = push(state, std::get<I>(package));
			return std::max(0, r) + Stackpusher<std::index_sequence<Is...>>::push(state, package);
		}
	};
}

/**
 * Allows you to use multiple return values.
 */
template <typename... A>
struct Value<std::tuple<A...>> {
	static inline
	int push(State* state, const std::tuple<A...>& value) {
		return internal::Stackpusher<std::make_index_sequence<sizeof...(A)>>::push(state, value);
	}
};

LUWRA_NS_END

#endif
<|endoftext|>


<commit_before>//===---- ScheduleDAGList.cpp - Implement a list scheduler for isel DAG ---===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This implements a top-down list scheduler, using standard algorithms.
// The basic approach uses a priority queue of available nodes to schedule.
// One at a time, nodes are taken from the priority queue (thus in priority
// order), checked for legality to schedule, and emitted if legal.
//
// Nodes may not be legal to schedule either due to structural hazards (e.g.
// pipeline or resource constraints) or because an input to the instruction has
// not completed execution.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "pre-RA-sched"
#include "ScheduleDAGSDNodes.h"
#include "llvm/CodeGen/LatencyPriorityQueue.h"
#include "llvm/CodeGen/ScheduleHazardRecognizer.h"
#include "llvm/CodeGen/SchedulerRegistry.h"
#include "llvm/CodeGen/SelectionDAGISel.h"
#include "llvm/Target/TargetRegisterInfo.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/PriorityQueue.h"
#include "llvm/ADT/Statistic.h"
#include <climits>
using namespace llvm;

STATISTIC(NumNoops , "Number of noops inserted");
STATISTIC(NumStalls, "Number of pipeline stalls");

static RegisterScheduler
  tdListDAGScheduler("list-td", "Top-down list scheduler",
                     createTDListDAGScheduler);

namespace {
//===----------------------------------------------------------------------===//
/// ScheduleDAGList - The actual list scheduler implementation.  This supports
/// top-down scheduling.
///
class ScheduleDAGList : public ScheduleDAGSDNodes {
private:
  /// AvailableQueue - The priority queue to use for the available SUnits.
  ///
  SchedulingPriorityQueue *AvailableQueue;

  /// PendingQueue - This contains all of the instructions whose operands have
  /// been issued, but their results are not ready yet (due to the latency of
  /// the operation).  Once the operands become available, the instruction is
  /// added to the AvailableQueue.
  std::vector<SUnit*> PendingQueue;

  /// HazardRec - The hazard recognizer to use.
  ScheduleHazardRecognizer *HazardRec;

public:
  ScheduleDAGList(MachineFunction &mf,
                  SchedulingPriorityQueue *availqueue,
                  ScheduleHazardRecognizer *HR)
    : ScheduleDAGSDNodes(mf),
      AvailableQueue(availqueue), HazardRec(HR) {
    }

  ~ScheduleDAGList() {
    delete HazardRec;
    delete AvailableQueue;
  }

  void Schedule();

private:
  void ReleaseSucc(SUnit *SU, const SDep &D);
  void ReleaseSuccessors(SUnit *SU);
  void ScheduleNodeTopDown(SUnit *SU, unsigned CurCycle);
  void ListScheduleTopDown();
};
}  // end anonymous namespace

/// Schedule - Schedule the DAG using list scheduling.
void ScheduleDAGList::Schedule() {
  DEBUG(errs() << "********** List Scheduling **********\n");

  // Build the scheduling graph.
  BuildSchedGraph(NULL);

  AvailableQueue->initNodes(SUnits);

  ListScheduleTopDown();

  AvailableQueue->releaseState();
}

//===----------------------------------------------------------------------===//
//  Top-Down Scheduling
//===----------------------------------------------------------------------===//

/// ReleaseSucc - Decrement the NumPredsLeft count of a successor. Add it to
/// the PendingQueue if the count reaches zero. Also update its cycle bound.
void ScheduleDAGList::ReleaseSucc(SUnit *SU, const SDep &D) {
  SUnit *SuccSU = D.getSUnit();

#ifndef NDEBUG
  if (SuccSU->NumPredsLeft == 0) {
    errs() << "*** Scheduling failed! ***\n";
    SuccSU->dump(this);
    errs() << " has been released too many times!\n";
    llvm_unreachable(0);
  }
#endif
  --SuccSU->NumPredsLeft;

  SuccSU->setDepthToAtLeast(SU->getDepth() + D.getLatency());

  // If all the node's predecessors are scheduled, this node is ready
  // to be scheduled. Ignore the special ExitSU node.
  if (SuccSU->NumPredsLeft == 0 && SuccSU != &ExitSU)
    PendingQueue.push_back(SuccSU);
}

void ScheduleDAGList::ReleaseSuccessors(SUnit *SU) {
  // Top down: release successors.
  for (SUnit::succ_iterator I = SU->Succs.begin(), E = SU->Succs.end();
       I != E; ++I) {
    assert(!I->isAssignedRegDep() &&
           "The list-td scheduler doesn't yet support physreg dependencies!");

    ReleaseSucc(SU, *I);
  }
}

/// ScheduleNodeTopDown - Add the node to the schedule. Decrement the pending
/// count of its successors. If a successor pending count is zero, add it to
/// the Available queue.
void ScheduleDAGList::ScheduleNodeTopDown(SUnit *SU, unsigned CurCycle) {
  DEBUG(errs() << "*** Scheduling [" << CurCycle << "]: ");
  DEBUG(SU->dump(this));

  Sequence.push_back(SU);
  assert(CurCycle >= SU->getDepth() && "Node scheduled above its depth!");
  SU->setDepthToAtLeast(CurCycle);

  ReleaseSuccessors(SU);
  SU->isScheduled = true;
  AvailableQueue->ScheduledNode(SU);
}

/// ListScheduleTopDown - The main loop of list scheduling for top-down
/// schedulers.
void ScheduleDAGList::ListScheduleTopDown() {
  unsigned CurCycle = 0;

  // Release any successors of the special Entry node.
  ReleaseSuccessors(&EntrySU);

  // All leaves to Available queue.
  for (unsigned i = 0, e = SUnits.size(); i != e; ++i) {
    // It is available if it has no predecessors.
    if (SUnits[i].Preds.empty()) {
      AvailableQueue->push(&SUnits[i]);
      SUnits[i].isAvailable = true;
    }
  }

  // While Available queue is not empty, grab the node with the highest
  // priority. If it is not ready put it back.  Schedule the node.
  std::vector<SUnit*> NotReady;
  Sequence.reserve(SUnits.size());
  while (!AvailableQueue->empty() || !PendingQueue.empty()) {
    // Check to see if any of the pending instructions are ready to issue.  If
    // so, add them to the available queue.
    for (unsigned i = 0, e = PendingQueue.size(); i != e; ++i) {
      if (PendingQueue[i]->getDepth() == CurCycle) {
        AvailableQueue->push(PendingQueue[i]);
        PendingQueue[i]->isAvailable = true;
        PendingQueue[i] = PendingQueue.back();
        PendingQueue.pop_back();
        --i; --e;
      } else {
        assert(PendingQueue[i]->getDepth() > CurCycle && "Negative latency?");
      }
    }

    // If there are no instructions available, don't try to issue anything, and
    // don't advance the hazard recognizer.
    if (AvailableQueue->empty()) {
      ++CurCycle;
      continue;
    }

    SUnit *FoundSUnit = 0;

    bool HasNoopHazards = false;
    while (!AvailableQueue->empty()) {
      SUnit *CurSUnit = AvailableQueue->pop();

      ScheduleHazardRecognizer::HazardType HT =
        HazardRec->getHazardType(CurSUnit);
      if (HT == ScheduleHazardRecognizer::NoHazard) {
        FoundSUnit = CurSUnit;
        break;
      }

      // Remember if this is a noop hazard.
      HasNoopHazards |= HT == ScheduleHazardRecognizer::NoopHazard;

      NotReady.push_back(CurSUnit);
    }

    // Add the nodes that aren't ready back onto the available list.
    if (!NotReady.empty()) {
      AvailableQueue->push_all(NotReady);
      NotReady.clear();
    }

    // If we found a node to schedule, do it now.
    if (FoundSUnit) {
      ScheduleNodeTopDown(FoundSUnit, CurCycle);
      HazardRec->EmitInstruction(FoundSUnit);

      // If this is a pseudo-op node, we don't want to increment the current
      // cycle.
      if (FoundSUnit->Latency)  // Don't increment CurCycle for pseudo-ops!
        ++CurCycle;
    } else if (!HasNoopHazards) {
      // Otherwise, we have a pipeline stall, but no other problem, just advance
      // the current cycle and try again.
      DEBUG(errs() << "*** Advancing cycle, no work to do\n");
      HazardRec->AdvanceCycle();
      ++NumStalls;
      ++CurCycle;
    } else {
      // Otherwise, we have no instructions to issue and we have instructions
      // that will fault if we don't do this right.  This is the case for
      // processors without pipeline interlocks and other cases.
      DEBUG(errs() << "*** Emitting noop\n");
      HazardRec->EmitNoop();
      Sequence.push_back(0);   // NULL here means noop
      ++NumNoops;
      ++CurCycle;
    }
  }

#ifndef NDEBUG
  VerifySchedule(/*isBottomUp=*/false);
#endif
}

//===----------------------------------------------------------------------===//
//                         Public Constructor Functions
//===----------------------------------------------------------------------===//

/// createTDListDAGScheduler - This creates a top-down list scheduler with a
/// new hazard recognizer. This scheduler takes ownership of the hazard
/// recognizer and deletes it when done.
ScheduleDAGSDNodes *
llvm::createTDListDAGScheduler(SelectionDAGISel *IS, CodeGenOpt::Level) {
  return new ScheduleDAGList(*IS->MF,
                             new LatencyPriorityQueue(),
                             IS->CreateTargetHazardRecognizer());
}
<commit_msg>Change errs() to dbgs().<commit_after>//===---- ScheduleDAGList.cpp - Implement a list scheduler for isel DAG ---===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This implements a top-down list scheduler, using standard algorithms.
// The basic approach uses a priority queue of available nodes to schedule.
// One at a time, nodes are taken from the priority queue (thus in priority
// order), checked for legality to schedule, and emitted if legal.
//
// Nodes may not be legal to schedule either due to structural hazards (e.g.
// pipeline or resource constraints) or because an input to the instruction has
// not completed execution.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "pre-RA-sched"
#include "ScheduleDAGSDNodes.h"
#include "llvm/CodeGen/LatencyPriorityQueue.h"
#include "llvm/CodeGen/ScheduleHazardRecognizer.h"
#include "llvm/CodeGen/SchedulerRegistry.h"
#include "llvm/CodeGen/SelectionDAGISel.h"
#include "llvm/Target/TargetRegisterInfo.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/PriorityQueue.h"
#include "llvm/ADT/Statistic.h"
#include <climits>
using namespace llvm;

STATISTIC(NumNoops , "Number of noops inserted");
STATISTIC(NumStalls, "Number of pipeline stalls");

static RegisterScheduler
  tdListDAGScheduler("list-td", "Top-down list scheduler",
                     createTDListDAGScheduler);

namespace {
//===----------------------------------------------------------------------===//
/// ScheduleDAGList - The actual list scheduler implementation.  This supports
/// top-down scheduling.
///
class ScheduleDAGList : public ScheduleDAGSDNodes {
private:
  /// AvailableQueue - The priority queue to use for the available SUnits.
  ///
  SchedulingPriorityQueue *AvailableQueue;

  /// PendingQueue - This contains all of the instructions whose operands have
  /// been issued, but their results are not ready yet (due to the latency of
  /// the operation).  Once the operands become available, the instruction is
  /// added to the AvailableQueue.
  std::vector<SUnit*> PendingQueue;

  /// HazardRec - The hazard recognizer to use.
  ScheduleHazardRecognizer *HazardRec;

public:
  ScheduleDAGList(MachineFunction &mf,
                  SchedulingPriorityQueue *availqueue,
                  ScheduleHazardRecognizer *HR)
    : ScheduleDAGSDNodes(mf),
      AvailableQueue(availqueue), HazardRec(HR) {
    }

  ~ScheduleDAGList() {
    delete HazardRec;
    delete AvailableQueue;
  }

  void Schedule();

private:
  void ReleaseSucc(SUnit *SU, const SDep &D);
  void ReleaseSuccessors(SUnit *SU);
  void ScheduleNodeTopDown(SUnit *SU, unsigned CurCycle);
  void ListScheduleTopDown();
};
}  // end anonymous namespace

/// Schedule - Schedule the DAG using list scheduling.
void ScheduleDAGList::Schedule() {
  DEBUG(dbgs() << "********** List Scheduling **********\n");

  // Build the scheduling graph.
  BuildSchedGraph(NULL);

  AvailableQueue->initNodes(SUnits);

  ListScheduleTopDown();

  AvailableQueue->releaseState();
}

//===----------------------------------------------------------------------===//
//  Top-Down Scheduling
//===----------------------------------------------------------------------===//

/// ReleaseSucc - Decrement the NumPredsLeft count of a successor. Add it to
/// the PendingQueue if the count reaches zero. Also update its cycle bound.
void ScheduleDAGList::ReleaseSucc(SUnit *SU, const SDep &D) {
  SUnit *SuccSU = D.getSUnit();

#ifndef NDEBUG
  if (SuccSU->NumPredsLeft == 0) {
    dbgs() << "*** Scheduling failed! ***\n";
    SuccSU->dump(this);
    dbgs() << " has been released too many times!\n";
    llvm_unreachable(0);
  }
#endif
  --SuccSU->NumPredsLeft;

  SuccSU->setDepthToAtLeast(SU->getDepth() + D.getLatency());

  // If all the node's predecessors are scheduled, this node is ready
  // to be scheduled. Ignore the special ExitSU node.
  if (SuccSU->NumPredsLeft == 0 && SuccSU != &ExitSU)
    PendingQueue.push_back(SuccSU);
}

void ScheduleDAGList::ReleaseSuccessors(SUnit *SU) {
  // Top down: release successors.
  for (SUnit::succ_iterator I = SU->Succs.begin(), E = SU->Succs.end();
       I != E; ++I) {
    assert(!I->isAssignedRegDep() &&
           "The list-td scheduler doesn't yet support physreg dependencies!");

    ReleaseSucc(SU, *I);
  }
}

/// ScheduleNodeTopDown - Add the node to the schedule. Decrement the pending
/// count of its successors. If a successor pending count is zero, add it to
/// the Available queue.
void ScheduleDAGList::ScheduleNodeTopDown(SUnit *SU, unsigned CurCycle) {
  DEBUG(dbgs() << "*** Scheduling [" << CurCycle << "]: ");
  DEBUG(SU->dump(this));

  Sequence.push_back(SU);
  assert(CurCycle >= SU->getDepth() && "Node scheduled above its depth!");
  SU->setDepthToAtLeast(CurCycle);

  ReleaseSuccessors(SU);
  SU->isScheduled = true;
  AvailableQueue->ScheduledNode(SU);
}

/// ListScheduleTopDown - The main loop of list scheduling for top-down
/// schedulers.
void ScheduleDAGList::ListScheduleTopDown() {
  unsigned CurCycle = 0;

  // Release any successors of the special Entry node.
  ReleaseSuccessors(&EntrySU);

  // All leaves to Available queue.
  for (unsigned i = 0, e = SUnits.size(); i != e; ++i) {
    // It is available if it has no predecessors.
    if (SUnits[i].Preds.empty()) {
      AvailableQueue->push(&SUnits[i]);
      SUnits[i].isAvailable = true;
    }
  }

  // While Available queue is not empty, grab the node with the highest
  // priority. If it is not ready put it back.  Schedule the node.
  std::vector<SUnit*> NotReady;
  Sequence.reserve(SUnits.size());
  while (!AvailableQueue->empty() || !PendingQueue.empty()) {
    // Check to see if any of the pending instructions are ready to issue.  If
    // so, add them to the available queue.
    for (unsigned i = 0, e = PendingQueue.size(); i != e; ++i) {
      if (PendingQueue[i]->getDepth() == CurCycle) {
        AvailableQueue->push(PendingQueue[i]);
        PendingQueue[i]->isAvailable = true;
        PendingQueue[i] = PendingQueue.back();
        PendingQueue.pop_back();
        --i; --e;
      } else {
        assert(PendingQueue[i]->getDepth() > CurCycle && "Negative latency?");
      }
    }

    // If there are no instructions available, don't try to issue anything, and
    // don't advance the hazard recognizer.
    if (AvailableQueue->empty()) {
      ++CurCycle;
      continue;
    }

    SUnit *FoundSUnit = 0;

    bool HasNoopHazards = false;
    while (!AvailableQueue->empty()) {
      SUnit *CurSUnit = AvailableQueue->pop();

      ScheduleHazardRecognizer::HazardType HT =
        HazardRec->getHazardType(CurSUnit);
      if (HT == ScheduleHazardRecognizer::NoHazard) {
        FoundSUnit = CurSUnit;
        break;
      }

      // Remember if this is a noop hazard.
      HasNoopHazards |= HT == ScheduleHazardRecognizer::NoopHazard;

      NotReady.push_back(CurSUnit);
    }

    // Add the nodes that aren't ready back onto the available list.
    if (!NotReady.empty()) {
      AvailableQueue->push_all(NotReady);
      NotReady.clear();
    }

    // If we found a node to schedule, do it now.
    if (FoundSUnit) {
      ScheduleNodeTopDown(FoundSUnit, CurCycle);
      HazardRec->EmitInstruction(FoundSUnit);

      // If this is a pseudo-op node, we don't want to increment the current
      // cycle.
      if (FoundSUnit->Latency)  // Don't increment CurCycle for pseudo-ops!
        ++CurCycle;
    } else if (!HasNoopHazards) {
      // Otherwise, we have a pipeline stall, but no other problem, just advance
      // the current cycle and try again.
      DEBUG(dbgs() << "*** Advancing cycle, no work to do\n");
      HazardRec->AdvanceCycle();
      ++NumStalls;
      ++CurCycle;
    } else {
      // Otherwise, we have no instructions to issue and we have instructions
      // that will fault if we don't do this right.  This is the case for
      // processors without pipeline interlocks and other cases.
      DEBUG(dbgs() << "*** Emitting noop\n");
      HazardRec->EmitNoop();
      Sequence.push_back(0);   // NULL here means noop
      ++NumNoops;
      ++CurCycle;
    }
  }

#ifndef NDEBUG
  VerifySchedule(/*isBottomUp=*/false);
#endif
}

//===----------------------------------------------------------------------===//
//                         Public Constructor Functions
//===----------------------------------------------------------------------===//

/// createTDListDAGScheduler - This creates a top-down list scheduler with a
/// new hazard recognizer. This scheduler takes ownership of the hazard
/// recognizer and deletes it when done.
ScheduleDAGSDNodes *
llvm::createTDListDAGScheduler(SelectionDAGISel *IS, CodeGenOpt::Level) {
  return new ScheduleDAGList(*IS->MF,
                             new LatencyPriorityQueue(),
                             IS->CreateTargetHazardRecognizer());
}
<|endoftext|>