<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 "RestEdgesHandler.h"
#include "Basics/ScopeGuard.h"
#include "Cluster/ClusterMethods.h"
#include "VocBase/Traverser.h"

#include <velocypack/Iterator.h>
#include <velocypack/velocypack-aliases.h>

using namespace arangodb;
using namespace arangodb::rest;

RestEdgesHandler::RestEdgesHandler(HttpRequest* request)
    : RestVocbaseBaseHandler(request) {}

HttpHandler::status_t RestEdgesHandler::execute() {
  // extract the sub-request type
  HttpRequest::HttpRequestType type = _request->requestType();

  // execute one of the CRUD methods
  switch (type) {
    case HttpRequest::HTTP_REQUEST_GET: {
      std::vector<traverser::TraverserExpression*> empty;
      readEdges(empty);
      break;
    }
    case HttpRequest::HTTP_REQUEST_PUT:
      readFilteredEdges();
      break;
    case HttpRequest::HTTP_REQUEST_POST:
      readEdgesForMultipleVertices();
      break;
    case HttpRequest::HTTP_REQUEST_HEAD:
    case HttpRequest::HTTP_REQUEST_DELETE:
    case HttpRequest::HTTP_REQUEST_ILLEGAL:
    default: {
      generateNotImplemented("ILLEGAL " + EDGES_PATH);
      break;
    }
  }

  // this handler is done
  return status_t(HANDLER_DONE);
}

bool RestEdgesHandler::getEdgesForVertex(
    std::string const& id,
    std::vector<traverser::TraverserExpression*> const& expressions,
    TRI_edge_direction_e direction, SingleCollectionReadOnlyTransaction& trx,
    arangodb::basics::Json& result, size_t& scannedIndex, size_t& filtered) {
  arangodb::traverser::VertexId start;
  try {
    start = arangodb::traverser::IdStringToVertexId(trx.resolver(), id);
  } catch (arangodb::basics::Exception& e) {
    handleError(e);
    return false;
  }
  TRI_document_collection_t* docCol =
      trx.trxCollection()->_collection->_collection;

  std::vector<TRI_doc_mptr_copy_t>&& edges = TRI_LookupEdgesDocumentCollection(
      &trx, docCol, direction, start.cid, const_cast<char*>(start.key));

  // generate result
  result.reserve(edges.size());
  scannedIndex += edges.size();

  if (expressions.empty()) {
    for (auto& e : edges) {
      DocumentAccessor da(trx.resolver(), docCol, &e);
      result.add(da.toJson());
    }
  } else {
    for (auto& e : edges) {
      bool add = true;
      // Expressions symbolize an and, so all have to be matched
      for (auto& exp : expressions) {
        if (exp->isEdgeAccess &&
            !exp->matchesCheck(e, docCol, trx.resolver())) {
          ++filtered;
          add = false;
          break;
        }
      }
      if (add) {
        DocumentAccessor da(trx.resolver(), docCol, &e);
        result.add(da.toJson());
      }
    }
  }
  return true;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock API_EDGE_READINOUTBOUND
////////////////////////////////////////////////////////////////////////////////

bool RestEdgesHandler::readEdges(
    std::vector<traverser::TraverserExpression*> const& expressions) {
  std::vector<std::string> const& suffix = _request->suffix();

  if (suffix.size() != 1) {
    generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER,
                  "expected GET " + EDGES_PATH +
                      "/<collection-identifier>?vertex=<vertex-handle>&"
                      "direction=<direction>");
    return false;
  }

  std::string collectionName = suffix[0];
  CollectionNameResolver resolver(_vocbase);
  TRI_col_type_t colType = resolver.getCollectionTypeCluster(collectionName);
  if (colType == TRI_COL_TYPE_UNKNOWN) {
    generateError(HttpResponse::NOT_FOUND,
                  TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND);
    return false;
  } else if (colType != TRI_COL_TYPE_EDGE) {
    generateError(HttpResponse::BAD, TRI_ERROR_ARANGO_COLLECTION_TYPE_INVALID);
    return false;
  }

  bool found;
  char const* dir = _request->value("direction", found);

  if (!found || *dir == '\0') {
    dir = "any";
  }

  std::string dirString(dir);
  TRI_edge_direction_e direction;

  if (dirString == "any") {
    direction = TRI_EDGE_ANY;
  } else if (dirString == "out" || dirString == "outbound") {
    direction = TRI_EDGE_OUT;
  } else if (dirString == "in" || dirString == "inbound") {
    direction = TRI_EDGE_IN;
  } else {
    generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER,
                  "<direction> must by any, in, or out, not: " + dirString);
    return false;
  }

  char const* startVertex = _request->value("vertex", found);

  if (!found || *startVertex == '\0') {
    generateError(HttpResponse::BAD, TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD,
                  "illegal document handle");
    return false;
  }

  if (ServerState::instance()->isCoordinator()) {
    std::string vertexString(startVertex);
    arangodb::rest::HttpResponse::HttpResponseCode responseCode;
    std::string contentType;
    arangodb::basics::Json resultDocument(arangodb::basics::Json::Object, 3);
    int res = getFilteredEdgesOnCoordinator(
        _vocbase->_name, collectionName, vertexString, direction, expressions,
        responseCode, contentType, resultDocument);
    if (res != TRI_ERROR_NO_ERROR) {
      generateError(responseCode, res);
      return false;
    }

    resultDocument.set("error", arangodb::basics::Json(false));
    resultDocument.set("code", arangodb::basics::Json(200));
    generateResult(resultDocument.json());
    return true;
  }

  // find and load collection given by name or identifier
  SingleCollectionReadOnlyTransaction trx(new StandaloneTransactionContext(),
                                          _vocbase, collectionName);

  // .............................................................................
  // inside read transaction
  // .............................................................................

  int res = trx.begin();
  if (res != TRI_ERROR_NO_ERROR) {
    generateTransactionError(collectionName, res);
    return false;
  }

  // If we are a DBserver, we want to use the cluster-wide collection
  // name for error reporting:
  if (ServerState::instance()->isDBServer()) {
    collectionName = trx.resolver()->getCollectionName(trx.cid());
  }

  size_t filtered = 0;
  size_t scannedIndex = 0;

  arangodb::basics::Json documents(arangodb::basics::Json::Array);
  bool ok = getEdgesForVertex(startVertex, expressions, direction, trx,
                              documents, scannedIndex, filtered);
  res = trx.finish(res);
  if (!ok) {
    // Error has been built internally
    return false;
  }

  if (res != TRI_ERROR_NO_ERROR) {
    generateTransactionError(collectionName, res);
    return false;
  }

  arangodb::basics::Json result(arangodb::basics::Json::Object, 4);
  result("edges", documents);
  result("error", arangodb::basics::Json(false));
  result("code", arangodb::basics::Json(200));
  arangodb::basics::Json stats(arangodb::basics::Json::Object, 2);

  stats("scannedIndex",
        arangodb::basics::Json(static_cast<int32_t>(scannedIndex)));
  stats("filtered", arangodb::basics::Json(static_cast<int32_t>(filtered)));
  result("stats", stats);

  // and generate a response
  generateResult(result.json());

  return true;
}

////////////////////////////////////////////////////////////////////////////////
/// Internal function to receive all edges for a list of vertices
/// Not publicly documented on purpose.
/// NOTE: It ONLY except _id strings. Nothing else
////////////////////////////////////////////////////////////////////////////////

bool RestEdgesHandler::readEdgesForMultipleVertices() {
  std::vector<std::string> const& suffix = _request->suffix();

  if (suffix.size() != 1) {
    generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER,
                  "expected POST " + EDGES_PATH +
                      "/<collection-identifier>?direction=<direction>");
    return false;
  }

  bool parseSuccess = true;
  VPackOptions options;
  std::shared_ptr<VPackBuilder> parsedBody =
      parseVelocyPackBody(&options, parseSuccess);

  if (!parseSuccess) {
    // A body is required
    return false;
  }
  VPackSlice body = parsedBody->slice();

  if (!body.isArray()) {
    generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER,
                  "Expected an array of vertex _id's in body parameter");
    return false;
  }

  std::string collectionName = suffix[0];
  CollectionNameResolver resolver(_vocbase);
  TRI_col_type_t colType = resolver.getCollectionTypeCluster(collectionName);

  if (colType == TRI_COL_TYPE_UNKNOWN) {
    generateError(HttpResponse::NOT_FOUND,
                  TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND);
    return false;
  } else if (colType != TRI_COL_TYPE_EDGE) {
    generateError(HttpResponse::BAD, TRI_ERROR_ARANGO_COLLECTION_TYPE_INVALID);
    return false;
  }

  bool found;
  char const* dir = _request->value("direction", found);

  if (!found || *dir == '\0') {
    dir = "any";
  }

  std::string dirString(dir);
  TRI_edge_direction_e direction;

  if (dirString == "any") {
    direction = TRI_EDGE_ANY;
  } else if (dirString == "out" || dirString == "outbound") {
    direction = TRI_EDGE_OUT;
  } else if (dirString == "in" || dirString == "inbound") {
    direction = TRI_EDGE_IN;
  } else {
    generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER,
                  "<direction> must by any, in, or out, not: " + dirString);
    return false;
  }

  if (ServerState::instance()->isCoordinator()) {
    // This API is only for internal use on DB servers and is not (yet) allowed
    // to
    // be executed on the coordinator
    return false;
  }

  // find and load collection given by name or identifier
  SingleCollectionReadOnlyTransaction trx(new StandaloneTransactionContext(),
                                          _vocbase, collectionName);

  // .............................................................................
  // inside read transaction
  // .............................................................................

  int res = trx.begin();
  if (res != TRI_ERROR_NO_ERROR) {
    generateTransactionError(collectionName, res);
    return false;
  }

  // If we are a DBserver, we want to use the cluster-wide collection
  // name for error reporting:
  if (ServerState::instance()->isDBServer()) {
    collectionName = trx.resolver()->getCollectionName(trx.cid());
  }

  size_t filtered = 0;
  size_t scannedIndex = 0;
  std::vector<traverser::TraverserExpression*> const expressions;

  arangodb::basics::Json documents(arangodb::basics::Json::Array);
  for (auto const& vertexSlice : VPackArrayIterator(body)) {
    if (vertexSlice.isString()) {
      std::string vertex = vertexSlice.copyString();
      bool ok = getEdgesForVertex(vertex, expressions, direction, trx,
                                  documents, scannedIndex, filtered);
      if (!ok) {
        // Ignore the error
      }
    }
  }

  res = trx.finish(res);
  if (res != TRI_ERROR_NO_ERROR) {
    generateTransactionError(collectionName, res);
    return false;
  }

  arangodb::basics::Json result(arangodb::basics::Json::Object, 4);
  result("edges", documents);
  result("error", arangodb::basics::Json(false));
  result("code", arangodb::basics::Json(200));
  arangodb::basics::Json stats(arangodb::basics::Json::Object, 2);

  stats("scannedIndex",
        arangodb::basics::Json(static_cast<int32_t>(scannedIndex)));
  stats("filtered", arangodb::basics::Json(static_cast<int32_t>(filtered)));
  result("stats", stats);

  // and generate a response
  generateResult(result.json());

  return true;
}

////////////////////////////////////////////////////////////////////////////////
/// Internal function for optimized edge retrieval.
/// Allows to send an TraverserExpression for filtering in the body
/// Not publicly documented on purpose.
////////////////////////////////////////////////////////////////////////////////

