<commit_before>/*  Namecoin RPC library.
 *  Copyright (C) 2013  Daniel Kraft <d@domob.eu>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  See the distributed file COPYING for additional permissions in addition
 *  to those of the GNU Affero General Public License.
 */

/* Source code for NamecoinInterface.hpp.  */

#include "NamecoinInterface.hpp"

#include <ctime>
#include <sstream>

namespace nmcrpc
{

/* ************************************************************************** */
/* High-level interface to Namecoin.  */

const unsigned NamecoinInterface::UNLOCK_SECONDS = 3600;

/**
 * Run a simple test command and return whether the connection seems to
 * be up and running.  It also produces a message that can be shown, which
 * either includes the running version or an error string.
 * @param msg Set this to the message string.
 * @return True iff the connection seems to be fine.
 */
bool
NamecoinInterface::testConnection (std::string& msg)
{
  try
    {
      const JsonRpc::JsonData res = rpc.executeRpc ("getinfo");
      int version = res["version"].asInt ();

      unsigned v3 = version % 100;
      version /= 100;
      unsigned v2 = version % 100;
      version /= 100;

      std::ostringstream msgOut;
      msgOut << "Success!  Namecoind version "
             << "0." << version << "." << v2;
      if (v3 > 0)
        msgOut << "." << v3;
      msgOut << " running.";
      msg = msgOut.str ();

      return true;
    }
  catch (const JsonRpc::HttpError& exc)
    {
      std::ostringstream res;
      res << "HTTP-Error (" << exc.getResponseCode () << "): "
          << exc.what ();
      msg = res.str ();
    }
  catch (const JsonRpc::Exception& exc)
    {
      msg = exc.what ();
    }

  return false;
}

/**
 * Query for an address by string.  This immediately checks whether the
 * address is valid and owned by the user, so that this information can be
 * encapsulated into the returned object.
 * @param addr The address to check.
 * @return The created address object.
 */
NamecoinInterface::Address
NamecoinInterface::queryAddress (const std::string& addr)
{
  return Address (rpc, addr);
}

/**
 * Create a new address (as per "getnewaddress") and return it.
 * @return Newly created address.
 */
NamecoinInterface::Address
NamecoinInterface::createAddress ()
{
  const JsonRpc::JsonData addr = rpc.executeRpc ("getnewaddress");
  return Address (rpc, addr.asString ());
}

/**
 * Query for a name by string.  If the name is registered, this immediately
 * queries for the name's associated data.  If the name does not yet exist,
 * this still succeeds and returns a Name object that can be used to find
 * out that fact as well as register the name.
 * @param name The name to check.
 * @return The created name object.
 */
NamecoinInterface::Name
NamecoinInterface::queryName (const std::string& name)
{
  return Name (name, *this, rpc);
}

/**
 * Query for a name by namespace and name.
 * @see queryName (const std::string&)
 * @param ns The namespace.
 * @param name The (namespace-less) name.
 * @return The created name object.
 */
NamecoinInterface::Name
NamecoinInterface::queryName (const std::string& ns, const std::string& name)
{
  std::ostringstream full;
  full << ns << "/" << name;
  return queryName (full.str ());
}

/**
 * Query for the number of confirmations a transaction has.
 * @param txid The transaction id to check for.
 * @return Number of confirmations the transaction has.
 * @throws JsonRpc::RpcError if the tx is not found.
 */
unsigned
NamecoinInterface::getNumberOfConfirmations (const std::string& txid)
{
  const JsonRpc::JsonData res = rpc.executeRpc ("gettransaction", txid);
  assert (res.isObject ());

  return res["confirmations"].asInt ();
}

/**
 * Check whether the wallet needs to be unlocked or not.  This routine is
 * used to decide whether we need to ask for a passphrase or not before
 * using WalletUnlocker.
 * @return True iff we need a passphrase.
 */
bool
NamecoinInterface::needWalletPassphrase ()
{
  const JsonRpc::JsonData res = rpc.executeRpc ("getinfo");

  if (res["unlocked_until"].isNull ())
    return false;

  const int until = res["unlocked_until"].asInt ();
  return (until < std::time (nullptr) + UNLOCK_SECONDS);
}

/* ************************************************************************** */
/* Address object.  */

/**
 * Construct the address.  This is meant to be used only
 * from inside NamecoinInterface.  Outside users should use
 * NamecoinInterface::queryAddress or other methods to obtain
 * address objects.
 * @param r The namecoin interface to use.
 * @param a The address as string.
 */
NamecoinInterface::Address::Address (JsonRpc& r, const std::string& a)
  : rpc(&r), addr(a)
{
  const JsonRpc::JsonData res = rpc->executeRpc ("validateaddress", addr);
  valid = res["isvalid"].asBool ();
  mine = false;
  if (valid)
    mine = res["ismine"].asBool ();
}

/**
 * Check a message signature against this address.  If this address
 * is invalid, false is returned.
 * @param msg The message that should be signed.
 * @param sig The message's signature.
 * @return True iff the signature matches the message.
 */
bool
NamecoinInterface::Address::verifySignature (const std::string& msg,
                                             const std::string& sig) const
{
  if (!valid)
    return false;

  try
    {
      JsonRpc::JsonData res = rpc->executeRpc ("verifymessage", addr, sig, msg);
      return (res.isBool () && res.asBool ());
    }
  catch (const JsonRpc::RpcError& exc)
    {
      // Malformed base64?
      if (exc.getErrorCode () == -5)
        return false;
      throw exc;
    }
}

/**
 * Sign a message with this address.  This may throw if the address
 * is invalid, the wallet locked or the private key is not owned.
 * @param msg The message that should be signed.
 * @return The message's signature.
 * @throws NoPrivateKey if this address is not owned.
 * @throws std::runtime_error if the address is invalid or the wallet locked.
 */
std::string
NamecoinInterface::Address::signMessage (const std::string& msg) const
{
  if (!valid)
    throw std::runtime_error ("Can't sign with invalid address.");

  try
    {
      const JsonRpc::JsonData res = rpc->executeRpc ("signmessage", addr, msg);
      return res.asString ();
    }
  catch (const JsonRpc::RpcError& exc)
    {
      switch (exc.getErrorCode ())
        {
        case -13:
          throw std::runtime_error ("Need to unlock the wallet first.");

        case -3:
          {
            std::ostringstream msg;
            msg << "You don't have the private key of " << addr << " in order"
                << " to sign messages with that address.";
            throw NoPrivateKey (msg.str ());
          }

        default:
          throw exc;
        }
    }
}

/* ************************************************************************** */
/* Name object.  */

/**
 * Construct the name.  This is meant to be used only
 * from inside NamecoinInterface.  Outside users should use
 * NamecoinInterface::queryName or other methods to obtain
 * name objects.
 * @param n The name's string.
 * @param nc NamecoinInterface object.
 * @param rpc The RPC object to use for finding info about the name.
 */
NamecoinInterface::Name::Name (const std::string& n, NamecoinInterface& nc,
                               JsonRpc& rpc)
  : initialised(true), name(n)
{
  try
    {
      data = rpc.executeRpc ("name_show", n);
      ex = true;
      addr = nc.queryAddress (data["address"].asString ());
    }
  catch (const JsonRpc::RpcError& exc)
    {
      if (exc.getErrorCode () == -4)
        {
          ex = false;
          return;
        }
      throw exc;
    }
}

/**
 * Ensure that this object has status "exists".
 * @throws NameNotFound if the name doesn't yet exist.
 */
void
NamecoinInterface::Name::ensureExists () const
{
  if (!ex)
    throw NameNotFound (name);
}

/* ************************************************************************** */
/* Wallet unlocker.  */

/**
 * Construct it, not yet unlocking.
 * @param n The NamecoinInterface to use.
 */
NamecoinInterface::WalletUnlocker::WalletUnlocker (NamecoinInterface& n)
  : rpc(n.rpc), nc(n), unlocked(false)
{
  // Nothing else to do.
}

/**
 * Lock the wallet on destruct.
 */
NamecoinInterface::WalletUnlocker::~WalletUnlocker ()
{
  if (unlocked)
    rpc.executeRpc ("walletlock");
}

/**
 * Perform the unlock (if necessary).  The passphrase must be correct if the
 * wallet is actually locked, and can be anything else.
 * @param passphrase Passphrase to use for unlocking.
 * @throws UnlockFailure if the passphrase is wrong.
 */
void
NamecoinInterface::WalletUnlocker::unlock (const std::string& passphrase)
{
  if (unlocked)
    throw std::runtime_error ("Wallet is already unlocked!");

  unlocked = nc.needWalletPassphrase ();
  if (unlocked)
    {
      /* Ensure the wallet is indeed locked before we send the passphrase.
         It could be the case that it is unlocked although for too short
         a time, then lock it now.  */
      rpc.executeRpc ("walletlock");
      try
        {
          rpc.executeRpc ("walletpassphrase", passphrase, UNLOCK_SECONDS);
        }
      catch (const JsonRpc::RpcError& exc)
        {
          if (exc.getErrorCode () == -14)
            throw UnlockFailure ("Wrong wallet passphrase.");
          throw exc;
        }
    }
}

} // namespace nmcrpc
<commit_msg>Set unlocked = true only in case of success.<commit_after>/*  Namecoin RPC library.
 *  Copyright (C) 2013  Daniel Kraft <d@domob.eu>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  See the distributed file COPYING for additional permissions in addition
 *  to those of the GNU Affero General Public License.
 */

/* Source code for NamecoinInterface.hpp.  */

#include "NamecoinInterface.hpp"

#include <ctime>
#include <sstream>

