<commit_before>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2021 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 Markus Pfeiffer
////////////////////////////////////////////////////////////////////////////////

#include "SimpleModifier.h"

#include "Aql/AqlValue.h"
#include "Aql/Collection.h"
#include "Aql/ExecutionEngine.h"
#include "Aql/ModificationExecutor.h"
#include "Aql/ModificationExecutorHelpers.h"
#include "Aql/OutputAqlItemRow.h"
#include "Aql/SharedQueryState.h"
#include "Basics/Common.h"
#include "Basics/StaticStrings.h"
#include "Basics/StringUtils.h"
#include "Basics/VelocyPackHelper.h"
#include "Basics/application-exit.h"
#include "Cluster/ServerState.h"
#include "VocBase/LogicalCollection.h"

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

using namespace arangodb;
using namespace arangodb::aql;
using namespace arangodb::aql::ModificationExecutorHelpers;
using namespace arangodb::basics;

template <class ModifierCompletion, typename Enable>
SimpleModifier<ModifierCompletion, Enable>::OutputIterator::OutputIterator(
    SimpleModifier<ModifierCompletion, Enable> const& modifier)
    : _modifier(modifier),
      _operationsIterator(modifier._operations.begin()),
      _resultsIterator(modifier.getResultsIterator()) {}

template <typename ModifierCompletion, typename Enable>
typename SimpleModifier<ModifierCompletion, Enable>::OutputIterator&
SimpleModifier<ModifierCompletion, Enable>::OutputIterator::next() {
  // Only move results on if there has been a document
  // submitted to the transaction
  if (_operationsIterator->first == ModifierOperationType::ReturnIfAvailable) {
    ++_resultsIterator;
  }
  ++_operationsIterator;
  return *this;
}

template <class ModifierCompletion, typename Enable>
typename SimpleModifier<ModifierCompletion, Enable>::OutputIterator&
SimpleModifier<ModifierCompletion, Enable>::OutputIterator::operator++() {
  return next();
}

template <class ModifierCompletion, typename Enable>
bool SimpleModifier<ModifierCompletion, Enable>::OutputIterator::operator!=(
    SimpleModifier<ModifierCompletion, Enable>::OutputIterator const& other) const
    noexcept {
  return _operationsIterator != other._operationsIterator;
}

template <class ModifierCompletion, typename Enable>
ModifierOutput SimpleModifier<ModifierCompletion, Enable>::OutputIterator::operator*() const {
  switch (_operationsIterator->first) {
    case ModifierOperationType::ReturnIfAvailable: {
      // This means the results slice is relevant
      if (_modifier.resultAvailable()) {
        VPackSlice elm = *_resultsIterator;
        bool error = VelocyPackHelper::getBooleanValue(elm, StaticStrings::Error, false);

        if (error) {
          return ModifierOutput{_operationsIterator->second, ModifierOutput::Type::SkipRow};
        } else {
          return ModifierOutput{
              _operationsIterator->second, ModifierOutput::Type::ReturnIfRequired,
              ModificationExecutorHelpers::getDocumentOrNull(elm, StaticStrings::Old),
              ModificationExecutorHelpers::getDocumentOrNull(elm, StaticStrings::New)};
        }
      } else {
        return ModifierOutput{_operationsIterator->second, ModifierOutput::Type::CopyRow};
      }
      case ModifierOperationType::CopyRow:
        return ModifierOutput{_operationsIterator->second, ModifierOutput::Type::CopyRow};
      case ModifierOperationType::SkipRow:
        return ModifierOutput{_operationsIterator->second, ModifierOutput::Type::SkipRow};
    }
  }

  // Shut up compiler
  TRI_ASSERT(false);
  return ModifierOutput{_operationsIterator->second, ModifierOutput::Type::SkipRow};
}

template <class ModifierCompletion, typename Enable>
typename SimpleModifier<ModifierCompletion, Enable>::OutputIterator
SimpleModifier<ModifierCompletion, Enable>::OutputIterator::begin() const {
  return SimpleModifier<ModifierCompletion, Enable>::OutputIterator(this->_modifier);
}

template <class ModifierCompletion, typename Enable>
typename SimpleModifier<ModifierCompletion, Enable>::OutputIterator
SimpleModifier<ModifierCompletion, Enable>::OutputIterator::end() const {
  auto it = SimpleModifier<ModifierCompletion, Enable>::OutputIterator(this->_modifier);
  it._operationsIterator = _modifier._operations.end();

  return it;
}

template <typename ModifierCompletion, typename Enable>
void SimpleModifier<ModifierCompletion, Enable>::checkException() const {
  throwOperationResultException(_infos, std::get<OperationResult>(_results));
}

template <typename ModifierCompletion, typename Enable>
void SimpleModifier<ModifierCompletion, Enable>::resetResult() noexcept {
  std::lock_guard<std::mutex> guard(_resultMutex);
  _results = NoResult{};
}

template <typename ModifierCompletion, typename Enable>
void SimpleModifier<ModifierCompletion, Enable>::reset() {
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
  {
    std::unique_lock<std::mutex> guard(_resultMutex, std::try_to_lock);
    TRI_ASSERT(guard.owns_lock());
    TRI_ASSERT(!std::holds_alternative<Waiting>(_results));
  }
#endif
  _accumulator.reset();
  _operations.clear();
  resetResult();
}

template <typename ModifierCompletion, typename Enable>
void SimpleModifier<ModifierCompletion, Enable>::accumulate(InputAqlItemRow& row) {
  auto result = _completion.accumulate(_accumulator, row);
  _operations.push_back({result, row});
}

template <typename ModifierCompletion, typename Enable>
ExecutionState SimpleModifier<ModifierCompletion, Enable>::transact(transaction::Methods& trx) {
  std::unique_lock<std::mutex> guard(_resultMutex);
  if (std::holds_alternative<Waiting>(_results)) {
    return ExecutionState::WAITING;
  } else if (std::holds_alternative<OperationResult>(_results)) {
    return ExecutionState::DONE;
  } else if (auto* ex = std::get_if<std::exception_ptr>(&_results); ex != nullptr) {
    std::rethrow_exception(*ex);
  } else {
    TRI_ASSERT(std::holds_alternative<NoResult>(_results));
  }

  _results = NoResult{};

  auto result = _completion.transact(trx, _accumulator.closeAndGetContents());

  if (result.isReady()) {
    _results = std::move(result.get());
    return ExecutionState::DONE;
  }

  _results = Waiting{};

  TRI_ASSERT(!ServerState::instance()->isSingleServer());
  TRI_ASSERT(_infos.engine() != nullptr);
  TRI_ASSERT(_infos.engine()->sharedState() != nullptr);

  // The guard has to be unlocked before "thenValue" is called, otherwise locking
  // the mutex there will cause a deadlock if the result is already available.
  guard.unlock();

  auto self = this->shared_from_this();
  std::move(result).thenFinal([self, sqs = _infos.engine()->sharedState()](futures::Try<OperationResult>&& opRes) {
    sqs->executeAndWakeup([&]() noexcept {
      std::unique_lock<std::mutex> guard(self->_resultMutex);
      try {
        TRI_ASSERT(std::holds_alternative<Waiting>(self->_results));
        if (std::holds_alternative<Waiting>(self->_results)) {
          // get() will throw if opRes holds an exception, which is intended.
          self->_results = std::move(opRes.get());
        } else {
          // This can never happen.
          using namespace std::string_literals;
          auto state =
              std::visit(overload{[&](NoResult) { return "NoResults"s; },
                                  [&](Waiting) { return "Waiting"s; },
                                  [&](OperationResult const&) {
                                    return "Result"s;
                                  },
                                  [&](std::exception_ptr const& ep) {
                                    auto what = std::string{};
                                    try {
                                      std::rethrow_exception(ep);
                                    } catch (std::exception const& ex) {
                                      what = ex.what();
                                    } catch (...) {
                                      // Exception unknown, give up immediately.
                                      LOG_TOPIC("4646a", FATAL, Logger::AQL) << "Caught an exception while handling another one, giving up.";
                                      FATAL_ERROR_ABORT();
                                    }
                                    return StringUtils::concatT("Exception: ", what);
                                  }},
                         self->_results);
          auto message = StringUtils::concatT(
              "Unexpected state when reporting modification result, expected "
              "'Waiting' but got: ",
              state);
          LOG_TOPIC("1f48d", ERR, Logger::AQL) << message;
          if (std::holds_alternative<std::exception_ptr>(self->_results)) {
            // Avoid overwriting an exception with another exception.
            LOG_TOPIC("2d310", FATAL, Logger::AQL)
                << "Caught an exception while handling another one, giving up.";
            FATAL_ERROR_ABORT();
          }
          THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL_AQL, std::move(message));
        }
      } catch(...) {
        auto exptr = std::current_exception();
        self->_results = exptr;
      }
      return true;
    });
  });

  return ExecutionState::WAITING;
}

template <typename ModifierCompletion, typename Enable>
size_t SimpleModifier<ModifierCompletion, Enable>::nrOfOperations() const {
  return _operations.size();
}

template <typename ModifierCompletion, typename Enable>
size_t SimpleModifier<ModifierCompletion, Enable>::nrOfDocuments() const {
  return _accumulator.nrOfDocuments();
}

template <typename ModifierCompletion, typename Enable>
size_t SimpleModifier<ModifierCompletion, Enable>::nrOfResults() const {
  if (auto* res = std::get_if<OperationResult>(&_results);
      res != nullptr && res->hasSlice() && res->slice().isArray()) {
    return res->slice().length();
  } else {
    return 0;
  }
}

template <typename ModifierCompletion, typename Enable>
size_t SimpleModifier<ModifierCompletion, Enable>::nrOfErrors() const {
  size_t nrOfErrors{0};

  for (auto const& pair : std::get<OperationResult>(_results).countErrorCodes) {
    nrOfErrors += pair.second;
  }
  return nrOfErrors;
}