bool RestEdgesHandler::readFilteredEdges() {
  std::vector<traverser::TraverserExpression*> expressions;
  bool parseSuccess = true;
  VPackOptions options;
  std::shared_ptr<VPackBuilder> parsedBody =
      parseVelocyPackBody(&options, parseSuccess);
  if (!parseSuccess) {
    // We continue unfiltered
    // Filter could be done by caller
    delete _response;
    _response = nullptr;
    return readEdges(expressions);
  }
  VPackSlice body = parsedBody->slice();
  arangodb::basics::ScopeGuard guard{[]() -> void {},
                                     [&expressions]() -> void {
                                       for (auto& e : expressions) {
                                         delete e;
                                       }
                                     }};

  if (!body.isArray()) {
    generateError(
        HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER,
        "Expected an array of traverser expressions as body parameter");
    return false;
  }

  expressions.reserve(body.length());

  for (auto const& exp : VPackArrayIterator(body)) {
    if (exp.isObject()) {
      auto expression = std::make_unique<traverser::TraverserExpression>(exp);
      expressions.emplace_back(expression.get());
      expression.release();
    }
  }
  return readEdges(expressions);
}
<commit_msg>order barriers while accessing edges<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 "RestEdgesHandler.h"
#include "Basics/ScopeGuard.h"
#include "Cluster/ClusterMethods.h"
#include "VocBase/Traverser.h"

#include <velocypack/Iterator.h>
#include <velocypack/velocypack-aliases.h>

using namespace arangodb;
using namespace arangodb::rest;

RestEdgesHandler::RestEdgesHandler(HttpRequest* request)
    : RestVocbaseBaseHandler(request) {}

HttpHandler::status_t RestEdgesHandler::execute() {
  // extract the sub-request type
  HttpRequest::HttpRequestType type = _request->requestType();

  // execute one of the CRUD methods
  try {
    switch (type) {
      case HttpRequest::HTTP_REQUEST_GET: {
        std::vector<traverser::TraverserExpression*> empty;
        readEdges(empty);
        break;
      }
      case HttpRequest::HTTP_REQUEST_PUT:
        readFilteredEdges();
        break;
      case HttpRequest::HTTP_REQUEST_POST:
        readEdgesForMultipleVertices();
        break;
      case HttpRequest::HTTP_REQUEST_HEAD:
      case HttpRequest::HTTP_REQUEST_DELETE:
      case HttpRequest::HTTP_REQUEST_ILLEGAL:
      default: {
        generateNotImplemented("ILLEGAL " + EDGES_PATH);
        break;
      }
    }
  } catch (arangodb::basics::Exception const& ex) {
    generateError(HttpResponse::responseCode(ex.code()), ex.code(), ex.what());
  }
  catch (std::exception const& ex) {
    generateError(HttpResponse::SERVER_ERROR, TRI_ERROR_INTERNAL, ex.what());
  }
  catch (...) {
    generateError(HttpResponse::SERVER_ERROR, TRI_ERROR_INTERNAL);
  }

  // this handler is done
  return status_t(HANDLER_DONE);
}

bool RestEdgesHandler::getEdgesForVertex(
    std::string const& id,
    std::vector<traverser::TraverserExpression*> const& expressions,
    TRI_edge_direction_e direction, SingleCollectionReadOnlyTransaction& trx,
    arangodb::basics::Json& result, size_t& scannedIndex, size_t& filtered) {
  arangodb::traverser::VertexId start;
  try {
    start = arangodb::traverser::IdStringToVertexId(trx.resolver(), id);
  } catch (arangodb::basics::Exception& e) {
    handleError(e);
    return false;
  }
  TRI_document_collection_t* docCol =
      trx.trxCollection()->_collection->_collection;

  if (trx.orderDitch(trx.trxCollection()) == nullptr) {
    THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
  }

  std::vector<TRI_doc_mptr_copy_t>&& edges = TRI_LookupEdgesDocumentCollection(
      &trx, docCol, direction, start.cid, const_cast<char*>(start.key));

  // generate result
  result.reserve(edges.size());
  scannedIndex += edges.size();

  if (expressions.empty()) {
    for (auto& e : edges) {
      DocumentAccessor da(trx.resolver(), docCol, &e);
      result.add(da.toJson());
    }
  } else {
    for (auto& e : edges) {
      bool add = true;
      // Expressions symbolize an and, so all have to be matched
      for (auto& exp : expressions) {
        if (exp->isEdgeAccess &&
            !exp->matchesCheck(e, docCol, trx.resolver())) {
          ++filtered;
          add = false;
          break;
        }
      }
      if (add) {
        DocumentAccessor da(trx.resolver(), docCol, &e);
        result.add(da.toJson());
      }
    }
  }
  return true;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock API_EDGE_READINOUTBOUND
////////////////////////////////////////////////////////////////////////////////

bool RestEdgesHandler::readEdges(
    std::vector<traverser::TraverserExpression*> const& expressions) {
  std::vector<std::string> const& suffix = _request->suffix();

  if (suffix.size() != 1) {
    generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER,
                  "expected GET " + EDGES_PATH +
                      "/<collection-identifier>?vertex=<vertex-handle>&"
                      "direction=<direction>");
    return false;
  }

  std::string collectionName = suffix[0];
  CollectionNameResolver resolver(_vocbase);
  TRI_col_type_t colType = resolver.getCollectionTypeCluster(collectionName);
  if (colType == TRI_COL_TYPE_UNKNOWN) {
    generateError(HttpResponse::NOT_FOUND,
                  TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND);
    return false;
  } else if (colType != TRI_COL_TYPE_EDGE) {
    generateError(HttpResponse::BAD, TRI_ERROR_ARANGO_COLLECTION_TYPE_INVALID);
    return false;
  }

  bool found;
  char const* dir = _request->value("direction", found);

  if (!found || *dir == '\0') {
    dir = "any";
  }

  std::string dirString(dir);
  TRI_edge_direction_e direction;

  if (dirString == "any") {
    direction = TRI_EDGE_ANY;
  } else if (dirString == "out" || dirString == "outbound") {
    direction = TRI_EDGE_OUT;
  } else if (dirString == "in" || dirString == "inbound") {
    direction = TRI_EDGE_IN;
  } else {
    generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER,
                  "<direction> must by any, in, or out, not: " + dirString);
    return false;
  }

  char const* startVertex = _request->value("vertex", found);

  if (!found || *startVertex == '\0') {
    generateError(HttpResponse::BAD, TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD,
                  "illegal document handle");
    return false;
  }

  if (ServerState::instance()->isCoordinator()) {
    std::string vertexString(startVertex);
    arangodb::rest::HttpResponse::HttpResponseCode responseCode;
    std::string contentType;
    arangodb::basics::Json resultDocument(arangodb::basics::Json::Object, 3);
    int res = getFilteredEdgesOnCoordinator(
        _vocbase->_name, collectionName, vertexString, direction, expressions,
        responseCode, contentType, resultDocument);
    if (res != TRI_ERROR_NO_ERROR) {
      generateError(responseCode, res);
      return false;
    }

    resultDocument.set("error", arangodb::basics::Json(false));
    resultDocument.set("code", arangodb::basics::Json(200));
    generateResult(resultDocument.json());
    return true;
  }

  // find and load collection given by name or identifier
  SingleCollectionReadOnlyTransaction trx(new StandaloneTransactionContext(),
                                          _vocbase, collectionName);

  // .............................................................................
  // inside read transaction
  // .............................................................................

  int res = trx.begin();
  if (res != TRI_ERROR_NO_ERROR) {
    generateTransactionError(collectionName, res);
    return false;
  }

  // If we are a DBserver, we want to use the cluster-wide collection
  // name for error reporting:
  if (ServerState::instance()->isDBServer()) {
    collectionName = trx.resolver()->getCollectionName(trx.cid());
  }

  size_t filtered = 0;
  size_t scannedIndex = 0;

  arangodb::basics::Json documents(arangodb::basics::Json::Array);
  bool ok = getEdgesForVertex(startVertex, expressions, direction, trx,
                              documents, scannedIndex, filtered);
  res = trx.finish(res);
  if (!ok) {
    // Error has been built internally
    return false;
  }

  if (res != TRI_ERROR_NO_ERROR) {
    generateTransactionError(collectionName, res);
    return false;
  }

  arangodb::basics::Json result(arangodb::basics::Json::Object, 4);
  result("edges", documents);
  result("error", arangodb::basics::Json(false));
  result("code", arangodb::basics::Json(200));
  arangodb::basics::Json stats(arangodb::basics::Json::Object, 2);

  stats("scannedIndex",
        arangodb::basics::Json(static_cast<int32_t>(scannedIndex)));
  stats("filtered", arangodb::basics::Json(static_cast<int32_t>(filtered)));
  result("stats", stats);

  // and generate a response
  generateResult(result.json());

  return true;
}

////////////////////////////////////////////////////////////////////////////////
/// Internal function to receive all edges for a list of vertices
/// Not publicly documented on purpose.
/// NOTE: It ONLY except _id strings. Nothing else
////////////////////////////////////////////////////////////////////////////////

bool RestEdgesHandler::readEdgesForMultipleVertices() {
  std::vector<std::string> const& suffix = _request->suffix();

  if (suffix.size() != 1) {
    generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER,
                  "expected POST " + EDGES_PATH +
                      "/<collection-identifier>?direction=<direction>");
    return false;
  }

  bool parseSuccess = true;
  VPackOptions options;
  std::shared_ptr<VPackBuilder> parsedBody =
      parseVelocyPackBody(&options, parseSuccess);

  if (!parseSuccess) {
    // A body is required
    return false;
  }
  VPackSlice body = parsedBody->slice();

  if (!body.isArray()) {
    generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER,
                  "Expected an array of vertex _id's in body parameter");
    return false;
  }

  std::string collectionName = suffix[0];
  CollectionNameResolver resolver(_vocbase);
  TRI_col_type_t colType = resolver.getCollectionTypeCluster(collectionName);

  if (colType == TRI_COL_TYPE_UNKNOWN) {
    generateError(HttpResponse::NOT_FOUND,
                  TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND);
    return false;
  } else if (colType != TRI_COL_TYPE_EDGE) {
    generateError(HttpResponse::BAD, TRI_ERROR_ARANGO_COLLECTION_TYPE_INVALID);
    return false;
  }

  bool found;
  char const* dir = _request->value("direction", found);

  if (!found || *dir == '\0') {
    dir = "any";
  }

  std::string dirString(dir);
  TRI_edge_direction_e direction;

  if (dirString == "any") {
    direction = TRI_EDGE_ANY;
  } else if (dirString == "out" || dirString == "outbound") {
    direction = TRI_EDGE_OUT;
  } else if (dirString == "in" || dirString == "inbound") {
    direction = TRI_EDGE_IN;
  } else {
    generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER,
                  "<direction> must by any, in, or out, not: " + dirString);
    return false;
  }

  if (ServerState::instance()->isCoordinator()) {
    // This API is only for internal use on DB servers and is not (yet) allowed
    // to
    // be executed on the coordinator
    return false;
  }

  // find and load collection given by name or identifier
  SingleCollectionReadOnlyTransaction trx(new StandaloneTransactionContext(),
                                          _vocbase, collectionName);

  // .............................................................................
  // inside read transaction
  // .............................................................................

  int res = trx.begin();
  if (res != TRI_ERROR_NO_ERROR) {
    generateTransactionError(collectionName, res);
    return false;
  }

  // If we are a DBserver, we want to use the cluster-wide collection
  // name for error reporting:
  if (ServerState::instance()->isDBServer()) {
    collectionName = trx.resolver()->getCollectionName(trx.cid());
  }

  size_t filtered = 0;
  size_t scannedIndex = 0;
  std::vector<traverser::TraverserExpression*> const expressions;

  arangodb::basics::Json documents(arangodb::basics::Json::Array);
  for (auto const& vertexSlice : VPackArrayIterator(body)) {
    if (vertexSlice.isString()) {
      std::string vertex = vertexSlice.copyString();
      bool ok = getEdgesForVertex(vertex, expressions, direction, trx,
                                  documents, scannedIndex, filtered);
      if (!ok) {
        // Ignore the error
      }
    }
  }

  res = trx.finish(res);
  if (res != TRI_ERROR_NO_ERROR) {
    generateTransactionError(collectionName, res);
    return false;
  }

  arangodb::basics::Json result(arangodb::basics::Json::Object, 4);
  result("edges", documents);
  result("error", arangodb::basics::Json(false));
  result("code", arangodb::basics::Json(200));
  arangodb::basics::Json stats(arangodb::basics::Json::Object, 2);

  stats("scannedIndex",
        arangodb::basics::Json(static_cast<int32_t>(scannedIndex)));
  stats("filtered", arangodb::basics::Json(static_cast<int32_t>(filtered)));
  result("stats", stats);

  // and generate a response
  generateResult(result.json());

  return true;
}