namespace nmcrpc
{

/* ************************************************************************** */
/* High-level interface to Namecoin.  */

const unsigned NamecoinInterface::UNLOCK_SECONDS = 3600;

/**
 * Run a simple test command and return whether the connection seems to
 * be up and running.  It also produces a message that can be shown, which
 * either includes the running version or an error string.
 * @param msg Set this to the message string.
 * @return True iff the connection seems to be fine.
 */
bool
NamecoinInterface::testConnection (std::string& msg)
{
  try
    {
      const JsonRpc::JsonData res = rpc.executeRpc ("getinfo");
      int version = res["version"].asInt ();

      unsigned v3 = version % 100;
      version /= 100;
      unsigned v2 = version % 100;
      version /= 100;

      std::ostringstream msgOut;
      msgOut << "Success!  Namecoind version "
             << "0." << version << "." << v2;
      if (v3 > 0)
        msgOut << "." << v3;
      msgOut << " running.";
      msg = msgOut.str ();

      return true;
    }
  catch (const JsonRpc::HttpError& exc)
    {
      std::ostringstream res;
      res << "HTTP-Error (" << exc.getResponseCode () << "): "
          << exc.what ();
      msg = res.str ();
    }
  catch (const JsonRpc::Exception& exc)
    {
      msg = exc.what ();
    }

  return false;
}

/**
 * Query for an address by string.  This immediately checks whether the
 * address is valid and owned by the user, so that this information can be
 * encapsulated into the returned object.
 * @param addr The address to check.
 * @return The created address object.
 */
NamecoinInterface::Address
NamecoinInterface::queryAddress (const std::string& addr)
{
  return Address (rpc, addr);
}

/**
 * Create a new address (as per "getnewaddress") and return it.
 * @return Newly created address.
 */
NamecoinInterface::Address
NamecoinInterface::createAddress ()
{
  const JsonRpc::JsonData addr = rpc.executeRpc ("getnewaddress");
  return Address (rpc, addr.asString ());
}

/**
 * Query for a name by string.  If the name is registered, this immediately
 * queries for the name's associated data.  If the name does not yet exist,
 * this still succeeds and returns a Name object that can be used to find
 * out that fact as well as register the name.
 * @param name The name to check.
 * @return The created name object.
 */
NamecoinInterface::Name
NamecoinInterface::queryName (const std::string& name)
{
  return Name (name, *this, rpc);
}

/**
 * Query for a name by namespace and name.
 * @see queryName (const std::string&)
 * @param ns The namespace.
 * @param name The (namespace-less) name.
 * @return The created name object.
 */
NamecoinInterface::Name
NamecoinInterface::queryName (const std::string& ns, const std::string& name)
{
  std::ostringstream full;
  full << ns << "/" << name;
  return queryName (full.str ());
}

/**
 * Query for the number of confirmations a transaction has.
 * @param txid The transaction id to check for.
 * @return Number of confirmations the transaction has.
 * @throws JsonRpc::RpcError if the tx is not found.
 */
unsigned
NamecoinInterface::getNumberOfConfirmations (const std::string& txid)
{
  const JsonRpc::JsonData res = rpc.executeRpc ("gettransaction", txid);
  assert (res.isObject ());

  return res["confirmations"].asInt ();
}

/**
 * Check whether the wallet needs to be unlocked or not.  This routine is
 * used to decide whether we need to ask for a passphrase or not before
 * using WalletUnlocker.
 * @return True iff we need a passphrase.
 */
bool
NamecoinInterface::needWalletPassphrase ()
{
  const JsonRpc::JsonData res = rpc.executeRpc ("getinfo");

  if (res["unlocked_until"].isNull ())
    return false;

  const int until = res["unlocked_until"].asInt ();
  return (until < std::time (nullptr) + UNLOCK_SECONDS);
}

/* ************************************************************************** */
/* Address object.  */

/**
 * Construct the address.  This is meant to be used only
 * from inside NamecoinInterface.  Outside users should use
 * NamecoinInterface::queryAddress or other methods to obtain
 * address objects.
 * @param r The namecoin interface to use.
 * @param a The address as string.
 */
NamecoinInterface::Address::Address (JsonRpc& r, const std::string& a)
  : rpc(&r), addr(a)
{
  const JsonRpc::JsonData res = rpc->executeRpc ("validateaddress", addr);
  valid = res["isvalid"].asBool ();
  mine = false;
  if (valid)
    mine = res["ismine"].asBool ();
}

/**
 * Check a message signature against this address.  If this address
 * is invalid, false is returned.
 * @param msg The message that should be signed.
 * @param sig The message's signature.
 * @return True iff the signature matches the message.
 */
bool
NamecoinInterface::Address::verifySignature (const std::string& msg,
                                             const std::string& sig) const
{
  if (!valid)
    return false;

  try
    {
      JsonRpc::JsonData res = rpc->executeRpc ("verifymessage", addr, sig, msg);
      return (res.isBool () && res.asBool ());
    }
  catch (const JsonRpc::RpcError& exc)
    {
      // Malformed base64?
      if (exc.getErrorCode () == -5)
        return false;
      throw exc;
    }
}

/**
 * Sign a message with this address.  This may throw if the address
 * is invalid, the wallet locked or the private key is not owned.
 * @param msg The message that should be signed.
 * @return The message's signature.
 * @throws NoPrivateKey if this address is not owned.
 * @throws std::runtime_error if the address is invalid or the wallet locked.
 */
std::string
NamecoinInterface::Address::signMessage (const std::string& msg) const
{
  if (!valid)
    throw std::runtime_error ("Can't sign with invalid address.");

  try
    {
      const JsonRpc::JsonData res = rpc->executeRpc ("signmessage", addr, msg);
      return res.asString ();
    }
  catch (const JsonRpc::RpcError& exc)
    {
      switch (exc.getErrorCode ())
        {
        case -13:
          throw std::runtime_error ("Need to unlock the wallet first.");

        case -3:
          {
            std::ostringstream msg;
            msg << "You don't have the private key of " << addr << " in order"
                << " to sign messages with that address.";
            throw NoPrivateKey (msg.str ());
          }

        default:
          throw exc;
        }
    }
}

/* ************************************************************************** */
/* Name object.  */

/**
 * Construct the name.  This is meant to be used only
 * from inside NamecoinInterface.  Outside users should use
 * NamecoinInterface::queryName or other methods to obtain
 * name objects.
 * @param n The name's string.
 * @param nc NamecoinInterface object.
 * @param rpc The RPC object to use for finding info about the name.
 */
NamecoinInterface::Name::Name (const std::string& n, NamecoinInterface& nc,
                               JsonRpc& rpc)
  : initialised(true), name(n)
{
  try
    {
      data = rpc.executeRpc ("name_show", n);
      ex = true;
      addr = nc.queryAddress (data["address"].asString ());
    }
  catch (const JsonRpc::RpcError& exc)
    {
      if (exc.getErrorCode () == -4)
        {
          ex = false;
          return;
        }
      throw exc;
    }
}

/**
 * Ensure that this object has status "exists".
 * @throws NameNotFound if the name doesn't yet exist.
 */
void
NamecoinInterface::Name::ensureExists () const
{
  if (!ex)
    throw NameNotFound (name);
}

/* ************************************************************************** */
/* Wallet unlocker.  */

/**
 * Construct it, not yet unlocking.
 * @param n The NamecoinInterface to use.
 */
NamecoinInterface::WalletUnlocker::WalletUnlocker (NamecoinInterface& n)
  : rpc(n.rpc), nc(n), unlocked(false)
{
  // Nothing else to do.
}

/**
 * Lock the wallet on destruct.
 */
NamecoinInterface::WalletUnlocker::~WalletUnlocker ()
{
  if (unlocked)
    rpc.executeRpc ("walletlock");
}

/**
 * Perform the unlock (if necessary).  The passphrase must be correct if the
 * wallet is actually locked, and can be anything else.
 * @param passphrase Passphrase to use for unlocking.
 * @throws UnlockFailure if the passphrase is wrong.
 */
void
NamecoinInterface::WalletUnlocker::unlock (const std::string& passphrase)
{
  if (unlocked)
    throw std::runtime_error ("Wallet is already unlocked!");

  const bool needPwd = nc.needWalletPassphrase ();
  if (needPwd)
    {
      /* Ensure the wallet is indeed locked before we send the passphrase.
         It could be the case that it is unlocked although for too short
         a time, then lock it now.  */
      rpc.executeRpc ("walletlock");

      try
        {
          rpc.executeRpc ("walletpassphrase", passphrase, UNLOCK_SECONDS);
          unlocked = true;
        }
      catch (const JsonRpc::RpcError& exc)
        {
          if (exc.getErrorCode () == -14)
            throw UnlockFailure ("Wrong wallet passphrase.");
          throw exc;
        }
    }
}

} // namespace nmcrpc
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * 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.
 */

#include "otbImage.h"
#include "otbImageFileReader.h"
#include "itkScalableAffineTransform.h"
#include "otbStreamingResampleImageFilter.h"
#include "otbBCOInterpolateImageFunction.h"
#include "otbKeyPointSetsMatchingFilter.h"
#include "otbSiftFastImageFilter.h"
#include "otbImageToSIFTKeyPointSetFilter.h"
#include "otbImageToSURFKeyPointSetFilter.h"
#include "itkPointSet.h"

#include <tuple>

using ImageType          = otb::Image<double>;
using ReaderType         = otb::ImageFileReader<ImageType>;
using TransformType      = itk::ScalableAffineTransform<double, 2>;
using ResamplerType      = otb::StreamingResampleImageFilter<ImageType, ImageType, double>;
using InterpolatorType   = otb::BCOInterpolateImageFunction<ImageType, double>;
using VectorType         = itk::VariableLengthVector<double>;
using PointSetType       = itk::PointSet<VectorType, 2>;
using SiftFastFilterType = otb::SiftFastImageFilter<ImageType, PointSetType>;
using SiftFilterType     = otb::ImageToSIFTKeyPointSetFilter<ImageType, PointSetType>;
using SurfFilterType     = otb::ImageToSURFKeyPointSetFilter<ImageType, PointSetType>;
using MatchingFilterType = otb::KeyPointSetsMatchingFilter<PointSetType>;

auto printResult = [](bool value) { return value ? "Ok" : "Nok"; };

bool testMatchingFilter()
{
  auto ps1 = PointSetType::New();
  auto ps2 = PointSetType::New();

  PointSetType::PointType p1,p2,p3,p4,p5,p6;
  p1.Fill(1.);
  p2.Fill(2.);
  p3.Fill(3.);
  p4.Fill(4.);
  p5.Fill(5.);
  p6.Fill(6.);
  ps1->SetPoint(0, p1);
  ps1->SetPoint(1, p2);
  ps1->SetPoint(2, p3);
  ps2->SetPoint(0, p4);
  ps2->SetPoint(1, p5);
  ps2->SetPoint(2, p6);

  VectorType d1(1), d2(1), d3(1), d4(1),d5(1), d6(1);
  d1[0]=0.7;
  d2[0]=0.8;
  d3[0]=10.;
  d4[0]=0.;
  d5[0]=1.;
  d6[0]=11.;

  ps1->SetPointData(0, d1);
  ps1->SetPointData(1, d2);
  ps1->SetPointData(2, d3);
  ps2->SetPointData(0, d4);
  ps2->SetPointData(1, d5);
  ps2->SetPointData(2, d6);

  auto filter1 = MatchingFilterType::New();
  filter1->SetDistanceThreshold(0.6);
  filter1->SetUseBackMatching(false);
  filter1->SetInput1(ps1);
  filter1->SetInput2(ps2);
  filter1->Update();

  auto matches1 = filter1->GetOutput();

  std::cout<<"Matches without backmatching: "<<std::endl;

  for (auto it = matches1->Begin(); it != matches1->End(); ++it)
    {
    std::cout<<it.Get()->GetPoint1()<<" <-> "<<it.Get()->GetPoint2()<<std::endl;
    }

  auto filter2 = MatchingFilterType::New();
  filter2->SetDistanceThreshold(0.6);
  filter2->SetUseBackMatching(true);
  filter2->SetInput1(ps1);
  filter2->SetInput2(ps2);
  filter2->Update();

  auto matches2 = filter2->GetOutput();

  std::cout<<"Matches with backmatching: "<<std::endl;

  for (auto it = matches2->Begin(); it != matches2->End(); ++it)
    {
    std::cout<<it.Get()->GetPoint1()<<" <-> "<<it.Get()->GetPoint2()<<std::endl;
    }

  bool success = true;

  // Without backmatching, matches should be:
  // p1 <-> p5
  // p2 <-> p5
  // p3 <-> p6
  unsigned int nb_matches = matches1->Size();

  bool test = nb_matches == 3;
  std::cout<<"Without backmatching, the number of matches is 3:\t"<<printResult(test)<<std::endl;
  success = success && test;

  test = nb_matches > 1 && matches1->GetNthElement(0)->GetPoint1() == p1 && matches1->GetNthElement(0)->GetPoint2() == p5;
  std::cout<<"Without backmatching, p1 matches with p5:\t\t"<<printResult(test)<<std::endl;
  success = success && test;

  test = nb_matches > 0 && matches1->GetNthElement(1)->GetPoint1() == p2 && matches1->GetNthElement(1)->GetPoint2() == p5;
  std::cout<<"Without backmatching, p2 matches with p5:\t\t"<<printResult(test)<<std::endl;
  success = success && test;

  test = nb_matches > 2 && matches1->GetNthElement(2)->GetPoint1() == p3 && matches1->GetNthElement(2)->GetPoint2() == p6;
  std::cout<<"Without backmatching, p3 matches with p6:\t\t"<<printResult(test)<<std::endl;
  success = success && test;

  // With back-matching there should be only 2 matches:
  // p2 <-> p5
  // p3 <-> p6
  test = matches2->Size() == 2;
  std::cout<<"With backmatching, the number of matches is 2:\t"<<printResult(test)<<std::endl;
  success = success && test;

  test = matches2->GetNthElement(0)->GetPoint1() == p2 && matches2->GetNthElement(0)->GetPoint2() == p5;
  std::cout<<"With backmatching, p2 matches with p5:\t\t"<<printResult(test)<<std::endl;
  success = success && test;

  test = matches2->GetNthElement(1)->GetPoint1() == p3 && matches2->GetNthElement(1)->GetPoint2() == p6;
  std::cout<<"With backmatching, p3 matches with p6:\t\t"<<printResult(test)<<std::endl;
  success = success && test;

  return success;
}