template <typename ModifierCompletion, typename Enable>
size_t SimpleModifier<ModifierCompletion, Enable>::nrOfWritesExecuted() const {
  return nrOfDocuments() - nrOfErrors();
}

template <typename ModifierCompletion, typename Enable>
size_t SimpleModifier<ModifierCompletion, Enable>::nrOfWritesIgnored() const {
  return nrOfErrors();
}

template <typename ModifierCompletion, typename Enable>
ModificationExecutorInfos& SimpleModifier<ModifierCompletion, Enable>::getInfos() const
    noexcept {
  return _infos;
}

template <typename ModifierCompletion, typename Enable>
size_t SimpleModifier<ModifierCompletion, Enable>::getBatchSize() const noexcept {
  return _batchSize;
}

template <typename ModifierCompletion, typename Enable>
bool SimpleModifier<ModifierCompletion, Enable>::resultAvailable() const {
  return (nrOfDocuments() > 0 && !_infos._options.silent);
}

template <typename ModifierCompletion, typename Enable>
VPackArrayIterator SimpleModifier<ModifierCompletion, Enable>::getResultsIterator() const {
  if (resultAvailable()) {
    auto const& results = std::get<OperationResult>(_results);
    TRI_ASSERT(results.hasSlice() && results.slice().isArray());
    return VPackArrayIterator{results.slice()};
  }
  return VPackArrayIterator(VPackArrayIterator::Empty{});
}

template <typename ModifierCompletion, typename Enable>
bool SimpleModifier<ModifierCompletion, Enable>::hasResultOrException() const noexcept {
  return std::visit(overload{
                        [](NoResult) { return false; },
                        [](Waiting) { return false; },
                        [](OperationResult const&) { return true; },
                        [](std::exception_ptr const&) { return true; },
                    },
                    _results);
}

template <typename ModifierCompletion, typename Enable>
bool SimpleModifier<ModifierCompletion, Enable>::hasNeitherResultNorOperationPending() const noexcept {
  return std::visit(overload{
                        [](NoResult) { return true; },
                        [](Waiting) { return false; },
                        [](OperationResult const&) { return false; },
                        [](std::exception_ptr const&) { return false; },
                    },
                    _results);
}

template class ::arangodb::aql::SimpleModifier<InsertModifierCompletion>;
template class ::arangodb::aql::SimpleModifier<RemoveModifierCompletion>;
template class ::arangodb::aql::SimpleModifier<UpdateReplaceModifierCompletion>;
<commit_msg>make AQL modification operations blocking again (#14795)<commit_after>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2021 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 Markus Pfeiffer
////////////////////////////////////////////////////////////////////////////////

#include "SimpleModifier.h"

#include "Aql/AqlValue.h"
#include "Aql/Collection.h"
#include "Aql/ExecutionEngine.h"
#include "Aql/ModificationExecutor.h"
#include "Aql/ModificationExecutorHelpers.h"
#include "Aql/OutputAqlItemRow.h"
#include "Aql/SharedQueryState.h"
#include "Basics/Common.h"
#include "Basics/StaticStrings.h"
#include "Basics/StringUtils.h"
#include "Basics/VelocyPackHelper.h"
#include "Basics/application-exit.h"
#include "Cluster/ServerState.h"
#include "VocBase/LogicalCollection.h"

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

using namespace arangodb;
using namespace arangodb::aql;
using namespace arangodb::aql::ModificationExecutorHelpers;
using namespace arangodb::basics;

template <class ModifierCompletion, typename Enable>
SimpleModifier<ModifierCompletion, Enable>::OutputIterator::OutputIterator(
    SimpleModifier<ModifierCompletion, Enable> const& modifier)
    : _modifier(modifier),
      _operationsIterator(modifier._operations.begin()),
      _resultsIterator(modifier.getResultsIterator()) {}

template <typename ModifierCompletion, typename Enable>
typename SimpleModifier<ModifierCompletion, Enable>::OutputIterator&
SimpleModifier<ModifierCompletion, Enable>::OutputIterator::next() {
  // Only move results on if there has been a document
  // submitted to the transaction
  if (_operationsIterator->first == ModifierOperationType::ReturnIfAvailable) {
    ++_resultsIterator;
  }
  ++_operationsIterator;
  return *this;
}

template <class ModifierCompletion, typename Enable>
typename SimpleModifier<ModifierCompletion, Enable>::OutputIterator&
SimpleModifier<ModifierCompletion, Enable>::OutputIterator::operator++() {
  return next();
}

template <class ModifierCompletion, typename Enable>
bool SimpleModifier<ModifierCompletion, Enable>::OutputIterator::operator!=(
    SimpleModifier<ModifierCompletion, Enable>::OutputIterator const& other) const
    noexcept {
  return _operationsIterator != other._operationsIterator;
}

template <class ModifierCompletion, typename Enable>
ModifierOutput SimpleModifier<ModifierCompletion, Enable>::OutputIterator::operator*() const {
  switch (_operationsIterator->first) {
    case ModifierOperationType::ReturnIfAvailable: {
      // This means the results slice is relevant
      if (_modifier.resultAvailable()) {
        VPackSlice elm = *_resultsIterator;
        bool error = VelocyPackHelper::getBooleanValue(elm, StaticStrings::Error, false);

        if (error) {
          return ModifierOutput{_operationsIterator->second, ModifierOutput::Type::SkipRow};
        } else {
          return ModifierOutput{
              _operationsIterator->second, ModifierOutput::Type::ReturnIfRequired,
              ModificationExecutorHelpers::getDocumentOrNull(elm, StaticStrings::Old),
              ModificationExecutorHelpers::getDocumentOrNull(elm, StaticStrings::New)};
        }
      } else {
        return ModifierOutput{_operationsIterator->second, ModifierOutput::Type::CopyRow};
      }
      case ModifierOperationType::CopyRow:
        return ModifierOutput{_operationsIterator->second, ModifierOutput::Type::CopyRow};
      case ModifierOperationType::SkipRow:
        return ModifierOutput{_operationsIterator->second, ModifierOutput::Type::SkipRow};
    }
  }

  // Shut up compiler
  TRI_ASSERT(false);
  return ModifierOutput{_operationsIterator->second, ModifierOutput::Type::SkipRow};
}

template <class ModifierCompletion, typename Enable>
typename SimpleModifier<ModifierCompletion, Enable>::OutputIterator
SimpleModifier<ModifierCompletion, Enable>::OutputIterator::begin() const {
  return SimpleModifier<ModifierCompletion, Enable>::OutputIterator(this->_modifier);
}

template <class ModifierCompletion, typename Enable>
typename SimpleModifier<ModifierCompletion, Enable>::OutputIterator
SimpleModifier<ModifierCompletion, Enable>::OutputIterator::end() const {
  auto it = SimpleModifier<ModifierCompletion, Enable>::OutputIterator(this->_modifier);
  it._operationsIterator = _modifier._operations.end();

  return it;
}

template <typename ModifierCompletion, typename Enable>
void SimpleModifier<ModifierCompletion, Enable>::checkException() const {
  throwOperationResultException(_infos, std::get<OperationResult>(_results));
}

template <typename ModifierCompletion, typename Enable>
void SimpleModifier<ModifierCompletion, Enable>::resetResult() noexcept {
  std::lock_guard<std::mutex> guard(_resultMutex);
  _results = NoResult{};
}

template <typename ModifierCompletion, typename Enable>
void SimpleModifier<ModifierCompletion, Enable>::reset() {
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
  {
    std::unique_lock<std::mutex> guard(_resultMutex, std::try_to_lock);
    TRI_ASSERT(guard.owns_lock());
    TRI_ASSERT(!std::holds_alternative<Waiting>(_results));
  }
#endif
  _accumulator.reset();
  _operations.clear();
  resetResult();
}

template <typename ModifierCompletion, typename Enable>
void SimpleModifier<ModifierCompletion, Enable>::accumulate(InputAqlItemRow& row) {
  auto result = _completion.accumulate(_accumulator, row);
  _operations.push_back({result, row});
}

template <typename ModifierCompletion, typename Enable>
ExecutionState SimpleModifier<ModifierCompletion, Enable>::transact(transaction::Methods& trx) {
  std::unique_lock<std::mutex> guard(_resultMutex);
  if (std::holds_alternative<Waiting>(_results)) {
    return ExecutionState::WAITING;
  } else if (std::holds_alternative<OperationResult>(_results)) {
    return ExecutionState::DONE;
  } else if (auto* ex = std::get_if<std::exception_ptr>(&_results); ex != nullptr) {
    std::rethrow_exception(*ex);
  } else {
    TRI_ASSERT(std::holds_alternative<NoResult>(_results));
  }

  _results = NoResult{};

  auto result = _completion.transact(trx, _accumulator.closeAndGetContents());

  // we are currently waiting here for the `result` future to get
  // ready before we continue. this makes the AQL modification
  // operations blocking as in previous versions of ArangoDB.
  // TODO: fix this and make it truly non-blocking (requires to
  // fix some lifecycle issues for AQL queries first).
  result.wait();

  if (result.isReady()) {
    _results = std::move(result.get());
    return ExecutionState::DONE;
  }

  _results = Waiting{};

  TRI_ASSERT(!ServerState::instance()->isSingleServer());
  TRI_ASSERT(_infos.engine() != nullptr);
  TRI_ASSERT(_infos.engine()->sharedState() != nullptr);

  // The guard has to be unlocked before "thenValue" is called, otherwise locking
  // the mutex there will cause a deadlock if the result is already available.
  guard.unlock();

  auto self = this->shared_from_this();
  std::move(result).thenFinal([self, sqs = _infos.engine()->sharedState()](futures::Try<OperationResult>&& opRes) {
    sqs->executeAndWakeup([&]() noexcept {
      std::unique_lock<std::mutex> guard(self->_resultMutex);
      try {
        TRI_ASSERT(std::holds_alternative<Waiting>(self->_results));
        if (std::holds_alternative<Waiting>(self->_results)) {
          // get() will throw if opRes holds an exception, which is intended.
          self->_results = std::move(opRes.get());
        } else {
          // This can never happen.
          using namespace std::string_literals;
          auto state =
              std::visit(overload{[&](NoResult) { return "NoResults"s; },
                                  [&](Waiting) { return "Waiting"s; },
                                  [&](OperationResult const&) {
                                    return "Result"s;
                                  },
                                  [&](std::exception_ptr const& ep) {
                                    auto what = std::string{};
                                    try {
                                      std::rethrow_exception(ep);
                                    } catch (std::exception const& ex) {
                                      what = ex.what();
                                    } catch (...) {
                                      // Exception unknown, give up immediately.
                                      LOG_TOPIC("4646a", FATAL, Logger::AQL) << "Caught an exception while handling another one, giving up.";
                                      FATAL_ERROR_ABORT();
                                    }
                                    return StringUtils::concatT("Exception: ", what);
                                  }},
                         self->_results);
          auto message = StringUtils::concatT(
              "Unexpected state when reporting modification result, expected "
              "'Waiting' but got: ",
              state);
          LOG_TOPIC("1f48d", ERR, Logger::AQL) << message;
          if (std::holds_alternative<std::exception_ptr>(self->_results)) {
            // Avoid overwriting an exception with another exception.
            LOG_TOPIC("2d310", FATAL, Logger::AQL)
                << "Caught an exception while handling another one, giving up.";
            FATAL_ERROR_ABORT();
          }
          THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL_AQL, std::move(message));
        }
      } catch(...) {
        auto exptr = std::current_exception();
        self->_results = exptr;
      }
      return true;
    });
  });

  return ExecutionState::WAITING;
}