////////////////////////////////////////////////////////////////////////////////
/// Internal function for optimized edge retrieval.
/// Allows to send an TraverserExpression for filtering in the body
/// Not publicly documented on purpose.
////////////////////////////////////////////////////////////////////////////////

bool RestEdgesHandler::readFilteredEdges() {
  std::vector<traverser::TraverserExpression*> expressions;
  bool parseSuccess = true;
  VPackOptions options;
  std::shared_ptr<VPackBuilder> parsedBody =
      parseVelocyPackBody(&options, parseSuccess);
  if (!parseSuccess) {
    // We continue unfiltered
    // Filter could be done by caller
    delete _response;
    _response = nullptr;
    return readEdges(expressions);
  }
  VPackSlice body = parsedBody->slice();
  arangodb::basics::ScopeGuard guard{[]() -> void {},
                                     [&expressions]() -> void {
                                       for (auto& e : expressions) {
                                         delete e;
                                       }
                                     }};

  if (!body.isArray()) {
    generateError(
        HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER,
        "Expected an array of traverser expressions as body parameter");
    return false;
  }

  expressions.reserve(body.length());

  for (auto const& exp : VPackArrayIterator(body)) {
    if (exp.isObject()) {
      auto expression = std::make_unique<traverser::TraverserExpression>(exp);
      expressions.emplace_back(expression.get());
      expression.release();
    }
  }
  return readEdges(expressions);
}
<|endoftext|>


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

#include <climits>

#include <string>
#include <vector>
#include <set>
#include <ostream>
#include <iostream>

#include "Poco/Util/ServerApplication.h"
#include "Poco/URI.h"
#include "Poco/File.h"
#include "Poco/DirectoryIterator.h"
#include "Poco/NumberFormatter.h"

#include "IndigoRequestHandler.h"
#include "IndigoConfiguration.h"

using namespace std;

using namespace Poco;
using namespace Poco::Util;
using namespace Poco::Net;

POCO_DECLARE_EXCEPTION(, ShareNotFoundException, ApplicationException)
POCO_IMPLEMENT_EXCEPTION(ShareNotFoundException, ApplicationException, "ShareNotFoundException")

IndigoRequestHandler::IndigoRequestHandler()
{
}

void IndigoRequestHandler::handleRequest(HTTPServerRequest &request, HTTPServerResponse &response)
{
	logRequest(request);

	const string &method = request.getMethod();

	if (method != HTTPRequest::HTTP_GET)
	{
		sendMethodNotAllowed(response);
		return;
	}

	const string &uri = request.getURI();
	Path uriPath(URI(uri).getPath(), Path::PATH_UNIX);

	if (!uriPath.isAbsolute())
	{
		sendBadRequest(response);
		return;
	}

	const IndigoConfiguration &configuration = IndigoConfiguration::get();

	if (uriPath.isDirectory() && uriPath.depth() == 0)
	{
		if (configuration.getRoot().empty())
		{
			sendVirtualRootDirectory(response);
			return;
		}
	}

	try
	{
		const Path &fsPath = resolveFSPath(uriPath);
		const string &target = fsPath.toString();

		File f(target);
		if (f.isDirectory())
		{
			if (uriPath.isDirectory())
			{
				sendDirectory(response, target, uriPath.toString(Path::PATH_UNIX));
			}
			else
			{
				Path uriDirPath = uriPath;
				uriDirPath.makeDirectory();
				redirectToDirectory(response, uriDirPath.toString(Path::PATH_UNIX), false);
			}
		}
		else
		{
			const string &ext = fsPath.getExtension();
			const string &mediaType = configuration.getMimeType(ext);
			response.sendFile(target, mediaType);
		}
	}
	catch (ShareNotFoundException &snfe)
	{
		sendNotFound(response);
	}
	catch (FileNotFoundException &fnfe)
	{
		sendNotFound(response);
	}
	catch (FileAccessDeniedException &fade)
	{
		sendForbidden(response);
	}
	catch (FileException &fe)
	{
		sendInternalServerError(response);
	}
	catch (PathSyntaxException &pse)
	{
		sendNotImplemented(response);
	}
	catch (...)
	{
		sendInternalServerError(response);
	}
}

Path IndigoRequestHandler::resolveFSPath(const Path &uriPath)
{
	const IndigoConfiguration &configuration = IndigoConfiguration::get();

	const string &shareName = uriPath[0];
	string base = configuration.getSharePath(shareName);
	bool share = true;

	if (base.empty())
	{
		base = configuration.getRoot();
		if (base.empty())
			throw ShareNotFoundException();

		share = false;
	}

	Path fsPath(base);

	if (share)
	{
		fsPath.makeDirectory();

		const int d = uriPath.depth();
		for (int i = 1; i <= d; i++)
			fsPath.pushDirectory(uriPath[i]);
	}
	else
	{
		fsPath.append(uriPath);
	}

	fsPath.makeFile();
	return fsPath;
}

void IndigoRequestHandler::sendDirectoryListing(HTTPServerResponse &response, const string &dirURI, const vector<string> &entries)
{
	bool root = (dirURI == "/");

	ostream &out = response.send();

	out << "" << endl;
	out << "" << endl;
	out << "<title>";
	out << "Index of " << dirURI;
	out << "</title>" << endl;
	out << "" << endl;
	out << "" << endl;
	out << "
";
	out << "Index of " << dirURI;
	out << "
" << endl;

	if (!root)
	{
		out << "<Parent Directory>
" << endl;
	}

	const int l = entries.size();
	for (int i = 0; i < l; i++)
	{
		out << "<a href=\"" << entries[i] << "\">" << entries[i] << "" << "
" << endl;
	}

	out << "" << endl;
	out << "" << endl;
}

void IndigoRequestHandler::sendVirtualRootDirectory(HTTPServerResponse &response)
{
	const IndigoConfiguration &configuration = IndigoConfiguration::get();
	const set<string> &shares = configuration.getShares();

	vector<string> entries;

	set<string>::const_iterator it;
	const set<string>::const_iterator &end = shares.end();
	for (it = shares.begin(); it != end; ++it)
	{
		const string &shareName = *it;
		try
		{
			const Path &fsPath = resolveFSPath(Path("/" + shareName, Path::PATH_UNIX));
			File f(fsPath);

			if (!f.isHidden())
			{
				string entry = shareName;
				if (f.isDirectory())
					entry += '/';

				entries.push_back(entry);
			}
		}
		catch (ShareNotFoundException &snfe)
		{
		}
		catch (FileException &fe)
		{
		}
		catch (PathSyntaxException &pse)
		{
		}
	}

	sendDirectoryListing(response, "/", entries);
}

void IndigoRequestHandler::sendDirectory(HTTPServerResponse &response, const string &path, const string &dirURI)
{
	vector<string> entries;

	DirectoryIterator it(path);
	DirectoryIterator end;
	while (it != end)
	{
		try
		{
			if (!it->isHidden())
			{
				string entry = it.name();
				if (it->isDirectory())
					entry += '/';

				entries.push_back(entry);
			}
		}
		catch (FileException &fe)
		{
		}
		catch (PathSyntaxException &pse)
		{
		}

		++it;
	}

	sendDirectoryListing(response, dirURI, entries);
}

void IndigoRequestHandler::redirectToDirectory(HTTPServerResponse &response, const string &dirURI, bool permanent)
{
	if (!permanent)
	{
		response.redirect(dirURI);
	}
	else
	{
		response.setStatusAndReason(HTTPResponse::HTTP_MOVED_PERMANENTLY);
		response.setContentLength(0);
		response.setChunkedTransferEncoding(false);
		response.set("Location", dirURI);
		response.send();
	}
}

void IndigoRequestHandler::logRequest(const HTTPServerRequest &request)
{
	const ServerApplication &app = dynamic_cast<ServerApplication &>(Application::instance());
	if (app.isInteractive())
	{
		const string &method = request.getMethod();
		const string &uri = request.getURI();
		const string &host = request.clientAddress().host().toString();

		string logString = host + " - " + method + " " + uri;

		app.logger().information(logString);
	}
}

void IndigoRequestHandler::sendError(HTTPServerResponse &response, int code)
{
	if (response.sent())
		return;

	response.setStatusAndReason(HTTPResponse::HTTPStatus(code));
	response.setChunkedTransferEncoding(true);
	response.setContentType("text/html");

	const string &reason = response.getReason();

	ostream &out = response.send();
	out << "";
	out << "<title>" + NumberFormatter::format(code) + " " + reason + "</title>";
	out << "
" + reason + "
";
	out << "";
}

void IndigoRequestHandler::sendMethodNotAllowed(HTTPServerResponse &response)
{
	sendError(response, HTTPResponse::HTTP_METHOD_NOT_ALLOWED);
}

void IndigoRequestHandler::sendRequestURITooLong(HTTPServerResponse &response)
{
	sendError(response, HTTPResponse::HTTP_REQUESTURITOOLONG);
}

void IndigoRequestHandler::sendBadRequest(HTTPServerResponse &response)
{
	sendError(response, HTTPResponse::HTTP_BAD_REQUEST);
}

void IndigoRequestHandler::sendNotImplemented(HTTPServerResponse &response)
{
	sendError(response, HTTPResponse::HTTP_NOT_IMPLEMENTED);
}

void IndigoRequestHandler::sendNotFound(HTTPServerResponse &response)
{
	sendError(response, HTTPResponse::HTTP_NOT_FOUND);
}

void IndigoRequestHandler::sendForbidden(HTTPServerResponse &response)
{
	sendError(response, HTTPResponse::HTTP_FORBIDDEN);
}

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

#include <climits>

#include <string>
#include <vector>
#include <set>
#include <ostream>
#include <iostream>

#include "Poco/Util/ServerApplication.h"
#include "Poco/URI.h"
#include "Poco/File.h"
#include "Poco/DirectoryIterator.h"
#include "Poco/NumberFormatter.h"