/** Generate a pair of images, one beeing slightly warped wrt the
 * other */
auto generateImagePair(const std::string& infname, double rotation, double scaling)
{
  // Read reference image
  auto reader = ReaderType::New();
  reader->SetFileName(infname);
  reader->Update();
  ImageType::Pointer reference = reader->GetOutput();

  // Create secondary image

  // Setup transform
  auto transform = TransformType::New();

  // Set rotation center as image center
  auto origin  = reference->GetOrigin();
  auto spacing = reference->GetSpacing();
  auto size    = reference->GetLargestPossibleRegion().GetSize();
  auto center  = origin;
  center[0] += 0.5 * spacing[0] * size[0];
  center[1] += 0.5 * spacing[1] * size[1];
  transform->SetCenter(center);

  // Set rotation angle
  transform->Rotate2D(rotation * otb::CONST_PI_180);

  // Set scale
  ImageType::SpacingType scalingVector;
  scalingVector.Fill(scaling);
  transform->Scale(scalingVector);

  // Invert transform
  auto inverse = TransformType::New();
  bool ok      = transform->GetInverse(inverse);
  if (!ok)
    throw std::logic_error("Could not inverse transform");
  // Setup interpolator
  auto interpolator = InterpolatorType::New();

  // Setup resampler
  auto resampler = ResamplerType::New();
  resampler->SetInput(reference);
  resampler->SetTransform(transform);
  resampler->SetInterpolator(interpolator);

  // Since rotation and scaling are small, use same image parameter
  // for secondary image
  resampler->SetOutputOrigin(origin);
  resampler->SetOutputSize(size);
  resampler->SetOutputSpacing(spacing);
  resampler->Update();
  ImageType::Pointer secondary = resampler->GetOutput();

  return std::make_tuple(reference, secondary, transform);
}

/** Perform checks for one keypoints algorithm */
template <typename TKeyPointsFilter, typename TParameterSetter>
bool checkKeyPointsFilter(const ImageType* reference, const ImageType* secondary, const TransformType* transform, const TParameterSetter& configureFilter,
                          double match_rate_thresh, double good_match_rate_thresh)
{
  // Keypoints on first image
  auto filterReference = TKeyPointsFilter::New();
  filterReference->SetInput(reference);
  configureFilter(filterReference);

  // Keypoints on secondary image
  auto filterSecondary = TKeyPointsFilter::New();
  filterSecondary->SetInput(secondary);
  configureFilter(filterSecondary);

  // Match keypoints
  auto matcher = MatchingFilterType::New();
  matcher->SetUseBackMatching(false);
  matcher->SetDistanceThreshold(0.6);
  matcher->SetInput1(filterReference->GetOutput());
  matcher->SetInput2(filterSecondary->GetOutput());
  matcher->Update();

  PointSetType::Pointer referencePoints   = filterReference->GetOutput();
  const size_t          nbReferencePoints = referencePoints->GetPoints()->Size();

  typename PointSetType::Pointer secondaryPoints   = filterSecondary->GetOutput();
  const size_t                   nbSecondaryPoints = secondaryPoints->GetPoints()->Size();

  std::cout << "Found " << nbReferencePoints << " points in reference image and " << nbSecondaryPoints << " points in secondary image" << std::endl;

  auto         matches    = matcher->GetOutput();
  const size_t nb_matches = matches->Size();

  size_t       good_matches             = 0;
  const double threshold_for_good_match = 0.5; // pixels

  // Count good and bad matches
  for (auto it = matches->Begin(); it != matches->End(); ++it)
  {
    const auto p1 = it.Get()->GetPoint1();
    const auto p2 = it.Get()->GetPoint2();

    const auto p2_mapped = transform->TransformPoint(p2);

    // Check that matches are good up to 0.1 pixel
    if ((p1[0] - p2_mapped[0]) * (p1[0] - p2_mapped[0]) + (p1[1] - p2_mapped[1]) * (p1[1] - p2_mapped[1]) <=
        threshold_for_good_match * threshold_for_good_match)
      ++good_matches;
  }

  // Performances metrics
  const float reference_match_rate = nb_matches / static_cast<float>(nbReferencePoints);
  const float secondary_match_rate = nb_matches / static_cast<float>(nbSecondaryPoints);
  const float good_match_rate      = good_matches / static_cast<float>(nb_matches);

  std::cout << "Found " << nb_matches << " matches with " << good_matches << " valid matches (tolerance of 0.5 pixels)" << std::endl;

  // Quality gate
  bool current_test   = reference_match_rate > match_rate_thresh;
  bool overall_status = current_test;
  std::cout << "More than " << 100 * match_rate_thresh << "% of reference points have a match:\t" << printResult(current_test) << " ("
            << 100 * reference_match_rate << "%)" << std::endl;

  current_test = secondary_match_rate > match_rate_thresh;
  std::cout << "More than " << 100 * match_rate_thresh << "% of secondary points have a match:\t" << printResult(current_test) << " ("
            << 100 * secondary_match_rate << "%)" << std::endl;
  overall_status = overall_status && current_test;

  current_test = good_match_rate > good_match_rate_thresh;
  std::cout << "More than " << good_match_rate_thresh * 100 << "% of matches are good:             \t" << printResult(current_test) << " ("
            << 100 * good_match_rate << "% at 0.5 pixel accuracy)"
            << "\n";
  overall_status = overall_status && current_test;
  return overall_status;
}

int otbKeyPointsAlgorithmsTest(int argc, char* argv[])
{
  if (argc != 2)
  {
    std::cerr << "Usage: " << argv[0] << " infname" << std::endl;
    return EXIT_FAILURE;
  }
  const char* infname = argv[1];

  // Generate reference and secondary image
  ImageType::Pointer     reference, secondary;
  TransformType::Pointer transform;

  // Small rotation and scaling
  const double rotation = 2.5; // 5°
  const double scaling  = 0.99;

  // First test matching filter alone
  std::cout<<"Checking matching filter:"<<std::endl;
  std::cout<<"========================="<<std::endl;
  bool status = testMatchingFilter();

  std::tie(reference, secondary, transform) = generateImagePair(infname, rotation, scaling);

  std::cout << "Secondary image generated by applying a rotation of " << rotation << " degrees and scaling of " << scaling << "." << std::endl;

  // Test Surf filter
  std::cout << "Checking Surf implementation:" << std::endl;
  std::cout << "=============================" << std::endl;

  // Lambda to configure surf algorithm
  auto configureSurf = [](SurfFilterType* filter) {
    filter->SetOctavesNumber(4);
    filter->SetScalesNumber(8);
  };

  status = checkKeyPointsFilter<SurfFilterType>(reference, secondary, transform, configureSurf, 0.15, 0.65) && status;

  // Test Sift filter
  std::cout << "Checking Sift implementation:" << std::endl;
  std::cout << "=============================" << std::endl;

  // Lambda to configure sift algorithm
  auto configureSift = [](SiftFilterType* filter) {
    filter->SetOctavesNumber(4);
    filter->SetScalesNumber(8);
    filter->SetDoGThreshold(0.01);
    filter->SetEdgeThreshold(10.);
  };

  status = checkKeyPointsFilter<SiftFilterType>(reference, secondary, transform, configureSift, 0.45, 0.85) && status;

#ifdef OTB_USE_SIFTFAST
  // Test SiftFast filter
  std::cout << "Checking SiftFast implementation:" << std::endl;
  std::cout << "=================================" << std::endl;

  // lambda to set specific filter parameter
  auto configureSiftFast = [](SiftFastFilterType* filter) { filter->SetScalesNumber(8); };

  status = checkKeyPointsFilter<SiftFastFilterType>(reference, secondary, transform, configureSiftFast, 0.65, 0.95) && status;
#endif

  return status ? EXIT_SUCCESS : EXIT_FAILURE;
}
<commit_msg>TEST: Reduce input size for better test duration<commit_after>/*
 * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * 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.
 */

#include "otbImage.h"
#include "otbImageFileReader.h"
#include "otbExtractROI.h"
#include "itkScalableAffineTransform.h"
#include "otbStreamingResampleImageFilter.h"
#include "otbBCOInterpolateImageFunction.h"
#include "otbKeyPointSetsMatchingFilter.h"
#include "otbSiftFastImageFilter.h"
#include "otbImageToSIFTKeyPointSetFilter.h"
#include "otbImageToSURFKeyPointSetFilter.h"
#include "itkPointSet.h"

#include <tuple>

using ImageType          = otb::Image<double>;
using ReaderType         = otb::ImageFileReader<ImageType>;
using ExtractType        = otb::ExtractROI<double,double>;
using TransformType      = itk::ScalableAffineTransform<double, 2>;
using ResamplerType      = otb::StreamingResampleImageFilter<ImageType, ImageType, double>;
using InterpolatorType   = otb::BCOInterpolateImageFunction<ImageType, double>;
using VectorType         = itk::VariableLengthVector<double>;
using PointSetType       = itk::PointSet<VectorType, 2>;
using SiftFastFilterType = otb::SiftFastImageFilter<ImageType, PointSetType>;
using SiftFilterType     = otb::ImageToSIFTKeyPointSetFilter<ImageType, PointSetType>;
using SurfFilterType     = otb::ImageToSURFKeyPointSetFilter<ImageType, PointSetType>;
using MatchingFilterType = otb::KeyPointSetsMatchingFilter<PointSetType>;

auto printResult = [](bool value) { return value ? "Ok" : "Nok"; };

bool testMatchingFilter()
{
  auto ps1 = PointSetType::New();
  auto ps2 = PointSetType::New();

  PointSetType::PointType p1,p2,p3,p4,p5,p6;
  p1.Fill(1.);
  p2.Fill(2.);
  p3.Fill(3.);
  p4.Fill(4.);
  p5.Fill(5.);
  p6.Fill(6.);
  ps1->SetPoint(0, p1);
  ps1->SetPoint(1, p2);
  ps1->SetPoint(2, p3);
  ps2->SetPoint(0, p4);
  ps2->SetPoint(1, p5);
  ps2->SetPoint(2, p6);

  VectorType d1(1), d2(1), d3(1), d4(1),d5(1), d6(1);
  d1[0]=0.7;
  d2[0]=0.8;
  d3[0]=10.;
  d4[0]=0.;
  d5[0]=1.;
  d6[0]=11.;

  ps1->SetPointData(0, d1);
  ps1->SetPointData(1, d2);
  ps1->SetPointData(2, d3);
  ps2->SetPointData(0, d4);
  ps2->SetPointData(1, d5);
  ps2->SetPointData(2, d6);

  auto filter1 = MatchingFilterType::New();
  filter1->SetDistanceThreshold(0.6);
  filter1->SetUseBackMatching(false);
  filter1->SetInput1(ps1);
  filter1->SetInput2(ps2);
  filter1->Update();

  auto matches1 = filter1->GetOutput();

  std::cout<<"Matches without backmatching: "<<std::endl;

  for (auto it = matches1->Begin(); it != matches1->End(); ++it)
    {
    std::cout<<it.Get()->GetPoint1()<<" <-> "<<it.Get()->GetPoint2()<<std::endl;
    }

  auto filter2 = MatchingFilterType::New();
  filter2->SetDistanceThreshold(0.6);
  filter2->SetUseBackMatching(true);
  filter2->SetInput1(ps1);
  filter2->SetInput2(ps2);
  filter2->Update();

  auto matches2 = filter2->GetOutput();

  std::cout<<"Matches with backmatching: "<<std::endl;

  for (auto it = matches2->Begin(); it != matches2->End(); ++it)
    {
    std::cout<<it.Get()->GetPoint1()<<" <-> "<<it.Get()->GetPoint2()<<std::endl;
    }

  bool success = true;

  // Without backmatching, matches should be:
  // p1 <-> p5
  // p2 <-> p5
  // p3 <-> p6
  unsigned int nb_matches = matches1->Size();

  bool test = nb_matches == 3;
  std::cout<<"Without backmatching, the number of matches is 3:\t"<<printResult(test)<<std::endl;
  success = success && test;

  test = nb_matches > 1 && matches1->GetNthElement(0)->GetPoint1() == p1 && matches1->GetNthElement(0)->GetPoint2() == p5;
  std::cout<<"Without backmatching, p1 matches with p5:\t\t"<<printResult(test)<<std::endl;
  success = success && test;

  test = nb_matches > 0 && matches1->GetNthElement(1)->GetPoint1() == p2 && matches1->GetNthElement(1)->GetPoint2() == p5;
  std::cout<<"Without backmatching, p2 matches with p5:\t\t"<<printResult(test)<<std::endl;
  success = success && test;

  test = nb_matches > 2 && matches1->GetNthElement(2)->GetPoint1() == p3 && matches1->GetNthElement(2)->GetPoint2() == p6;
  std::cout<<"Without backmatching, p3 matches with p6:\t\t"<<printResult(test)<<std::endl;
  success = success && test;

  // With back-matching there should be only 2 matches:
  // p2 <-> p5
  // p3 <-> p6
  test = matches2->Size() == 2;
  std::cout<<"With backmatching, the number of matches is 2:\t"<<printResult(test)<<std::endl;
  success = success && test;

  test = matches2->GetNthElement(0)->GetPoint1() == p2 && matches2->GetNthElement(0)->GetPoint2() == p5;
  std::cout<<"With backmatching, p2 matches with p5:\t\t"<<printResult(test)<<std::endl;
  success = success && test;

  test = matches2->GetNthElement(1)->GetPoint1() == p3 && matches2->GetNthElement(1)->GetPoint2() == p6;
  std::cout<<"With backmatching, p3 matches with p6:\t\t"<<printResult(test)<<std::endl;
  success = success && test;

  return success;
}