template <typename ModifierCompletion, typename Enable>
size_t SimpleModifier<ModifierCompletion, Enable>::nrOfOperations() const {
  return _operations.size();
}

template <typename ModifierCompletion, typename Enable>
size_t SimpleModifier<ModifierCompletion, Enable>::nrOfDocuments() const {
  return _accumulator.nrOfDocuments();
}

template <typename ModifierCompletion, typename Enable>
size_t SimpleModifier<ModifierCompletion, Enable>::nrOfResults() const {
  if (auto* res = std::get_if<OperationResult>(&_results);
      res != nullptr && res->hasSlice() && res->slice().isArray()) {
    return res->slice().length();
  } else {
    return 0;
  }
}

template <typename ModifierCompletion, typename Enable>
size_t SimpleModifier<ModifierCompletion, Enable>::nrOfErrors() const {
  size_t nrOfErrors{0};

  for (auto const& pair : std::get<OperationResult>(_results).countErrorCodes) {
    nrOfErrors += pair.second;
  }
  return nrOfErrors;
}

template <typename ModifierCompletion, typename Enable>
size_t SimpleModifier<ModifierCompletion, Enable>::nrOfWritesExecuted() const {
  return nrOfDocuments() - nrOfErrors();
}

template <typename ModifierCompletion, typename Enable>
size_t SimpleModifier<ModifierCompletion, Enable>::nrOfWritesIgnored() const {
  return nrOfErrors();
}

template <typename ModifierCompletion, typename Enable>
ModificationExecutorInfos& SimpleModifier<ModifierCompletion, Enable>::getInfos() const
    noexcept {
  return _infos;
}

template <typename ModifierCompletion, typename Enable>
size_t SimpleModifier<ModifierCompletion, Enable>::getBatchSize() const noexcept {
  return _batchSize;
}

template <typename ModifierCompletion, typename Enable>
bool SimpleModifier<ModifierCompletion, Enable>::resultAvailable() const {
  return (nrOfDocuments() > 0 && !_infos._options.silent);
}

template <typename ModifierCompletion, typename Enable>
VPackArrayIterator SimpleModifier<ModifierCompletion, Enable>::getResultsIterator() const {
  if (resultAvailable()) {
    auto const& results = std::get<OperationResult>(_results);
    TRI_ASSERT(results.hasSlice() && results.slice().isArray());
    return VPackArrayIterator{results.slice()};
  }
  return VPackArrayIterator(VPackArrayIterator::Empty{});
}

template <typename ModifierCompletion, typename Enable>
bool SimpleModifier<ModifierCompletion, Enable>::hasResultOrException() const noexcept {
  return std::visit(overload{
                        [](NoResult) { return false; },
                        [](Waiting) { return false; },
                        [](OperationResult const&) { return true; },
                        [](std::exception_ptr const&) { return true; },
                    },
                    _results);
}

template <typename ModifierCompletion, typename Enable>
bool SimpleModifier<ModifierCompletion, Enable>::hasNeitherResultNorOperationPending() const noexcept {
  return std::visit(overload{
                        [](NoResult) { return true; },
                        [](Waiting) { return false; },
                        [](OperationResult const&) { return false; },
                        [](std::exception_ptr const&) { return false; },
                    },
                    _results);
}

template class ::arangodb::aql::SimpleModifier<InsertModifierCompletion>;
template class ::arangodb::aql::SimpleModifier<RemoveModifierCompletion>;
template class ::arangodb::aql::SimpleModifier<UpdateReplaceModifierCompletion>;
<|endoftext|>


<commit_before>// @(#)root/gui:$Name:  $:$Id: TRootContextMenu.cxx,v 1.16 2006/08/22 18:27:38 rdm Exp $
// Author: Fons Rademakers   12/02/98

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

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TRootContextMenu                                                     //
//                                                                      //
// This class provides an interface to context sensitive popup menus.   //
// These menus pop up when the user hits the right mouse button, and    //
// are destroyed when the menu pops downs.                              //
// The picture below shows a canvas with a pop-up menu.                 //
//                                                                      //
//Begin_Html  End_Html                      //
//                                                                      //
// The picture below shows a canvas with a pop-up menu and a dialog box.//
//                                                                      //
//Begin_Html  End_Html                    //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TRootContextMenu.h"
#include "TROOT.h"
#include "TGClient.h"
#include "TList.h"
#include "TContextMenu.h"
#include "TMethod.h"
#include "TMethodArg.h"
#include "TClass.h"
#include "TVirtualX.h"
#include "TCanvas.h"
#include "TDataMember.h"
#include "TToggle.h"
#include "TRootDialog.h"
#include "TDataType.h"
#include "TCanvas.h"
#include "TBrowser.h"
#include "TRootCanvas.h"
#include "TRootBrowser.h"
#include "TClassMenuItem.h"
#include "TObjectSpy.h"

enum EContextMenu {
   kToggleStart       = 1000, // first id of toggle menu items
   kToggleListStart   = 2000, // first id of toggle list menu items
   kUserFunctionStart = 3000  // first id of user added functions/methods, etc...
};

ClassImp(TRootContextMenu)

//______________________________________________________________________________
TRootContextMenu::TRootContextMenu(TContextMenu *c, const char *)
    : TGPopupMenu(gClient->GetDefaultRoot()), TContextMenuImp(c)
{
   // Create context menu.

   fDialog  = 0;
   fTrash = new TList;

   // Context menu handles its own messages
   Associate(this);
}

//______________________________________________________________________________
TRootContextMenu::~TRootContextMenu()
{
   // Delete a context menu.

   delete fDialog;
   if (fTrash) fTrash->Delete();
   delete fTrash;
}

//______________________________________________________________________________
void TRootContextMenu::DisplayPopup(Int_t x, Int_t y)
{
   // Display context popup menu for currently selected object.

   if (fClient->IsEditable()) return;

   // delete menu items releated to previous object and reset menu size
   if (fEntryList) fEntryList->Delete();
   if (fTrash)   fTrash->Delete();
   fMenuHeight = 6;
   fMenuWidth  = 8;

   // delete previous dialog
   if (fDialog) {
      delete fDialog;
      fDialog = 0;
   }

   // add menu items to popup menu
   CreateMenu(fContextMenu->GetSelectedObject());

   int    xx, yy, topx = 0, topy = 0;
   UInt_t w, h;

   if (fContextMenu->GetSelectedCanvas())
      gVirtualX->GetGeometry(fContextMenu->GetSelectedCanvas()->GetCanvasID(),
                        topx, topy, w, h);

   xx = topx + x + 1;
   yy = topy + y + 1;

   PlaceMenu(xx, yy, kFALSE, kTRUE);
}