#include "IndigoRequestHandler.h"
#include "IndigoConfiguration.h"

using namespace std;

using namespace Poco;
using namespace Poco::Util;
using namespace Poco::Net;

POCO_DECLARE_EXCEPTION(, ShareNotFoundException, ApplicationException)
POCO_IMPLEMENT_EXCEPTION(ShareNotFoundException, ApplicationException, "ShareNotFoundException")

IndigoRequestHandler::IndigoRequestHandler()
{
}

void IndigoRequestHandler::handleRequest(HTTPServerRequest &request, HTTPServerResponse &response)
{
	logRequest(request);

	const string &method = request.getMethod();

	if (method != HTTPRequest::HTTP_GET)
	{
		sendMethodNotAllowed(response);
		return;
	}

	URI uri;
	try
	{
		uri = request.getURI();
	}
	catch (SyntaxException &se)
	{
		sendBadRequest(response);
		return;
	}

	Path uriPath(uri.getPath(), Path::PATH_UNIX);

	if (!uriPath.isAbsolute())
	{
		sendBadRequest(response);
		return;
	}

	const IndigoConfiguration &configuration = IndigoConfiguration::get();

	if (uriPath.isDirectory() && uriPath.depth() == 0)
	{
		if (configuration.getRoot().empty())
		{
			sendVirtualRootDirectory(response);
			return;
		}
	}

	try
	{
		const Path &fsPath = resolveFSPath(uriPath);
		const string &target = fsPath.toString();

		File f(target);
		if (f.isDirectory())
		{
			if (uriPath.isDirectory())
			{
				sendDirectory(response, target, uriPath.toString(Path::PATH_UNIX));
			}
			else
			{
				Path uriDirPath = uriPath;
				uriDirPath.makeDirectory();
				redirectToDirectory(response, uriDirPath.toString(Path::PATH_UNIX), false);
			}
		}
		else
		{
			const string &ext = fsPath.getExtension();
			const string &mediaType = configuration.getMimeType(ext);
			response.sendFile(target, mediaType);
		}
	}
	catch (ShareNotFoundException &snfe)
	{
		sendNotFound(response);
	}
	catch (FileNotFoundException &fnfe)
	{
		sendNotFound(response);
	}
	catch (FileAccessDeniedException &fade)
	{
		sendForbidden(response);
	}
	catch (FileException &fe)
	{
		sendInternalServerError(response);
	}
	catch (PathSyntaxException &pse)
	{
		sendNotImplemented(response);
	}
	catch (...)
	{
		sendInternalServerError(response);
	}
}

Path IndigoRequestHandler::resolveFSPath(const Path &uriPath)
{
	const IndigoConfiguration &configuration = IndigoConfiguration::get();

	const string &shareName = uriPath[0];
	string base = configuration.getSharePath(shareName);
	bool share = true;

	if (base.empty())
	{
		base = configuration.getRoot();
		if (base.empty())
			throw ShareNotFoundException();

		share = false;
	}

	Path fsPath(base);

	if (share)
	{
		fsPath.makeDirectory();

		const int d = uriPath.depth();
		for (int i = 1; i <= d; i++)
			fsPath.pushDirectory(uriPath[i]);
	}
	else
	{
		fsPath.append(uriPath);
	}

	fsPath.makeFile();
	return fsPath;
}

void IndigoRequestHandler::sendDirectoryListing(HTTPServerResponse &response, const string &dirURI, const vector<string> &entries)
{
	bool root = (dirURI == "/");

	ostream &out = response.send();

	out << "" << endl;
	out << "" << endl;
	out << "<title>";
	out << "Index of " << dirURI;
	out << "</title>" << endl;
	out << "" << endl;
	out << "" << endl;
	out << "
";
	out << "Index of " << dirURI;
	out << "
" << endl;

	if (!root)
	{
		out << "<Parent Directory>
" << endl;
	}

	const int l = entries.size();
	for (int i = 0; i < l; i++)
	{
		out << "<a href=\"" << entries[i] << "\">" << entries[i] << "" << "
" << endl;
	}

	out << "" << endl;
	out << "" << endl;
}

void IndigoRequestHandler::sendVirtualRootDirectory(HTTPServerResponse &response)
{
	const IndigoConfiguration &configuration = IndigoConfiguration::get();
	const set<string> &shares = configuration.getShares();

	vector<string> entries;

	set<string>::const_iterator it;
	const set<string>::const_iterator &end = shares.end();
	for (it = shares.begin(); it != end; ++it)
	{
		const string &shareName = *it;
		try
		{
			const Path &fsPath = resolveFSPath(Path("/" + shareName, Path::PATH_UNIX));
			File f(fsPath);

			if (!f.isHidden())
			{
				string entry = shareName;
				if (f.isDirectory())
					entry += '/';

				entries.push_back(entry);
			}
		}
		catch (ShareNotFoundException &snfe)
		{
		}
		catch (FileException &fe)
		{
		}
		catch (PathSyntaxException &pse)
		{
		}
	}

	sendDirectoryListing(response, "/", entries);
}

void IndigoRequestHandler::sendDirectory(HTTPServerResponse &response, const string &path, const string &dirURI)
{
	vector<string> entries;

	DirectoryIterator it(path);
	DirectoryIterator end;
	while (it != end)
	{
		try
		{
			if (!it->isHidden())
			{
				string entry = it.name();
				if (it->isDirectory())
					entry += '/';

				entries.push_back(entry);
			}
		}
		catch (FileException &fe)
		{
		}
		catch (PathSyntaxException &pse)
		{
		}

		++it;
	}

	sendDirectoryListing(response, dirURI, entries);
}

void IndigoRequestHandler::redirectToDirectory(HTTPServerResponse &response, const string &dirURI, bool permanent)
{
	if (!permanent)
	{
		response.redirect(dirURI);
	}
	else
	{
		response.setStatusAndReason(HTTPResponse::HTTP_MOVED_PERMANENTLY);
		response.setContentLength(0);
		response.setChunkedTransferEncoding(false);
		response.set("Location", dirURI);
		response.send();
	}
}

void IndigoRequestHandler::logRequest(const HTTPServerRequest &request)
{
	const ServerApplication &app = dynamic_cast<ServerApplication &>(Application::instance());
	if (app.isInteractive())
	{
		const string &method = request.getMethod();
		const string &uri = request.getURI();
		const string &host = request.clientAddress().host().toString();

		string logString = host + " - " + method + " " + uri;

		app.logger().information(logString);
	}
}

void IndigoRequestHandler::sendError(HTTPServerResponse &response, int code)
{
	if (response.sent())
		return;

	response.setStatusAndReason(HTTPResponse::HTTPStatus(code));
	response.setChunkedTransferEncoding(true);
	response.setContentType("text/html");

	const string &reason = response.getReason();

	ostream &out = response.send();
	out << "";
	out << "<title>" + NumberFormatter::format(code) + " " + reason + "</title>";
	out << "
" + reason + "
";
	out << "";
}

void IndigoRequestHandler::sendMethodNotAllowed(HTTPServerResponse &response)
{
	sendError(response, HTTPResponse::HTTP_METHOD_NOT_ALLOWED);
}

void IndigoRequestHandler::sendRequestURITooLong(HTTPServerResponse &response)
{
	sendError(response, HTTPResponse::HTTP_REQUESTURITOOLONG);
}

void IndigoRequestHandler::sendBadRequest(HTTPServerResponse &response)
{
	sendError(response, HTTPResponse::HTTP_BAD_REQUEST);
}

void IndigoRequestHandler::sendNotImplemented(HTTPServerResponse &response)
{
	sendError(response, HTTPResponse::HTTP_NOT_IMPLEMENTED);
}

void IndigoRequestHandler::sendNotFound(HTTPServerResponse &response)
{
	sendError(response, HTTPResponse::HTTP_NOT_FOUND);
}

void IndigoRequestHandler::sendForbidden(HTTPServerResponse &response)
{
	sendError(response, HTTPResponse::HTTP_FORBIDDEN);
}

void IndigoRequestHandler::sendInternalServerError(HTTPServerResponse &response)
{
	sendError(response, HTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
}
<|endoftext|>


<commit_before>/*
    Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org>
    Copyright (c) 2008 Stephen Kelly <steveire@gmail.com>

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "entitytreeview.h"

#include <QtCore/QDebug>
#include <QtCore/QTimer>
#include <QtGui/QApplication>
#include <QtGui/QDragMoveEvent>
#include <QtGui/QHeaderView>
#include <QtGui/QMenu>

#include <KAction>
#include <KLocale>
#include <KMessageBox>
#include <KUrl>
#include <KXMLGUIFactory>

#include <kxmlguiclient.h>

#include <akonadi/collection.h>
#include <akonadi/control.h>
#include <akonadi/item.h>
#include <akonadi/entitytreemodel.h>

#include <kdebug.h>

using namespace Akonadi;

/**
 * @internal
 */
class EntityTreeView::Private
{
public:
  Private( EntityTreeView *parent )
      : mParent( parent ),
      xmlGuiClient( 0 )
  {
  }

  void init();
  void dragExpand();
  void itemClicked( const QModelIndex& );
  void itemDoubleClicked( const QModelIndex& );
  void itemCurrentChanged( const QModelIndex& );
  bool hasParent( const QModelIndex& idx, Collection::Id parentId );

  EntityTreeView *mParent;
  QModelIndex dragOverIndex;
  QTimer dragExpandTimer;

  KXMLGUIClient *xmlGuiClient;
};
void EntityTreeView::Private::init()
{
  mParent->header()->setClickable( true );
  mParent->header()->setStretchLastSection( false );
//   mParent->setRootIsDecorated( false );

//   mParent->setAutoExpandDelay ( QApplication::startDragTime() );

  mParent->setSortingEnabled( true );
  mParent->sortByColumn( 0, Qt::AscendingOrder );
  mParent->setEditTriggers( QAbstractItemView::EditKeyPressed );
  mParent->setAcceptDrops( true );
  mParent->setDropIndicatorShown( true );
  mParent->setDragDropMode( DragDrop );
  mParent->setDragEnabled( true );

  dragExpandTimer.setSingleShot( true );
  mParent->connect( &dragExpandTimer, SIGNAL( timeout() ), SLOT( dragExpand() ) );

  mParent->connect( mParent, SIGNAL( clicked( const QModelIndex& ) ),
                    mParent, SLOT( itemClicked( const QModelIndex& ) ) );
  mParent->connect( mParent, SIGNAL( doubleClicked( const QModelIndex& ) ),
                    mParent, SLOT( itemDoubleClicked( const QModelIndex& ) ) );

  Control::widgetNeedsAkonadi( mParent );
}

bool EntityTreeView::Private::hasParent( const QModelIndex& idx, Collection::Id parentId )
{
  QModelIndex idx2 = idx;
  while ( idx2.isValid() ) {
    if ( mParent->model()->data( idx2, EntityTreeModel::CollectionIdRole ).toLongLong() == parentId )
      return true;

    idx2 = idx2.parent();
  }
  return false;
}

void EntityTreeView::Private::dragExpand()
{
  mParent->setExpanded( dragOverIndex, true );
  dragOverIndex = QModelIndex();
}

void EntityTreeView::Private::itemClicked( const QModelIndex &index )
{
  if ( !index.isValid() )
    return;

  const Collection collection = index.model()->data( index, EntityTreeModel::CollectionRole ).value<Collection>();
  if ( collection.isValid() ) {
    emit mParent->clicked( collection );
  } else {
    const Item item = index.model()->data( index, EntityTreeModel::ItemRole ).value<Item>();
    if ( item.isValid() )
      emit mParent->clicked( item );
  }
}

void EntityTreeView::Private::itemDoubleClicked( const QModelIndex &index )
{
  if ( !index.isValid() )
    return;

  const Collection collection = index.model()->data( index, EntityTreeModel::CollectionRole ).value<Collection>();
  if ( collection.isValid() ) {
    emit mParent->doubleClicked( collection );
  } else {
    const Item item = index.model()->data( index, EntityTreeModel::ItemRole ).value<Item>();
    if ( item.isValid() )
      emit mParent->doubleClicked( item );
  }
}

void EntityTreeView::Private::itemCurrentChanged( const QModelIndex &index )
{
  if ( !index.isValid() )
    return;

  const Collection collection = index.model()->data( index, EntityTreeModel::CollectionRole ).value<Collection>();
  if ( collection.isValid() ) {
    emit mParent->currentChanged( collection );
  } else {
    const Item item = index.model()->data( index, EntityTreeModel::ItemRole ).value<Item>();
    if ( item.isValid() )
      emit mParent->currentChanged( item );
  }
}

EntityTreeView::EntityTreeView( QWidget * parent ) :
    QTreeView( parent ),
    d( new Private( this ) )
{

  setSelectionMode( QAbstractItemView::SingleSelection );
  d->init();
}

EntityTreeView::EntityTreeView( KXMLGUIClient *xmlGuiClient, QWidget * parent ) :
    QTreeView( parent ),
    d( new Private( this ) )
{
  d->xmlGuiClient = xmlGuiClient;
  d->init();
}

EntityTreeView::~EntityTreeView()
{
  delete d;
}

void EntityTreeView::setModel( QAbstractItemModel * model )
{
  QTreeView::setModel( model );
  header()->setStretchLastSection( true );

  connect( selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ),
           this, SLOT( itemCurrentChanged( const QModelIndex& ) ) );
}