/** Generate a pair of images, one beeing slightly warped wrt the
 * other */
auto generateImagePair(const std::string& infname, double rotation, double scaling)
{
  // Read reference image
  auto reader = ReaderType::New();
  reader->SetFileName(infname);

  auto extractor = ExtractType::New();
  extractor->SetInput(reader->GetOutput());
  extractor->SetSizeX(50);
  extractor->SetSizeY(50);
  extractor->Update();

  ImageType::Pointer reference = extractor->GetOutput();

  // Create secondary image

  // Setup transform
  auto transform = TransformType::New();

  // Set rotation center as image center
  auto origin  = reference->GetOrigin();
  auto spacing = reference->GetSpacing();
  auto size    = reference->GetLargestPossibleRegion().GetSize();
  auto center  = origin;
  center[0] += 0.5 * spacing[0] * size[0];
  center[1] += 0.5 * spacing[1] * size[1];
  transform->SetCenter(center);

  // Set rotation angle
  transform->Rotate2D(rotation * otb::CONST_PI_180);

  // Set scale
  ImageType::SpacingType scalingVector;
  scalingVector.Fill(scaling);
  transform->Scale(scalingVector);

  // Invert transform
  auto inverse = TransformType::New();
  bool ok      = transform->GetInverse(inverse);
  if (!ok)
    throw std::logic_error("Could not inverse transform");
  // Setup interpolator
  auto interpolator = InterpolatorType::New();

  // Setup resampler
  auto resampler = ResamplerType::New();
  resampler->SetInput(reference);
  resampler->SetTransform(transform);
  resampler->SetInterpolator(interpolator);

  // Since rotation and scaling are small, use same image parameter
  // for secondary image
  resampler->SetOutputOrigin(origin);
  resampler->SetOutputSize(size);
  resampler->SetOutputSpacing(spacing);
  resampler->Update();
  ImageType::Pointer secondary = resampler->GetOutput();

  return std::make_tuple(reference, secondary, transform);
}

/** Perform checks for one keypoints algorithm */
template <typename TKeyPointsFilter, typename TParameterSetter>
bool checkKeyPointsFilter(const ImageType* reference, const ImageType* secondary, const TransformType* transform, const TParameterSetter& configureFilter,
                          double match_rate_thresh, double good_match_rate_thresh)
{
  // Keypoints on first image
  auto filterReference = TKeyPointsFilter::New();
  filterReference->SetInput(reference);
  configureFilter(filterReference);

  // Keypoints on secondary image
  auto filterSecondary = TKeyPointsFilter::New();
  filterSecondary->SetInput(secondary);
  configureFilter(filterSecondary);

  // Match keypoints
  auto matcher = MatchingFilterType::New();
  matcher->SetUseBackMatching(false);
  matcher->SetDistanceThreshold(0.6);
  matcher->SetInput1(filterReference->GetOutput());
  matcher->SetInput2(filterSecondary->GetOutput());
  matcher->Update();

  PointSetType::Pointer referencePoints   = filterReference->GetOutput();
  const size_t          nbReferencePoints = referencePoints->GetPoints()->Size();

  typename PointSetType::Pointer secondaryPoints   = filterSecondary->GetOutput();
  const size_t                   nbSecondaryPoints = secondaryPoints->GetPoints()->Size();

  std::cout << "Found " << nbReferencePoints << " points in reference image and " << nbSecondaryPoints << " points in secondary image" << std::endl;

  auto         matches    = matcher->GetOutput();
  const size_t nb_matches = matches->Size();

  size_t       good_matches             = 0;
  const double threshold_for_good_match = 0.5; // pixels

  // Count good and bad matches
  for (auto it = matches->Begin(); it != matches->End(); ++it)
  {
    const auto p1 = it.Get()->GetPoint1();
    const auto p2 = it.Get()->GetPoint2();

    const auto p2_mapped = transform->TransformPoint(p2);

    // Check that matches are good up to 0.1 pixel
    if ((p1[0] - p2_mapped[0]) * (p1[0] - p2_mapped[0]) + (p1[1] - p2_mapped[1]) * (p1[1] - p2_mapped[1]) <=
        threshold_for_good_match * threshold_for_good_match)
      ++good_matches;
  }

  // Performances metrics
  const float reference_match_rate = nb_matches / static_cast<float>(nbReferencePoints);
  const float secondary_match_rate = nb_matches / static_cast<float>(nbSecondaryPoints);
  const float good_match_rate      = good_matches / static_cast<float>(nb_matches);

  std::cout << "Found " << nb_matches << " matches with " << good_matches << " valid matches (tolerance of 0.5 pixels)" << std::endl;

  // Quality gate
  bool current_test   = reference_match_rate > match_rate_thresh;
  bool overall_status = current_test;
  std::cout << "More than " << 100 * match_rate_thresh << "% of reference points have a match:\t" << printResult(current_test) << " ("
            << 100 * reference_match_rate << "%)" << std::endl;

  current_test = secondary_match_rate > match_rate_thresh;
  std::cout << "More than " << 100 * match_rate_thresh << "% of secondary points have a match:\t" << printResult(current_test) << " ("
            << 100 * secondary_match_rate << "%)" << std::endl;
  overall_status = overall_status && current_test;

  current_test = good_match_rate > good_match_rate_thresh;
  std::cout << "More than " << good_match_rate_thresh * 100 << "% of matches are good:             \t" << printResult(current_test) << " ("
            << 100 * good_match_rate << "% at 0.5 pixel accuracy)"
            << "\n";
  overall_status = overall_status && current_test;
  return overall_status;
}

int otbKeyPointsAlgorithmsTest(int argc, char* argv[])
{
  if (argc != 2)
  {
    std::cerr << "Usage: " << argv[0] << " infname" << std::endl;
    return EXIT_FAILURE;
  }
  const char* infname = argv[1];

  // Generate reference and secondary image
  ImageType::Pointer     reference, secondary;
  TransformType::Pointer transform;

  // Small rotation and scaling
  const double rotation = 2.5; // 5°
  const double scaling  = 0.99;

  // First test matching filter alone
  std::cout<<"Checking matching filter:"<<std::endl;
  std::cout<<"========================="<<std::endl;
  bool status = testMatchingFilter();

  std::tie(reference, secondary, transform) = generateImagePair(infname, rotation, scaling);

  std::cout << "Secondary image generated by applying a rotation of " << rotation << " degrees and scaling of " << scaling << "." << std::endl;

  // Test Surf filter
  std::cout << "Checking Surf implementation:" << std::endl;
  std::cout << "=============================" << std::endl;

  // Lambda to configure surf algorithm
  auto configureSurf = [](SurfFilterType* filter) {
    filter->SetOctavesNumber(4);
    filter->SetScalesNumber(8);
  };

  status = checkKeyPointsFilter<SurfFilterType>(reference, secondary, transform, configureSurf, 0.13, 0.64) && status;

  // Test Sift filter
  std::cout << "Checking Sift implementation:" << std::endl;
  std::cout << "=============================" << std::endl;

  // Lambda to configure sift algorithm
  auto configureSift = [](SiftFilterType* filter) {
    filter->SetOctavesNumber(4);
    filter->SetScalesNumber(8);
    filter->SetDoGThreshold(0.01);
    filter->SetEdgeThreshold(10.);
  };

  status = checkKeyPointsFilter<SiftFilterType>(reference, secondary, transform, configureSift, 0.44, 0.82) && status;

#ifdef OTB_USE_SIFTFAST
  // Test SiftFast filter
  std::cout << "Checking SiftFast implementation:" << std::endl;
  std::cout << "=================================" << std::endl;

  // lambda to set specific filter parameter
  auto configureSiftFast = [](SiftFastFilterType* filter) { filter->SetScalesNumber(8); };

  status = checkKeyPointsFilter<SiftFastFilterType>(reference, secondary, transform, configureSiftFast, 0.73, 0.95) && status;
#endif

  return status ? EXIT_SUCCESS : EXIT_FAILURE;
}
<|endoftext|>


<commit_before>// Copyright (c) 2020 The Orbit Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "TopDownWidget.h"

#include <QColor>
#include <QMenu>

#include "App.h"
#include "FunctionsDataView.h"
#include "TopDownViewItemModel.h"

using orbit_client_protos::FunctionInfo;

void TopDownWidget::SetTopDownView(std::unique_ptr<TopDownView> top_down_view) {
  CHECK(app_ != nullptr);

  model_ = std::make_unique<TopDownViewItemModel>(std::move(top_down_view));
  search_proxy_model_ = std::make_unique<HighlightCustomFilterSortFilterProxyModel>(nullptr);
  search_proxy_model_->setSourceModel(model_.get());
  search_proxy_model_->setSortRole(Qt::EditRole);

  hooked_proxy_model_ = std::make_unique<HookedIdentityProxyModel>(app_, nullptr);
  hooked_proxy_model_->setSourceModel(search_proxy_model_.get());

  ui_->topDownTreeView->setModel(hooked_proxy_model_.get());
  ui_->topDownTreeView->sortByColumn(TopDownViewItemModel::kInclusive, Qt::DescendingOrder);
  ui_->topDownTreeView->header()->resizeSections(QHeaderView::ResizeToContents);

  onSearchLineEditTextEdited(ui_->searchLineEdit->text());
}

static void CopyFromIndices(OrbitApp* app, const QModelIndexList& indices) {
  std::string clipboard;
  std::optional<QModelIndex> prev_index;
  // Note: indices are sorted by row in order of selection and then by column in ascending order.
  for (const QModelIndex& index : indices) {
    if (prev_index.has_value()) {
      // row() is the position among siblings: also compare parent().
      if (index.row() != prev_index->row() || index.parent() != prev_index->parent()) {
        clipboard += "\n";
      } else {
        clipboard += ", ";
      }
    }
    clipboard += index.data().toString().toStdString();
    prev_index = index;
  }
  app->SetClipboard(clipboard);
}