//______________________________________________________________________________
void TRootContextMenu::CreateMenu(TObject *object)
{
   // Create the context menu depending on the selected object.

   if (fClient->IsEditable()) return;

   int entry = 0, toggle = kToggleStart, togglelist = kToggleListStart;
   int userfunction = kUserFunctionStart;

   // Add a title
   AddLabel(fContextMenu->CreatePopupTitle(object));
   AddSeparator();

   // Get list of menu items from the selected object's class
   TList *menuItemList = object->IsA()->GetMenuList();

   TClassMenuItem *menuItem;
   TIter nextItem(menuItemList);

   while ((menuItem = (TClassMenuItem*) nextItem())) {
      switch (menuItem->GetType()) {
         case TClassMenuItem::kPopupSeparator:
            AddSeparator();
            break;
         case TClassMenuItem::kPopupStandardList:
            {
               // Standard list of class methods. Rebuild from scratch.
               // Get linked list of objects menu items (i.e. member functions
               // with the token *MENU in their comment fields.
               TList *methodList = new TList;
               object->IsA()->GetMenuItems(methodList);

               TMethod *method;
               TClass  *classPtr = 0;
               TIter next(methodList);

               while ((method = (TMethod*) next())) {
                  if (classPtr != method->GetClass()) {
                     AddSeparator();
                     classPtr = method->GetClass();
                  }

                  TDataMember *m;
                  EMenuItemKind menuKind = method->IsMenuItem();
                  switch (menuKind) {
                     case kMenuDialog:
                        AddEntry(method->GetName(), entry++, method);
                        break;
                     case kMenuSubMenu:
                        if ((m = method->FindDataMember())) {
                           if (m->GetterMethod()) {
                              TGPopupMenu *r = new TGPopupMenu(gClient->GetDefaultRoot());
                              AddPopup(method->GetName(), r);
                              fTrash->Add(r);
                              TIter nxt(m->GetOptions());
                              TOptionListItem *it;
                              while ((it = (TOptionListItem*) nxt())) {
                                 char  *name  = it->fOptName;
                                 Long_t val   = it->fValue;

                                 TToggle *t = new TToggle;
                                 t->SetToggledObject(object, method);
                                 t->SetOnValue(val);
                                 fTrash->Add(t);

                                 r->AddSeparator();
                                 r->AddEntry(name, togglelist++, t);
                                 if (t->GetState()) r->CheckEntry(togglelist-1);

                              }
                           } else {
                              AddEntry(method->GetName(), entry++, method);
                           }
                        }
                        break;

                     case kMenuToggle:
                        {
                           TToggle *t = new TToggle;
                           t->SetToggledObject(object, method);
                           t->SetOnValue(1);
                           fTrash->Add(t);
                           AddEntry(method->GetName(), toggle++, t);
                           if (t->GetState()) CheckEntry(toggle-1);
                        }
                        break;

                     default:
                        break;
                  }
               }
               delete methodList;
            }
            break;
         case TClassMenuItem::kPopupUserFunction:
            {
               if (menuItem->IsToggle()) {
                  if (object) {
                     TMethod* method =
                           object->IsA()->GetMethodWithPrototype(menuItem->GetFunctionName(),menuItem->GetArgs());
                     TToggle *t = new TToggle;
                     t->SetToggledObject(object, method);
                     t->SetOnValue(1);
                     fTrash->Add(t);

                     AddEntry(method->GetName(), toggle++, t);
                     if (t->GetState()) CheckEntry(toggle-1);
                  } else {
                     Warning("Dialog","Cannot use toggle for a global function");
                  }
               } else {
                  const char* menuItemTitle = menuItem->GetTitle();
                  if (strlen(menuItemTitle)==0) menuItemTitle = menuItem->GetFunctionName();
                  AddEntry(menuItemTitle,userfunction++,menuItem);
               }
            }
            break;
         default:
            break;
      }
   }
}

//______________________________________________________________________________
void TRootContextMenu::Dialog(TObject *object, TMethod *method)
{
   // Create dialog object with OK and Cancel buttons. This dialog
   // prompts for the arguments of "method".

   Dialog(object,(TFunction*)method);
}

//______________________________________________________________________________
void TRootContextMenu::Dialog(TObject *object, TFunction *function)
{
   // Create dialog object with OK and Cancel buttons. This dialog
   // prompts for the arguments of "function".
   // function may be a global function or a method

   Int_t selfobjpos;

   if (!function) return;

   // Position, if it exists, of the argument that correspond to the object itself
   if (fContextMenu->GetSelectedMenuItem())
      selfobjpos =  fContextMenu->GetSelectedMenuItem()->GetSelfObjectPos();
   else selfobjpos = -1;

   const TGWindow *w;
   if (fContextMenu->GetSelectedCanvas()) {
      TCanvas *c = (TCanvas *) fContextMenu->GetSelectedCanvas();
      // Embedded canvas has no canvasimp that is a TGFrame
      if (c->GetCanvasImp()->IsA()->InheritsFrom(TGFrame::Class())) {
         w = fClient->GetWindowById(gVirtualX->GetWindowID(c->GetCanvasID()));
         if (!w) w = (TRootCanvas *) c->GetCanvasImp();
      } else {
         w = gClient->GetDefaultRoot();
      }
   } else if (fContextMenu->GetBrowser()) {
      TBrowser *b = (TBrowser *) fContextMenu->GetBrowser();
      w = (TRootBrowser *) b->GetBrowserImp();
   } else {
      w = gClient->GetDefaultRoot();
   }
   fDialog = new TRootDialog(this, w, fContextMenu->CreateDialogTitle(object, function));

   // iterate through all arguments and create apropriate input-data objects:
   // inputlines, option menus...
   TMethodArg *argument = 0;

   TIter next(function->GetListOfMethodArgs());
   Int_t argpos = 0;

   while ((argument = (TMethodArg *) next())) {
      // Do not input argument for self object
      if (selfobjpos != argpos) {
         Text_t       *argname    = fContextMenu->CreateArgumentTitle(argument);
         const Text_t *type       = argument->GetTypeName();
         TDataType    *datatype   = gROOT->GetType(type);
         const Text_t *charstar   = "char*";
         Text_t        basictype[32];

         if (datatype) {
            strcpy(basictype, datatype->GetTypeName());
         } else {
            TClass *cl = TClass::GetClass(type);
            if (strncmp(type, "enum", 4) && (cl && !(cl->Property() & kIsEnum)))
               Warning("Dialog", "data type is not basic type, assuming (int)");
            strcpy(basictype, "int");
         }

         if (strchr(argname, '*')) {
            strcat(basictype, "*");
            type = charstar;
         }

         TDataMember *m = argument->GetDataMember();
         if (m && m->GetterMethod(object->IsA())) {

            // Get the current value and form it as a text:

            Text_t val[256];

            if (!strncmp(basictype, "char*", 5)) {
               Text_t *tdefval;
               m->GetterMethod()->Execute(object, "", &tdefval);
               strncpy(val, tdefval, 255);
            } else if (!strncmp(basictype, "float", 5) ||
                       !strncmp(basictype, "double", 6)) {
               Double_t ddefval;
               m->GetterMethod()->Execute(object, "", ddefval);
               sprintf(val, "%g", ddefval);
            } else if (!strncmp(basictype, "char", 4) ||
                       !strncmp(basictype, "bool", 4) ||
                       !strncmp(basictype, "int", 3)  ||
                       !strncmp(basictype, "long", 4) ||
                       !strncmp(basictype, "short", 5)) {
               Long_t ldefval;
               m->GetterMethod()->Execute(object, "", ldefval);
               sprintf(val, "%li", ldefval);
            }

            // Find out whether we have options ...

            TList *opt;
            if ((opt = m->GetOptions())) {
               Warning("Dialog", "option menu not yet implemented", opt);
#if 0
               TMotifOptionMenu *o= new TMotifOptionMenu(argname);
               TIter nextopt(opt);
               TOptionListItem *it = 0;
               while ((it = (TOptionListItem*) nextopt())) {
                  Text_t *name  = it->fOptName;
                  Text_t *label = it->fOptLabel;
                  Long_t value  = it->fValue;
                  if (value != -9999) {
                     Text_t val[256];
                     sprintf(val, "%li", value);
                     o->AddItem(name, val);
                  }else
                     o->AddItem(name, label);
               }
               o->SetData(val);
               fDialog->Add(o);
#endif
            } else {
               // we haven't got options - textfield ...
               fDialog->Add(argname, val, type);
            }
         } else {    // if m not found ...

            char val[256] = "";
            const char *tval = argument->GetDefault();
            if (tval) strncpy(val, tval, 255);
            fDialog->Add(argname, val, type);
         }
      }
      argpos++;
   }

   fDialog->Popup();
}

//______________________________________________________________________________
Bool_t TRootContextMenu::ProcessMessage(Long_t msg, Long_t parm1, Long_t parm2)
{
   // Handle context menu messages.

   TObjectSpy savedPad;
   if (GetContextMenu()->GetSelectedPad()) {
      savedPad.SetObject(gPad);
      gPad = GetContextMenu()->GetSelectedPad();
   }

   switch (GET_MSG(msg)) {

      case kC_COMMAND:

         switch (GET_SUBMSG(msg)) {

            case kCM_MENU:

               if (parm1 < kToggleStart) {
                  TMethod *m = (TMethod *) parm2;
                  GetContextMenu()->Action(m);
               } else if (parm1 >= kToggleStart && parm1 < kToggleListStart) {
                  TToggle *t = (TToggle *) parm2;
                  GetContextMenu()->Action(t);
               } else if (parm1 >= kToggleListStart && parm1<kUserFunctionStart) {
                  TToggle *t = (TToggle *) parm2;
                  if (t->GetState() == 0)
                     t->SetState(1);
               } else {
                  TClassMenuItem *mi = (TClassMenuItem*)parm2;
                  GetContextMenu()->Action(mi);
               }
               break;

            case kCM_BUTTON:
               if (parm1 == 1) {
                  const char *args = fDialog->GetParameters();
                  GetContextMenu()->Execute((char *)args);
                  delete fDialog;
                  fDialog = 0;
               }
               if (parm1 == 2) {
                  const char *args = fDialog->GetParameters();
                  GetContextMenu()->Execute((char *)args);
               }
               if (parm1 == 3) {
                  delete fDialog;
                  fDialog = 0;
               }
               break;

            default:
               break;
         }
         break;

      case kC_TEXTENTRY:

         switch (GET_SUBMSG(msg)) {

            case kTE_ENTER:
               {
                  const char *args = fDialog->GetParameters();
                  GetContextMenu()->Execute((char *)args);
                  delete fDialog;
                  fDialog = 0;
               }
               break;

            default:
               break;
         }
         break;

      default:
         break;
   }

   if (savedPad.GetObject()) gPad = (TVirtualPad*) savedPad.GetObject();

   return kTRUE;
}
<commit_msg>Fix in TRootContextMenu::Dialog method: anytime the argument name contained a '*' the argument type was considered as "char *" what is not true for cases as "Double_t *" etc.<commit_after>// @(#)root/gui:$Name:  $:$Id: TRootContextMenu.cxx,v 1.17 2007/01/30 11:55:33 brun Exp $
// Author: Fons Rademakers   12/02/98

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

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TRootContextMenu                                                     //
//                                                                      //
// This class provides an interface to context sensitive popup menus.   //
// These menus pop up when the user hits the right mouse button, and    //
// are destroyed when the menu pops downs.                              //
// The picture below shows a canvas with a pop-up menu.                 //
//                                                                      //
//Begin_Html  End_Html                      //
//                                                                      //
// The picture below shows a canvas with a pop-up menu and a dialog box.//
//                                                                      //
//Begin_Html  End_Html                    //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TRootContextMenu.h"
#include "TROOT.h"
#include "TGClient.h"
#include "TList.h"
#include "TContextMenu.h"
#include "TMethod.h"
#include "TMethodArg.h"
#include "TClass.h"
#include "TVirtualX.h"
#include "TCanvas.h"
#include "TDataMember.h"
#include "TToggle.h"
#include "TRootDialog.h"
#include "TDataType.h"
#include "TCanvas.h"
#include "TBrowser.h"
#include "TRootCanvas.h"
#include "TRootBrowser.h"
#include "TClassMenuItem.h"
#include "TObjectSpy.h"