void EntityTreeView::dragMoveEvent( QDragMoveEvent * event )
{
  QModelIndex index = indexAt( event->pos() );
  if ( d->dragOverIndex != index ) {
    d->dragExpandTimer.stop();
    if ( index.isValid() && !isExpanded( index ) && itemsExpandable() ) {
      d->dragExpandTimer.start( QApplication::startDragTime() );
      d->dragOverIndex = index;
    }
  }

  // Check if the collection under the cursor accepts this data type
  Collection col = model()->data( index, EntityTreeModel::CollectionRole ).value<Collection>();
  if (!col.isValid())
  {
    Item item = model()->data( index, EntityTreeModel::ItemRole ).value<Item>();
    if (item.isValid())
    {
      col = model()->data( index.parent(), EntityTreeModel::CollectionRole ).value<Collection>();
    }
  }
  if ( col.isValid() )
  {
    QStringList supportedContentTypes = col.contentMimeTypes();
    const QMimeData *data = event->mimeData();
    KUrl::List urls = KUrl::List::fromMimeData( data );
    foreach( const KUrl &url, urls ) {
      const Collection collection = Collection::fromUrl( url );
      if ( collection.isValid() ) {
        if ( !supportedContentTypes.contains( Collection::mimeType() ) )
          break;

        // Check if we don't try to drop on one of the children
        if ( d->hasParent( index, collection.id() ) )
          break;
      } else { // This is an item.
        QString type = url.queryItems()[ QString::fromLatin1( "type" )];
        if ( !supportedContentTypes.contains( type ) )
          break;
      }
      // All urls are supported. process the event.
      QTreeView::dragMoveEvent( event );
      return;
    }
  }

  event->setDropAction( Qt::IgnoreAction );
  return;
}

void EntityTreeView::dragLeaveEvent( QDragLeaveEvent * event )
{
  d->dragExpandTimer.stop();
  d->dragOverIndex = QModelIndex();
  QTreeView::dragLeaveEvent( event );
}

void EntityTreeView::dropEvent( QDropEvent * event )
{
  d->dragExpandTimer.stop();
  d->dragOverIndex = QModelIndex();

  QModelIndexList idxs = selectedIndexes();

  QMenu popup( this );
  QAction* moveDropAction;
  // TODO If possible, hide unavailable actions ...
  // Use the model to determine if a move is ok.
//   if (...)
//   {
  moveDropAction = popup.addAction( KIcon( QString::fromLatin1( "edit-rename" ) ), i18n( "&Move here" ) );
//   }

  //TODO: If dropping on one of the selectedIndexes, just return.
  // open a context menu offering different drop actions (move, copy and cancel)
  QAction* copyDropAction = popup.addAction( KIcon( QString::fromLatin1( "edit-copy" ) ), i18n( "&Copy here" ) );
  popup.addSeparator();
  popup.addAction( KIcon( QString::fromLatin1( "process-stop" ) ), i18n( "Cancel" ) );

  QAction *activatedAction = popup.exec( QCursor::pos() );

  if ( activatedAction == moveDropAction ) {
    event->setDropAction( Qt::MoveAction );
  } else if ( activatedAction == copyDropAction ) {
    event->setDropAction( Qt::CopyAction );
  }
  // TODO: Handle link action.
  else return;

  QTreeView::dropEvent( event );
}

void EntityTreeView::contextMenuEvent( QContextMenuEvent * event )
{
  if ( !d->xmlGuiClient )
    return;

  const QModelIndex index = indexAt( event->pos() );

  QMenu *popup = 0;

  // check if the index under the cursor is a collection or item
  const Item item = model()->data( index, EntityTreeModel::ItemRole ).value<Item>();
  if ( item.isValid() )
    popup = static_cast<QMenu*>( d->xmlGuiClient->factory()->container(
                                 QLatin1String( "akonadi_itemview_contextmenu" ), d->xmlGuiClient ) );
  else
    popup = static_cast<QMenu*>( d->xmlGuiClient->factory()->container(
                                 QLatin1String( "akonadi_collectionview_contextmenu" ), d->xmlGuiClient ) );
  if ( popup )
    popup->exec( event->globalPos() );
}

void EntityTreeView::setXmlGuiClient( KXMLGUIClient * xmlGuiClient )
{
  d->xmlGuiClient = xmlGuiClient;
}

#include "entitytreeview.moc"
<commit_msg>Try to fetch more items on click.<commit_after>/*
    Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org>
    Copyright (c) 2008 Stephen Kelly <steveire@gmail.com>

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "entitytreeview.h"

#include <QtCore/QDebug>
#include <QtCore/QTimer>
#include <QtGui/QApplication>
#include <QtGui/QDragMoveEvent>
#include <QtGui/QHeaderView>
#include <QtGui/QMenu>

#include <KAction>
#include <KLocale>
#include <KMessageBox>
#include <KUrl>
#include <KXMLGUIFactory>

#include <kxmlguiclient.h>

#include <akonadi/collection.h>
#include <akonadi/control.h>
#include <akonadi/item.h>
#include <akonadi/entitytreemodel.h>

#include <kdebug.h>

using namespace Akonadi;

/**
 * @internal
 */
class EntityTreeView::Private
{
public:
  Private( EntityTreeView *parent )
      : mParent( parent ),
      xmlGuiClient( 0 )
  {
  }

  void init();
  void dragExpand();
  void itemClicked( const QModelIndex& );
  void itemDoubleClicked( const QModelIndex& );
  void itemCurrentChanged( const QModelIndex& );
  bool hasParent( const QModelIndex& idx, Collection::Id parentId );

  EntityTreeView *mParent;
  QModelIndex dragOverIndex;
  QTimer dragExpandTimer;

  KXMLGUIClient *xmlGuiClient;
};
void EntityTreeView::Private::init()
{
  mParent->header()->setClickable( true );
  mParent->header()->setStretchLastSection( false );
//   mParent->setRootIsDecorated( false );

//   mParent->setAutoExpandDelay ( QApplication::startDragTime() );

  mParent->setSortingEnabled( true );
  mParent->sortByColumn( 0, Qt::AscendingOrder );
  mParent->setEditTriggers( QAbstractItemView::EditKeyPressed );
  mParent->setAcceptDrops( true );
  mParent->setDropIndicatorShown( true );
  mParent->setDragDropMode( DragDrop );
  mParent->setDragEnabled( true );

  dragExpandTimer.setSingleShot( true );
  mParent->connect( &dragExpandTimer, SIGNAL( timeout() ), SLOT( dragExpand() ) );

  mParent->connect( mParent, SIGNAL( clicked( const QModelIndex& ) ),
                    mParent, SLOT( itemClicked( const QModelIndex& ) ) );
  mParent->connect( mParent, SIGNAL( doubleClicked( const QModelIndex& ) ),
                    mParent, SLOT( itemDoubleClicked( const QModelIndex& ) ) );

  Control::widgetNeedsAkonadi( mParent );
}

bool EntityTreeView::Private::hasParent( const QModelIndex& idx, Collection::Id parentId )
{
  QModelIndex idx2 = idx;
  while ( idx2.isValid() ) {
    if ( mParent->model()->data( idx2, EntityTreeModel::CollectionIdRole ).toLongLong() == parentId )
      return true;

    idx2 = idx2.parent();
  }
  return false;
}

void EntityTreeView::Private::dragExpand()
{
  mParent->setExpanded( dragOverIndex, true );
  dragOverIndex = QModelIndex();
}

void EntityTreeView::Private::itemClicked( const QModelIndex &index )
{
  if ( !index.isValid() )
    return;

  if (mParent->model()->canFetchMore(index))
    mParent->model()->fetchMore(index);

  const Collection collection = index.model()->data( index, EntityTreeModel::CollectionRole ).value<Collection>();
  if ( collection.isValid() ) {
    emit mParent->clicked( collection );
  } else {
    const Item item = index.model()->data( index, EntityTreeModel::ItemRole ).value<Item>();
    if ( item.isValid() )
      emit mParent->clicked( item );
  }
}

void EntityTreeView::Private::itemDoubleClicked( const QModelIndex &index )
{
  if ( !index.isValid() )
    return;

  const Collection collection = index.model()->data( index, EntityTreeModel::CollectionRole ).value<Collection>();
  if ( collection.isValid() ) {
    emit mParent->doubleClicked( collection );
  } else {
    const Item item = index.model()->data( index, EntityTreeModel::ItemRole ).value<Item>();
    if ( item.isValid() )
      emit mParent->doubleClicked( item );
  }
}

void EntityTreeView::Private::itemCurrentChanged( const QModelIndex &index )
{
  if ( !index.isValid() )
    return;

  const Collection collection = index.model()->data( index, EntityTreeModel::CollectionRole ).value<Collection>();
  if ( collection.isValid() ) {
    emit mParent->currentChanged( collection );
  } else {
    const Item item = index.model()->data( index, EntityTreeModel::ItemRole ).value<Item>();
    if ( item.isValid() )
      emit mParent->currentChanged( item );
  }
}

EntityTreeView::EntityTreeView( QWidget * parent ) :
    QTreeView( parent ),
    d( new Private( this ) )
{

  setSelectionMode( QAbstractItemView::SingleSelection );
  d->init();
}