void TopDownWidget::onCopyKeySequencePressed() {
  CopyFromIndices(app_, ui_->topDownTreeView->selectionModel()->selectedIndexes());
}

const QString TopDownWidget::kActionExpandRecursively = QStringLiteral("&Expand recursively");
const QString TopDownWidget::kActionCollapseRecursively = QStringLiteral("&Collapse recursively");
const QString TopDownWidget::kActionCollapseChildrenRecursively =
    QStringLiteral("Collapse children recursively");
const QString TopDownWidget::kActionExpandAll = QStringLiteral("Expand all");
const QString TopDownWidget::kActionCollapseAll = QStringLiteral("Collapse all");
const QString TopDownWidget::kActionLoadSymbols = QStringLiteral("&Load Symbols");
const QString TopDownWidget::kActionSelect = QStringLiteral("&Hook");
const QString TopDownWidget::kActionDeselect = QStringLiteral("&Unhook");
const QString TopDownWidget::kActionDisassembly = QStringLiteral("Go to &Disassembly");
const QString TopDownWidget::kActionCopySelection = QStringLiteral("Copy Selection");

static void ExpandRecursively(QTreeView* tree_view, const QModelIndex& index) {
  if (!index.isValid()) {
    return;
  }
  for (int i = 0; i < index.model()->rowCount(index); ++i) {
    const QModelIndex& child = index.child(i, 0);
    ExpandRecursively(tree_view, child);
  }
  if (!tree_view->isExpanded(index)) {
    tree_view->expand(index);
  }
}

static void CollapseRecursively(QTreeView* tree_view, const QModelIndex& index) {
  if (!index.isValid()) {
    return;
  }
  for (int i = 0; i < index.model()->rowCount(index); ++i) {
    const QModelIndex& child = index.child(i, 0);
    CollapseRecursively(tree_view, child);
  }
  if (tree_view->isExpanded(index)) {
    tree_view->collapse(index);
  }
}

static void CollapseChildrenRecursively(QTreeView* tree_view, const QModelIndex& index) {
  if (!index.isValid()) {
    return;
  }
  for (int i = 0; i < index.model()->rowCount(index); ++i) {
    const QModelIndex& child = index.child(i, 0);
    CollapseRecursively(tree_view, child);
  }
}

static std::vector<std::shared_ptr<Module>> GetModulesFromIndices(
    OrbitApp* app, const std::vector<QModelIndex>& indices) {
  const std::shared_ptr<Process>& process = app->GetCaptureData().process();
  CHECK(process != nullptr);

  std::set<std::string> unique_module_paths;
  for (const auto& index : indices) {
    std::string module_path =
        index.model()
            ->index(index.row(), TopDownViewItemModel::kModule, index.parent())
            .data(TopDownViewItemModel::kModulePathRole)
            .toString()
            .toStdString();
    unique_module_paths.insert(module_path);
  }

  std::vector<std::shared_ptr<Module>> modules;
  for (const std::string& module_path : unique_module_paths) {
    std::shared_ptr<Module> module = process->GetModuleFromPath(module_path);
    if (module != nullptr) {
      modules.emplace_back(std::move(module));
    }
  }
  return modules;
}

static std::vector<FunctionInfo*> GetFunctionsFromIndices(OrbitApp* app,
                                                          const std::vector<QModelIndex>& indices) {
  const std::shared_ptr<Process>& process = app->GetCaptureData().process();
  CHECK(process != nullptr);

  absl::flat_hash_set<FunctionInfo*> functions_set;
  for (const auto& index : indices) {
    uint64_t absolute_address =
        index.model()
            ->index(index.row(), TopDownViewItemModel::kFunctionAddress, index.parent())
            .data(Qt::EditRole)
            .toLongLong();
    FunctionInfo* function = process->GetFunctionFromAddress(absolute_address);
    if (function != nullptr) {
      functions_set.insert(function);
    }
  }
  return std::vector<FunctionInfo*>(functions_set.begin(), functions_set.end());
}

void TopDownWidget::onCustomContextMenuRequested(const QPoint& point) {
  QModelIndex index = ui_->topDownTreeView->indexAt(point);
  if (!index.isValid()) {
    return;
  }

  std::vector<QModelIndex> selected_tree_indices;
  for (const QModelIndex& selected_index :
       ui_->topDownTreeView->selectionModel()->selectedIndexes()) {
    if (selected_index.column() != TopDownViewItemModel::kThreadOrFunction) {
      continue;
    }
    selected_tree_indices.push_back(selected_index);
  }

  bool enable_expand_recursively = false;
  bool enable_collapse_recursively = false;
  for (const QModelIndex& selected_index : selected_tree_indices) {
    if (selected_index.model()->rowCount(selected_index) > 0) {
      // As long as at least one of the selected nodes has children, always show
      // "Expand recursively", as even if the selected node is expanded there
      // could be subtrees not expanded. But only show "Collapse recursively"
      // and "Collapse children recursively" when at least one selected node is
      // expanded, as it would otherwise be unintuitive to collapse subtrees
      // none of which is visible.
      enable_expand_recursively = true;
      if (ui_->topDownTreeView->isExpanded(selected_index)) {
        enable_collapse_recursively = true;
      }
    }
  }

  std::vector<std::shared_ptr<Module>> modules_to_load;
  for (const auto& module : GetModulesFromIndices(app_, selected_tree_indices)) {
    if (!module->IsLoaded()) {
      modules_to_load.push_back(module);
    }
  }
  bool enable_load = !modules_to_load.empty();

  std::vector<FunctionInfo*> functions = GetFunctionsFromIndices(app_, selected_tree_indices);
  bool enable_select = false;
  bool enable_deselect = false;
  for (FunctionInfo* function : functions) {
    enable_select |= !app_->IsFunctionSelected(*function);
    enable_deselect |= app_->IsFunctionSelected(*function);
  }

  bool enable_disassembly = !functions.empty();

  bool enable_copy = ui_->topDownTreeView->selectionModel()->hasSelection();

  QMenu menu{ui_->topDownTreeView};
  if (enable_expand_recursively) {
    menu.addAction(kActionExpandRecursively);
  }
  if (enable_collapse_recursively) {
    menu.addAction(kActionCollapseRecursively);
    menu.addAction(kActionCollapseChildrenRecursively);
  }
  menu.addSeparator();
  menu.addAction(kActionExpandAll);
  menu.addAction(kActionCollapseAll);
  menu.addSeparator();
  if (enable_load) {
    menu.addAction(kActionLoadSymbols);
  }
  if (enable_select) {
    menu.addAction(kActionSelect);
  }
  if (enable_deselect) {
    menu.addAction(kActionDeselect);
  }
  if (enable_disassembly) {
    menu.addAction(kActionDisassembly);
  }
  menu.addSeparator();
  if (enable_copy) {
    menu.addAction(kActionCopySelection);
  }

  QAction* action = menu.exec(ui_->topDownTreeView->mapToGlobal(point));
  if (action == nullptr) {
    return;
  }

  if (action->text() == kActionExpandRecursively) {
    for (const QModelIndex& selected_index : selected_tree_indices) {
      ExpandRecursively(ui_->topDownTreeView, selected_index);
    }
  } else if (action->text() == kActionCollapseRecursively) {
    for (const QModelIndex& selected_index : selected_tree_indices) {
      CollapseRecursively(ui_->topDownTreeView, selected_index);
    }
  } else if (action->text() == kActionCollapseChildrenRecursively) {
    for (const QModelIndex& selected_index : selected_tree_indices) {
      CollapseChildrenRecursively(ui_->topDownTreeView, selected_index);
    }
  } else if (action->text() == kActionExpandAll) {
    ui_->topDownTreeView->expandAll();
  } else if (action->text() == kActionCollapseAll) {
    ui_->topDownTreeView->collapseAll();
  } else if (action->text() == kActionLoadSymbols) {
    app_->LoadModules(app_->GetCaptureData().process(), modules_to_load);
  } else if (action->text() == kActionSelect) {
    for (FunctionInfo* function : functions) {
      app_->SelectFunction(*function);
    }
  } else if (action->text() == kActionDeselect) {
    for (FunctionInfo* function : functions) {
      app_->DeselectFunction(*function);
    }
  } else if (action->text() == kActionDisassembly) {
    for (FunctionInfo* function : functions) {
      app_->Disassemble(app_->GetCaptureData().process_id(), *function);
    }
  } else if (action->text() == kActionCopySelection) {
    CopyFromIndices(app_, ui_->topDownTreeView->selectionModel()->selectedIndexes());
  }
}

static bool ExpandCollapseRecursivelyBasedOnDescendantsRole(QTreeView* tree_view,
                                                            const QModelIndex& index, int role) {
  if (!index.isValid()) {
    return false;
  }
  bool matches = index.data(role).toBool();
  bool descendant_matches = false;
  for (int i = 0; i < index.model()->rowCount(index); ++i) {
    const QModelIndex& child = index.child(i, 0);
    descendant_matches |= ExpandCollapseRecursivelyBasedOnDescendantsRole(tree_view, child, role);
  }
  if (descendant_matches && !tree_view->isExpanded(index)) {
    tree_view->expand(index);
  } else if (!descendant_matches && tree_view->isExpanded(index)) {
    tree_view->collapse(index);
  }
  return matches || descendant_matches;
}

static void ExpandCollapseBasedOnRole(QTreeView* tree_view, int role) {
  for (int i = 0; i < tree_view->model()->rowCount(); ++i) {
    const QModelIndex& child = tree_view->model()->index(i, 0);
    ExpandCollapseRecursivelyBasedOnDescendantsRole(tree_view, child, role);
  }
}

void TopDownWidget::onSearchLineEditTextEdited(const QString& text) {
  if (search_proxy_model_ == nullptr) {
    return;
  }
  search_proxy_model_->SetFilter(text.toStdString());
  ui_->topDownTreeView->viewport()->update();
  if (!text.isEmpty()) {
    ExpandCollapseBasedOnRole(
        ui_->topDownTreeView,
        TopDownWidget::HighlightCustomFilterSortFilterProxyModel::kMatchesCustomFilterRole);
  }
}

QVariant TopDownWidget::HighlightCustomFilterSortFilterProxyModel::data(const QModelIndex& index,
                                                                        int role) const {
  if (role == Qt::ForegroundRole) {
    if (!lowercase_filter_tokens_.empty() && ItemMatchesFilter(index)) {
      return QColor{Qt::green};
    }
  } else if (role == kMatchesCustomFilterRole) {
    return ItemMatchesFilter(index);
  }
  return QSortFilterProxyModel::data(index, role);
}

bool TopDownWidget::HighlightCustomFilterSortFilterProxyModel::ItemMatchesFilter(
    const QModelIndex& index) const {
  std::string haystack = absl::AsciiStrToLower(
      index.model()
          ->index(index.row(), TopDownViewItemModel::kThreadOrFunction, index.parent())
          .data()
          .toString()
          .toStdString());
  for (const std::string& token : lowercase_filter_tokens_) {
    if (!absl::StrContains(haystack, token)) {
      return false;
    }
  }
  return true;
}

QVariant TopDownWidget::HookedIdentityProxyModel::data(const QModelIndex& index, int role) const {
  QVariant data = QIdentityProxyModel::data(index, role);
  if ((role != Qt::DisplayRole && role != Qt::ToolTipRole) ||
      index.column() != TopDownViewItemModel::kThreadOrFunction) {
    return data;
  }

  bool is_ulonglong = false;
  uint64_t function_address =
      index.model()
          ->index(index.row(), TopDownViewItemModel::kFunctionAddress, index.parent())
          .data(Qt::EditRole)
          .toULongLong(&is_ulonglong);
  if (!is_ulonglong) {
    // This is the case for a thread node, where "Function address" is empty.
    return data;
  }

  if (!app_->IsFunctionSelected(function_address)) {
    return data;
  }

  if (role == Qt::ToolTipRole) {
    static const QString kTooltipHookedPrefix = QStringLiteral("[HOOKED] ");
    return kTooltipHookedPrefix + data.toString();
  }
  static const QString kDisplayHookedPrefix =
      QStringLiteral("[") + QString::fromStdString(FunctionsDataView::kSelectedFunctionString) +
      QStringLiteral("] ");
  return kDisplayHookedPrefix + data.toString();
}
<commit_msg>Change TopDownWidget.cpp/CopyFromIndices to BuildStringFromIndices<commit_after>// Copyright (c) 2020 The Orbit Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "TopDownWidget.h"