enum EContextMenu {
   kToggleStart       = 1000, // first id of toggle menu items
   kToggleListStart   = 2000, // first id of toggle list menu items
   kUserFunctionStart = 3000  // first id of user added functions/methods, etc...
};

ClassImp(TRootContextMenu)

//______________________________________________________________________________
TRootContextMenu::TRootContextMenu(TContextMenu *c, const char *)
    : TGPopupMenu(gClient->GetDefaultRoot()), TContextMenuImp(c)
{
   // Create context menu.

   fDialog  = 0;
   fTrash = new TList;

   // Context menu handles its own messages
   Associate(this);
}

//______________________________________________________________________________
TRootContextMenu::~TRootContextMenu()
{
   // Delete a context menu.

   delete fDialog;
   if (fTrash) fTrash->Delete();
   delete fTrash;
}

//______________________________________________________________________________
void TRootContextMenu::DisplayPopup(Int_t x, Int_t y)
{
   // Display context popup menu for currently selected object.

   if (fClient->IsEditable()) return;

   // delete menu items releated to previous object and reset menu size
   if (fEntryList) fEntryList->Delete();
   if (fTrash)   fTrash->Delete();
   fMenuHeight = 6;
   fMenuWidth  = 8;

   // delete previous dialog
   if (fDialog) {
      delete fDialog;
      fDialog = 0;
   }

   // add menu items to popup menu
   CreateMenu(fContextMenu->GetSelectedObject());

   int    xx, yy, topx = 0, topy = 0;
   UInt_t w, h;

   if (fContextMenu->GetSelectedCanvas())
      gVirtualX->GetGeometry(fContextMenu->GetSelectedCanvas()->GetCanvasID(),
                        topx, topy, w, h);

   xx = topx + x + 1;
   yy = topy + y + 1;

   PlaceMenu(xx, yy, kFALSE, kTRUE);
}

//______________________________________________________________________________
void TRootContextMenu::CreateMenu(TObject *object)
{
   // Create the context menu depending on the selected object.

   if (fClient->IsEditable()) return;

   int entry = 0, toggle = kToggleStart, togglelist = kToggleListStart;
   int userfunction = kUserFunctionStart;

   // Add a title
   AddLabel(fContextMenu->CreatePopupTitle(object));
   AddSeparator();

   // Get list of menu items from the selected object's class
   TList *menuItemList = object->IsA()->GetMenuList();

   TClassMenuItem *menuItem;
   TIter nextItem(menuItemList);

   while ((menuItem = (TClassMenuItem*) nextItem())) {
      switch (menuItem->GetType()) {
         case TClassMenuItem::kPopupSeparator:
            AddSeparator();
            break;
         case TClassMenuItem::kPopupStandardList:
            {
               // Standard list of class methods. Rebuild from scratch.
               // Get linked list of objects menu items (i.e. member functions
               // with the token *MENU in their comment fields.
               TList *methodList = new TList;
               object->IsA()->GetMenuItems(methodList);

               TMethod *method;
               TClass  *classPtr = 0;
               TIter next(methodList);

               while ((method = (TMethod*) next())) {
                  if (classPtr != method->GetClass()) {
                     AddSeparator();
                     classPtr = method->GetClass();
                  }

                  TDataMember *m;
                  EMenuItemKind menuKind = method->IsMenuItem();
                  switch (menuKind) {
                     case kMenuDialog:
                        AddEntry(method->GetName(), entry++, method);
                        break;
                     case kMenuSubMenu:
                        if ((m = method->FindDataMember())) {
                           if (m->GetterMethod()) {
                              TGPopupMenu *r = new TGPopupMenu(gClient->GetDefaultRoot());
                              AddPopup(method->GetName(), r);
                              fTrash->Add(r);
                              TIter nxt(m->GetOptions());
                              TOptionListItem *it;
                              while ((it = (TOptionListItem*) nxt())) {
                                 char  *name  = it->fOptName;
                                 Long_t val   = it->fValue;

                                 TToggle *t = new TToggle;
                                 t->SetToggledObject(object, method);
                                 t->SetOnValue(val);
                                 fTrash->Add(t);

                                 r->AddSeparator();
                                 r->AddEntry(name, togglelist++, t);
                                 if (t->GetState()) r->CheckEntry(togglelist-1);

                              }
                           } else {
                              AddEntry(method->GetName(), entry++, method);
                           }
                        }
                        break;

                     case kMenuToggle:
                        {
                           TToggle *t = new TToggle;
                           t->SetToggledObject(object, method);
                           t->SetOnValue(1);
                           fTrash->Add(t);
                           AddEntry(method->GetName(), toggle++, t);
                           if (t->GetState()) CheckEntry(toggle-1);
                        }
                        break;

                     default:
                        break;
                  }
               }
               delete methodList;
            }
            break;
         case TClassMenuItem::kPopupUserFunction:
            {
               if (menuItem->IsToggle()) {
                  if (object) {
                     TMethod* method =
                           object->IsA()->GetMethodWithPrototype(menuItem->GetFunctionName(),menuItem->GetArgs());
                     TToggle *t = new TToggle;
                     t->SetToggledObject(object, method);
                     t->SetOnValue(1);
                     fTrash->Add(t);

                     AddEntry(method->GetName(), toggle++, t);
                     if (t->GetState()) CheckEntry(toggle-1);
                  } else {
                     Warning("Dialog","Cannot use toggle for a global function");
                  }
               } else {
                  const char* menuItemTitle = menuItem->GetTitle();
                  if (strlen(menuItemTitle)==0) menuItemTitle = menuItem->GetFunctionName();
                  AddEntry(menuItemTitle,userfunction++,menuItem);
               }
            }
            break;
         default:
            break;
      }
   }
}

//______________________________________________________________________________
void TRootContextMenu::Dialog(TObject *object, TMethod *method)
{
   // Create dialog object with OK and Cancel buttons. This dialog
   // prompts for the arguments of "method".

   Dialog(object,(TFunction*)method);
}

//______________________________________________________________________________
void TRootContextMenu::Dialog(TObject *object, TFunction *function)
{
   // Create dialog object with OK and Cancel buttons. This dialog
   // prompts for the arguments of "function".
   // function may be a global function or a method

   Int_t selfobjpos;

   if (!function) return;

   // Position, if it exists, of the argument that correspond to the object itself
   if (fContextMenu->GetSelectedMenuItem())
      selfobjpos =  fContextMenu->GetSelectedMenuItem()->GetSelfObjectPos();
   else selfobjpos = -1;

   const TGWindow *w;
   if (fContextMenu->GetSelectedCanvas()) {
      TCanvas *c = (TCanvas *) fContextMenu->GetSelectedCanvas();
      // Embedded canvas has no canvasimp that is a TGFrame
      if (c->GetCanvasImp()->IsA()->InheritsFrom(TGFrame::Class())) {
         w = fClient->GetWindowById(gVirtualX->GetWindowID(c->GetCanvasID()));
         if (!w) w = (TRootCanvas *) c->GetCanvasImp();
      } else {
         w = gClient->GetDefaultRoot();
      }
   } else if (fContextMenu->GetBrowser()) {
      TBrowser *b = (TBrowser *) fContextMenu->GetBrowser();
      w = (TRootBrowser *) b->GetBrowserImp();
   } else {
      w = gClient->GetDefaultRoot();
   }
   fDialog = new TRootDialog(this, w, fContextMenu->CreateDialogTitle(object, function));

   // iterate through all arguments and create apropriate input-data objects:
   // inputlines, option menus...
   TMethodArg *argument = 0;

   TIter next(function->GetListOfMethodArgs());
   Int_t argpos = 0;

   while ((argument = (TMethodArg *) next())) {
      // Do not input argument for self object
      if (selfobjpos != argpos) {
         Text_t       *argname    = fContextMenu->CreateArgumentTitle(argument);
         const Text_t *type       = argument->GetTypeName();
         TDataType    *datatype   = gROOT->GetType(type);
         Text_t        basictype[32];

         if (datatype) {
            strcpy(basictype, datatype->GetTypeName());
         } else {
            TClass *cl = TClass::GetClass(type);
            if (strncmp(type, "enum", 4) && (cl && !(cl->Property() & kIsEnum)))
               Warning("Dialog", "data type is not basic type, assuming (int)");
            strcpy(basictype, "int");
         }

         if (strchr(argname, '*')) {
            strcat(basictype, "*");
         }

         TDataMember *m = argument->GetDataMember();
         if (m && m->GetterMethod(object->IsA())) {

            // Get the current value and form it as a text:

            Text_t val[256];

            if (!strncmp(basictype, "char*", 5)) {
               Text_t *tdefval;
               m->GetterMethod()->Execute(object, "", &tdefval);
               strncpy(val, tdefval, 255);
            } else if (!strncmp(basictype, "float", 5) ||
                       !strncmp(basictype, "double", 6)) {
               Double_t ddefval;
               m->GetterMethod()->Execute(object, "", ddefval);
               sprintf(val, "%g", ddefval);
            } else if (!strncmp(basictype, "char", 4) ||
                       !strncmp(basictype, "bool", 4) ||
                       !strncmp(basictype, "int", 3)  ||
                       !strncmp(basictype, "long", 4) ||
                       !strncmp(basictype, "short", 5)) {
               Long_t ldefval;
               m->GetterMethod()->Execute(object, "", ldefval);
               sprintf(val, "%li", ldefval);
            }

            // Find out whether we have options ...

            TList *opt;
            if ((opt = m->GetOptions())) {
               Warning("Dialog", "option menu not yet implemented", opt);
#if 0
               TMotifOptionMenu *o= new TMotifOptionMenu(argname);
               TIter nextopt(opt);
               TOptionListItem *it = 0;
               while ((it = (TOptionListItem*) nextopt())) {
                  Text_t *name  = it->fOptName;
                  Text_t *label = it->fOptLabel;
                  Long_t value  = it->fValue;
                  if (value != -9999) {
                     Text_t val[256];
                     sprintf(val, "%li", value);
                     o->AddItem(name, val);
                  }else
                     o->AddItem(name, label);
               }
               o->SetData(val);
               fDialog->Add(o);
#endif
            } else {
               // we haven't got options - textfield ...
               fDialog->Add(argname, val, type);
            }
         } else {    // if m not found ...

            char val[256] = "";
            const char *tval = argument->GetDefault();
            if (tval) strncpy(val, tval, 255);
            fDialog->Add(argname, val, type);
         }
      }
      argpos++;
   }

   fDialog->Popup();
}