EntityTreeView::EntityTreeView( KXMLGUIClient *xmlGuiClient, QWidget * parent ) :
    QTreeView( parent ),
    d( new Private( this ) )
{
  d->xmlGuiClient = xmlGuiClient;
  d->init();
}

EntityTreeView::~EntityTreeView()
{
  delete d;
}

void EntityTreeView::setModel( QAbstractItemModel * model )
{
  QTreeView::setModel( model );
  header()->setStretchLastSection( true );

  connect( selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ),
           this, SLOT( itemCurrentChanged( const QModelIndex& ) ) );
}

void EntityTreeView::dragMoveEvent( QDragMoveEvent * event )
{
  QModelIndex index = indexAt( event->pos() );
  if ( d->dragOverIndex != index ) {
    d->dragExpandTimer.stop();
    if ( index.isValid() && !isExpanded( index ) && itemsExpandable() ) {
      d->dragExpandTimer.start( QApplication::startDragTime() );
      d->dragOverIndex = index;
    }
  }

  // Check if the collection under the cursor accepts this data type
  Collection col = model()->data( index, EntityTreeModel::CollectionRole ).value<Collection>();
  if (!col.isValid())
  {
    Item item = model()->data( index, EntityTreeModel::ItemRole ).value<Item>();
    if (item.isValid())
    {
      col = model()->data( index.parent(), EntityTreeModel::CollectionRole ).value<Collection>();
    }
  }
  if ( col.isValid() )
  {
    QStringList supportedContentTypes = col.contentMimeTypes();
    const QMimeData *data = event->mimeData();
    KUrl::List urls = KUrl::List::fromMimeData( data );
    foreach( const KUrl &url, urls ) {
      const Collection collection = Collection::fromUrl( url );
      if ( collection.isValid() ) {
        if ( !supportedContentTypes.contains( Collection::mimeType() ) )
          break;

        // Check if we don't try to drop on one of the children
        if ( d->hasParent( index, collection.id() ) )
          break;
      } else { // This is an item.
        QString type = url.queryItems()[ QString::fromLatin1( "type" )];
        if ( !supportedContentTypes.contains( type ) )
          break;
      }
      // All urls are supported. process the event.
      QTreeView::dragMoveEvent( event );
      return;
    }
  }

  event->setDropAction( Qt::IgnoreAction );
  return;
}

void EntityTreeView::dragLeaveEvent( QDragLeaveEvent * event )
{
  d->dragExpandTimer.stop();
  d->dragOverIndex = QModelIndex();
  QTreeView::dragLeaveEvent( event );
}

void EntityTreeView::dropEvent( QDropEvent * event )
{
  d->dragExpandTimer.stop();
  d->dragOverIndex = QModelIndex();

  QModelIndexList idxs = selectedIndexes();

  QMenu popup( this );
  QAction* moveDropAction;
  // TODO If possible, hide unavailable actions ...
  // Use the model to determine if a move is ok.
//   if (...)
//   {
  moveDropAction = popup.addAction( KIcon( QString::fromLatin1( "edit-rename" ) ), i18n( "&Move here" ) );
//   }

  //TODO: If dropping on one of the selectedIndexes, just return.
  // open a context menu offering different drop actions (move, copy and cancel)
  QAction* copyDropAction = popup.addAction( KIcon( QString::fromLatin1( "edit-copy" ) ), i18n( "&Copy here" ) );
  popup.addSeparator();
  popup.addAction( KIcon( QString::fromLatin1( "process-stop" ) ), i18n( "Cancel" ) );

  QAction *activatedAction = popup.exec( QCursor::pos() );

  if ( activatedAction == moveDropAction ) {
    event->setDropAction( Qt::MoveAction );
  } else if ( activatedAction == copyDropAction ) {
    event->setDropAction( Qt::CopyAction );
  }
  // TODO: Handle link action.
  else return;

  QTreeView::dropEvent( event );
}

void EntityTreeView::contextMenuEvent( QContextMenuEvent * event )
{
  if ( !d->xmlGuiClient )
    return;

  const QModelIndex index = indexAt( event->pos() );

  QMenu *popup = 0;

  // check if the index under the cursor is a collection or item
  const Item item = model()->data( index, EntityTreeModel::ItemRole ).value<Item>();
  if ( item.isValid() )
    popup = static_cast<QMenu*>( d->xmlGuiClient->factory()->container(
                                 QLatin1String( "akonadi_itemview_contextmenu" ), d->xmlGuiClient ) );
  else
    popup = static_cast<QMenu*>( d->xmlGuiClient->factory()->container(
                                 QLatin1String( "akonadi_collectionview_contextmenu" ), d->xmlGuiClient ) );
  if ( popup )
    popup->exec( event->globalPos() );
}

void EntityTreeView::setXmlGuiClient( KXMLGUIClient * xmlGuiClient )
{
  d->xmlGuiClient = xmlGuiClient;
}

#include "entitytreeview.moc"
<|endoftext|>