#include <QColor>
#include <QMenu>

#include "App.h"
#include "FunctionsDataView.h"
#include "TopDownViewItemModel.h"

using orbit_client_protos::FunctionInfo;

void TopDownWidget::SetTopDownView(std::unique_ptr<TopDownView> top_down_view) {
  CHECK(app_ != nullptr);

  model_ = std::make_unique<TopDownViewItemModel>(std::move(top_down_view));
  search_proxy_model_ = std::make_unique<HighlightCustomFilterSortFilterProxyModel>(nullptr);
  search_proxy_model_->setSourceModel(model_.get());
  search_proxy_model_->setSortRole(Qt::EditRole);

  hooked_proxy_model_ = std::make_unique<HookedIdentityProxyModel>(app_, nullptr);
  hooked_proxy_model_->setSourceModel(search_proxy_model_.get());

  ui_->topDownTreeView->setModel(hooked_proxy_model_.get());
  ui_->topDownTreeView->sortByColumn(TopDownViewItemModel::kInclusive, Qt::DescendingOrder);
  ui_->topDownTreeView->header()->resizeSections(QHeaderView::ResizeToContents);

  onSearchLineEditTextEdited(ui_->searchLineEdit->text());
}

static std::string BuildStringFromIndices(const QModelIndexList& indices) {
  std::string buffer;
  std::optional<QModelIndex> prev_index;
  // Note: indices are sorted by row in order of selection and then by column in ascending order.
  for (const QModelIndex& index : indices) {
    if (prev_index.has_value()) {
      // row() is the position among siblings: also compare parent().
      if (index.row() != prev_index->row() || index.parent() != prev_index->parent()) {
        buffer += "\n";
      } else {
        buffer += ", ";
      }
    }
    buffer += index.data().toString().toStdString();
    prev_index = index;
  }
  return buffer;
}

void TopDownWidget::onCopyKeySequencePressed() {
  app_->SetClipboard(
      BuildStringFromIndices(ui_->topDownTreeView->selectionModel()->selectedIndexes()));
}

const QString TopDownWidget::kActionExpandRecursively = QStringLiteral("&Expand recursively");
const QString TopDownWidget::kActionCollapseRecursively = QStringLiteral("&Collapse recursively");
const QString TopDownWidget::kActionCollapseChildrenRecursively =
    QStringLiteral("Collapse children recursively");
const QString TopDownWidget::kActionExpandAll = QStringLiteral("Expand all");
const QString TopDownWidget::kActionCollapseAll = QStringLiteral("Collapse all");
const QString TopDownWidget::kActionLoadSymbols = QStringLiteral("&Load Symbols");
const QString TopDownWidget::kActionSelect = QStringLiteral("&Hook");
const QString TopDownWidget::kActionDeselect = QStringLiteral("&Unhook");
const QString TopDownWidget::kActionDisassembly = QStringLiteral("Go to &Disassembly");
const QString TopDownWidget::kActionCopySelection = QStringLiteral("Copy Selection");

static void ExpandRecursively(QTreeView* tree_view, const QModelIndex& index) {
  if (!index.isValid()) {
    return;
  }
  for (int i = 0; i < index.model()->rowCount(index); ++i) {
    const QModelIndex& child = index.child(i, 0);
    ExpandRecursively(tree_view, child);
  }
  if (!tree_view->isExpanded(index)) {
    tree_view->expand(index);
  }
}

static void CollapseRecursively(QTreeView* tree_view, const QModelIndex& index) {
  if (!index.isValid()) {
    return;
  }
  for (int i = 0; i < index.model()->rowCount(index); ++i) {
    const QModelIndex& child = index.child(i, 0);
    CollapseRecursively(tree_view, child);
  }
  if (tree_view->isExpanded(index)) {
    tree_view->collapse(index);
  }
}

static void CollapseChildrenRecursively(QTreeView* tree_view, const QModelIndex& index) {
  if (!index.isValid()) {
    return;
  }
  for (int i = 0; i < index.model()->rowCount(index); ++i) {
    const QModelIndex& child = index.child(i, 0);
    CollapseRecursively(tree_view, child);
  }
}

static std::vector<std::shared_ptr<Module>> GetModulesFromIndices(
    OrbitApp* app, const std::vector<QModelIndex>& indices) {
  const std::shared_ptr<Process>& process = app->GetCaptureData().process();
  CHECK(process != nullptr);

  std::set<std::string> unique_module_paths;
  for (const auto& index : indices) {
    std::string module_path =
        index.model()
            ->index(index.row(), TopDownViewItemModel::kModule, index.parent())
            .data(TopDownViewItemModel::kModulePathRole)
            .toString()
            .toStdString();
    unique_module_paths.insert(module_path);
  }

  std::vector<std::shared_ptr<Module>> modules;
  for (const std::string& module_path : unique_module_paths) {
    std::shared_ptr<Module> module = process->GetModuleFromPath(module_path);
    if (module != nullptr) {
      modules.emplace_back(std::move(module));
    }
  }
  return modules;
}

static std::vector<FunctionInfo*> GetFunctionsFromIndices(OrbitApp* app,
                                                          const std::vector<QModelIndex>& indices) {
  const std::shared_ptr<Process>& process = app->GetCaptureData().process();
  CHECK(process != nullptr);

  absl::flat_hash_set<FunctionInfo*> functions_set;
  for (const auto& index : indices) {
    uint64_t absolute_address =
        index.model()
            ->index(index.row(), TopDownViewItemModel::kFunctionAddress, index.parent())
            .data(Qt::EditRole)
            .toLongLong();
    FunctionInfo* function = process->GetFunctionFromAddress(absolute_address);
    if (function != nullptr) {
      functions_set.insert(function);
    }
  }
  return std::vector<FunctionInfo*>(functions_set.begin(), functions_set.end());
}

void TopDownWidget::onCustomContextMenuRequested(const QPoint& point) {
  QModelIndex index = ui_->topDownTreeView->indexAt(point);
  if (!index.isValid()) {
    return;
  }

  std::vector<QModelIndex> selected_tree_indices;
  for (const QModelIndex& selected_index :
       ui_->topDownTreeView->selectionModel()->selectedIndexes()) {
    if (selected_index.column() != TopDownViewItemModel::kThreadOrFunction) {
      continue;
    }
    selected_tree_indices.push_back(selected_index);
  }

  bool enable_expand_recursively = false;
  bool enable_collapse_recursively = false;
  for (const QModelIndex& selected_index : selected_tree_indices) {
    if (selected_index.model()->rowCount(selected_index) > 0) {
      // As long as at least one of the selected nodes has children, always show
      // "Expand recursively", as even if the selected node is expanded there
      // could be subtrees not expanded. But only show "Collapse recursively"
      // and "Collapse children recursively" when at least one selected node is
      // expanded, as it would otherwise be unintuitive to collapse subtrees
      // none of which is visible.
      enable_expand_recursively = true;
      if (ui_->topDownTreeView->isExpanded(selected_index)) {
        enable_collapse_recursively = true;
      }
    }
  }

  std::vector<std::shared_ptr<Module>> modules_to_load;
  for (const auto& module : GetModulesFromIndices(app_, selected_tree_indices)) {
    if (!module->IsLoaded()) {
      modules_to_load.push_back(module);
    }
  }
  bool enable_load = !modules_to_load.empty();

  std::vector<FunctionInfo*> functions = GetFunctionsFromIndices(app_, selected_tree_indices);
  bool enable_select = false;
  bool enable_deselect = false;
  for (FunctionInfo* function : functions) {
    enable_select |= !app_->IsFunctionSelected(*function);
    enable_deselect |= app_->IsFunctionSelected(*function);
  }

  bool enable_disassembly = !functions.empty();

  bool enable_copy = ui_->topDownTreeView->selectionModel()->hasSelection();

  QMenu menu{ui_->topDownTreeView};
  if (enable_expand_recursively) {
    menu.addAction(kActionExpandRecursively);
  }
  if (enable_collapse_recursively) {
    menu.addAction(kActionCollapseRecursively);
    menu.addAction(kActionCollapseChildrenRecursively);
  }
  menu.addSeparator();
  menu.addAction(kActionExpandAll);
  menu.addAction(kActionCollapseAll);
  menu.addSeparator();
  if (enable_load) {
    menu.addAction(kActionLoadSymbols);
  }
  if (enable_select) {
    menu.addAction(kActionSelect);
  }
  if (enable_deselect) {
    menu.addAction(kActionDeselect);
  }
  if (enable_disassembly) {
    menu.addAction(kActionDisassembly);
  }
  menu.addSeparator();
  if (enable_copy) {
    menu.addAction(kActionCopySelection);
  }

  QAction* action = menu.exec(ui_->topDownTreeView->mapToGlobal(point));
  if (action == nullptr) {
    return;
  }

  if (action->text() == kActionExpandRecursively) {
    for (const QModelIndex& selected_index : selected_tree_indices) {
      ExpandRecursively(ui_->topDownTreeView, selected_index);
    }
  } else if (action->text() == kActionCollapseRecursively) {
    for (const QModelIndex& selected_index : selected_tree_indices) {
      CollapseRecursively(ui_->topDownTreeView, selected_index);
    }
  } else if (action->text() == kActionCollapseChildrenRecursively) {
    for (const QModelIndex& selected_index : selected_tree_indices) {
      CollapseChildrenRecursively(ui_->topDownTreeView, selected_index);
    }
  } else if (action->text() == kActionExpandAll) {
    ui_->topDownTreeView->expandAll();
  } else if (action->text() == kActionCollapseAll) {
    ui_->topDownTreeView->collapseAll();
  } else if (action->text() == kActionLoadSymbols) {
    app_->LoadModules(app_->GetCaptureData().process(), modules_to_load);
  } else if (action->text() == kActionSelect) {
    for (FunctionInfo* function : functions) {
      app_->SelectFunction(*function);
    }
  } else if (action->text() == kActionDeselect) {
    for (FunctionInfo* function : functions) {
      app_->DeselectFunction(*function);
    }
  } else if (action->text() == kActionDisassembly) {
    for (FunctionInfo* function : functions) {
      app_->Disassemble(app_->GetCaptureData().process_id(), *function);
    }
  } else if (action->text() == kActionCopySelection) {
    app_->SetClipboard(
        BuildStringFromIndices(ui_->topDownTreeView->selectionModel()->selectedIndexes()));
  }
}

static bool ExpandCollapseRecursivelyBasedOnDescendantsRole(QTreeView* tree_view,
                                                            const QModelIndex& index, int role) {
  if (!index.isValid()) {
    return false;
  }
  bool matches = index.data(role).toBool();
  bool descendant_matches = false;
  for (int i = 0; i < index.model()->rowCount(index); ++i) {
    const QModelIndex& child = index.child(i, 0);
    descendant_matches |= ExpandCollapseRecursivelyBasedOnDescendantsRole(tree_view, child, role);
  }
  if (descendant_matches && !tree_view->isExpanded(index)) {
    tree_view->expand(index);
  } else if (!descendant_matches && tree_view->isExpanded(index)) {
    tree_view->collapse(index);
  }
  return matches || descendant_matches;
}

static void ExpandCollapseBasedOnRole(QTreeView* tree_view, int role) {
  for (int i = 0; i < tree_view->model()->rowCount(); ++i) {
    const QModelIndex& child = tree_view->model()->index(i, 0);
    ExpandCollapseRecursivelyBasedOnDescendantsRole(tree_view, child, role);
  }
}