//______________________________________________________________________________
Bool_t TRootContextMenu::ProcessMessage(Long_t msg, Long_t parm1, Long_t parm2)
{
   // Handle context menu messages.

   TObjectSpy savedPad;
   if (GetContextMenu()->GetSelectedPad()) {
      savedPad.SetObject(gPad);
      gPad = GetContextMenu()->GetSelectedPad();
   }

   switch (GET_MSG(msg)) {

      case kC_COMMAND:

         switch (GET_SUBMSG(msg)) {

            case kCM_MENU:

               if (parm1 < kToggleStart) {
                  TMethod *m = (TMethod *) parm2;
                  GetContextMenu()->Action(m);
               } else if (parm1 >= kToggleStart && parm1 < kToggleListStart) {
                  TToggle *t = (TToggle *) parm2;
                  GetContextMenu()->Action(t);
               } else if (parm1 >= kToggleListStart && parm1<kUserFunctionStart) {
                  TToggle *t = (TToggle *) parm2;
                  if (t->GetState() == 0)
                     t->SetState(1);
               } else {
                  TClassMenuItem *mi = (TClassMenuItem*)parm2;
                  GetContextMenu()->Action(mi);
               }
               break;

            case kCM_BUTTON:
               if (parm1 == 1) {
                  const char *args = fDialog->GetParameters();
                  GetContextMenu()->Execute((char *)args);
                  delete fDialog;
                  fDialog = 0;
               }
               if (parm1 == 2) {
                  const char *args = fDialog->GetParameters();
                  GetContextMenu()->Execute((char *)args);
               }
               if (parm1 == 3) {
                  delete fDialog;
                  fDialog = 0;
               }
               break;

            default:
               break;
         }
         break;

      case kC_TEXTENTRY:

         switch (GET_SUBMSG(msg)) {

            case kTE_ENTER:
               {
                  const char *args = fDialog->GetParameters();
                  GetContextMenu()->Execute((char *)args);
                  delete fDialog;
                  fDialog = 0;
               }
               break;

            default:
               break;
         }
         break;

      default:
         break;
   }

   if (savedPad.GetObject()) gPad = (TVirtualPad*) savedPad.GetObject();

   return kTRUE;
}
<|endoftext|>


<commit_before>#include "ros/ros.h"
#include "geometry_msgs/Pose.h"
#include "geometry_msgs/Twist.h"
#include "geometry_msgs/Wrench.h"
#include "uranus_dp/State.h"
#include <Eigen/Dense>
#include <cmath>

class Controller
{
public:
    Controller()
    {
        outputPub   = n.advertise<geometry_msgs::Wrench>("outputTopic", 1);
        stateSub    = n.subscribe("stateTopic", 1, &Controller::stateCallback, this);
        setpointSub = n.subscribe("setpointTopic", 1, &Controller::setpointCallback, this);

        // Set default frequency
        frequency = 10;

        // Allocate dynamic member variables
        T   = Eigen::MatrixXd(4,3);
        tau = Eigen::VectorXd(6);

        // Initialize controller gains
        K_lin_p = Eigen::Matrix3d::Identity();
        K_lin_d = Eigen::Matrix3d::Identity();
        K_ang_p = Eigen::Matrix3d::Identity();
        K_ang_d = Eigen::Matrix3d::Identity();
    }

    // stateCallback updates the private state variables when a new state message arrives.
    void stateCallback(const uranus_dp::State &state_msg)
    {
        // Copy position values
        p(0) = state_msg.pose.position.x;
        p(1) = state_msg.pose.position.y;
        p(2) = state_msg.pose.position.z;

        // Copy orientation values (quaternion)
        q(0) = state_msg.pose.orientation.x;
        q(1) = state_msg.pose.orientation.y;
        q(2) = state_msg.pose.orientation.z;
        q(3) = state_msg.pose.orientation.w;

        // Copy linear velocity values
        v(0) = state_msg.twist.linear.x;
        v(1) = state_msg.twist.linear.y;
        v(2) = state_msg.twist.linear.z;

        // Copy angular velocity values
        omega(0) = state_msg.twist.angular.x;
        omega(1) = state_msg.twist.angular.y;
        omega(2) = state_msg.twist.angular.z;
    }

    // setpointCallback updates the private setpoint variable when a new setpoint message arrives.
    void setpointCallback(const geometry_msgs::Twist &setpoint_msg)
    {
        // Copy linear velocity setpoint values
        v_sp(0) = setpoint_msg.linear.x;
        v_sp(1) = setpoint_msg.linear.y;
        v_sp(2) = setpoint_msg.linear.z;

        // Copy angular velocity setpoint values
        omega_sp(0) = setpoint_msg.angular.x;
        omega_sp(1) = setpoint_msg.angular.y;
        omega_sp(2) = setpoint_msg.angular.z;
    }

    // compute contains the control algorithm, and computes the control output based on the
    // current state and setpoint.
    void compute(void)
    {
        // Only pose control for now

        // Position and orientation errors
        p_err = p_sp - p;
        q_err = q_sp - q;

        // Control output
        updateTransformationMatrices();
        Eigen::Vector3d tau_lin = K_lin_p * p_err + K_lin_d * v;
        Eigen::Vector3d tau_ang = - K_ang_p * T.transpose() * q_err - K_ang_d * omega;
        tau << tau_lin, tau_ang;

        // Create and send output message
        geometry_msgs::Wrench output;
        output.force.x  = tau(0);
        output.force.y  = tau(1);
        output.force.z  = tau(2);
        output.torque.x = tau(3);
        output.torque.y = tau(4);
        output.torque.z = tau(5);
        outputPub.publish(output);
    }

    // setFrequency tells the controller which frequency in Hz compute() is called with.
    void setFrequency(unsigned int newFrequency)
    {
        frequency = newFrequency;
    }
private:
    ros::NodeHandle n;
    ros::Publisher  outputPub;
    ros::Subscriber stateSub;
    ros::Subscriber setpointSub;

    // Controller frequency
    unsigned int frequency;

    // State
    Eigen::Vector3d p;     // Position
    Eigen::Vector4d q;     // Orientation
    Eigen::Vector3d v;     // Linear velocity
    Eigen::Vector3d omega; // Angular velocity

    // Setpoints
    Eigen::Vector3d p_sp;     // Position
    Eigen::Vector4d q_sp;     // Orientation
    Eigen::Vector3d v_sp;     // Linear velocity
    Eigen::Vector3d omega_sp; // Angular velocity

    // Errors
    Eigen::Vector3d p_err;     // Position
    Eigen::Vector4d q_err;     // Orientation
    Eigen::Vector3d v_err;     // Linear velocity
    Eigen::Vector3d omega_err; // Angular velocity

    // Transformation matrices
    Eigen::Matrix3d R;
    Eigen::MatrixXd T;

    // Control output
    Eigen::VectorXd tau;

    // Controller gains
    Eigen::Matrix3d K_lin_d;
    Eigen::Matrix3d K_lin_p;
    Eigen::Matrix3d K_ang_d;
    Eigen::Matrix3d K_ang_p;

    void updateTransformationMatrices(void)
    {
        // Linear velocity transformation matrix
        R(0,0) = 1 - 2*(pow(q(2),2) + pow(q(3),2));
        R(0,1) = 2*(q(1)*q(2) - q(3)*q(0));
        R(0,2) = 2*(q(1)*q(3) - q(2)*q(0));

        R(1,0) = 2*(q(1)*q(2) + q(3)*q(0));
        R(1,1) = 1 - 2*(pow(q(1),2) + pow(q(0),2));
        R(1,2) = 2*(q(2)*q(3) + q(1)*q(0));

        R(2,0) = 2*(q(1)*q(3) + q(2)*q(0));
        R(2,1) = 2*(q(2)*q(3) + q(1)*q(0));
        R(2,2) = 1 - 2*(pow(q(1),2) + pow(q(2),2));

        // Angular velocity transformation matrix
        T(0,0) = -q(1);
        T(0,1) = -q(2);
        T(0,2) = -q(3);

        T(1,0) =  q(0);
        T(1,1) = -q(3);
        T(1,2) =  q(2);

        T(2,0) =  q(3);
        T(2,1) =  q(3);
        T(2,2) = -q(1);

        T(3,0) = -q(2);
        T(3,1) =  q(1);
        T(3,2) =  q(0);

        T *= 0.5;
    }
}; // End of class Controller

int main(int argc, char **argv)
{
    ros::init(argc, argv, "controller");

    Controller controllerObject;

    unsigned int frequency = 10; // Get from parameter server sometime in the future
    controllerObject.setFrequency(frequency);

    ros::Rate loop_rate(frequency);
    while (ros::ok())
    {
        ros::spinOnce();

        controllerObject.compute();

        loop_rate.sleep();
    }

    return 0;
}
<commit_msg>Change controller class and instance name<commit_after>#include "ros/ros.h"
#include "geometry_msgs/Pose.h"
#include "geometry_msgs/Twist.h"
#include "geometry_msgs/Wrench.h"
#include "uranus_dp/State.h"
#include <Eigen/Dense>
#include <cmath>