<commit_before>/*
 * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __MEM_RUBY_SLICC_INTERFACE_RUBYSLICC_COMPONENTMAPPINGS_HH__
#define __MEM_RUBY_SLICC_INTERFACE_RUBYSLICC_COMPONENTMAPPINGS_HH__

#include "mem/protocol/GenericMachineType.hh"
#include "mem/protocol/MachineType.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/common/Global.hh"
#include "mem/ruby/common/NetDest.hh"
#include "mem/ruby/common/Set.hh"
#include "mem/ruby/system/DirectoryMemory.hh"
#include "mem/ruby/system/MachineID.hh"
#include "mem/ruby/system/NodeID.hh"

#ifdef MACHINETYPE_L1Cache
#define MACHINETYPE_L1CACHE_ENUM MachineType_L1Cache
#else
#define MACHINETYPE_L1CACHE_ENUM MachineType_NUM
#endif

#ifdef MACHINETYPE_L2Cache
#define MACHINETYPE_L2CACHE_ENUM MachineType_L2Cache
#else
#define MACHINETYPE_L2CACHE_ENUM MachineType_NUM
#endif

#ifdef MACHINETYPE_L3Cache
#define MACHINETYPE_L3CACHE_ENUM MachineType_L3Cache
#else
#define MACHINETYPE_L3CACHE_ENUM MachineType_NUM
#endif

#ifdef MACHINETYPE_DMA
#define MACHINETYPE_DMA_ENUM MachineType_DMA
#else
#define MACHINETYPE_DMA_ENUM MachineType_NUM
#endif

// used to determine the home directory
// returns a value between 0 and total_directories_within_the_system
inline NodeID
map_Address_to_DirectoryNode(const Address& addr)
{
    return DirectoryMemory::mapAddressToDirectoryVersion(addr);
}

// used to determine the home directory
// returns a value between 0 and total_directories_within_the_system
inline MachineID
map_Address_to_Directory(const Address &addr)
{
    MachineID mach =
        {MachineType_Directory, map_Address_to_DirectoryNode(addr)};
    return mach;
}

inline MachineID
map_Address_to_DMA(const Address & addr)
{
    MachineID dma = {MACHINETYPE_DMA_ENUM, 0};
    return dma;
}

inline NetDest
broadcast(MachineType type)
{
    NetDest dest;
    for (int i = 0; i < MachineType_base_count(type); i++) {
        MachineID mach = {type, i};
        dest.add(mach);
    }
    return dest;
}

inline MachineID
mapAddressToRange(const Address & addr, MachineType type, int low_bit,
                  int num_bits)
{
    MachineID mach = {type, 0};
    if (num_bits == 0)
        return mach;
    mach.num = addr.bitSelect(low_bit, low_bit + num_bits - 1);
    return mach;
}

inline NodeID
machineIDToNodeID(MachineID machID)
{
    return machID.num;
}

inline MachineType
machineIDToMachineType(MachineID machID)
{
    return machID.type;
}

inline NodeID
L1CacheMachIDToProcessorNum(MachineID machID)
{
    assert(machID.type == MachineType_L1Cache);
    return machID.num;
}

inline MachineID
getL1MachineID(NodeID L1RubyNode)
{
    MachineID mach = {MACHINETYPE_L1CACHE_ENUM, L1RubyNode};
    return mach;
}

inline GenericMachineType
ConvertMachToGenericMach(MachineType machType)
{
    if (machType == MACHINETYPE_L1CACHE_ENUM)
        return GenericMachineType_L1Cache;

    if (machType == MACHINETYPE_L2CACHE_ENUM)
        return GenericMachineType_L2Cache;

    if (machType == MACHINETYPE_L3CACHE_ENUM)
        return GenericMachineType_L3Cache;

    if (machType == MachineType_Directory)
        return GenericMachineType_Directory;

    panic("cannot convert to a GenericMachineType");
}

inline int
machineCount(MachineType machType)
{
    return MachineType_base_count(machType);
}

#endif  // __MEM_RUBY_SLICC_INTERFACE_COMPONENTMAPPINGS_HH__
<commit_msg>Ruby: minor bugfix, line did not adhere to some macro usage conventions.<commit_after>/*
 * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __MEM_RUBY_SLICC_INTERFACE_RUBYSLICC_COMPONENTMAPPINGS_HH__
#define __MEM_RUBY_SLICC_INTERFACE_RUBYSLICC_COMPONENTMAPPINGS_HH__

#include "mem/protocol/GenericMachineType.hh"
#include "mem/protocol/MachineType.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/common/Global.hh"
#include "mem/ruby/common/NetDest.hh"
#include "mem/ruby/common/Set.hh"
#include "mem/ruby/system/DirectoryMemory.hh"
#include "mem/ruby/system/MachineID.hh"
#include "mem/ruby/system/NodeID.hh"

#ifdef MACHINETYPE_L1Cache
#define MACHINETYPE_L1CACHE_ENUM MachineType_L1Cache
#else
#define MACHINETYPE_L1CACHE_ENUM MachineType_NUM
#endif

#ifdef MACHINETYPE_L2Cache
#define MACHINETYPE_L2CACHE_ENUM MachineType_L2Cache
#else
#define MACHINETYPE_L2CACHE_ENUM MachineType_NUM
#endif

#ifdef MACHINETYPE_L3Cache
#define MACHINETYPE_L3CACHE_ENUM MachineType_L3Cache
#else
#define MACHINETYPE_L3CACHE_ENUM MachineType_NUM
#endif

#ifdef MACHINETYPE_DMA
#define MACHINETYPE_DMA_ENUM MachineType_DMA
#else
#define MACHINETYPE_DMA_ENUM MachineType_NUM
#endif

// used to determine the home directory
// returns a value between 0 and total_directories_within_the_system
inline NodeID
map_Address_to_DirectoryNode(const Address& addr)
{
    return DirectoryMemory::mapAddressToDirectoryVersion(addr);
}

// used to determine the home directory
// returns a value between 0 and total_directories_within_the_system
inline MachineID
map_Address_to_Directory(const Address &addr)
{
    MachineID mach =
        {MachineType_Directory, map_Address_to_DirectoryNode(addr)};
    return mach;
}

inline MachineID
map_Address_to_DMA(const Address & addr)
{
    MachineID dma = {MACHINETYPE_DMA_ENUM, 0};
    return dma;
}

inline NetDest
broadcast(MachineType type)
{
    NetDest dest;
    for (int i = 0; i < MachineType_base_count(type); i++) {
        MachineID mach = {type, i};
        dest.add(mach);
    }
    return dest;
}

inline MachineID
mapAddressToRange(const Address & addr, MachineType type, int low_bit,
                  int num_bits)
{
    MachineID mach = {type, 0};
    if (num_bits == 0)
        return mach;
    mach.num = addr.bitSelect(low_bit, low_bit + num_bits - 1);
    return mach;
}

inline NodeID
machineIDToNodeID(MachineID machID)
{
    return machID.num;
}

inline MachineType
machineIDToMachineType(MachineID machID)
{
    return machID.type;
}

inline NodeID
L1CacheMachIDToProcessorNum(MachineID machID)
{
    assert(machID.type == MACHINETYPE_L1CACHE_ENUM);
    return machID.num;
}

inline MachineID
getL1MachineID(NodeID L1RubyNode)
{
    MachineID mach = {MACHINETYPE_L1CACHE_ENUM, L1RubyNode};
    return mach;
}

inline GenericMachineType
ConvertMachToGenericMach(MachineType machType)
{
    if (machType == MACHINETYPE_L1CACHE_ENUM)
        return GenericMachineType_L1Cache;

    if (machType == MACHINETYPE_L2CACHE_ENUM)
        return GenericMachineType_L2Cache;

    if (machType == MACHINETYPE_L3CACHE_ENUM)
        return GenericMachineType_L3Cache;

    if (machType == MachineType_Directory)
        return GenericMachineType_Directory;

    panic("cannot convert to a GenericMachineType");
}

inline int
machineCount(MachineType machType)
{
    return MachineType_base_count(machType);
}

#endif  // __MEM_RUBY_SLICC_INTERFACE_COMPONENTMAPPINGS_HH__
<|endoftext|>


<commit_before>//===- CodeEmitterGen.cpp - Code Emitter Generator ------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// CodeEmitterGen uses the descriptions of instructions and their fields to
// construct an automated code emitter: a function that, given a MachineInstr,
// returns the (currently, 32-bit unsigned) value of the instruction.
//
//===----------------------------------------------------------------------===//

#include "CodeEmitterGen.h"
#include "CodeGenTarget.h"
#include "Record.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include <map>
using namespace llvm;

// FIXME: Somewhat hackish to use a command line option for this. There should
// be a CodeEmitter class in the Target.td that controls this sort of thing
// instead.
static cl::opt<bool>
MCEmitter("mc-emitter",
          cl::desc("Generate CodeEmitter for use with the MC library."),
          cl::init(false));

void CodeEmitterGen::reverseBits(std::vector<Record*> &Insts) {
  for (std::vector<Record*>::iterator I = Insts.begin(), E = Insts.end();
       I != E; ++I) {
    Record *R = *I;
    if (R->getValueAsString("Namespace") == "TargetOpcode")
      continue;

    BitsInit *BI = R->getValueAsBitsInit("Inst");

    unsigned numBits = BI->getNumBits();
    BitsInit *NewBI = new BitsInit(numBits);
    for (unsigned bit = 0, end = numBits / 2; bit != end; ++bit) {
      unsigned bitSwapIdx = numBits - bit - 1;
      Init *OrigBit = BI->getBit(bit);
      Init *BitSwap = BI->getBit(bitSwapIdx);
      NewBI->setBit(bit, BitSwap);
      NewBI->setBit(bitSwapIdx, OrigBit);
    }
    if (numBits % 2) {
      unsigned middle = (numBits + 1) / 2;
      NewBI->setBit(middle, BI->getBit(middle));
    }

    // Update the bits in reversed order so that emitInstrOpBits will get the
    // correct endianness.
    R->getValue("Inst")->setValue(NewBI);
  }
}

// If the VarBitInit at position 'bit' matches the specified variable then
// return the variable bit position.  Otherwise return -1.
int CodeEmitterGen::getVariableBit(const std::string &VarName,
                                   BitsInit *BI, int bit) {
  if (VarBitInit *VBI = dynamic_cast<VarBitInit*>(BI->getBit(bit)))
    if (VarInit *VI = dynamic_cast<VarInit*>(VBI->getVariable()))
      if (VI->getName() == VarName)
        return VBI->getBitNum();

  return -1;
}

void CodeEmitterGen::
AddCodeToMergeInOperand(Record *R, BitsInit *BI, const std::string &VarName,
                        unsigned &NumberedOp,
                        std::string &Case, CodeGenTarget &Target) {
  CodeGenInstruction &CGI = Target.getInstruction(R);

  // Determine if VarName actually contributes to the Inst encoding.
  int bit = BI->getNumBits()-1;

  // Scan for a bit that this contributed to.
  for (; bit >= 0; ) {
    if (getVariableBit(VarName, BI, bit) != -1)
      break;

    --bit;
  }

  // If we found no bits, ignore this value, otherwise emit the call to get the
  // operand encoding.
  if (bit < 0) return;

  // If the operand matches by name, reference according to that
  // operand number. Non-matching operands are assumed to be in
  // order.
  unsigned OpIdx;
  if (CGI.Operands.hasOperandNamed(VarName, OpIdx)) {
    // Get the machine operand number for the indicated operand.
    OpIdx = CGI.Operands[OpIdx].MIOperandNo;
    assert(!CGI.Operands.isFlatOperandNotEmitted(OpIdx) &&
           "Explicitly used operand also marked as not emitted!");
  } else {
    /// If this operand is not supposed to be emitted by the
    /// generated emitter, skip it.
    while (CGI.Operands.isFlatOperandNotEmitted(NumberedOp))
      ++NumberedOp;
    OpIdx = NumberedOp++;
  }

  std::pair<unsigned, unsigned> SO = CGI.Operands.getSubOperandNumber(OpIdx);
  std::string &EncoderMethodName = CGI.Operands[SO.first].EncoderMethodName;

  // If the source operand has a custom encoder, use it. This will
  // get the encoding for all of the suboperands.
  if (!EncoderMethodName.empty()) {
    // A custom encoder has all of the information for the
    // sub-operands, if there are more than one, so only
    // query the encoder once per source operand.
    if (SO.second == 0) {
      Case += "      // op: " + VarName + "\n" +
              "      op = " + EncoderMethodName + "(MI, " + utostr(OpIdx);
      if (MCEmitter)
        Case += ", Fixups";
      Case += ");\n";
    }
  } else {
    Case += "      // op: " + VarName + "\n" +
      "      op = getMachineOpValue(MI, MI.getOperand(" + utostr(OpIdx) + ")";
    if (MCEmitter)
      Case += ", Fixups";
    Case += ");\n";
  }

  for (; bit >= 0; ) {
    int varBit = getVariableBit(VarName, BI, bit);

    // If this bit isn't from a variable, skip it.
    if (varBit == -1) {
      --bit;
      continue;
    }

    // Figure out the consecutive range of bits covered by this operand, in
    // order to generate better encoding code.
    int beginInstBit = bit;
    int beginVarBit = varBit;
    int N = 1;
    for (--bit; bit >= 0;) {
      varBit = getVariableBit(VarName, BI, bit);
      if (varBit == -1 || varBit != (beginVarBit - N)) break;
      ++N;
      --bit;
    }

    unsigned opMask = ~0U >> (32-N);
    int opShift = beginVarBit - N + 1;
    opMask <<= opShift;
    opShift = beginInstBit - beginVarBit;

    if (opShift > 0) {
      Case += "      Value |= (op & " + utostr(opMask) + "U) << " +
              itostr(opShift) + ";\n";
    } else if (opShift < 0) {
      Case += "      Value |= (op & " + utostr(opMask) + "U) >> " +
              itostr(-opShift) + ";\n";
    } else {
      Case += "      Value |= op & " + utostr(opMask) + "U;\n";
    }
  }
}

std::string CodeEmitterGen::getInstructionCase(Record *R,
                                               CodeGenTarget &Target) {
  std::string Case;

  BitsInit *BI = R->getValueAsBitsInit("Inst");
  const std::vector<RecordVal> &Vals = R->getValues();
  unsigned NumberedOp = 0;

  // Loop over all of the fields in the instruction, determining which are the
  // operands to the instruction.
  for (unsigned i = 0, e = Vals.size(); i != e; ++i) {
    // Ignore fixed fields in the record, we're looking for values like:
    //    bits<5> RST = { ?, ?, ?, ?, ? };
    if (Vals[i].getPrefix() || Vals[i].getValue()->isComplete())
      continue;

    AddCodeToMergeInOperand(R, BI, Vals[i].getName(), NumberedOp, Case, Target);
  }

  std::string PostEmitter = R->getValueAsString("PostEncoderMethod");
  if (!PostEmitter.empty())
    Case += "      Value = " + PostEmitter + "(MI, Value);\n";

  return Case;
}

void CodeEmitterGen::run(raw_ostream &o) {
  CodeGenTarget Target(Records);
  std::vector<Record*> Insts = Records.getAllDerivedDefinitions("Instruction");

  // For little-endian instruction bit encodings, reverse the bit order
  if (Target.isLittleEndianEncoding()) reverseBits(Insts);

  EmitSourceFileHeader("Machine Code Emitter", o);
  std::string Namespace = Insts[0]->getValueAsString("Namespace") + "::";

  const std::vector<const CodeGenInstruction*> &NumberedInstructions =
    Target.getInstructionsByEnumValue();

  // Emit function declaration
  o << "unsigned " << Target.getName();
  if (MCEmitter)
    o << "MCCodeEmitter::getBinaryCodeForInstr(const MCInst &MI,\n"
      << "    SmallVectorImpl<MCFixup> &Fixups) const {\n";
  else
    o << "CodeEmitter::getBinaryCodeForInstr(const MachineInstr &MI) const {\n";

  // Emit instruction base values
  o << "  static const unsigned InstBits[] = {\n";
  for (std::vector<const CodeGenInstruction*>::const_iterator
          IN = NumberedInstructions.begin(),
          EN = NumberedInstructions.end();
       IN != EN; ++IN) {
    const CodeGenInstruction *CGI = *IN;
    Record *R = CGI->TheDef;

    if (R->getValueAsString("Namespace") == "TargetOpcode") {
      o << "    0U,\n";
      continue;
    }

    BitsInit *BI = R->getValueAsBitsInit("Inst");

    // Start by filling in fixed values.
    unsigned Value = 0;
    for (unsigned i = 0, e = BI->getNumBits(); i != e; ++i) {
      if (BitInit *B = dynamic_cast<BitInit*>(BI->getBit(e-i-1)))
        Value |= B->getValue() << (e-i-1);
    }
    o << "    " << Value << "U," << '\t' << "// " << R->getName() << "\n";
  }
  o << "    0U\n  };\n";

  // Map to accumulate all the cases.
  std::map<std::string, std::vector<std::string> > CaseMap;

  // Construct all cases statement for each opcode
  for (std::vector<Record*>::iterator IC = Insts.begin(), EC = Insts.end();
        IC != EC; ++IC) {
    Record *R = *IC;
    if (R->getValueAsString("Namespace") == "TargetOpcode")
      continue;
    const std::string &InstName = R->getName();
    std::string Case = getInstructionCase(R, Target);

    CaseMap[Case].push_back(InstName);
  }

  // Emit initial function code
  o << "  const unsigned opcode = MI.getOpcode();\n"
    << "  unsigned Value = InstBits[opcode];\n"
    << "  unsigned op = 0;\n"
    << "  (void)op;  // suppress warning\n"
    << "  switch (opcode) {\n";

  // Emit each case statement
  std::map<std::string, std::vector<std::string> >::iterator IE, EE;
  for (IE = CaseMap.begin(), EE = CaseMap.end(); IE != EE; ++IE) {
    const std::string &Case = IE->first;
    std::vector<std::string> &InstList = IE->second;

    for (int i = 0, N = InstList.size(); i < N; i++) {
      if (i) o << "\n";
      o << "    case " << Namespace << InstList[i]  << ":";
    }
    o << " {\n";
    o << Case;
    o << "      break;\n"
      << "    }\n";
  }

  // Default case: unhandled opcode
  o << "  default:\n"
    << "    std::string msg;\n"
    << "    raw_string_ostream Msg(msg);\n"
    << "    Msg << \"Not supported instr: \" << MI;\n"
    << "    report_fatal_error(Msg.str());\n"
    << "  }\n"
    << "  return Value;\n"
    << "}\n\n";
}
<commit_msg>Tidy up a bit.<commit_after>//===- CodeEmitterGen.cpp - Code Emitter Generator ------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// CodeEmitterGen uses the descriptions of instructions and their fields to
// construct an automated code emitter: a function that, given a MachineInstr,
// returns the (currently, 32-bit unsigned) value of the instruction.
//
//===----------------------------------------------------------------------===//

#include "CodeEmitterGen.h"
#include "CodeGenTarget.h"
#include "Record.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include <map>
using namespace llvm;

// FIXME: Somewhat hackish to use a command line option for this. There should
// be a CodeEmitter class in the Target.td that controls this sort of thing
// instead.
static cl::opt<bool>
MCEmitter("mc-emitter",
          cl::desc("Generate CodeEmitter for use with the MC library."),
          cl::init(false));

void CodeEmitterGen::reverseBits(std::vector<Record*> &Insts) {
  for (std::vector<Record*>::iterator I = Insts.begin(), E = Insts.end();
       I != E; ++I) {
    Record *R = *I;
    if (R->getValueAsString("Namespace") == "TargetOpcode")
      continue;

    BitsInit *BI = R->getValueAsBitsInit("Inst");

    unsigned numBits = BI->getNumBits();
    BitsInit *NewBI = new BitsInit(numBits);
    for (unsigned bit = 0, end = numBits / 2; bit != end; ++bit) {
      unsigned bitSwapIdx = numBits - bit - 1;
      Init *OrigBit = BI->getBit(bit);
      Init *BitSwap = BI->getBit(bitSwapIdx);
      NewBI->setBit(bit, BitSwap);
      NewBI->setBit(bitSwapIdx, OrigBit);
    }
    if (numBits % 2) {
      unsigned middle = (numBits + 1) / 2;
      NewBI->setBit(middle, BI->getBit(middle));
    }

    // Update the bits in reversed order so that emitInstrOpBits will get the
    // correct endianness.
    R->getValue("Inst")->setValue(NewBI);
  }
}

// If the VarBitInit at position 'bit' matches the specified variable then
// return the variable bit position.  Otherwise return -1.
int CodeEmitterGen::getVariableBit(const std::string &VarName,
                                   BitsInit *BI, int bit) {
  if (VarBitInit *VBI = dynamic_cast<VarBitInit*>(BI->getBit(bit)))
    if (VarInit *VI = dynamic_cast<VarInit*>(VBI->getVariable()))
      if (VI->getName() == VarName)
        return VBI->getBitNum();

  return -1;
}

void CodeEmitterGen::
AddCodeToMergeInOperand(Record *R, BitsInit *BI, const std::string &VarName,
                        unsigned &NumberedOp,
                        std::string &Case, CodeGenTarget &Target) {
  CodeGenInstruction &CGI = Target.getInstruction(R);

  // Determine if VarName actually contributes to the Inst encoding.
  int bit = BI->getNumBits()-1;

  // Scan for a bit that this contributed to.
  for (; bit >= 0; ) {
    if (getVariableBit(VarName, BI, bit) != -1)
      break;

    --bit;
  }

  // If we found no bits, ignore this value, otherwise emit the call to get the
  // operand encoding.
  if (bit < 0) return;

  // If the operand matches by name, reference according to that
  // operand number. Non-matching operands are assumed to be in
  // order.
  unsigned OpIdx;
  if (CGI.Operands.hasOperandNamed(VarName, OpIdx)) {
    // Get the machine operand number for the indicated operand.
    OpIdx = CGI.Operands[OpIdx].MIOperandNo;
    assert(!CGI.Operands.isFlatOperandNotEmitted(OpIdx) &&
           "Explicitly used operand also marked as not emitted!");
  } else {
    /// If this operand is not supposed to be emitted by the
    /// generated emitter, skip it.
    while (CGI.Operands.isFlatOperandNotEmitted(NumberedOp))
      ++NumberedOp;
    OpIdx = NumberedOp++;
  }

  std::pair<unsigned, unsigned> SO = CGI.Operands.getSubOperandNumber(OpIdx);
  std::string &EncoderMethodName = CGI.Operands[SO.first].EncoderMethodName;

  // If the source operand has a custom encoder, use it. This will
  // get the encoding for all of the suboperands.
  if (!EncoderMethodName.empty()) {
    // A custom encoder has all of the information for the
    // sub-operands, if there are more than one, so only
    // query the encoder once per source operand.
    if (SO.second == 0) {
      Case += "      // op: " + VarName + "\n" +
              "      op = " + EncoderMethodName + "(MI, " + utostr(OpIdx);
      if (MCEmitter)
        Case += ", Fixups";
      Case += ");\n";
    }
  } else {
    Case += "      // op: " + VarName + "\n" +
      "      op = getMachineOpValue(MI, MI.getOperand(" + utostr(OpIdx) + ")";
    if (MCEmitter)
      Case += ", Fixups";
    Case += ");\n";
  }

  for (; bit >= 0; ) {
    int varBit = getVariableBit(VarName, BI, bit);

    // If this bit isn't from a variable, skip it.
    if (varBit == -1) {
      --bit;
      continue;
    }

    // Figure out the consecutive range of bits covered by this operand, in
    // order to generate better encoding code.
    int beginInstBit = bit;
    int beginVarBit = varBit;
    int N = 1;
    for (--bit; bit >= 0;) {
      varBit = getVariableBit(VarName, BI, bit);
      if (varBit == -1 || varBit != (beginVarBit - N)) break;
      ++N;
      --bit;
    }

    unsigned opMask = ~0U >> (32-N);
    int opShift = beginVarBit - N + 1;
    opMask <<= opShift;
    opShift = beginInstBit - beginVarBit;

    if (opShift > 0) {
      Case += "      Value |= (op & " + utostr(opMask) + "U) << " +
              itostr(opShift) + ";\n";
    } else if (opShift < 0) {
      Case += "      Value |= (op & " + utostr(opMask) + "U) >> " +
              itostr(-opShift) + ";\n";
    } else {
      Case += "      Value |= op & " + utostr(opMask) + "U;\n";
    }
  }
}

std::string CodeEmitterGen::getInstructionCase(Record *R,
                                               CodeGenTarget &Target) {
  std::string Case;

  BitsInit *BI = R->getValueAsBitsInit("Inst");
  const std::vector<RecordVal> &Vals = R->getValues();
  unsigned NumberedOp = 0;

  // Loop over all of the fields in the instruction, determining which are the
  // operands to the instruction.
  for (unsigned i = 0, e = Vals.size(); i != e; ++i) {
    // Ignore fixed fields in the record, we're looking for values like:
    //    bits<5> RST = { ?, ?, ?, ?, ? };
    if (Vals[i].getPrefix() || Vals[i].getValue()->isComplete())
      continue;

    AddCodeToMergeInOperand(R, BI, Vals[i].getName(), NumberedOp, Case, Target);
  }

  std::string PostEmitter = R->getValueAsString("PostEncoderMethod");
  if (!PostEmitter.empty())
    Case += "      Value = " + PostEmitter + "(MI, Value);\n";

  return Case;
}

void CodeEmitterGen::run(raw_ostream &o) {
  CodeGenTarget Target(Records);
  std::vector<Record*> Insts = Records.getAllDerivedDefinitions("Instruction");

  // For little-endian instruction bit encodings, reverse the bit order
  if (Target.isLittleEndianEncoding()) reverseBits(Insts);

  EmitSourceFileHeader("Machine Code Emitter", o);

  const std::vector<const CodeGenInstruction*> &NumberedInstructions =
    Target.getInstructionsByEnumValue();

  // Emit function declaration
  o << "unsigned " << Target.getName();
  if (MCEmitter)
    o << "MCCodeEmitter::getBinaryCodeForInstr(const MCInst &MI,\n"
      << "    SmallVectorImpl<MCFixup> &Fixups) const {\n";
  else
    o << "CodeEmitter::getBinaryCodeForInstr(const MachineInstr &MI) const {\n";

  // Emit instruction base values
  o << "  static const unsigned InstBits[] = {\n";
  for (std::vector<const CodeGenInstruction*>::const_iterator
          IN = NumberedInstructions.begin(),
          EN = NumberedInstructions.end();
       IN != EN; ++IN) {
    const CodeGenInstruction *CGI = *IN;
    Record *R = CGI->TheDef;

    if (R->getValueAsString("Namespace") == "TargetOpcode") {
      o << "    0U,\n";
      continue;
    }

    BitsInit *BI = R->getValueAsBitsInit("Inst");

    // Start by filling in fixed values.
    unsigned Value = 0;
    for (unsigned i = 0, e = BI->getNumBits(); i != e; ++i) {
      if (BitInit *B = dynamic_cast<BitInit*>(BI->getBit(e-i-1)))
        Value |= B->getValue() << (e-i-1);
    }
    o << "    " << Value << "U," << '\t' << "// " << R->getName() << "\n";
  }
  o << "    0U\n  };\n";

  // Map to accumulate all the cases.
  std::map<std::string, std::vector<std::string> > CaseMap;

  // Construct all cases statement for each opcode
  for (std::vector<Record*>::iterator IC = Insts.begin(), EC = Insts.end();
        IC != EC; ++IC) {
    Record *R = *IC;
    if (R->getValueAsString("Namespace") == "TargetOpcode")
      continue;
    const std::string &InstName = R->getValueAsString("Namespace") + "::"
      + R->getName();
    std::string Case = getInstructionCase(R, Target);

    CaseMap[Case].push_back(InstName);
  }

  // Emit initial function code
  o << "  const unsigned opcode = MI.getOpcode();\n"
    << "  unsigned Value = InstBits[opcode];\n"
    << "  unsigned op = 0;\n"
    << "  (void)op;  // suppress warning\n"
    << "  switch (opcode) {\n";

  // Emit each case statement
  std::map<std::string, std::vector<std::string> >::iterator IE, EE;
  for (IE = CaseMap.begin(), EE = CaseMap.end(); IE != EE; ++IE) {
    const std::string &Case = IE->first;
    std::vector<std::string> &InstList = IE->second;

    for (int i = 0, N = InstList.size(); i < N; i++) {
      if (i) o << "\n";
      o << "    case " << InstList[i]  << ":";
    }
    o << " {\n";
    o << Case;
    o << "      break;\n"
      << "    }\n";
  }

  // Default case: unhandled opcode
  o << "  default:\n"
    << "    std::string msg;\n"
    << "    raw_string_ostream Msg(msg);\n"
    << "    Msg << \"Not supported instr: \" << MI;\n"
    << "    report_fatal_error(Msg.str());\n"
    << "  }\n"
    << "  return Value;\n"
    << "}\n\n";
}
<|endoftext|>