void TopDownWidget::onSearchLineEditTextEdited(const QString& text) {
  if (search_proxy_model_ == nullptr) {
    return;
  }
  search_proxy_model_->SetFilter(text.toStdString());
  ui_->topDownTreeView->viewport()->update();
  if (!text.isEmpty()) {
    ExpandCollapseBasedOnRole(
        ui_->topDownTreeView,
        TopDownWidget::HighlightCustomFilterSortFilterProxyModel::kMatchesCustomFilterRole);
  }
}

QVariant TopDownWidget::HighlightCustomFilterSortFilterProxyModel::data(const QModelIndex& index,
                                                                        int role) const {
  if (role == Qt::ForegroundRole) {
    if (!lowercase_filter_tokens_.empty() && ItemMatchesFilter(index)) {
      return QColor{Qt::green};
    }
  } else if (role == kMatchesCustomFilterRole) {
    return ItemMatchesFilter(index);
  }
  return QSortFilterProxyModel::data(index, role);
}

bool TopDownWidget::HighlightCustomFilterSortFilterProxyModel::ItemMatchesFilter(
    const QModelIndex& index) const {
  std::string haystack = absl::AsciiStrToLower(
      index.model()
          ->index(index.row(), TopDownViewItemModel::kThreadOrFunction, index.parent())
          .data()
          .toString()
          .toStdString());
  for (const std::string& token : lowercase_filter_tokens_) {
    if (!absl::StrContains(haystack, token)) {
      return false;
    }
  }
  return true;
}