class NonlinearQuaternionPidController
{
public:
    NonlinearQuaternionPidController()
    {
        outputPub   = n.advertise<geometry_msgs::Wrench>("outputTopic", 1);
        stateSub    = n.subscribe("stateTopic", 1, &NonlinearQuaternionPidController::stateCallback, this);
        setpointSub = n.subscribe("setpointTopic", 1, &NonlinearQuaternionPidController::setpointCallback, this);

        // Set default frequency
        frequency = 10;

        // Allocate dynamic member variables
        T   = Eigen::MatrixXd(4,3);
        tau = Eigen::VectorXd(6);

        // Initialize controller gains
        K_lin_p = Eigen::Matrix3d::Identity();
        K_lin_d = Eigen::Matrix3d::Identity();
        K_ang_p = Eigen::Matrix3d::Identity();
        K_ang_d = Eigen::Matrix3d::Identity();
    }

    // stateCallback updates the private state variables when a new state message arrives.
    void stateCallback(const uranus_dp::State &state_msg)
    {
        // Copy position values
        p(0) = state_msg.pose.position.x;
        p(1) = state_msg.pose.position.y;
        p(2) = state_msg.pose.position.z;

        // Copy orientation values (quaternion)
        q(0) = state_msg.pose.orientation.x;
        q(1) = state_msg.pose.orientation.y;
        q(2) = state_msg.pose.orientation.z;
        q(3) = state_msg.pose.orientation.w;

        // Copy linear velocity values
        v(0) = state_msg.twist.linear.x;
        v(1) = state_msg.twist.linear.y;
        v(2) = state_msg.twist.linear.z;

        // Copy angular velocity values
        omega(0) = state_msg.twist.angular.x;
        omega(1) = state_msg.twist.angular.y;
        omega(2) = state_msg.twist.angular.z;
    }

    // setpointCallback updates the private setpoint variable when a new setpoint message arrives.
    void setpointCallback(const geometry_msgs::Twist &setpoint_msg)
    {
        // Copy linear velocity setpoint values
        v_sp(0) = setpoint_msg.linear.x;
        v_sp(1) = setpoint_msg.linear.y;
        v_sp(2) = setpoint_msg.linear.z;

        // Copy angular velocity setpoint values
        omega_sp(0) = setpoint_msg.angular.x;
        omega_sp(1) = setpoint_msg.angular.y;
        omega_sp(2) = setpoint_msg.angular.z;
    }

    // compute contains the control algorithm, and computes the control output based on the
    // current state and setpoint.
    void compute(void)
    {
        // Only pose control for now

        // Position and orientation errors
        p_err = p_sp - p;
        q_err = q_sp - q;

        // Control output
        updateTransformationMatrices();
        Eigen::Vector3d tau_lin = K_lin_p * p_err + K_lin_d * v;
        Eigen::Vector3d tau_ang = - K_ang_p * T.transpose() * q_err - K_ang_d * omega;
        tau << tau_lin, tau_ang;

        // Create and send output message
        geometry_msgs::Wrench output;
        output.force.x  = tau(0);
        output.force.y  = tau(1);
        output.force.z  = tau(2);
        output.torque.x = tau(3);
        output.torque.y = tau(4);
        output.torque.z = tau(5);
        outputPub.publish(output);
    }

    // setFrequency tells the controller which frequency in Hz compute() is called with.
    void setFrequency(unsigned int newFrequency)
    {
        frequency = newFrequency;
    }
private:
    ros::NodeHandle n;
    ros::Publisher  outputPub;
    ros::Subscriber stateSub;
    ros::Subscriber setpointSub;

    // Controller frequency
    unsigned int frequency;

    // State
    Eigen::Vector3d p;     // Position
    Eigen::Vector4d q;     // Orientation
    Eigen::Vector3d v;     // Linear velocity
    Eigen::Vector3d omega; // Angular velocity

    // Setpoints
    Eigen::Vector3d p_sp;     // Position
    Eigen::Vector4d q_sp;     // Orientation
    Eigen::Vector3d v_sp;     // Linear velocity
    Eigen::Vector3d omega_sp; // Angular velocity

    // Errors
    Eigen::Vector3d p_err;     // Position
    Eigen::Vector4d q_err;     // Orientation
    Eigen::Vector3d v_err;     // Linear velocity
    Eigen::Vector3d omega_err; // Angular velocity

    // Transformation matrices
    Eigen::Matrix3d R;
    Eigen::MatrixXd T;

    // Control output
    Eigen::VectorXd tau;

    // Controller gains
    Eigen::Matrix3d K_lin_d;
    Eigen::Matrix3d K_lin_p;
    Eigen::Matrix3d K_ang_d;
    Eigen::Matrix3d K_ang_p;

    void updateTransformationMatrices(void)
    {
        // Linear velocity transformation matrix
        R(0,0) = 1 - 2*(pow(q(2),2) + pow(q(3),2));
        R(0,1) = 2*(q(1)*q(2) - q(3)*q(0));
        R(0,2) = 2*(q(1)*q(3) - q(2)*q(0));

        R(1,0) = 2*(q(1)*q(2) + q(3)*q(0));
        R(1,1) = 1 - 2*(pow(q(1),2) + pow(q(0),2));
        R(1,2) = 2*(q(2)*q(3) + q(1)*q(0));

        R(2,0) = 2*(q(1)*q(3) + q(2)*q(0));
        R(2,1) = 2*(q(2)*q(3) + q(1)*q(0));
        R(2,2) = 1 - 2*(pow(q(1),2) + pow(q(2),2));

        // Angular velocity transformation matrix
        T(0,0) = -q(1);
        T(0,1) = -q(2);
        T(0,2) = -q(3);

        T(1,0) =  q(0);
        T(1,1) = -q(3);
        T(1,2) =  q(2);

        T(2,0) =  q(3);
        T(2,1) =  q(3);
        T(2,2) = -q(1);

        T(3,0) = -q(2);
        T(3,1) =  q(1);
        T(3,2) =  q(0);

        T *= 0.5;
    }
}; // End of class NonlinearQuaternionPidController

int main(int argc, char **argv)
{
    ros::init(argc, argv, "controller");

    NonlinearQuaternionPidController controller;

    unsigned int frequency = 10; // Get from parameter server sometime in the future
    controller.setFrequency(frequency);

    ros::Rate loop_rate(frequency);
    while (ros::ok())
    {
        ros::spinOnce();

        controller.compute();

        loop_rate.sleep();
    }

    return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2009 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_SYSTEM_MEMORYVECTOR_HH__
#define __MEM_RUBY_SYSTEM_MEMORYVECTOR_HH__

#include "mem/ruby/common/Address.hh"

class DirectoryMemory;

/**
 *  MemoryVector holds memory data (DRAM only)
 */
class MemoryVector
{
  public:
    MemoryVector();
    MemoryVector(uint32 size);
    ~MemoryVector();
    friend class DirectoryMemory;

    void resize(uint32 size);  // destructive

    void write(const Address & paddr, uint8* data, int len);
    uint8* read(const Address & paddr, uint8* data, int len);

  private:
    uint8* getBlockPtr(const PhysAddress & addr);

    uint32 m_size;
    uint8** m_pages;
    uint32 m_num_pages;
    const uint32 m_page_offset_mask;
};

inline
MemoryVector::MemoryVector()
    : m_page_offset_mask(4095)
{
    m_size = 0;
    m_num_pages = 0;
    m_pages = NULL;
}

inline
MemoryVector::MemoryVector(uint32 size)
    : m_page_offset_mask(4095)
{
    resize(size);
}

inline
MemoryVector::~MemoryVector()
{
    for (int i = 0; i < m_num_pages; i++) {
        if (m_pages[i] != 0) {
            delete [] m_pages[i];
        }
    }
    delete [] m_pages;
}

inline void
MemoryVector::resize(uint32 size)
{
    if (m_pages != NULL){
        for (int i = 0; i < m_num_pages; i++) {
            if (m_pages[i] != 0) {
                delete [] m_pages[i];
            }
        }
        delete [] m_pages;
    }
    m_size = size;
    assert(size%4096 == 0);
    m_num_pages = size >> 12;
    m_pages = new uint8*[m_num_pages];
    memset(m_pages, 0, m_num_pages * sizeof(uint8*));
}

inline void
MemoryVector::write(const Address & paddr, uint8* data, int len)
{
    assert(paddr.getAddress() + len <= m_size);
    uint32 page_num = paddr.getAddress() >> 12;
    if (m_pages[page_num] == 0) {
        bool all_zeros = true;
        for (int i = 0; i < len;i++) {
            if (data[i] != 0) {
                all_zeros = false;
                break;
            }
        }
        if (all_zeros)
            return;
        m_pages[page_num] = new uint8[4096];
        memset(m_pages[page_num], 0, 4096);
        uint32 offset = paddr.getAddress() & m_page_offset_mask;
        memcpy(&m_pages[page_num][offset], data, len);
    } else {
        memcpy(&m_pages[page_num][paddr.getAddress()&m_page_offset_mask],
               data, len);
    }
}

inline uint8*
MemoryVector::read(const Address & paddr, uint8* data, int len)
{
    assert(paddr.getAddress() + len <= m_size);
    uint32 page_num = paddr.getAddress() >> 12;
    if (m_pages[page_num] == 0) {
        memset(data, 0, len);
    } else {
        memcpy(data, &m_pages[page_num][paddr.getAddress()&m_page_offset_mask],
               len);
    }
    return data;
}

inline uint8*
MemoryVector::getBlockPtr(const PhysAddress & paddr)
{
    uint32 page_num = paddr.getAddress() >> 12;
    if (m_pages[page_num] == 0) {
        m_pages[page_num] = new uint8[4096];
        memset(m_pages[page_num], 0, 4096);
    }
    return &m_pages[page_num][paddr.getAddress()&m_page_offset_mask];
}

#endif // __MEM_RUBY_SYSTEM_MEMORYVECTOR_HH__
<commit_msg>Ruby Memory Vector: Functions for collating and populating pages This patch adds functions to the memory vector class that can be used for collating memory pages to raw trace and for populating pages from a raw trace.<commit_after>/*
 * Copyright (c) 2009 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_SYSTEM_MEMORYVECTOR_HH__
#define __MEM_RUBY_SYSTEM_MEMORYVECTOR_HH__

#include "base/trace.hh"
#include "mem/ruby/common/Address.hh"

class DirectoryMemory;

/**
 *  MemoryVector holds memory data (DRAM only)
 */
class MemoryVector
{
  public:
    MemoryVector();
    MemoryVector(uint32 size);
    ~MemoryVector();
    friend class DirectoryMemory;

    void resize(uint32 size);  // destructive

    void write(const Address & paddr, uint8* data, int len);
    uint8* read(const Address & paddr, uint8* data, int len);
    uint32 collatePages(uint8* &raw_data);
    void populatePages(uint8* raw_data);

  private:
    uint8* getBlockPtr(const PhysAddress & addr);

    uint32 m_size;
    uint8** m_pages;
    uint32 m_num_pages;
    const uint32 m_page_offset_mask;
    static const uint32 PAGE_SIZE = 4096;
};

inline
MemoryVector::MemoryVector()
    : m_page_offset_mask(4095)
{
    m_size = 0;
    m_num_pages = 0;
    m_pages = NULL;
}

inline
MemoryVector::MemoryVector(uint32 size)
    : m_page_offset_mask(4095)
{
    resize(size);
}

inline
MemoryVector::~MemoryVector()
{
    for (int i = 0; i < m_num_pages; i++) {
        if (m_pages[i] != 0) {
            delete [] m_pages[i];
        }
    }
    delete [] m_pages;
}

inline void
MemoryVector::resize(uint32 size)
{
    if (m_pages != NULL){
        for (int i = 0; i < m_num_pages; i++) {
            if (m_pages[i] != 0) {
                delete [] m_pages[i];
            }
        }
        delete [] m_pages;
    }
    m_size = size;
    assert(size%PAGE_SIZE == 0);
    m_num_pages = size >> 12;
    m_pages = new uint8*[m_num_pages];
    memset(m_pages, 0, m_num_pages * sizeof(uint8*));
}

inline void
MemoryVector::write(const Address & paddr, uint8* data, int len)
{
    assert(paddr.getAddress() + len <= m_size);
    uint32 page_num = paddr.getAddress() >> 12;
    if (m_pages[page_num] == 0) {
        bool all_zeros = true;
        for (int i = 0; i < len;i++) {
            if (data[i] != 0) {
                all_zeros = false;
                break;
            }
        }
        if (all_zeros)
            return;
        m_pages[page_num] = new uint8[PAGE_SIZE];
        memset(m_pages[page_num], 0, PAGE_SIZE);
        uint32 offset = paddr.getAddress() & m_page_offset_mask;
        memcpy(&m_pages[page_num][offset], data, len);
    } else {
        memcpy(&m_pages[page_num][paddr.getAddress()&m_page_offset_mask],
               data, len);
    }
}

inline uint8*
MemoryVector::read(const Address & paddr, uint8* data, int len)
{
    assert(paddr.getAddress() + len <= m_size);
    uint32 page_num = paddr.getAddress() >> 12;
    if (m_pages[page_num] == 0) {
        memset(data, 0, len);
    } else {
        memcpy(data, &m_pages[page_num][paddr.getAddress()&m_page_offset_mask],
               len);
    }
    return data;
}

inline uint8*
MemoryVector::getBlockPtr(const PhysAddress & paddr)
{
    uint32 page_num = paddr.getAddress() >> 12;
    if (m_pages[page_num] == 0) {
        m_pages[page_num] = new uint8[PAGE_SIZE];
        memset(m_pages[page_num], 0, PAGE_SIZE);
    }
    return &m_pages[page_num][paddr.getAddress()&m_page_offset_mask];
}

/*!
 * Function for collating all the pages of the physical memory together.
 * In case a pointer for a page is NULL, this page needs only a single byte
 * to represent that the pointer is NULL. Otherwise, it needs 1 + PAGE_SIZE
 * bytes. The first represents that the page pointer is not NULL, and rest of
 * the bytes represent the data on the page.
 */

inline uint32
MemoryVector::collatePages(uint8* &raw_data)
{
    uint32 num_zero_pages = 0;
    uint32 data_size = 0;

    for (uint32 i = 0;i < m_num_pages; ++i)
    {
        if (m_pages[i] == 0) num_zero_pages++;
    }

    raw_data = new uint8[  sizeof(uint32) /* number of pages*/
                         + m_num_pages /* whether the page is all zeros */
                         + PAGE_SIZE * (m_num_pages - num_zero_pages)];

    /* Write the number of pages to be stored. */
    memcpy(raw_data, &m_num_pages, sizeof(uint32));
    data_size = sizeof(uint32);

    for (uint32 i = 0;i < m_num_pages; ++i)
    {
        if (m_pages[i] == 0) {
            raw_data[data_size] = 0;
        } else {
            raw_data[data_size] = 1;
            memcpy(raw_data + data_size + 1, m_pages[i], PAGE_SIZE);
            data_size += PAGE_SIZE;
        }
        data_size += 1;
    }

    return data_size;
}

/*!
 * Function for populating the pages of the memory using the available raw
 * data. Each page has a byte associate with it, which represents whether the
 * page was NULL or not, when all the pages were collated. The function assumes
 * that the number of pages in the memory are same as those that were recorded
 * in the checkpoint.
 */
inline void
MemoryVector::populatePages(uint8* raw_data)
{
    uint32 data_size = 0;
    uint32 num_pages = 0;

    /* Read the number of pages that were stored. */
    memcpy(&num_pages, raw_data, sizeof(uint32));
    data_size = sizeof(uint32);
    assert(num_pages == m_num_pages);

    for (uint32 i = 0;i < m_num_pages; ++i)
    {
        assert(m_pages[i] == 0);
        if (raw_data[data_size] != 0) {
            m_pages[i] = new uint8[PAGE_SIZE];
            memcpy(m_pages[i], raw_data + data_size + 1, PAGE_SIZE);
            data_size += PAGE_SIZE;
        }
        data_size += 1;
    }
}

#endif // __MEM_RUBY_SYSTEM_MEMORYVECTOR_HH__
<|endoftext|>


<commit_before>/**
 * @author Parikshit Ram (pram@cc.gatech.edu)
 * @file mog_em_main.cpp
 *
 * This program test drives the parametric estimation
 * of a Gaussian Mixture model using maximum likelihood.
 *
 * PARAMETERS TO BE INPUT:
 *
 * --data
 * This is the file that contains the data on which
 * the model is to be fit
 *
 * --mog_em/K
 * This is the number of gaussians we want to fit
 * on the data, defaults to '1'
 *
 * --output
 * This file will contain the parameters estimated,
 * defaults to 'ouotput.csv'
 *
 */
#include "mog_em.hpp"

PROGRAM_INFO("Mixture of Gaussians",
    "This program takes a parametric estimate of a Gaussian mixture model (GMM)"
    " using maximum likelihood.", "mog");

PARAM_STRING_REQ("data", "A file containing the data on which the model has to "
    "be fit.", "mog");
PARAM_STRING("output", "The file into which the output is to be written into.",
    "mog", "output.csv");

using namespace mlpack;
using namespace mlpack::gmm;

int main(int argc, char* argv[]) {
  CLI::ParseCommandLine(argc, argv);

  ////// READING PARAMETERS AND LOADING DATA //////
  arma::mat data_points;
  data::Load(CLI::GetParam<std::string>("mog/data").c_str(), data_points, true);

  ////// MIXTURE OF GAUSSIANS USING EM //////
  MoGEM mog;

  CLI::GetParam<int>("mog/k") = 1;
  CLI::GetParam<int>("mog/d") = data_points.n_rows;

  ////// Timing the initialization of the mixture model //////
  Timers::StartTimer("mog/model_init");
  mog.Init(1, data_points.n_rows);
  Timers::StopTimer("mog/model_init");

  ////// Computing the parameters of the model using the EM algorithm //////
  std::vector<double> results;

  Timers::StartTimer("mog/EM");
  mog.ExpectationMaximization(data_points);
  Timers::StopTimer("mog/EM");

  mog.Display();
  mog.OutputResults(results);

  ////// OUTPUT RESULTS //////
  // We need a better solution for this.  So, currently, we do nothing.
  // XML is probably the right tool for the job.
}
<commit_msg>Flesh out main executable for GMMs.<commit_after>/**
 * @author Parikshit Ram (pram@cc.gatech.edu)
 * @file gmm_main.cpp
 *
 * This program trains a mixture of Gaussians on a given data matrix.
 */
#include "gmm.hpp"

PROGRAM_INFO("GMM",
    "This program takes a parametric estimate of a Gaussian mixture model (GMM)"
    " using the EM algorithm to find the maximum likelihood estimate.", "");

PARAM_STRING_REQ("data", "A file containing the data on which the model has to "
    "be fit.", "gmm");
PARAM_INT("gaussians", "g", "", 1);

using namespace mlpack;
using namespace mlpack::gmm;

int main(int argc, char* argv[]) {
  CLI::ParseCommandLine(argc, argv);

  ////// READING PARAMETERS AND LOADING DATA //////
  arma::mat data_points;
  data::Load(CLI::GetParam<std::string>("gmm/data").c_str(), data_points, true);

  ////// MIXTURE OF GAUSSIANS USING EM //////
  GMM gmm(CLI::GetParam<int>("gaussians"), data_points.n_rows);

  ////// Computing the parameters of the model using the EM algorithm //////
  Timers::StartTimer("gmm/em");
  gmm.ExpectationMaximization(data_points);
  Timers::StopTimer("gmm/em");

  ////// OUTPUT RESULTS //////
  // We need a better solution for this.  So, currently, we do nothing.
  // XML is probably the right tool for the job.
}
<|endoftext|>