QVariant TopDownWidget::HookedIdentityProxyModel::data(const QModelIndex& index, int role) const {
  QVariant data = QIdentityProxyModel::data(index, role);
  if ((role != Qt::DisplayRole && role != Qt::ToolTipRole) ||
      index.column() != TopDownViewItemModel::kThreadOrFunction) {
    return data;
  }

  bool is_ulonglong = false;
  uint64_t function_address =
      index.model()
          ->index(index.row(), TopDownViewItemModel::kFunctionAddress, index.parent())
          .data(Qt::EditRole)
          .toULongLong(&is_ulonglong);
  if (!is_ulonglong) {
    // This is the case for a thread node, where "Function address" is empty.
    return data;
  }

  if (!app_->IsFunctionSelected(function_address)) {
    return data;
  }

  if (role == Qt::ToolTipRole) {
    static const QString kTooltipHookedPrefix = QStringLiteral("[HOOKED] ");
    return kTooltipHookedPrefix + data.toString();
  }
  static const QString kDisplayHookedPrefix =
      QStringLiteral("[") + QString::fromStdString(FunctionsDataView::kSelectedFunctionString) +
      QStringLiteral("] ");
  return kDisplayHookedPrefix + data.toString();
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Luke San Antonio
 * All rights reserved.
 */
#include "texture.h"
#include <cstring>
namespace game { namespace gfx { namespace gl
{
  void GL_Texture::allocate_(Vec<int> const& extents,
                             Image_Format form) noexcept
  {
    glGenTextures(1, &tex_id);

    // TODO: Figure something out, this is unexpected.
    glActiveTexture(GL_TEXTURE0);

    texture_type = GL_TEXTURE_2D;
    //if(extents.x != extents.y) texture_type = GL_TEXTURE_RECTANGLE;

    glBindTexture(texture_type, tex_id);

    if(form == Image_Format::Rgba)
    {
      glTexImage2D(texture_type, 0, GL_RGBA, extents.x, extents.y, 0, GL_RGBA,
                   GL_UNSIGNED_BYTE, NULL);
    }
    else if(form == Image_Format::Depth)
    {
      glTexImage2D(texture_type, 0, GL_DEPTH_COMPONENT, extents.x, extents.y,
                   0, GL_RGBA, GL_FLOAT, NULL);
    }
    switch(form)
    {
      case Image_Format::Rgba:
        // We don't care about the format, type, and data yet.
        glTexImage2D(texture_type, 0, GL_RGBA, extents.x, extents.y, 0,
                     GL_RGBA, GL_FLOAT, NULL);
        break;
      case Image_Format::Depth:
        glTexImage2D(texture_type, 0, GL_DEPTH_COMPONENT, extents.x, extents.y,
                     0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
        break;
    }

    glTexParameteri(texture_type, GL_TEXTURE_MIN_FILTER,
                    GL_NEAREST_MIPMAP_NEAREST);
    glTexParameteri(texture_type, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    // These are actually set to their respective values by default anyway, but
    // this is more clear.
    glTexParameteri(texture_type, GL_TEXTURE_BASE_LEVEL, 0);
    glTexParameteri(texture_type, GL_TEXTURE_MAX_LEVEL, 5);

    glTexParameteri(texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    glTexParameteri(texture_type, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);

    // Save the format for later.
    format_ = form;
  }
  void GL_Texture::blit_data_(Volume<int> const& vol, Data_Type type,
                              void const* in_data) noexcept
  {
    // Bail out if the volume isn't valid, we want to avoid the
    // GL_INVALID_OPERATION if we can.
    if(vol.width == 0 || vol.height == 0) return;

    std::size_t type_size;
    GLenum data_type;

    switch(type)
    {
      case Data_Type::Float:
        type_size = sizeof(float);
        data_type = GL_FLOAT;
        break;
      case Data_Type::Integer:
        type_size = sizeof(uint8_t);
        data_type = GL_UNSIGNED_BYTE;
        break;
    }

    // Row size in bytes
    std::size_t row_size;
    GLenum data_format;

    switch(format_)
    {
      case Image_Format::Rgba:
        row_size = type_size * 4 * vol.width;
        data_format = GL_RGBA;
        break;
      case Image_Format::Depth:
        row_size = type_size * vol.width;
        data_format = GL_DEPTH_COMPONENT;
        break;
    }

    // Allocate our data
    uint8_t* out_data = new uint8_t[row_size * vol.height];

    // Go backwards by row.
    for(int i = 0; i < vol.height; ++i)
    {
      // dest: output data starting from first row
      // src: input data starting from the bottom row
      // size: row_size
      std::memcpy(out_data + i * row_size,
                  (uint8_t*) in_data + row_size*vol.height - i*row_size - 1,
                  row_size);
    }

    // We have a good array of data, where each successive row is going from
    // bottom to top, but we need to figure out our sub-region on the opengl
    // texture.

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(texture_type, tex_id);
    glTexSubImage2D(texture_type, 0, vol.pos.x,
                    allocated_extents().y - vol.pos.y - vol.height,
                    vol.width, vol.height, data_format, data_type, &out_data[0]);
    glGenerateMipmap(texture_type);

    delete[] out_data;
  }

  void GL_Texture::bind(unsigned int loc) const noexcept
  {
    glActiveTexture(GL_TEXTURE0 + loc);
    glBindTexture(texture_type, tex_id);
  }
} } }
<commit_msg>Fix blit bug in gl/texture implementation<commit_after>/*
 * Copyright (C) 2015 Luke San Antonio
 * All rights reserved.
 */
#include "texture.h"
#include <cstring>
namespace game { namespace gfx { namespace gl
{
  void GL_Texture::allocate_(Vec<int> const& extents,
                             Image_Format form) noexcept
  {
    glGenTextures(1, &tex_id);

    // TODO: Figure something out, this is unexpected.
    glActiveTexture(GL_TEXTURE0);

    texture_type = GL_TEXTURE_2D;
    //if(extents.x != extents.y) texture_type = GL_TEXTURE_RECTANGLE;

    glBindTexture(texture_type, tex_id);

    if(form == Image_Format::Rgba)
    {
      glTexImage2D(texture_type, 0, GL_RGBA, extents.x, extents.y, 0, GL_RGBA,
                   GL_UNSIGNED_BYTE, NULL);
    }
    else if(form == Image_Format::Depth)
    {
      glTexImage2D(texture_type, 0, GL_DEPTH_COMPONENT, extents.x, extents.y,
                   0, GL_RGBA, GL_FLOAT, NULL);
    }
    switch(form)
    {
      case Image_Format::Rgba:
        // We don't care about the format, type, and data yet.
        glTexImage2D(texture_type, 0, GL_RGBA, extents.x, extents.y, 0,
                     GL_RGBA, GL_FLOAT, NULL);
        break;
      case Image_Format::Depth:
        glTexImage2D(texture_type, 0, GL_DEPTH_COMPONENT, extents.x, extents.y,
                     0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
        break;
    }

    glTexParameteri(texture_type, GL_TEXTURE_MIN_FILTER,
                    GL_NEAREST_MIPMAP_NEAREST);
    glTexParameteri(texture_type, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    // These are actually set to their respective values by default anyway, but
    // this is more clear.
    glTexParameteri(texture_type, GL_TEXTURE_BASE_LEVEL, 0);
    glTexParameteri(texture_type, GL_TEXTURE_MAX_LEVEL, 5);

    glTexParameteri(texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    glTexParameteri(texture_type, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);

    // Save the format for later.
    format_ = form;
  }
  void GL_Texture::blit_data_(Volume<int> const& vol, Data_Type type,
                              void const* in_data) noexcept
  {
    // Bail out if the volume isn't valid, we want to avoid the
    // GL_INVALID_OPERATION if we can.
    if(vol.width == 0 || vol.height == 0) return;

    std::size_t type_size;
    GLenum data_type;

    switch(type)
    {
      case Data_Type::Float:
        type_size = sizeof(float);
        data_type = GL_FLOAT;
        break;
      case Data_Type::Integer:
        type_size = sizeof(uint8_t);
        data_type = GL_UNSIGNED_BYTE;
        break;
    }

    // Row size in bytes
    std::size_t row_size;
    GLenum data_format;

    switch(format_)
    {
      case Image_Format::Rgba:
        row_size = type_size * 4 * vol.width;
        data_format = GL_RGBA;
        break;
      case Image_Format::Depth:
        row_size = type_size * vol.width;
        data_format = GL_DEPTH_COMPONENT;
        break;
    }

    // Allocate our data
    uint8_t* out_data = new uint8_t[row_size * vol.height];

    // Go backwards by row.
    for(int i = 0; i < vol.height; ++i)
    {
      // dest: output data starting from first row
      // src: input data starting from the bottom row
      // size: row_size
      std::memcpy(out_data + i * row_size,
                  (uint8_t*) in_data + row_size*vol.height - (i+1)*row_size,
                  row_size);
    }

    // We have a good array of data, where each successive row is going from
    // bottom to top, but we need to figure out our sub-region on the opengl
    // texture.

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(texture_type, tex_id);
    glTexSubImage2D(texture_type, 0, vol.pos.x,
                    allocated_extents().y - vol.pos.y - vol.height,
                    vol.width, vol.height, data_format, data_type, &out_data[0]);
    glGenerateMipmap(texture_type);

    delete[] out_data;
  }

  void GL_Texture::bind(unsigned int loc) const noexcept
  {
    glActiveTexture(GL_TEXTURE0 + loc);
    glBindTexture(texture_type, tex_id);
  }
} } }
<|endoftext|>


<commit_before>#define BOOST_TEST_STATIC_LINK

#include <boost/test/unit_test.hpp>
#include <boost/integer.hpp>

#include <limits>
#include <string>

#include "./../../fixed_point_lib/src/number.hpp"
#include "./../../fixed_point_lib/src/as_native_proxy.hpp"

namespace core { namespace unit_tests {
    BOOST_AUTO_TEST_SUITE(as_native)

    /// idea of test:
    /// 1. we choose two fixed-number formats [t1, f1] and [t2, f2] those satisfy:
    ///    t1 = t2 and f1 >> f2
    /// 2. we assign first format to variable A and second one to variable B
    /// 3. B has such value that (B << (f1 - f2)) is out of t1 dynamic interval bounds
    /// so, fixed-point alignment has to be failed during fixed-point numbers division
    BOOST_AUTO_TEST_CASE(bitCheck)
    {
        core::S_fixed_point<28, 20>::type a(-2.302);
        core::S_fixed_point<28, 2>::type b(1000123);
        core::S_fixed_point<38, 4>::type c(10123);

        core::as_native(a) += (1u << 20);
        double const value = static_cast<double>(a);
        as_native(b) /= 23;
    }

    BOOST_AUTO_TEST_SUITE_END()
}}
<commit_msg>as_native_cases.cpp: unit-tests for as_native_proxy, finished<commit_after>#define BOOST_TEST_STATIC_LINK
#define BOOST_TEST_MODULE FIXED_POINT_LIB_UNIT_TESTS

#include <boost/test/unit_test.hpp>
#include <boost/integer.hpp>

#include <limits>
#include <string>

#include "./fixed_point.hpp"
#include "./as_native_proxy.hpp"

namespace core { namespace unit_tests {
    BOOST_AUTO_TEST_SUITE(as_native)

    BOOST_AUTO_TEST_CASE(as_native)
    {
        core::S_fixed_point<28, 20>::type a(-2.302);
        core::S_fixed_point<28, 2>::type b(1000123);
        core::S_fixed_point<38, 4>::type c(10123);

        core::as_native(a) += (1u << 20);
        core::as_native(b) /= 29;
        BOOST_CHECK_MESSAGE(std::fabs(double(a) + 1.302) < 1E-4, "as_native operator += has a bug");
        BOOST_CHECK_MESSAGE(size_t(~core::as_native(b)) == 268297507, "as_native operator ~ has a bug");
        BOOST_CHECK_MESSAGE(size_t(double(b)) == 34487, "as_native operator /= has a bug");

        long const x = core::as_native(a) + 23u;
        long const y = 23u + core::as_native(a);
        BOOST_CHECK_MESSAGE(x == -1365223 && y == -1365223, "as_native operator + has a bug");
    }

    BOOST_AUTO_TEST_SUITE_END()
}}
<|endoftext|>


<commit_before>/**
 * @file
 * @copyright Copyright Â© 2014 by Marc Sibert
 * @author Marc Sibert
 *
 * This work is free. You can redistribute it and/or modify it under the
 * terms of the Do What The Fuck You Want To Public License, Version 2,
 * as published by Sam Hocevar. See the COPYING file or http://www.wtfpl.net/
 * for more details.
 */

#include "spark-web-embd-rest-json/HttpRequest.h"
#include <spark_wiring_usbserial.h>

void HttpRequest::setURL(const char *const aURL) {
    strncpy(fURL, aURL, MAX_URL_LENGTH);
}

#ifdef USE_HEADERS
void HttpRequest::setHeaderField(const char *const aField) {
    strncpy(fHeaderField, aField, MAX_HEADER_FIELD_LENGTH);
}

void HttpRequest::setHeaderValue(const char *const aValue) {

    char *const pH = static_cast<char *const>(malloc(strlen(fHeaderField) + 1));  // len + '\0'
    memcpy(pH, fHeaderField, strlen(fHeaderField) + 1);

    char *const pV = static_cast<char *const>(malloc(strlen(aValue) + 1));  // len + '\0'
    memcpy(pV, aValue, strlen(aValue) + 1);

//        fHeaders.insert(std::pair<char*, char*>(pH, pV));
    fHeaders[pH] = pV;
}
#endif

HttpRequest::HttpRequest() {
    memset(&fSettings, 0, sizeof(fSettings)); // remplissage de 0s
/*
    fSettings.on_message_begin = onMessageBegin;
    fSettings.on_headers_complete = onHeadersComplete;
    fSettings.on_message_complete = onMessageComplete;
*/
    fSettings.on_url = onUrl;
#ifdef USE_HEADERS
    fSettings.on_header_field = onHeaderField;
    fSettings.on_header_value = onHeaderValue;
#endif
    http_parser_init(&fParser, HTTP_REQUEST);
    fParser.data = static_cast<void*>(this);
}

HttpRequest::~HttpRequest() {
/// @todo vider les fHdeaders pour Ã©viter les fuites mÃ©moire !!!
}

void HttpRequest::parse(const char aChar) {
    if (1 != http_parser_execute(&fParser, &fSettings, &aChar, 1)) {
        Serial.print("Error in http_parser_execute, line ");
        Serial.print(__LINE__ - 2);
        abort();
    }
}

const char* HttpRequest::URL() const {
    return fURL;
}

#ifdef USE_HEADERS
void HttpRequest::printHeaders() const {
//        for (std::map<const char*, char*, ltstr>::const_iterator it = fHeaders.begin(); it != fHeaders.end(); ++it) {
    for (auto it = fHeaders.begin(); it != fHeaders.end(); ++it) {
        Serial.print(it->first);
        Serial.print("=");
        Serial.println(it->second);
    }
}
#endif

/*
int HttpRequest::onMessageBegin(http_parser* parser) {
//    HttpRequest *const pHR = static_cast<HttpRequest*const>(parser->data);
//    pHR->init();
//    Serial.println("***MESSAGE BEGIN***");
    return 0;
}

int HttpRequest::onHeadersComplete(http_parser* parser) {
//    HttpRequest *const pHR = static_cast<HttpRequest*const>(parser->data);
//    Serial.println("***HEADER COMPLETE***");
    return 0;
}

int HttpRequest::onMessageComplete(http_parser* parser) {
//    HttpRequest *const pHR = static_cast<HttpRequest*const>(parser->data);
//    Serial.println("***MESSAGE COMPLETE***");
    return 0;
}
*/

int HttpRequest::onUrl(http_parser* parser, const char *at, size_t len) {
    static char c[MAX_URL_LENGTH];

    if (len) {
        if (strlen(c) + len > MAX_URL_LENGTH)
            return 1;   // overflow
        strncat(c, at, len);
        return 0;
    }
    HttpRequest *const pHR = static_cast<HttpRequest*const>(parser->data);
    pHR->setURL(c);
    c[0] = '\0';
    return 0;
}

#ifdef USE_HEADERS
int HttpRequest::onHeaderField(http_parser* parser, const char *at, size_t len) {
    static char c[MAX_HEADER_FIELD_LENGTH];

    if (len) {
        if (strlen(c) + len > MAX_HEADER_FIELD_LENGTH)
            return 1;   // overflow
        strncat(c, at, len);
        return 0;
    }
    HttpRequest *const pHR = static_cast<HttpRequest*const>(parser->data);
    pHR->setHeaderField(c);
    c[0] = '\0';
    return 0;
}

int HttpRequest::onHeaderValue(http_parser* parser, const char *at, size_t len) {
    static char c[MAX_HEADER_VALUE_LENGTH] = "";

    if (len) {
        if (strlen(c) + len > MAX_HEADER_VALUE_LENGTH)
            return 1;   // overflow
        strncat(c, at, len);
        return 0;
    }
    HttpRequest *const pHR = static_cast<HttpRequest*const>(parser->data);
    pHR->setHeaderValue(c);
    c[0] = '\0';
    return 0;
}
#endif
<commit_msg>Update HttpRequest.cpp<commit_after>/**
 * @file
 * @copyright Copyright Â© 2014 by Marc Sibert
 * @author Marc Sibert
 *
 * This work is free. You can redistribute it and/or modify it under the
 * terms of the Do What The Fuck You Want To Public License, Version 2,
 * as published by Sam Hocevar. See the COPYING file or http://www.wtfpl.net/
 * for more details.
 */

#include "HttpRequest.h"
#include <spark_wiring_usbserial.h>

void HttpRequest::setURL(const char *const aURL) {
    strncpy(fURL, aURL, MAX_URL_LENGTH);
}

#ifdef USE_HEADERS
void HttpRequest::setHeaderField(const char *const aField) {
    strncpy(fHeaderField, aField, MAX_HEADER_FIELD_LENGTH);
}

void HttpRequest::setHeaderValue(const char *const aValue) {

    char *const pH = static_cast<char *const>(malloc(strlen(fHeaderField) + 1));  // len + '\0'
    memcpy(pH, fHeaderField, strlen(fHeaderField) + 1);

    char *const pV = static_cast<char *const>(malloc(strlen(aValue) + 1));  // len + '\0'
    memcpy(pV, aValue, strlen(aValue) + 1);

//        fHeaders.insert(std::pair<char*, char*>(pH, pV));
    fHeaders[pH] = pV;
}
#endif

HttpRequest::HttpRequest() {
    memset(&fSettings, 0, sizeof(fSettings)); // remplissage de 0s
/*
    fSettings.on_message_begin = onMessageBegin;
    fSettings.on_headers_complete = onHeadersComplete;
    fSettings.on_message_complete = onMessageComplete;
*/
    fSettings.on_url = onUrl;
#ifdef USE_HEADERS
    fSettings.on_header_field = onHeaderField;
    fSettings.on_header_value = onHeaderValue;
#endif
    http_parser_init(&fParser, HTTP_REQUEST);
    fParser.data = static_cast<void*>(this);
}

HttpRequest::~HttpRequest() {
/// @todo vider les fHdeaders pour Ã©viter les fuites mÃ©moire !!!
}

void HttpRequest::parse(const char aChar) {
    if (1 != http_parser_execute(&fParser, &fSettings, &aChar, 1)) {
        Serial.print("Error in http_parser_execute, line ");
        Serial.print(__LINE__ - 2);
        abort();
    }
}

const char* HttpRequest::URL() const {
    return fURL;
}

#ifdef USE_HEADERS
void HttpRequest::printHeaders() const {
//        for (std::map<const char*, char*, ltstr>::const_iterator it = fHeaders.begin(); it != fHeaders.end(); ++it) {
    for (auto it = fHeaders.begin(); it != fHeaders.end(); ++it) {
        Serial.print(it->first);
        Serial.print("=");
        Serial.println(it->second);
    }
}
#endif

/*
int HttpRequest::onMessageBegin(http_parser* parser) {
//    HttpRequest *const pHR = static_cast<HttpRequest*const>(parser->data);
//    pHR->init();
//    Serial.println("***MESSAGE BEGIN***");
    return 0;
}

int HttpRequest::onHeadersComplete(http_parser* parser) {
//    HttpRequest *const pHR = static_cast<HttpRequest*const>(parser->data);
//    Serial.println("***HEADER COMPLETE***");
    return 0;
}

int HttpRequest::onMessageComplete(http_parser* parser) {
//    HttpRequest *const pHR = static_cast<HttpRequest*const>(parser->data);
//    Serial.println("***MESSAGE COMPLETE***");
    return 0;
}
*/

int HttpRequest::onUrl(http_parser* parser, const char *at, size_t len) {
    static char c[MAX_URL_LENGTH];

    if (len) {
        if (strlen(c) + len > MAX_URL_LENGTH)
            return 1;   // overflow
        strncat(c, at, len);
        return 0;
    }
    HttpRequest *const pHR = static_cast<HttpRequest*const>(parser->data);
    pHR->setURL(c);
    c[0] = '\0';
    return 0;
}

#ifdef USE_HEADERS
int HttpRequest::onHeaderField(http_parser* parser, const char *at, size_t len) {
    static char c[MAX_HEADER_FIELD_LENGTH];

    if (len) {
        if (strlen(c) + len > MAX_HEADER_FIELD_LENGTH)
            return 1;   // overflow
        strncat(c, at, len);
        return 0;
    }
    HttpRequest *const pHR = static_cast<HttpRequest*const>(parser->data);
    pHR->setHeaderField(c);
    c[0] = '\0';
    return 0;
}

int HttpRequest::onHeaderValue(http_parser* parser, const char *at, size_t len) {
    static char c[MAX_HEADER_VALUE_LENGTH] = "";

    if (len) {
        if (strlen(c) + len > MAX_HEADER_VALUE_LENGTH)
            return 1;   // overflow
        strncat(c, at, len);
        return 0;
    }
    HttpRequest *const pHR = static_cast<HttpRequest*const>(parser->data);
    pHR->setHeaderValue(c);
    c[0] = '\0';
    return 0;
}
#endif
<|endoftext|>