<commit_before>//===- PrettyFunctionDumper.cpp --------------------------------- *- C++ *-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "PrettyFunctionDumper.h"
#include "LinePrinter.h"
#include "PrettyBuiltinDumper.h"

#include "llvm/DebugInfo/PDB/IPDBSession.h"
#include "llvm/DebugInfo/PDB/PDBExtras.h"
#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h"
#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/FormatVariadic.h"

using namespace llvm;
using namespace llvm::codeview;
using namespace llvm::pdb;

namespace {
template <class T>
void dumpClassParentWithScopeOperator(const T &Symbol, LinePrinter &Printer,
                                      FunctionDumper &Dumper) {
  uint32_t ClassParentId = Symbol.getClassParentId();
  auto ClassParent =
      Symbol.getSession().template getConcreteSymbolById<PDBSymbolTypeUDT>(
          ClassParentId);
  if (!ClassParent)
    return;

  WithColor(Printer, PDB_ColorItem::Type).get() << ClassParent->getName();
  Printer << "::";
}
}

FunctionDumper::FunctionDumper(LinePrinter &P)
    : PDBSymDumper(true), Printer(P) {}

void FunctionDumper::start(const PDBSymbolTypeFunctionSig &Symbol,
                           const char *Name, PointerType Pointer) {
  auto ReturnType = Symbol.getReturnType();
  if (!ReturnType)
    Printer << "<unknown-type>";
  else
    ReturnType->dump(*this);
  Printer << " ";
  uint32_t ClassParentId = Symbol.getClassParentId();
  auto ClassParent =
      Symbol.getSession().getConcreteSymbolById<PDBSymbolTypeUDT>(
          ClassParentId);

  PDB_CallingConv CC = Symbol.getCallingConvention();
  bool ShouldDumpCallingConvention = true;
  if ((ClassParent && CC == CallingConvention::ThisCall) ||
      (!ClassParent && CC == CallingConvention::NearStdCall)) {
    ShouldDumpCallingConvention = false;
  }

  if (Pointer == PointerType::None) {
    if (ShouldDumpCallingConvention)
      WithColor(Printer, PDB_ColorItem::Keyword).get() << CC << " ";
    if (ClassParent) {
      Printer << "(";
      WithColor(Printer, PDB_ColorItem::Identifier).get()
          << ClassParent->getName();
      Printer << "::)";
    }
  } else {
    Printer << "(";
    if (ShouldDumpCallingConvention)
      WithColor(Printer, PDB_ColorItem::Keyword).get() << CC << " ";
    if (ClassParent) {
      WithColor(Printer, PDB_ColorItem::Identifier).get()
          << ClassParent->getName();
      Printer << "::";
    }
    if (Pointer == PointerType::Reference)
      Printer << "&";
    else
      Printer << "*";
    if (Name)
      WithColor(Printer, PDB_ColorItem::Identifier).get() << Name;
    Printer << ")";
  }

  Printer << "(";
  if (auto ChildEnum = Symbol.getArguments()) {
    uint32_t Index = 0;
    while (auto Arg = ChildEnum->getNext()) {
      Arg->dump(*this);
      if (++Index < ChildEnum->getChildCount())
        Printer << ", ";
    }
  }
  Printer << ")";

  if (Symbol.isConstType())
    WithColor(Printer, PDB_ColorItem::Keyword).get() << " const";
  if (Symbol.isVolatileType())
    WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile";
}

void FunctionDumper::start(const PDBSymbolFunc &Symbol, PointerType Pointer) {
  uint64_t FuncStart = Symbol.getVirtualAddress();
  uint64_t FuncEnd = FuncStart + Symbol.getLength();

  Printer << "func [";
  WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(FuncStart, 10);
  if (auto DebugStart = Symbol.findOneChild<PDBSymbolFuncDebugStart>()) {
    uint64_t Prologue = DebugStart->getVirtualAddress() - FuncStart;
    WithColor(Printer, PDB_ColorItem::Offset).get()
        << formatv("+{0,2}", Prologue);
  }
  Printer << " - ";
  WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(FuncEnd, 10);
  if (auto DebugEnd = Symbol.findOneChild<PDBSymbolFuncDebugEnd>()) {
    uint64_t Epilogue = FuncEnd - DebugEnd->getVirtualAddress();
    WithColor(Printer, PDB_ColorItem::Offset).get()
        << formatv("-{0,2}", Epilogue);
  }

  WithColor(Printer, PDB_ColorItem::Comment).get()
      << formatv(" | sizeof={0,3}", Symbol.getLength());
  Printer << "] (";

  if (Symbol.hasFramePointer()) {
    WithColor(Printer, PDB_ColorItem::Register).get()
        << Symbol.getLocalBasePointerRegisterId();
  } else {
    WithColor(Printer, PDB_ColorItem::Register).get() << "FPO";
  }
  Printer << ") ";

  if (Symbol.isVirtual() || Symbol.isPureVirtual())
    WithColor(Printer, PDB_ColorItem::Keyword).get() << "virtual ";

  auto Signature = Symbol.getSignature();
  if (!Signature) {
    WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName();
    if (Pointer == PointerType::Pointer)
      Printer << "*";
    else if (Pointer == FunctionDumper::PointerType::Reference)
      Printer << "&";
    return;
  }

  auto ReturnType = Signature->getReturnType();
  ReturnType->dump(*this);
  Printer << " ";

  auto ClassParent = Symbol.getClassParent();
  CallingConvention CC = Signature->getCallingConvention();
  if (Pointer != FunctionDumper::PointerType::None)
    Printer << "(";

  if ((ClassParent && CC != CallingConvention::ThisCall) ||
      (!ClassParent && CC != CallingConvention::NearStdCall)) {
    WithColor(Printer, PDB_ColorItem::Keyword).get()
        << Signature->getCallingConvention() << " ";
  }
  WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName();
  if (Pointer != FunctionDumper::PointerType::None) {
    if (Pointer == PointerType::Pointer)
      Printer << "*";
    else if (Pointer == FunctionDumper::PointerType::Reference)
      Printer << "&";
    Printer << ")";
  }

  Printer << "(";
  if (auto Arguments = Symbol.getArguments()) {
    uint32_t Index = 0;
    while (auto Arg = Arguments->getNext()) {
      auto ArgType = Arg->getType();
      ArgType->dump(*this);
      WithColor(Printer, PDB_ColorItem::Identifier).get() << " "
                                                          << Arg->getName();
      if (++Index < Arguments->getChildCount())
        Printer << ", ";
    }
    if (Signature->isCVarArgs())
      Printer << ", ...";
  }
  Printer << ")";
  if (Symbol.isConstType())
    WithColor(Printer, PDB_ColorItem::Keyword).get() << " const";
  if (Symbol.isVolatileType())
    WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile";
  if (Symbol.isPureVirtual())
    Printer << " = 0";
}

void FunctionDumper::dump(const PDBSymbolTypeArray &Symbol) {
  auto ElementType = Symbol.getElementType();

  ElementType->dump(*this);
  Printer << "[";
  WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Symbol.getLength();
  Printer << "]";
}

void FunctionDumper::dump(const PDBSymbolTypeBuiltin &Symbol) {
  BuiltinDumper Dumper(Printer);
  Dumper.start(Symbol);
}

void FunctionDumper::dump(const PDBSymbolTypeEnum &Symbol) {
  dumpClassParentWithScopeOperator(Symbol, Printer, *this);
  WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
}

void FunctionDumper::dump(const PDBSymbolTypeFunctionArg &Symbol) {
  // PDBSymbolTypeFunctionArg is just a shim over the real argument.  Just drill
  // through to the real thing and dump it.
  uint32_t TypeId = Symbol.getTypeId();
  auto Type = Symbol.getSession().getSymbolById(TypeId);
  if (Type)
    Type->dump(*this);
  else
    Printer << "<unknown-type>";
}

void FunctionDumper::dump(const PDBSymbolTypeTypedef &Symbol) {
  dumpClassParentWithScopeOperator(Symbol, Printer, *this);
  WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
}

void FunctionDumper::dump(const PDBSymbolTypePointer &Symbol) {
  auto PointeeType = Symbol.getPointeeType();
  if (!PointeeType)
    return;

  if (auto FuncSig = unique_dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType)) {
    FunctionDumper NestedDumper(Printer);
    PointerType Pointer =
        Symbol.isReference() ? PointerType::Reference : PointerType::Pointer;
    NestedDumper.start(*FuncSig, nullptr, Pointer);
  } else {
    if (Symbol.isConstType())
      WithColor(Printer, PDB_ColorItem::Keyword).get() << "const ";
    if (Symbol.isVolatileType())
      WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile ";
    PointeeType->dump(*this);
    Printer << (Symbol.isReference() ? "&" : "*");

    if (Symbol.getRawSymbol().isRestrictedType())
      WithColor(Printer, PDB_ColorItem::Keyword).get() << " __restrict";
  }
}

void FunctionDumper::dump(const PDBSymbolTypeUDT &Symbol) {
  WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
}
<commit_msg>[PDB] Fixed null pointer dereference<commit_after>//===- PrettyFunctionDumper.cpp --------------------------------- *- C++ *-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "PrettyFunctionDumper.h"
#include "LinePrinter.h"
#include "PrettyBuiltinDumper.h"

#include "llvm/DebugInfo/PDB/IPDBSession.h"
#include "llvm/DebugInfo/PDB/PDBExtras.h"
#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h"
#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/FormatVariadic.h"

using namespace llvm;
using namespace llvm::codeview;
using namespace llvm::pdb;

namespace {
template <class T>
void dumpClassParentWithScopeOperator(const T &Symbol, LinePrinter &Printer,
                                      FunctionDumper &Dumper) {
  uint32_t ClassParentId = Symbol.getClassParentId();
  auto ClassParent =
      Symbol.getSession().template getConcreteSymbolById<PDBSymbolTypeUDT>(
          ClassParentId);
  if (!ClassParent)
    return;

  WithColor(Printer, PDB_ColorItem::Type).get() << ClassParent->getName();
  Printer << "::";
}
}

FunctionDumper::FunctionDumper(LinePrinter &P)
    : PDBSymDumper(true), Printer(P) {}

void FunctionDumper::start(const PDBSymbolTypeFunctionSig &Symbol,
                           const char *Name, PointerType Pointer) {
  auto ReturnType = Symbol.getReturnType();
  if (!ReturnType)
    Printer << "<unknown-type>";
  else
    ReturnType->dump(*this);
  Printer << " ";
  uint32_t ClassParentId = Symbol.getClassParentId();
  auto ClassParent =
      Symbol.getSession().getConcreteSymbolById<PDBSymbolTypeUDT>(
          ClassParentId);

  PDB_CallingConv CC = Symbol.getCallingConvention();
  bool ShouldDumpCallingConvention = true;
  if ((ClassParent && CC == CallingConvention::ThisCall) ||
      (!ClassParent && CC == CallingConvention::NearStdCall)) {
    ShouldDumpCallingConvention = false;
  }

  if (Pointer == PointerType::None) {
    if (ShouldDumpCallingConvention)
      WithColor(Printer, PDB_ColorItem::Keyword).get() << CC << " ";
    if (ClassParent) {
      Printer << "(";
      WithColor(Printer, PDB_ColorItem::Identifier).get()
          << ClassParent->getName();
      Printer << "::)";
    }
  } else {
    Printer << "(";
    if (ShouldDumpCallingConvention)
      WithColor(Printer, PDB_ColorItem::Keyword).get() << CC << " ";
    if (ClassParent) {
      WithColor(Printer, PDB_ColorItem::Identifier).get()
          << ClassParent->getName();
      Printer << "::";
    }
    if (Pointer == PointerType::Reference)
      Printer << "&";
    else
      Printer << "*";
    if (Name)
      WithColor(Printer, PDB_ColorItem::Identifier).get() << Name;
    Printer << ")";
  }

  Printer << "(";
  if (auto ChildEnum = Symbol.getArguments()) {
    uint32_t Index = 0;
    while (auto Arg = ChildEnum->getNext()) {
      Arg->dump(*this);
      if (++Index < ChildEnum->getChildCount())
        Printer << ", ";
    }
  }
  Printer << ")";

  if (Symbol.isConstType())
    WithColor(Printer, PDB_ColorItem::Keyword).get() << " const";
  if (Symbol.isVolatileType())
    WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile";
}

void FunctionDumper::start(const PDBSymbolFunc &Symbol, PointerType Pointer) {
  uint64_t FuncStart = Symbol.getVirtualAddress();
  uint64_t FuncEnd = FuncStart + Symbol.getLength();

  Printer << "func [";
  WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(FuncStart, 10);
  if (auto DebugStart = Symbol.findOneChild<PDBSymbolFuncDebugStart>()) {
    uint64_t Prologue = DebugStart->getVirtualAddress() - FuncStart;
    WithColor(Printer, PDB_ColorItem::Offset).get()
        << formatv("+{0,2}", Prologue);
  }
  Printer << " - ";
  WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(FuncEnd, 10);
  if (auto DebugEnd = Symbol.findOneChild<PDBSymbolFuncDebugEnd>()) {
    uint64_t Epilogue = FuncEnd - DebugEnd->getVirtualAddress();
    WithColor(Printer, PDB_ColorItem::Offset).get()
        << formatv("-{0,2}", Epilogue);
  }

  WithColor(Printer, PDB_ColorItem::Comment).get()
      << formatv(" | sizeof={0,3}", Symbol.getLength());
  Printer << "] (";

  if (Symbol.hasFramePointer()) {
    WithColor(Printer, PDB_ColorItem::Register).get()
        << Symbol.getLocalBasePointerRegisterId();
  } else {
    WithColor(Printer, PDB_ColorItem::Register).get() << "FPO";
  }
  Printer << ") ";

  if (Symbol.isVirtual() || Symbol.isPureVirtual())
    WithColor(Printer, PDB_ColorItem::Keyword).get() << "virtual ";

  auto Signature = Symbol.getSignature();
  if (!Signature) {
    WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName();
    if (Pointer == PointerType::Pointer)
      Printer << "*";
    else if (Pointer == FunctionDumper::PointerType::Reference)
      Printer << "&";
    return;
  }

  auto ReturnType = Signature->getReturnType();
  ReturnType->dump(*this);
  Printer << " ";

  auto ClassParent = Symbol.getClassParent();
  CallingConvention CC = Signature->getCallingConvention();
  if (Pointer != FunctionDumper::PointerType::None)
    Printer << "(";

  if ((ClassParent && CC != CallingConvention::ThisCall) ||
      (!ClassParent && CC != CallingConvention::NearStdCall)) {
    WithColor(Printer, PDB_ColorItem::Keyword).get()
        << Signature->getCallingConvention() << " ";
  }
  WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName();
  if (Pointer != FunctionDumper::PointerType::None) {
    if (Pointer == PointerType::Pointer)
      Printer << "*";
    else if (Pointer == FunctionDumper::PointerType::Reference)
      Printer << "&";
    Printer << ")";
  }

  Printer << "(";
  if (auto Arguments = Symbol.getArguments()) {
    uint32_t Index = 0;
    while (auto Arg = Arguments->getNext()) {
      auto ArgType = Arg->getType();
      ArgType->dump(*this);
      WithColor(Printer, PDB_ColorItem::Identifier).get() << " "
                                                          << Arg->getName();
      if (++Index < Arguments->getChildCount())
        Printer << ", ";
    }
    if (Signature->isCVarArgs())
      Printer << ", ...";
  }
  Printer << ")";
  if (Symbol.isConstType())
    WithColor(Printer, PDB_ColorItem::Keyword).get() << " const";
  if (Symbol.isVolatileType())
    WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile";
  if (Symbol.isPureVirtual())
    Printer << " = 0";
}

void FunctionDumper::dump(const PDBSymbolTypeArray &Symbol) {
  auto ElementType = Symbol.getElementType();

  ElementType->dump(*this);
  Printer << "[";
  WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Symbol.getLength();
  Printer << "]";
}

void FunctionDumper::dump(const PDBSymbolTypeBuiltin &Symbol) {
  BuiltinDumper Dumper(Printer);
  Dumper.start(Symbol);
}

void FunctionDumper::dump(const PDBSymbolTypeEnum &Symbol) {
  dumpClassParentWithScopeOperator(Symbol, Printer, *this);
  WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
}

void FunctionDumper::dump(const PDBSymbolTypeFunctionArg &Symbol) {
  // PDBSymbolTypeFunctionArg is just a shim over the real argument.  Just drill
  // through to the real thing and dump it.
  uint32_t TypeId = Symbol.getTypeId();
  auto Type = Symbol.getSession().getSymbolById(TypeId);
  if (!Type)
    Type->dump(*this);
  else
    Printer << "<unknown-type>";
}

void FunctionDumper::dump(const PDBSymbolTypeTypedef &Symbol) {
  dumpClassParentWithScopeOperator(Symbol, Printer, *this);
  WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
}

void FunctionDumper::dump(const PDBSymbolTypePointer &Symbol) {
  auto PointeeType = Symbol.getPointeeType();
  if (!PointeeType)
    return;

  if (auto FuncSig = unique_dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType)) {
    FunctionDumper NestedDumper(Printer);
    PointerType Pointer =
        Symbol.isReference() ? PointerType::Reference : PointerType::Pointer;
    NestedDumper.start(*FuncSig, nullptr, Pointer);
  } else {
    if (Symbol.isConstType())
      WithColor(Printer, PDB_ColorItem::Keyword).get() << "const ";
    if (Symbol.isVolatileType())
      WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile ";
    PointeeType->dump(*this);
    Printer << (Symbol.isReference() ? "&" : "*");

    if (Symbol.getRawSymbol().isRestrictedType())
      WithColor(Printer, PDB_ColorItem::Keyword).get() << " __restrict";
  }
}

void FunctionDumper::dump(const PDBSymbolTypeUDT &Symbol) {
  WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
}
<|endoftext|>


<commit_before><commit_msg>Make the tests use 64-bit nexes when running 64-bit Linux Chrome<commit_after><|endoftext|>


<commit_before><commit_msg>Fix error [FATAL:at_exit.cc(40)] Check failed: false. Tried to RegisterCallback without an AtExitManager in (possibly unused) binary perf_tests<commit_after><|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include "OptimizationEngine.hpp"

#include <boost/variant/static_visitor.hpp>

#include "Types.hpp"
#include "Options.hpp"

#include "StringPool.hpp"
#include "FunctionTable.hpp"

#include "IsConstantVisitor.hpp"
#include "GetTypeVisitor.hpp"
#include "VisitorUtils.hpp"
#include "ASTVisitor.hpp"
#include "Variable.hpp"

using namespace eddic;

struct GetIntValue : public boost::static_visitor<int> {
    int operator()(ast::ComposedValue& value) const {
        int acc = boost::apply_visitor(*this, value.Content->first);

        for(auto& operation : value.Content->operations){
            int v = boost::apply_visitor(*this, operation.get<1>());
            char op = operation.get<0>();

            switch(op){
                case '+':
                    acc += v;
                    break;
                case '-':
                    acc -= v;
                    break;
                case '*':
                    acc *= v;
                    break;
                case '/':
                    acc /= v;
                    break;
                case '%':
                    acc %= v;
                    break;
            }
        }

        return acc;
    }

    int operator()(ast::Integer& integer) const {
        return integer.value;
    }

    int operator()(ast::VariableValue&) const {
        assert(false); //A variable is not constant

        return -1;
    }

    int operator()(ast::Litteral&) const {
        assert(false); //A litteral is not integer

        return -1;
    }
};

struct GetStringValue : public boost::static_visitor<std::string> {
    std::string operator()(ast::ComposedValue& value) const {
        std::string acc = boost::apply_visitor(*this, value.Content->first);

        for(auto& operation : value.Content->operations){
            std::string v = boost::apply_visitor(*this, operation.get<1>());
            acc = acc.substr(0, acc.size() - 1).append(v.substr(1, acc.size() - 1));
        }

        return acc;
    }

    std::string operator()(ast::Integer&) const {
        assert(false); //An integer is not a string

        return "";
    }

    std::string operator()(ast::VariableValue&) const {
        assert(false); //A variable is not constant

        return "";
    }

    std::string operator()(ast::Litteral& litteral) const {
        return litteral.value;
    }
};

struct ValueOptimizer : public boost::static_visitor<ast::Value> {
    private:
        StringPool& pool;

    public:
        ValueOptimizer(StringPool& p) : pool(p) {}

        ast::Value operator()(ast::ComposedValue& value) const {
            if(value.Content->operations.empty()){
                return boost::apply_visitor(*this, value.Content->first);
            }

            //If the value is constant, we can replace it with the results of the computation
            if(IsConstantVisitor()(value)){
                Type type = GetTypeVisitor()(value);

                if(type == Type::INT){
                    if (OptimizeIntegers) {
                        ast::Integer integer;
                        integer.value = GetIntValue()(value);
                        return integer;
                    }
                } else if(type == Type::STRING){
                    if (OptimizeStrings) {
                        ast::Litteral litteral;
                        litteral.value = GetStringValue()(value);
                        litteral.label = pool.label(litteral.value);
                        return litteral;
                    }
                }
            }

            //Optimize the first value
            value.Content->first = boost::apply_visitor(*this, value.Content->first);

            //We can try to optimize every part of the composed value
            auto start = value.Content->operations.begin();
            auto end = value.Content->operations.end();

            while(start != end){
                start->get<1>() = boost::apply_visitor(*this, start->get<1>());

                ++start;
            }

            assert(value.Content->operations.size() > 0); //Once here, there is no more empty composed value

            //If we get there, that means that no optimization has been (or can be) performed
            return value;
        }

        ast::Value operator()(ast::VariableValue& variable) const {
            return variable; //A variable is not optimizable
        }

        ast::Value operator()(ast::Integer& integer) const {
            return integer; //A variable is not optimizable
        }

        ast::Value operator()(ast::Litteral& litteral) const {
            return litteral; //A variable is not optimizable
        }
};

struct CanBeRemoved : public boost::static_visitor<bool> {
    private:
        FunctionTable& functionTable;

    public:
        CanBeRemoved(FunctionTable& table) : functionTable(table) {}

        bool operator()(ast::FirstLevelBlock block){
            return boost::apply_visitor(*this, block);
        }

        bool optimizeVariable(std::shared_ptr<Context> context, const std::string& variable){
            if(OptimizeUnused){
                if(context->getVariable(variable)->referenceCount() <= 0){
                    //Removing from the AST is not enough, because it is stored in the context now
                    context->removeVariable(variable);

                    return true;
                }
            }

            return false;
        }

        bool operator()(ast::GlobalVariableDeclaration& declaration){
            return optimizeVariable(declaration.Content->context, declaration.Content->variableName);
        }

        bool operator()(ast::Declaration& declaration){
            return optimizeVariable(declaration.Content->context, declaration.Content->variableName);
        }

        bool operator()(ast::FunctionDeclaration& declaration){
            if(OptimizeUnused){
                if(declaration.Content->functionName != "main" && functionTable.referenceCount(declaration.Content->mangledName) <= 0){
                    return true;
                }
            }

            return false;
        }

        bool operator()(ast::Instruction& instruction){
           return boost::apply_visitor(*this, instruction);
        }

        //Nothing to optimize for the other types
        template<typename T>
        bool operator()(T&) {
            return false;
        }
};

struct OptimizationVisitor : public boost::static_visitor<> {
    private:
        FunctionTable& functionTable;
        StringPool& pool;
        ValueOptimizer optimizer;

    public:
        OptimizationVisitor(FunctionTable& t, StringPool& p) : functionTable(t), pool(p), optimizer(ValueOptimizer(pool)) {}

        AUTO_RECURSE_BRANCHES()
        AUTO_RECURSE_SIMPLE_LOOPS()
        AUTO_RECURSE_FOREACH()

        template<typename T>
        void removeUnused(std::vector<T>& vector){
            auto iter = vector.begin();
            auto end = vector.end();

            CanBeRemoved visitor(functionTable);
            auto newEnd = remove_if(iter, end, visitor);

            vector.erase(newEnd, end);

            visit_each(*this, vector);
        }

        void operator()(ast::Program& program){\
            removeUnused(program.Content->blocks);
        }

        void operator()(ast::FunctionDeclaration& function){
            removeUnused(function.Content->instructions);
        }

        void operator()(ast::GlobalVariableDeclaration&){
            //As the constantness of the value of a global variable is enforced, there is no need to optimize it
        }

        void operator()(ast::FunctionCall& functionCall){
            auto start = functionCall.Content->values.begin();
            auto end = functionCall.Content->values.end();

            while(start != end){
                *start = boost::apply_visitor(optimizer, *start);

                ++start;
            }
        }

        void operator()(ast::Assignment& assignment){
            assignment.Content->value = boost::apply_visitor(optimizer, assignment.Content->value);
        }

        void operator()(ast::Declaration& declaration){
            declaration.Content->value = boost::apply_visitor(optimizer, declaration.Content->value);
        }

        void operator()(ast::Swap&){
            //Nothing to optimize in a swap
        }

        void operator()(ast::BinaryCondition& binaryCondition){
            binaryCondition.Content->lhs = boost::apply_visitor(optimizer, binaryCondition.Content->lhs);
            binaryCondition.Content->rhs = boost::apply_visitor(optimizer, binaryCondition.Content->rhs);
        }

        void operator()(ast::False&){
            //Nothing to optimize
        }

        void operator()(ast::True&){
            //Nothing to optimize
        }
};

void OptimizationEngine::optimize(ast::Program& program, FunctionTable& functionTable, StringPool& pool){
    OptimizationVisitor visitor(functionTable, pool);
    visitor(program);
}
<commit_msg>Use function templates to simplify visitors<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include "OptimizationEngine.hpp"

#include <boost/variant/static_visitor.hpp>

#include "Types.hpp"
#include "Options.hpp"

#include "StringPool.hpp"
#include "FunctionTable.hpp"

#include "IsConstantVisitor.hpp"
#include "GetTypeVisitor.hpp"
#include "VisitorUtils.hpp"
#include "ASTVisitor.hpp"
#include "Variable.hpp"

using namespace eddic;

struct GetIntValue : public boost::static_visitor<int> {
    int operator()(ast::ComposedValue& value) const {
        int acc = boost::apply_visitor(*this, value.Content->first);

        for(auto& operation : value.Content->operations){
            int v = boost::apply_visitor(*this, operation.get<1>());
            char op = operation.get<0>();

            switch(op){
                case '+':
                    acc += v;
                    break;
                case '-':
                    acc -= v;
                    break;
                case '*':
                    acc *= v;
                    break;
                case '/':
                    acc /= v;
                    break;
                case '%':
                    acc %= v;
                    break;
            }
        }

        return acc;
    }

    int operator()(ast::Integer& integer) const {
        return integer.value;
    }

    //Other values are not integers
    template<typename T>
    int operator()(T&) const {
        assert(false);

        return -1;
    }
};

struct GetStringValue : public boost::static_visitor<std::string> {
    std::string operator()(ast::ComposedValue& value) const {
        std::string acc = boost::apply_visitor(*this, value.Content->first);

        for(auto& operation : value.Content->operations){
            std::string v = boost::apply_visitor(*this, operation.get<1>());
            acc = acc.substr(0, acc.size() - 1).append(v.substr(1, acc.size() - 1));
        }

        return acc;
    }

    std::string operator()(ast::Litteral& litteral) const {
        return litteral.value;
    }

    //Other values are not strings
    template<typename T>
    std::string operator()(T&) const {
        assert(false);

        return "";
    }
};

struct ValueOptimizer : public boost::static_visitor<ast::Value> {
    private:
        StringPool& pool;

    public:
        ValueOptimizer(StringPool& p) : pool(p) {}

        ast::Value operator()(ast::ComposedValue& value) const {
            if(value.Content->operations.empty()){
                return boost::apply_visitor(*this, value.Content->first);
            }

            //If the value is constant, we can replace it with the results of the computation
            if(IsConstantVisitor()(value)){
                Type type = GetTypeVisitor()(value);

                if(type == Type::INT){
                    if (OptimizeIntegers) {
                        ast::Integer integer;
                        integer.value = GetIntValue()(value);
                        return integer;
                    }
                } else if(type == Type::STRING){
                    if (OptimizeStrings) {
                        ast::Litteral litteral;
                        litteral.value = GetStringValue()(value);
                        litteral.label = pool.label(litteral.value);
                        return litteral;
                    }
                }
            }

            //Optimize the first value
            value.Content->first = boost::apply_visitor(*this, value.Content->first);

            //We can try to optimize every part of the composed value
            auto start = value.Content->operations.begin();
            auto end = value.Content->operations.end();

            while(start != end){
                start->get<1>() = boost::apply_visitor(*this, start->get<1>());

                ++start;
            }

            assert(value.Content->operations.size() > 0); //Once here, there is no more empty composed value

            //If we get there, that means that no optimization has been (or can be) performed
            return value;
        }

        //No optimizations for other kind of values
        template<typename T>
        ast::Value operator()(T& value) const {
            return value;
        }
};

struct CanBeRemoved : public boost::static_visitor<bool> {
    private:
        FunctionTable& functionTable;

    public:
        CanBeRemoved(FunctionTable& table) : functionTable(table) {}

        bool operator()(ast::FirstLevelBlock block){
            return boost::apply_visitor(*this, block);
        }

        bool optimizeVariable(std::shared_ptr<Context> context, const std::string& variable){
            if(OptimizeUnused){
                if(context->getVariable(variable)->referenceCount() <= 0){
                    //Removing from the AST is not enough, because it is stored in the context now
                    context->removeVariable(variable);

                    return true;
                }
            }

            return false;
        }

        bool operator()(ast::GlobalVariableDeclaration& declaration){
            return optimizeVariable(declaration.Content->context, declaration.Content->variableName);
        }

        bool operator()(ast::Declaration& declaration){
            return optimizeVariable(declaration.Content->context, declaration.Content->variableName);
        }

        bool operator()(ast::FunctionDeclaration& declaration){
            if(OptimizeUnused){
                if(declaration.Content->functionName != "main" && functionTable.referenceCount(declaration.Content->mangledName) <= 0){
                    return true;
                }
            }

            return false;
        }

        bool operator()(ast::Instruction& instruction){
           return boost::apply_visitor(*this, instruction);
        }

        //Nothing to optimize for the other types
        template<typename T>
        bool operator()(T&) {
            return false;
        }
};

struct OptimizationVisitor : public boost::static_visitor<> {
    private:
        FunctionTable& functionTable;
        StringPool& pool;
        ValueOptimizer optimizer;

    public:
        OptimizationVisitor(FunctionTable& t, StringPool& p) : functionTable(t), pool(p), optimizer(ValueOptimizer(pool)) {}

        AUTO_RECURSE_BRANCHES()
        AUTO_RECURSE_SIMPLE_LOOPS()
        AUTO_RECURSE_FOREACH()

        template<typename T>
        void removeUnused(std::vector<T>& vector){
            auto iter = vector.begin();
            auto end = vector.end();

            CanBeRemoved visitor(functionTable);
            auto newEnd = remove_if(iter, end, visitor);

            vector.erase(newEnd, end);

            visit_each(*this, vector);
        }

        void operator()(ast::Program& program){\
            removeUnused(program.Content->blocks);
        }

        void operator()(ast::FunctionDeclaration& function){
            removeUnused(function.Content->instructions);
        }

        void operator()(ast::FunctionCall& functionCall){
            auto start = functionCall.Content->values.begin();
            auto end = functionCall.Content->values.end();

            while(start != end){
                *start = boost::apply_visitor(optimizer, *start);

                ++start;
            }
        }

        void operator()(ast::Assignment& assignment){
            assignment.Content->value = boost::apply_visitor(optimizer, assignment.Content->value);
        }

        void operator()(ast::Declaration& declaration){
            declaration.Content->value = boost::apply_visitor(optimizer, declaration.Content->value);
        }

        void operator()(ast::BinaryCondition& binaryCondition){
            binaryCondition.Content->lhs = boost::apply_visitor(optimizer, binaryCondition.Content->lhs);
            binaryCondition.Content->rhs = boost::apply_visitor(optimizer, binaryCondition.Content->rhs);
        }

        template<typename T>
        void operator()(T&){
            //Nothing to optimize for other types
        }
};

void OptimizationEngine::optimize(ast::Program& program, FunctionTable& functionTable, StringPool& pool){
    OptimizationVisitor visitor(functionTable, pool);
    visitor(program);
}
<|endoftext|>


<commit_before>#include "OrthographicCamera.h"
#include <limits>
OrthographicCamera::OrthographicCamera()
  : Camera() {

}

OrthographicCamera::OrthographicCamera(const Point3D& position, const uint32_t image_height, const uint32_t image_width)
  : Camera(position, image_height, image_width) {

}

OrthographicCamera::~OrthographicCamera() {

}

const Point2D OrthographicCamera::projectTransform(const Point3D& point_camera) const {
  const Point2D point_projected = {
    ((point_camera.x )),
    ((point_camera.y ))
  };

  const double slopeX = 1.0 / (m_image_width);
  const double slopeY = 1.0 / (m_image_height);

  const Point2D point_ndc = {
    (float) (slopeX * (point_projected.x + m_image_width / 2.0)),
    (float) (slopeY * (point_projected.y + m_image_height / 2.0))
  };
  return point_ndc;
}

const Vector3D OrthographicCamera::viewDirection(const Point3D point) const {
  return m_forward;
}<commit_msg>Added multiplication by near plane in projection<commit_after>#include "OrthographicCamera.h"
#include <limits>
OrthographicCamera::OrthographicCamera()
  : Camera() {

}

OrthographicCamera::OrthographicCamera(const Point3D& position, const uint32_t image_height, const uint32_t image_width)
  : Camera(position, image_height, image_width) {

}

OrthographicCamera::~OrthographicCamera() {

}

const Point2D OrthographicCamera::projectTransform(const Point3D& point_camera) const {
  const Point2D point_projected = {
    point_camera.x * m_near ,
    point_camera.y * m_near
  };

  const double slopeX = 1.0 / (m_image_width);
  const double slopeY = 1.0 / (m_image_height);

  const Point2D point_ndc = {
    (float) (slopeX * (point_projected.x + m_image_width / 2.0)),
    (float) (slopeY * (point_projected.y + m_image_height / 2.0))
  };
  return point_ndc;
}

const Vector3D OrthographicCamera::viewDirection(const Point3D point) const {
  return m_forward;
}<|endoftext|>


<commit_before>/**
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed in accordance with the terms specified in
 *  the LICENSE file found in the root directory of this source tree.
 */

// clang-format off
#include <sys/types.h>
#include <sys/stat.h>
// clang-format on

#ifndef WIN32
#include <unistd.h>
#endif

#ifdef OSQUERY_POSIX
#include <fuzzy.h>
#endif

#include <set>
#include <thread>

#include <boost/filesystem.hpp>

#include <osquery/flags.h>
#include <osquery/filesystem/filesystem.h>
#include <osquery/hashing/hashing.h>
#include <osquery/logger.h>
#include <osquery/tables.h>
#include <osquery/sql/dynamic_table_row.h>
#include <osquery/utils/mutex.h>
#include <osquery/utils/info/platform_type.h>
#include <osquery/worker/ipc/platform_table_container_ipc.h>
#include <osquery/worker/logging/glog/glog_logger.h>
#include <osquery/worker/logging/logger.h>

namespace osquery {

FLAG(bool,
     disable_hash_cache,
     false,
     "Cache calculated file hashes, re-calculate only if inode times change");

FLAG(uint32, hash_cache_max, 500, "Size of LRU file hash cache");

HIDDEN_FLAG(uint32,
            hash_delay,
            20,
            "Number of milliseconds to delay after hashing");

namespace tables {

/// Clear this amount of rows every time cache eviction is triggered.
const size_t kHashCacheEvictSize{5};

/**
 * @brief Implements persistent in-memory caching of files' hashes.
 *
 * This cache has LRU eviction policy. The hash is recalculated
 * every time the mtime or size of the file changes.
 */
struct FileHashCache {
  /// The file's modification time, changes with a touch.
  time_t file_mtime;

  /// The file's serial or information number (inode).
  ino_t file_inode;

  /// The file's size.
  off_t file_size;

  /// For eviction, the last time this cache item was used.
  time_t cache_access_time;

  /// Cache content, the hashes.
  MultiHashes hashes;

  /// Cache index, the file path.
  std::string path;

  /// Comparison function for organizing the LRU heap.
  static bool greater(const FileHashCache* l, const FileHashCache* r) {
    return l->cache_access_time > r->cache_access_time;
  }

  /**
   * @brief Do-it-all access function.
   *
   * Maintains the cache of hash sums, stats file at path, if it has changed or
   * it is not present in cache calculates the hashes and caches the result.
   *
   * @param path the path of file to hash.
   * @param out stores the calculated hashes.
   *
   * @return true if succeeded, false if something went wrong.
   */
  static bool load(const std::string& path, MultiHashes& out, Logger& logger);
};

#if defined(WIN32)

#define stat _stat
#define strerror_r(e, buf, sz) strerror_s((buf), (sz), (e))

#endif

/**
 * @brief Checks the current stat output against the cached view.
 *
 * If the modified/altered time or the file's inode has changed then the hash
 * should be recalculated.
 */
static inline bool statInvalid(const struct stat& st, const FileHashCache& fh) {
  if (st.st_ino != fh.file_inode || st.st_mtime != fh.file_mtime) {
    // Most plausible case for modification detection.
    return true;
  }

  if (st.st_size != fh.file_size) {
    // Just in case there's tomfoolery.
    return true;
  }
  return false;
}

bool FileHashCache::load(const std::string& path,
                         MultiHashes& out,
                         Logger& logger) {
  // synchronize the access to cache
  static Mutex mx;
  // path => cache entry
  static std::unordered_map<std::string, FileHashCache> cache;
  // minheap on cache_access_time
  static std::vector<FileHashCache*> lru;

  WriteLock guard(mx);

  struct stat st;
  if (stat(path.c_str(), &st) != 0) {
    char buf[0x200] = {0};
    strerror_r(errno, buf, sizeof(buf));
    logger.log(google::GLOG_WARNING, "Cannot stat file: " + path + ": " + buf);
    return false;
  }

  auto entry = cache.find(path);
  if (entry == cache.end()) { // none, load
    if (cache.size() >= FLAGS_hash_cache_max) {
      // too large, evict
      for (size_t i = 0; i < kHashCacheEvictSize; ++i) {
        if (lru.empty()) {
          continue;
        }
        std::string key = lru[0]->path;
        std::pop_heap(lru.begin(), lru.end(), FileHashCache::greater);
        lru.pop_back();
        if (cache.find(key) != cache.end()) {
          cache.erase(key);
        }
      }
    }

    auto hashes = hashMultiFromFile(
        HASH_TYPE_MD5 | HASH_TYPE_SHA1 | HASH_TYPE_SHA256, path);
    FileHashCache rec = {st.st_mtime, // .file_mtime
                         st.st_ino, // .file_inode
                         st.st_size, // .file_size
                         time(nullptr), // .cache_access_time
                         std::move(hashes), // .hashes
                         path}; // .path
    cache[path] = std::move(rec);
    lru.push_back(&cache[path]);
    std::push_heap(lru.begin(), lru.end(), FileHashCache::greater);
    out = cache[path].hashes;
  } else if (statInvalid(st, entry->second)) { // changed, update
    auto hashes = hashMultiFromFile(
        HASH_TYPE_MD5 | HASH_TYPE_SHA1 | HASH_TYPE_SHA256, path);
    entry->second.cache_access_time = time(nullptr);
    entry->second.file_mtime = st.st_mtime;
    entry->second.file_size = st.st_size;
    entry->second.hashes = std::move(hashes);
    std::make_heap(lru.begin(), lru.end(), FileHashCache::greater);
    out = entry->second.hashes;
  } else { // ok, got it
    out = entry->second.hashes;
    entry->second.cache_access_time = time(nullptr);
    std::make_heap(lru.begin(), lru.end(), FileHashCache::greater);
  }
  return true;
}

Status genSsdeepForFile(const std::string& path, std::string& ssdeep_hash) {
#ifdef OSQUERY_POSIX
  ssdeep_hash.resize(FUZZY_MAX_RESULT, '\0');
  auto did_ssdeep_fail =
      fuzzy_hash_filename(path.c_str(), &ssdeep_hash.front());
  if (did_ssdeep_fail) {
    return Status::failure("ssdeep failed: " + path);
  }
  ssdeep_hash.resize(ssdeep_hash.find('\0'));
  return Status::success();
#else
  ssdeep_hash = "-1";
  return Status::success();
#endif
}

void genHashForFile(const std::string& path,
                    const std::string& dir,
                    QueryContext& context,
                    QueryData& results,
                    Logger& logger) {
  // Must provide the path, filename, directory separate from boost path->string
  // helpers to match any explicit (query-parsed) predicate constraints.
  auto tr = TableRowHolder(new DynamicTableRow());
  MultiHashes hashes;
  if (!FLAGS_disable_hash_cache) {
    FileHashCache::load(path, hashes, logger);
  } else {
    if (context.isCached(path)) {
      // Use the inner-query cache if the global hash cache is disabled.
      // This protects against hashing the same content twice in the same query.
      tr = context.getCache(path);
    } else {
      hashes = hashMultiFromFile(
          HASH_TYPE_MD5 | HASH_TYPE_SHA1 | HASH_TYPE_SHA256, path);
      std::this_thread::sleep_for(std::chrono::milliseconds(FLAGS_hash_delay));
    }
  }

  DynamicTableRow& r = *dynamic_cast<DynamicTableRow*>(tr.get());
  r["path"] = path;
  r["directory"] = dir;
  r["md5"] = std::move(hashes.md5);
  r["sha1"] = std::move(hashes.sha1);
  r["sha256"] = std::move(hashes.sha256);

  if (isPlatform(PlatformType::TYPE_POSIX) && context.isColumnUsed("ssdeep")) {
    auto status = genSsdeepForFile(path, r["ssdeep"]);

    if (!status.ok()) {
      logger.log(google::GLOG_WARNING, status.getMessage());
    }
  }

  if (FLAGS_disable_hash_cache) {
    context.setCache(path, tr);
  }

  r["pid_with_namespace"] = "0";

  results.push_back(static_cast<Row>(r));
}

void expandFSPathConstraints(QueryContext& context,
                             const std::string& path_column_name,
                             std::set<std::string>& paths) {
  context.expandConstraints(
      path_column_name,
      LIKE,
      paths,
      ([&](const std::string& pattern, std::set<std::string>& out) {
        std::vector<std::string> patterns;
        auto status =
            resolveFilePattern(pattern, patterns, GLOB_ALL | GLOB_NO_CANON);
        if (status.ok()) {
          for (const auto& resolved : patterns) {
            out.insert(resolved);
          }
        }
        return status;
      }));
}

QueryData genHashImpl(QueryContext& context, Logger& logger) {
  QueryData results;
  boost::system::error_code ec;

  // The query must provide a predicate with constraints including path or
  // directory. We search for the parsed predicate constraints with the equals
  // operator.
  auto paths = context.constraints["path"].getAll(EQUALS);
  expandFSPathConstraints(context, "path", paths);

  // Iterate through the file paths, adding the hash results
  for (const auto& path_string : paths) {
    boost::filesystem::path path = path_string;
    if (!boost::filesystem::is_regular_file(path, ec)) {
      continue;
    }

    genHashForFile(
        path_string, path.parent_path().string(), context, results, logger);
  }

  // Now loop through constraints using the directory column constraint.
  auto directories = context.constraints["directory"].getAll(EQUALS);
  expandFSPathConstraints(context, "directory", directories);

  // Iterate over the directory paths
  for (const auto& directory_string : directories) {
    boost::filesystem::path directory = directory_string;
    if (!boost::filesystem::is_directory(directory, ec)) {
      continue;
    }

    // Iterate over the directory files and generate a hash for each regular
    // file.
    boost::filesystem::directory_iterator begin(directory), end;
    for (; begin != end; ++begin) {
      if (boost::filesystem::is_regular_file(begin->path(), ec)) {
        genHashForFile(
            begin->path().string(), directory_string, context, results, logger);
      }
    }
  }

  return results;
}

QueryData genHash(QueryContext& context) {
  if (hasNamespaceConstraint(context)) {
    return generateInNamespace(context, "hash", genHashImpl);
  } else {
    GLOGLogger logger;
    return genHashImpl(context, logger);
  }
}
} // namespace tables
} // namespace osquery
<commit_msg>Update hash cache inode number (#6440)<commit_after>/**
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed in accordance with the terms specified in
 *  the LICENSE file found in the root directory of this source tree.
 */

// clang-format off
#include <sys/types.h>
#include <sys/stat.h>
// clang-format on

#ifndef WIN32
#include <unistd.h>
#endif

#ifdef OSQUERY_POSIX
#include <fuzzy.h>
#endif

#include <set>
#include <thread>

#include <boost/filesystem.hpp>

#include <osquery/flags.h>
#include <osquery/filesystem/filesystem.h>
#include <osquery/hashing/hashing.h>
#include <osquery/logger.h>
#include <osquery/tables.h>
#include <osquery/sql/dynamic_table_row.h>
#include <osquery/utils/mutex.h>
#include <osquery/utils/info/platform_type.h>
#include <osquery/worker/ipc/platform_table_container_ipc.h>
#include <osquery/worker/logging/glog/glog_logger.h>
#include <osquery/worker/logging/logger.h>

namespace osquery {

FLAG(bool,
     disable_hash_cache,
     false,
     "Cache calculated file hashes, re-calculate only if inode times change");

FLAG(uint32, hash_cache_max, 500, "Size of LRU file hash cache");

HIDDEN_FLAG(uint32,
            hash_delay,
            20,
            "Number of milliseconds to delay after hashing");

namespace tables {

/// Clear this amount of rows every time cache eviction is triggered.
const size_t kHashCacheEvictSize{5};

/**
 * @brief Implements persistent in-memory caching of files' hashes.
 *
 * This cache has LRU eviction policy. The hash is recalculated
 * every time the mtime or size of the file changes.
 */
struct FileHashCache {
  /// The file's modification time, changes with a touch.
  time_t file_mtime;

  /// The file's serial or information number (inode).
  ino_t file_inode;

  /// The file's size.
  off_t file_size;

  /// For eviction, the last time this cache item was used.
  time_t cache_access_time;

  /// Cache content, the hashes.
  MultiHashes hashes;

  /// Cache index, the file path.
  std::string path;

  /// Comparison function for organizing the LRU heap.
  static bool greater(const FileHashCache* l, const FileHashCache* r) {
    return l->cache_access_time > r->cache_access_time;
  }

  /**
   * @brief Do-it-all access function.
   *
   * Maintains the cache of hash sums, stats file at path, if it has changed or
   * it is not present in cache calculates the hashes and caches the result.
   *
   * @param path the path of file to hash.
   * @param out stores the calculated hashes.
   *
   * @return true if succeeded, false if something went wrong.
   */
  static bool load(const std::string& path, MultiHashes& out, Logger& logger);
};

#if defined(WIN32)

#define stat _stat
#define strerror_r(e, buf, sz) strerror_s((buf), (sz), (e))

#endif

/**
 * @brief Checks the current stat output against the cached view.
 *
 * If the modified/altered time or the file's inode has changed then the hash
 * should be recalculated.
 */
static inline bool statInvalid(const struct stat& st, const FileHashCache& fh) {
  if (st.st_ino != fh.file_inode || st.st_mtime != fh.file_mtime) {
    // Most plausible case for modification detection.
    return true;
  }

  if (st.st_size != fh.file_size) {
    // Just in case there's tomfoolery.
    return true;
  }
  return false;
}

bool FileHashCache::load(const std::string& path,
                         MultiHashes& out,
                         Logger& logger) {
  // synchronize the access to cache
  static Mutex mx;
  // path => cache entry
  static std::unordered_map<std::string, FileHashCache> cache;
  // minheap on cache_access_time
  static std::vector<FileHashCache*> lru;

  WriteLock guard(mx);

  struct stat st;
  if (stat(path.c_str(), &st) != 0) {
    char buf[0x200] = {0};
    strerror_r(errno, buf, sizeof(buf));
    logger.log(google::GLOG_WARNING, "Cannot stat file: " + path + ": " + buf);
    return false;
  }

  auto entry = cache.find(path);
  if (entry == cache.end()) { // none, load
    if (cache.size() >= FLAGS_hash_cache_max) {
      // too large, evict
      for (size_t i = 0; i < kHashCacheEvictSize; ++i) {
        if (lru.empty()) {
          continue;
        }
        std::string key = lru[0]->path;
        std::pop_heap(lru.begin(), lru.end(), FileHashCache::greater);
        lru.pop_back();
        if (cache.find(key) != cache.end()) {
          cache.erase(key);
        }
      }
    }

    auto hashes = hashMultiFromFile(
        HASH_TYPE_MD5 | HASH_TYPE_SHA1 | HASH_TYPE_SHA256, path);
    FileHashCache rec = {st.st_mtime, // .file_mtime
                         st.st_ino, // .file_inode
                         st.st_size, // .file_size
                         time(nullptr), // .cache_access_time
                         std::move(hashes), // .hashes
                         path}; // .path
    cache[path] = std::move(rec);
    lru.push_back(&cache[path]);
    std::push_heap(lru.begin(), lru.end(), FileHashCache::greater);
    out = cache[path].hashes;
  } else if (statInvalid(st, entry->second)) { // changed, update
    auto hashes = hashMultiFromFile(
        HASH_TYPE_MD5 | HASH_TYPE_SHA1 | HASH_TYPE_SHA256, path);
    entry->second.cache_access_time = time(nullptr);
    entry->second.file_inode = st.st_ino;
    entry->second.file_mtime = st.st_mtime;
    entry->second.file_size = st.st_size;
    entry->second.hashes = std::move(hashes);
    std::make_heap(lru.begin(), lru.end(), FileHashCache::greater);
    out = entry->second.hashes;
  } else { // ok, got it
    out = entry->second.hashes;
    entry->second.cache_access_time = time(nullptr);
    std::make_heap(lru.begin(), lru.end(), FileHashCache::greater);
  }
  return true;
}

Status genSsdeepForFile(const std::string& path, std::string& ssdeep_hash) {
#ifdef OSQUERY_POSIX
  ssdeep_hash.resize(FUZZY_MAX_RESULT, '\0');
  auto did_ssdeep_fail =
      fuzzy_hash_filename(path.c_str(), &ssdeep_hash.front());
  if (did_ssdeep_fail) {
    return Status::failure("ssdeep failed: " + path);
  }
  ssdeep_hash.resize(ssdeep_hash.find('\0'));
  return Status::success();
#else
  ssdeep_hash = "-1";
  return Status::success();
#endif
}

void genHashForFile(const std::string& path,
                    const std::string& dir,
                    QueryContext& context,
                    QueryData& results,
                    Logger& logger) {
  // Must provide the path, filename, directory separate from boost path->string
  // helpers to match any explicit (query-parsed) predicate constraints.
  auto tr = TableRowHolder(new DynamicTableRow());
  MultiHashes hashes;
  if (!FLAGS_disable_hash_cache) {
    FileHashCache::load(path, hashes, logger);
  } else {
    if (context.isCached(path)) {
      // Use the inner-query cache if the global hash cache is disabled.
      // This protects against hashing the same content twice in the same query.
      tr = context.getCache(path);
    } else {
      hashes = hashMultiFromFile(
          HASH_TYPE_MD5 | HASH_TYPE_SHA1 | HASH_TYPE_SHA256, path);
      std::this_thread::sleep_for(std::chrono::milliseconds(FLAGS_hash_delay));
    }
  }

  DynamicTableRow& r = *dynamic_cast<DynamicTableRow*>(tr.get());
  r["path"] = path;
  r["directory"] = dir;
  r["md5"] = std::move(hashes.md5);
  r["sha1"] = std::move(hashes.sha1);
  r["sha256"] = std::move(hashes.sha256);

  if (isPlatform(PlatformType::TYPE_POSIX) && context.isColumnUsed("ssdeep")) {
    auto status = genSsdeepForFile(path, r["ssdeep"]);

    if (!status.ok()) {
      logger.log(google::GLOG_WARNING, status.getMessage());
    }
  }

  if (FLAGS_disable_hash_cache) {
    context.setCache(path, tr);
  }

  r["pid_with_namespace"] = "0";

  results.push_back(static_cast<Row>(r));
}

void expandFSPathConstraints(QueryContext& context,
                             const std::string& path_column_name,
                             std::set<std::string>& paths) {
  context.expandConstraints(
      path_column_name,
      LIKE,
      paths,
      ([&](const std::string& pattern, std::set<std::string>& out) {
        std::vector<std::string> patterns;
        auto status =
            resolveFilePattern(pattern, patterns, GLOB_ALL | GLOB_NO_CANON);
        if (status.ok()) {
          for (const auto& resolved : patterns) {
            out.insert(resolved);
          }
        }
        return status;
      }));
}

QueryData genHashImpl(QueryContext& context, Logger& logger) {
  QueryData results;
  boost::system::error_code ec;

  // The query must provide a predicate with constraints including path or
  // directory. We search for the parsed predicate constraints with the equals
  // operator.
  auto paths = context.constraints["path"].getAll(EQUALS);
  expandFSPathConstraints(context, "path", paths);

  // Iterate through the file paths, adding the hash results
  for (const auto& path_string : paths) {
    boost::filesystem::path path = path_string;
    if (!boost::filesystem::is_regular_file(path, ec)) {
      continue;
    }

    genHashForFile(
        path_string, path.parent_path().string(), context, results, logger);
  }

  // Now loop through constraints using the directory column constraint.
  auto directories = context.constraints["directory"].getAll(EQUALS);
  expandFSPathConstraints(context, "directory", directories);

  // Iterate over the directory paths
  for (const auto& directory_string : directories) {
    boost::filesystem::path directory = directory_string;
    if (!boost::filesystem::is_directory(directory, ec)) {
      continue;
    }

    // Iterate over the directory files and generate a hash for each regular
    // file.
    boost::filesystem::directory_iterator begin(directory), end;
    for (; begin != end; ++begin) {
      if (boost::filesystem::is_regular_file(begin->path(), ec)) {
        genHashForFile(
            begin->path().string(), directory_string, context, results, logger);
      }
    }
  }

  return results;
}

QueryData genHash(QueryContext& context) {
  if (hasNamespaceConstraint(context)) {
    return generateInNamespace(context, "hash", genHashImpl);
  } else {
    GLOGLogger logger;
    return genHashImpl(context, logger);
  }
}
} // namespace tables
} // namespace osquery
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
 *
 * This file is part of ZLToolKit(https://github.com/xiongziliang/ZLToolKit).
 *
 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
 */

#include <fcntl.h>
#include <string.h>
#include <list>
#include "SelectWrap.h"
#include "EventPoller.h"
#include "Util/util.h"
#include "Util/logger.h"
#include "Util/uv_errno.h"
#include "Util/TimeTicker.h"
#include "Util/onceToken.h"
#include "Thread/ThreadPool.h"
#include "Network/sockutil.h"

#if defined(HAS_EPOLL)
    #include <sys/epoll.h>

    #if !defined(EPOLLEXCLUSIVE)
    #define EPOLLEXCLUSIVE 0
    #endif

    #define EPOLL_SIZE 1024
    #define toEpoll(event)    (((event) & Event_Read) ? EPOLLIN : 0) \
                                | (((event) & Event_Write) ? EPOLLOUT : 0) \
                                | (((event) & Event_Error) ? (EPOLLHUP | EPOLLERR) : 0) \
                                | (((event) & Event_LT) ?  0 : EPOLLET)
    #define toPoller(epoll_event) (((epoll_event) & EPOLLIN) ? Event_Read : 0) \
                                | (((epoll_event) & EPOLLOUT) ? Event_Write : 0) \
                                | (((epoll_event) & EPOLLHUP) ? Event_Error : 0) \
                                | (((epoll_event) & EPOLLERR) ? Event_Error : 0)
#endif //HAS_EPOLL

namespace toolkit {

EventPoller &EventPoller::Instance() {
    return *(EventPollerPool::Instance().getFirstPoller());
}

EventPoller::EventPoller(ThreadPool::Priority priority ) {
    _priority = priority;
    SockUtil::setNoBlocked(_pipe.readFD());
    SockUtil::setNoBlocked(_pipe.writeFD());

#if defined(HAS_EPOLL)
    _epoll_fd = epoll_create(EPOLL_SIZE);
    if (_epoll_fd == -1) {
        throw runtime_error(StrPrinter << "创建epoll文件描述符失败:" << get_uv_errmsg());
    }
#endif //HAS_EPOLL
    _logger = Logger::Instance().shared_from_this();
    _loopThreadId = this_thread::get_id();

    //添加内部管道事件
    if (addEvent(_pipe.readFD(), Event_Read, [this](int event) {onPipeEvent();}) == -1) {
        throw std::runtime_error("epoll添加管道失败");
    }
}

void EventPoller::shutdown() {
    async_l([](){
        throw ExitException();
    },false, true);

    if (_loopThread) {
        _loopThread->join();
        delete _loopThread;
        _loopThread = nullptr;
    }
}

EventPoller::~EventPoller() {
    shutdown();
    wait();
#if defined(HAS_EPOLL)
    if (_epoll_fd != -1) {
        close(_epoll_fd);
        _epoll_fd = -1;
    }
#endif //defined(HAS_EPOLL)
    //退出前清理管道中的数据
    _loopThreadId = this_thread::get_id();
    onPipeEvent();
    InfoL << this;
}

int EventPoller::addEvent(int fd, int event, PollEventCB &&cb) {
    TimeTicker();
    if (!cb) {
        WarnL << "PollEventCB 为空!";
        return -1;
    }

    if (isCurrentThread()) {
#if defined(HAS_EPOLL)
        struct epoll_event ev = {0};
        ev.events = (toEpoll(event)) | EPOLLEXCLUSIVE;
        ev.data.fd = fd;
        int ret = epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, fd, &ev);
        if (ret == 0) {
            _event_map.emplace(fd, std::make_shared<PollEventCB>(std::move(cb)));
        }
        return ret;
#else
        if(fd >= FD_SETSIZE || _event_map.size() >= FD_SETSIZE){
            WarnL << "select最多监听" << FD_SETSIZE << "个文件描述符";
            return -1;
        }
        Poll_Record::Ptr record(new Poll_Record);
        record->event = event;
        record->callBack = std::move(cb);
        _event_map.emplace(fd, record);
        return 0;
#endif //HAS_EPOLL
    }

    async([this, fd, event, cb]() {
        addEvent(fd, event, std::move(const_cast<PollEventCB &>(cb)));
    });
    return 0;

}

int EventPoller::delEvent(int fd, PollDelCB &&cb) {
    TimeTicker();
    if (!cb) {
        cb = [](bool success) {};
    }

    if (isCurrentThread()) {
#if defined(HAS_EPOLL)
        bool success = epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, NULL) == 0 && _event_map.erase(fd) > 0;
        cb(success);
        return success ? 0 : -1;
#else
        cb(_event_map.erase(fd));
        return 0;
#endif //HAS_EPOLL

    }

    //跨线程操作
    async([this, fd, cb]() {
        delEvent(fd, std::move(const_cast<PollDelCB &>(cb)));
    });
    return 0;
}

int EventPoller::modifyEvent(int fd, int event) {
    TimeTicker();
    //TraceL<<fd<<" "<<event;
#if defined(HAS_EPOLL)
    struct epoll_event ev = {0};
    ev.events = toEpoll(event);
    ev.data.fd = fd;
    return epoll_ctl(_epoll_fd, EPOLL_CTL_MOD, fd, &ev);
#else
    if (isCurrentThread()) {
        auto it = _event_map.find(fd);
        if (it != _event_map.end()) {
            it->second->event = event;
        }
        return 0;
    }
    async([this, fd, event]() {
        modifyEvent(fd, event);
    });
    return 0;
#endif //HAS_EPOLL
}

Task::Ptr EventPoller::async(TaskIn &&task, bool may_sync) {
    return async_l(std::move(task),may_sync, false);
}
Task::Ptr EventPoller::async_first(TaskIn &&task, bool may_sync) {
    return async_l(std::move(task),may_sync, true);
}

Task::Ptr EventPoller::async_l(TaskIn &&task,bool may_sync, bool first) {
    TimeTicker();
    if (may_sync && isCurrentThread()) {
        task();
        return nullptr;
    }

    auto ret = std::make_shared<Task>(std::move(task));
    {
        lock_guard<mutex> lck(_mtx_task);
        if(first){
            _list_task.emplace_front(ret);
        }else{
            _list_task.emplace_back(ret);
        }
    }
    //写数据到管道,唤醒主线程
    _pipe.write("",1);
    return ret;
}

bool EventPoller::isCurrentThread() {
    return _loopThreadId == this_thread::get_id();
}

inline void EventPoller::onPipeEvent() {
    TimeTicker();
    char buf[1024];
    int err = 0;
    do {
        if (_pipe.read(buf, sizeof(buf)) > 0) {
            continue;
        }
        err = get_uv_error(true);
    } while (err != UV_EAGAIN);

    decltype(_list_task) _list_swap;
    {
        lock_guard<mutex> lck(_mtx_task);
        _list_swap.swap(_list_task);
    }

    _list_swap.for_each([&](const Task::Ptr &task){
        try {
            (*task)();
        }catch (ExitException &ex){
            _exit_flag = true;
        }catch (std::exception &ex){
            ErrorL << "EventPoller执行异步任务捕获到异常:" << ex.what();
        }
    });
}

void EventPoller::wait() {
    lock_guard<mutex> lck(_mtx_runing);
}

static map<thread::id,weak_ptr<EventPoller> > s_allThreads;
static mutex s_allThreadsMtx;

//static
EventPoller::Ptr EventPoller::getCurrentPoller(){
    lock_guard<mutex> lck(s_allThreadsMtx);
    auto it = s_allThreads.find(this_thread::get_id());
    if(it == s_allThreads.end()){
        return nullptr;
    }
    return it->second.lock();
}
void EventPoller::runLoop(bool blocked,bool registCurrentPoller) {
    if (blocked) {
        ThreadPool::setPriority(_priority);
        lock_guard<mutex> lck(_mtx_runing);
        _loopThreadId = this_thread::get_id();
        if(registCurrentPoller){
            lock_guard<mutex> lck(s_allThreadsMtx);
            s_allThreads[_loopThreadId] = shared_from_this();
        }
        _sem_run_started.post();
        _exit_flag = false;
        uint64_t minDelay;
#if defined(HAS_EPOLL)
        struct epoll_event events[EPOLL_SIZE];
        while (!_exit_flag) {
            minDelay = getMinDelay();
            startSleep();//用于统计当前线程负载情况
            int ret = epoll_wait(_epoll_fd, events, EPOLL_SIZE, minDelay ? minDelay : -1);
            sleepWakeUp();//用于统计当前线程负载情况
            if(ret <= 0){
                //超时或被打断
                continue;
            }
            for (int i = 0; i < ret; ++i) {
                struct epoll_event &ev = events[i];
                int fd = ev.data.fd;
                auto it = _event_map.find(fd);
                if (it == _event_map.end()) {
                    epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, NULL);
                    continue;
                }
                auto cb = it->second;
                try{
                    (*cb)(toPoller(ev.events));
                }catch (std::exception &ex){
                    ErrorL << "EventPoller执行事件回调捕获到异常:" << ex.what();
                }
            }
        }
#else
        int ret, max_fd;
        FdSet set_read, set_write, set_err;
        List<Poll_Record::Ptr> callback_list;
        struct timeval tv;
        while (!_exit_flag) {
            //定时器事件中可能操作_event_map
            minDelay = getMinDelay();
            tv.tv_sec = minDelay / 1000;
            tv.tv_usec = 1000 * (minDelay % 1000);

            set_read.fdZero();
            set_write.fdZero();
            set_err.fdZero();
            max_fd = 0;
            for (auto &pr : _event_map) {
                if (pr.first > max_fd) {
                    max_fd = pr.first;
                }
                if (pr.second->event & Event_Read) {
                    set_read.fdSet(pr.first);//监听管道可读事件
                }
                if (pr.second->event & Event_Write) {
                    set_write.fdSet(pr.first);//监听管道可写事件
                }
                if (pr.second->event & Event_Error) {
                    set_err.fdSet(pr.first);//监听管道错误事件
                }
            }

            startSleep();//用于统计当前线程负载情况
            ret = zl_select(max_fd + 1, &set_read, &set_write, &set_err, minDelay ? &tv: NULL);
            sleepWakeUp();//用于统计当前线程负载情况

            if(ret <= 0) {
                //超时或被打断
                continue;
            }
            //收集select事件类型
            for (auto &pr : _event_map) {
                int event = 0;
                if (set_read.isSet(pr.first)) {
                    event |= Event_Read;
                }
                if (set_write.isSet(pr.first)) {
                    event |= Event_Write;
                }
                if (set_err.isSet(pr.first)) {
                    event |= Event_Error;
                }
                if (event != 0) {
                    pr.second->attach = event;
                    callback_list.emplace_back(pr.second);
                }
            }

            callback_list.for_each([](Poll_Record::Ptr &record){
                try{
                    record->callBack(record->attach);
                }catch (std::exception &ex){
                    ErrorL << "EventPoller执行事件回调捕获到异常:" << ex.what();
                }
            });
            callback_list.clear();
        }
#endif //HAS_EPOLL
    }else{
        _loopThread = new thread(&EventPoller::runLoop, this, true,registCurrentPoller);
        _sem_run_started.wait();
    }
}

uint64_t EventPoller::flushDelayTask(uint64_t now_time) {
    decltype(_delayTask) taskCopy;
    taskCopy.swap(_delayTask);

    for(auto it = taskCopy.begin() ; it != taskCopy.end() && it->first <= now_time ; it = taskCopy.erase(it)){
        //已到期的任务
        try {
            auto next_delay = (*(it->second))();
            if(next_delay){
                //可重复任务,更新时间截止线
                _delayTask.emplace(next_delay + now_time,std::move(it->second));
            }
        }catch (std::exception &ex){
            ErrorL << "EventPoller执行延时任务捕获到异常:" << ex.what();
        }
    }

    taskCopy.insert(_delayTask.begin(),_delayTask.end());
    taskCopy.swap(_delayTask);

    auto it = _delayTask.begin();
    if(it == _delayTask.end()){
        //没有剩余的定时器了
        return 0;
    }
    //最近一个定时器的执行延时
    return it->first - now_time;
}

uint64_t EventPoller::getMinDelay() {
    auto it = _delayTask.begin();
    if(it == _delayTask.end()){
        //没有剩余的定时器了
        return 0;
    }
    auto now =  getCurrentMillisecond();
    if(it->first > now){
        //所有任务尚未到期
        return it->first - now;
    }
    //执行已到期的任务并刷新休眠延时
    return flushDelayTask(now);
}

DelayTask::Ptr EventPoller::doDelayTask(uint64_t delayMS, function<uint64_t()> &&task) {
    DelayTask::Ptr ret = std::make_shared<DelayTask>(std::move(task));
    auto time_line = getCurrentMillisecond() + delayMS;
    async_first([time_line,ret,this](){
        //异步执行的目的是刷新select或epoll的休眠时间
        _delayTask.emplace(time_line,ret);
    });
    return ret;
}

///////////////////////////////////////////////
int EventPollerPool::s_pool_size = 0;

INSTANCE_IMP(EventPollerPool);

EventPoller::Ptr EventPollerPool::getFirstPoller(){
    return dynamic_pointer_cast<EventPoller>(_threads.front());
}

EventPoller::Ptr EventPollerPool::getPoller(){
    auto poller = EventPoller::getCurrentPoller();
    if(_preferCurrentThread && poller){
        return poller;
    }
    return dynamic_pointer_cast<EventPoller>(getExecutor());
}

void EventPollerPool::preferCurrentThread(bool flag){
    _preferCurrentThread = flag;
}

EventPollerPool::EventPollerPool(){
    auto size = s_pool_size > 0 ? s_pool_size : thread::hardware_concurrency();
    createThreads([](){
        EventPoller::Ptr ret(new EventPoller);
        ret->runLoop(false, true);
        return ret;
    },size);
    InfoL << "创建EventPoller个数:" << size;
}

void EventPollerPool::setPoolSize(int size) {
    s_pool_size = size;
}

}  // namespace toolkit

<commit_msg>修复select全局限制的bug2<commit_after>/*
 * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
 *
 * This file is part of ZLToolKit(https://github.com/xiongziliang/ZLToolKit).
 *
 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
 */

#include <fcntl.h>
#include <string.h>
#include <list>
#include "SelectWrap.h"
#include "EventPoller.h"
#include "Util/util.h"
#include "Util/logger.h"
#include "Util/uv_errno.h"
#include "Util/TimeTicker.h"
#include "Util/onceToken.h"
#include "Thread/ThreadPool.h"
#include "Network/sockutil.h"

#if defined(HAS_EPOLL)
    #include <sys/epoll.h>

    #if !defined(EPOLLEXCLUSIVE)
    #define EPOLLEXCLUSIVE 0
    #endif

    #define EPOLL_SIZE 1024
    #define toEpoll(event)    (((event) & Event_Read) ? EPOLLIN : 0) \
                                | (((event) & Event_Write) ? EPOLLOUT : 0) \
                                | (((event) & Event_Error) ? (EPOLLHUP | EPOLLERR) : 0) \
                                | (((event) & Event_LT) ?  0 : EPOLLET)
    #define toPoller(epoll_event) (((epoll_event) & EPOLLIN) ? Event_Read : 0) \
                                | (((epoll_event) & EPOLLOUT) ? Event_Write : 0) \
                                | (((epoll_event) & EPOLLHUP) ? Event_Error : 0) \
                                | (((epoll_event) & EPOLLERR) ? Event_Error : 0)
#endif //HAS_EPOLL

namespace toolkit {

EventPoller &EventPoller::Instance() {
    return *(EventPollerPool::Instance().getFirstPoller());
}

EventPoller::EventPoller(ThreadPool::Priority priority ) {
    _priority = priority;
    SockUtil::setNoBlocked(_pipe.readFD());
    SockUtil::setNoBlocked(_pipe.writeFD());

#if defined(HAS_EPOLL)
    _epoll_fd = epoll_create(EPOLL_SIZE);
    if (_epoll_fd == -1) {
        throw runtime_error(StrPrinter << "创建epoll文件描述符失败:" << get_uv_errmsg());
    }
#endif //HAS_EPOLL
    _logger = Logger::Instance().shared_from_this();
    _loopThreadId = this_thread::get_id();

    //添加内部管道事件
    if (addEvent(_pipe.readFD(), Event_Read, [this](int event) {onPipeEvent();}) == -1) {
        throw std::runtime_error("epoll添加管道失败");
    }
}

void EventPoller::shutdown() {
    async_l([](){
        throw ExitException();
    },false, true);

    if (_loopThread) {
        _loopThread->join();
        delete _loopThread;
        _loopThread = nullptr;
    }
}

EventPoller::~EventPoller() {
    shutdown();
    wait();
#if defined(HAS_EPOLL)
    if (_epoll_fd != -1) {
        close(_epoll_fd);
        _epoll_fd = -1;
    }
#endif //defined(HAS_EPOLL)
    //退出前清理管道中的数据
    _loopThreadId = this_thread::get_id();
    onPipeEvent();
    InfoL << this;
}

int EventPoller::addEvent(int fd, int event, PollEventCB &&cb) {
    TimeTicker();
    if (!cb) {
        WarnL << "PollEventCB 为空!";
        return -1;
    }

    if (isCurrentThread()) {
#if defined(HAS_EPOLL)
        struct epoll_event ev = {0};
        ev.events = (toEpoll(event)) | EPOLLEXCLUSIVE;
        ev.data.fd = fd;
        int ret = epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, fd, &ev);
        if (ret == 0) {
            _event_map.emplace(fd, std::make_shared<PollEventCB>(std::move(cb)));
        }
        return ret;
#else
#ifndef _WIN32
        //win32平台，socket套接字不等于文件描述符，所以可能不适用这个限制
        if(fd >= FD_SETSIZE || _event_map.size() >= FD_SETSIZE){
            WarnL << "select最多监听" << FD_SETSIZE << "个文件描述符";
            return -1;
        }
#endif
        Poll_Record::Ptr record(new Poll_Record);
        record->event = event;
        record->callBack = std::move(cb);
        _event_map.emplace(fd, record);
        return 0;
#endif //HAS_EPOLL
    }

    async([this, fd, event, cb]() {
        addEvent(fd, event, std::move(const_cast<PollEventCB &>(cb)));
    });
    return 0;

}

int EventPoller::delEvent(int fd, PollDelCB &&cb) {
    TimeTicker();
    if (!cb) {
        cb = [](bool success) {};
    }

    if (isCurrentThread()) {
#if defined(HAS_EPOLL)
        bool success = epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, NULL) == 0 && _event_map.erase(fd) > 0;
        cb(success);
        return success ? 0 : -1;
#else
        cb(_event_map.erase(fd));
        return 0;
#endif //HAS_EPOLL

    }

    //跨线程操作
    async([this, fd, cb]() {
        delEvent(fd, std::move(const_cast<PollDelCB &>(cb)));
    });
    return 0;
}

int EventPoller::modifyEvent(int fd, int event) {
    TimeTicker();
    //TraceL<<fd<<" "<<event;
#if defined(HAS_EPOLL)
    struct epoll_event ev = {0};
    ev.events = toEpoll(event);
    ev.data.fd = fd;
    return epoll_ctl(_epoll_fd, EPOLL_CTL_MOD, fd, &ev);
#else
    if (isCurrentThread()) {
        auto it = _event_map.find(fd);
        if (it != _event_map.end()) {
            it->second->event = event;
        }
        return 0;
    }
    async([this, fd, event]() {
        modifyEvent(fd, event);
    });
    return 0;
#endif //HAS_EPOLL
}

Task::Ptr EventPoller::async(TaskIn &&task, bool may_sync) {
    return async_l(std::move(task),may_sync, false);
}
Task::Ptr EventPoller::async_first(TaskIn &&task, bool may_sync) {
    return async_l(std::move(task),may_sync, true);
}

Task::Ptr EventPoller::async_l(TaskIn &&task,bool may_sync, bool first) {
    TimeTicker();
    if (may_sync && isCurrentThread()) {
        task();
        return nullptr;
    }

    auto ret = std::make_shared<Task>(std::move(task));
    {
        lock_guard<mutex> lck(_mtx_task);
        if(first){
            _list_task.emplace_front(ret);
        }else{
            _list_task.emplace_back(ret);
        }
    }
    //写数据到管道,唤醒主线程
    _pipe.write("",1);
    return ret;
}

bool EventPoller::isCurrentThread() {
    return _loopThreadId == this_thread::get_id();
}

inline void EventPoller::onPipeEvent() {
    TimeTicker();
    char buf[1024];
    int err = 0;
    do {
        if (_pipe.read(buf, sizeof(buf)) > 0) {
            continue;
        }
        err = get_uv_error(true);
    } while (err != UV_EAGAIN);

    decltype(_list_task) _list_swap;
    {
        lock_guard<mutex> lck(_mtx_task);
        _list_swap.swap(_list_task);
    }

    _list_swap.for_each([&](const Task::Ptr &task){
        try {
            (*task)();
        }catch (ExitException &ex){
            _exit_flag = true;
        }catch (std::exception &ex){
            ErrorL << "EventPoller执行异步任务捕获到异常:" << ex.what();
        }
    });
}

void EventPoller::wait() {
    lock_guard<mutex> lck(_mtx_runing);
}

static map<thread::id,weak_ptr<EventPoller> > s_allThreads;
static mutex s_allThreadsMtx;

//static
EventPoller::Ptr EventPoller::getCurrentPoller(){
    lock_guard<mutex> lck(s_allThreadsMtx);
    auto it = s_allThreads.find(this_thread::get_id());
    if(it == s_allThreads.end()){
        return nullptr;
    }
    return it->second.lock();
}
void EventPoller::runLoop(bool blocked,bool registCurrentPoller) {
    if (blocked) {
        ThreadPool::setPriority(_priority);
        lock_guard<mutex> lck(_mtx_runing);
        _loopThreadId = this_thread::get_id();
        if(registCurrentPoller){
            lock_guard<mutex> lck(s_allThreadsMtx);
            s_allThreads[_loopThreadId] = shared_from_this();
        }
        _sem_run_started.post();
        _exit_flag = false;
        uint64_t minDelay;
#if defined(HAS_EPOLL)
        struct epoll_event events[EPOLL_SIZE];
        while (!_exit_flag) {
            minDelay = getMinDelay();
            startSleep();//用于统计当前线程负载情况
            int ret = epoll_wait(_epoll_fd, events, EPOLL_SIZE, minDelay ? minDelay : -1);
            sleepWakeUp();//用于统计当前线程负载情况
            if(ret <= 0){
                //超时或被打断
                continue;
            }
            for (int i = 0; i < ret; ++i) {
                struct epoll_event &ev = events[i];
                int fd = ev.data.fd;
                auto it = _event_map.find(fd);
                if (it == _event_map.end()) {
                    epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, NULL);
                    continue;
                }
                auto cb = it->second;
                try{
                    (*cb)(toPoller(ev.events));
                }catch (std::exception &ex){
                    ErrorL << "EventPoller执行事件回调捕获到异常:" << ex.what();
                }
            }
        }
#else
        int ret, max_fd;
        FdSet set_read, set_write, set_err;
        List<Poll_Record::Ptr> callback_list;
        struct timeval tv;
        while (!_exit_flag) {
            //定时器事件中可能操作_event_map
            minDelay = getMinDelay();
            tv.tv_sec = minDelay / 1000;
            tv.tv_usec = 1000 * (minDelay % 1000);

            set_read.fdZero();
            set_write.fdZero();
            set_err.fdZero();
            max_fd = 0;
            for (auto &pr : _event_map) {
                if (pr.first > max_fd) {
                    max_fd = pr.first;
                }
                if (pr.second->event & Event_Read) {
                    set_read.fdSet(pr.first);//监听管道可读事件
                }
                if (pr.second->event & Event_Write) {
                    set_write.fdSet(pr.first);//监听管道可写事件
                }
                if (pr.second->event & Event_Error) {
                    set_err.fdSet(pr.first);//监听管道错误事件
                }
            }

            startSleep();//用于统计当前线程负载情况
            ret = zl_select(max_fd + 1, &set_read, &set_write, &set_err, minDelay ? &tv: NULL);
            sleepWakeUp();//用于统计当前线程负载情况

            if(ret <= 0) {
                //超时或被打断
                continue;
            }
            //收集select事件类型
            for (auto &pr : _event_map) {
                int event = 0;
                if (set_read.isSet(pr.first)) {
                    event |= Event_Read;
                }
                if (set_write.isSet(pr.first)) {
                    event |= Event_Write;
                }
                if (set_err.isSet(pr.first)) {
                    event |= Event_Error;
                }
                if (event != 0) {
                    pr.second->attach = event;
                    callback_list.emplace_back(pr.second);
                }
            }

            callback_list.for_each([](Poll_Record::Ptr &record){
                try{
                    record->callBack(record->attach);
                }catch (std::exception &ex){
                    ErrorL << "EventPoller执行事件回调捕获到异常:" << ex.what();
                }
            });
            callback_list.clear();
        }
#endif //HAS_EPOLL
    }else{
        _loopThread = new thread(&EventPoller::runLoop, this, true,registCurrentPoller);
        _sem_run_started.wait();
    }
}

uint64_t EventPoller::flushDelayTask(uint64_t now_time) {
    decltype(_delayTask) taskCopy;
    taskCopy.swap(_delayTask);

    for(auto it = taskCopy.begin() ; it != taskCopy.end() && it->first <= now_time ; it = taskCopy.erase(it)){
        //已到期的任务
        try {
            auto next_delay = (*(it->second))();
            if(next_delay){
                //可重复任务,更新时间截止线
                _delayTask.emplace(next_delay + now_time,std::move(it->second));
            }
        }catch (std::exception &ex){
            ErrorL << "EventPoller执行延时任务捕获到异常:" << ex.what();
        }
    }

    taskCopy.insert(_delayTask.begin(),_delayTask.end());
    taskCopy.swap(_delayTask);

    auto it = _delayTask.begin();
    if(it == _delayTask.end()){
        //没有剩余的定时器了
        return 0;
    }
    //最近一个定时器的执行延时
    return it->first - now_time;
}

uint64_t EventPoller::getMinDelay() {
    auto it = _delayTask.begin();
    if(it == _delayTask.end()){
        //没有剩余的定时器了
        return 0;
    }
    auto now =  getCurrentMillisecond();
    if(it->first > now){
        //所有任务尚未到期
        return it->first - now;
    }
    //执行已到期的任务并刷新休眠延时
    return flushDelayTask(now);
}

DelayTask::Ptr EventPoller::doDelayTask(uint64_t delayMS, function<uint64_t()> &&task) {
    DelayTask::Ptr ret = std::make_shared<DelayTask>(std::move(task));
    auto time_line = getCurrentMillisecond() + delayMS;
    async_first([time_line,ret,this](){
        //异步执行的目的是刷新select或epoll的休眠时间
        _delayTask.emplace(time_line,ret);
    });
    return ret;
}

///////////////////////////////////////////////
int EventPollerPool::s_pool_size = 0;

INSTANCE_IMP(EventPollerPool);

EventPoller::Ptr EventPollerPool::getFirstPoller(){
    return dynamic_pointer_cast<EventPoller>(_threads.front());
}

EventPoller::Ptr EventPollerPool::getPoller(){
    auto poller = EventPoller::getCurrentPoller();
    if(_preferCurrentThread && poller){
        return poller;
    }
    return dynamic_pointer_cast<EventPoller>(getExecutor());
}

void EventPollerPool::preferCurrentThread(bool flag){
    _preferCurrentThread = flag;
}

EventPollerPool::EventPollerPool(){
    auto size = s_pool_size > 0 ? s_pool_size : thread::hardware_concurrency();
    createThreads([](){
        EventPoller::Ptr ret(new EventPoller);
        ret->runLoop(false, true);
        return ret;
    },size);
    InfoL << "创建EventPoller个数:" << size;
}

void EventPollerPool::setPoolSize(int size) {
    s_pool_size = size;
}

}  // namespace toolkit

<|endoftext|>


<commit_before>#include "PyInterpreterState.h"
#include "PyThreadState.h"
#include "PyFrameObject.h"
#include "PyDictObject.h"

#include "fieldAsPyObject.h"

#include <engextcpp.hpp>

#include <cstdint>
#include <memory>
#include <vector>
#include <optional>
using namespace std;

namespace PyExt::Remote {

	PyInterpreterState::PyInterpreterState(Offset objectAddress)
		: RemoteType(objectAddress, "PyInterpreterState")
	{
	}

	PyInterpreterState::~PyInterpreterState()
	{
	}

	auto PyInterpreterState::makeAutoInterpreterState() -> unique_ptr<PyInterpreterState>
	{
		auto autoInterpreterState = ExtRemoteTyped("autoInterpreterState");
		return make_unique<PyInterpreterState>(autoInterpreterState.GetPtr());
	}

	auto PyInterpreterState::allInterpreterStates() -> std::vector<PyInterpreterState>
	{
		vector<PyInterpreterState> states;
		for (auto state = makeAutoInterpreterState(); state != nullptr; state = state->next()) {
			states.push_back(*state);
		}
		return states;
	}

	auto PyInterpreterState::findThreadStateBySystemThreadId(uint64_t systemThreadId) -> optional<PyThreadState>
	{
		for (auto istate = makeAutoInterpreterState(); istate != nullptr; istate = istate->next()) {
			for (auto tstate = istate->tstate_head(); tstate != nullptr; tstate = tstate->next()) {
				if (tstate->thread_id() == systemThreadId) {
					return move(*tstate);
				}
			}
		}

		return { };
	}

	auto PyInterpreterState::next() const -> std::unique_ptr<PyInterpreterState>
	{
		auto nextPtr = remoteType().Field("next").GetPtr();
		if (nextPtr == 0)
			return { };

		return make_unique<PyInterpreterState>(nextPtr);
	}

	auto PyInterpreterState::tstate_head() const -> unique_ptr<PyThreadState>
	{
		return make_unique<PyThreadState>(remoteType().Field("tstate_head").GetPtr());
	}

	auto PyInterpreterState::modules() const -> std::unique_ptr<PyDictObject>
	{
		return utils::fieldAsPyObject<PyDictObject>(remoteType(), "modules");
	}

	auto PyInterpreterState::sysdict() const -> std::unique_ptr<PyDictObject>
	{
		return utils::fieldAsPyObject<PyDictObject>(remoteType(), "sysdict");
	}

	auto PyInterpreterState::builtins() const -> std::unique_ptr<PyDictObject>
	{
		return utils::fieldAsPyObject<PyDictObject>(remoteType(), "builtins");
	}

	auto PyInterpreterState::allThreadStates() const -> std::vector<PyThreadState>
	{
		vector<PyThreadState> states;
		for (auto state = tstate_head(); state != nullptr; state = state->next()) {
			states.push_back(*state);
		}
		return states;
	}

}
<commit_msg>Add Python 3.7 support by checking for  `_PyRuntime.gilstate.autoInterpreterState` before falling back on the global  `autoInterpreterState`.<commit_after>#include "PyInterpreterState.h"
#include "PyThreadState.h"
#include "PyFrameObject.h"
#include "PyDictObject.h"

#include "fieldAsPyObject.h"

#include <engextcpp.hpp>

#include <cstdint>
#include <memory>
#include <vector>
#include <optional>
using namespace std;

namespace PyExt::Remote {

	PyInterpreterState::PyInterpreterState(Offset objectAddress)
		: RemoteType(objectAddress, "PyInterpreterState")
	{
	}

	PyInterpreterState::~PyInterpreterState()
	{
	}

	auto PyInterpreterState::makeAutoInterpreterState() -> unique_ptr<PyInterpreterState>
	{
		ExtRemoteTyped autoInterpreterState;

		try
		{
			// In Python 3.7, the autoInterpreterState has moved into the gilstate. Try it first.
			autoInterpreterState = ExtRemoteTyped("_PyRuntime.gilstate.autoInterpreterState");
		}
		catch (...)
		{
			// Fall back on the pre-3.7 global autoInterpreterState.
			autoInterpreterState = ExtRemoteTyped("autoInterpreterState");
		}

		return make_unique<PyInterpreterState>(autoInterpreterState.GetPtr());
	}

	auto PyInterpreterState::allInterpreterStates() -> std::vector<PyInterpreterState>
	{
		vector<PyInterpreterState> states;
		for (auto state = makeAutoInterpreterState(); state != nullptr; state = state->next()) {
			states.push_back(*state);
		}
		return states;
	}

	auto PyInterpreterState::findThreadStateBySystemThreadId(uint64_t systemThreadId) -> optional<PyThreadState>
	{
		for (auto istate = makeAutoInterpreterState(); istate != nullptr; istate = istate->next()) {
			for (auto tstate = istate->tstate_head(); tstate != nullptr; tstate = tstate->next()) {
				if (tstate->thread_id() == systemThreadId) {
					return move(*tstate);
				}
			}
		}

		return { };
	}

	auto PyInterpreterState::next() const -> std::unique_ptr<PyInterpreterState>
	{
		auto nextPtr = remoteType().Field("next").GetPtr();
		if (nextPtr == 0)
			return { };

		return make_unique<PyInterpreterState>(nextPtr);
	}

	auto PyInterpreterState::tstate_head() const -> unique_ptr<PyThreadState>
	{
		return make_unique<PyThreadState>(remoteType().Field("tstate_head").GetPtr());
	}

	auto PyInterpreterState::modules() const -> std::unique_ptr<PyDictObject>
	{
		return utils::fieldAsPyObject<PyDictObject>(remoteType(), "modules");
	}

	auto PyInterpreterState::sysdict() const -> std::unique_ptr<PyDictObject>
	{
		return utils::fieldAsPyObject<PyDictObject>(remoteType(), "sysdict");
	}

	auto PyInterpreterState::builtins() const -> std::unique_ptr<PyDictObject>
	{
		return utils::fieldAsPyObject<PyDictObject>(remoteType(), "builtins");
	}

	auto PyInterpreterState::allThreadStates() const -> std::vector<PyThreadState>
	{
		vector<PyThreadState> states;
		for (auto state = tstate_head(); state != nullptr; state = state->next()) {
			states.push_back(*state);
		}
		return states;
	}

}
<|endoftext|>


<commit_before>/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  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
 *
 *         https://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  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 "itkMultiphaseDenseFiniteDifferenceImageFilter.h"
#include "itkScalarChanAndVeseLevelSetFunction.h"
#include "itkTestingMacros.h"

namespace itk
{

template <typename TInputImage,
          typename TFeatureImage,
          typename TOutputImage,
          typename TFiniteDifferenceFunction,
          typename TIdCell>
class MultiphaseDenseFiniteDifferenceImageFilterTestHelper
  : public MultiphaseDenseFiniteDifferenceImageFilter<TInputImage,
                                                      TFeatureImage,
                                                      TOutputImage,
                                                      TFiniteDifferenceFunction,
                                                      TIdCell>
{
public:
  /** Standard class type aliases. */
  using Self = MultiphaseDenseFiniteDifferenceImageFilterTestHelper;
  using Superclass = MultiphaseDenseFiniteDifferenceImageFilter<TInputImage,
                                                                TFeatureImage,
                                                                TOutputImage,
                                                                TFiniteDifferenceFunction,
                                                                TIdCell>;
  using Pointer = SmartPointer<Self>;
  using ConstPointer = SmartPointer<const Self>;

  /** Run-time type information (and related methods) */
  itkTypeMacro(MultiphaseDenseFiniteDifferenceImageFilterTestHelper, MultiphaseDenseFiniteDifferenceImageFilter);

  itkNewMacro(Self);

  void
  AllocateUpdateBuffer() override
  {}

  using typename Superclass::TimeStepType;

  void
  ApplyUpdate(TimeStepType itkNotUsed(dt)) override
  {}

  TimeStepType
  CalculateChange() override
  {
    return TimeStepType(1.0);
  }

  void
  CopyInputToOutput() override
  {}
};

} // namespace itk

int
itkMultiphaseDenseFiniteDifferenceImageFilterTest(int, char *[])
{
  constexpr unsigned int Dimension = 3;

  using LevelSetImageType = itk::Image<double, Dimension>;
  using FeatureImageType = itk::Image<float, Dimension>;
  using OutputImageType = itk::Image<unsigned char, Dimension>;

  using DataHelperType = itk::ScalarChanAndVeseLevelSetFunctionData<LevelSetImageType, FeatureImageType>;
  using SharedDataHelperType =
    itk::ConstrainedRegionBasedLevelSetFunctionSharedData<LevelSetImageType, FeatureImageType, DataHelperType>;

  using RegionBasedLevelSetFunctionType =
    itk::ScalarChanAndVeseLevelSetFunction<LevelSetImageType, FeatureImageType, SharedDataHelperType>;

  auto function = RegionBasedLevelSetFunctionType::New();
  if (function.IsNull())
  {
    return EXIT_FAILURE;
  }

  using IdCellType = unsigned long;

  using FilterType = itk::MultiphaseDenseFiniteDifferenceImageFilterTestHelper<LevelSetImageType,
                                                                               FeatureImageType,
                                                                               OutputImageType,
                                                                               RegionBasedLevelSetFunctionType,
                                                                               IdCellType>;

  auto filter = FilterType::New();

  ITK_EXERCISE_BASIC_OBJECT_METHODS(
    filter, MultiphaseDenseFiniteDifferenceImageFilterTestHelper, MultiphaseDenseFiniteDifferenceImageFilter);

  unsigned int reinitializeCounter = 1;
  filter->SetReinitializeCounter(reinitializeCounter);
  ITK_TEST_SET_GET_VALUE(reinitializeCounter, flter->GetReinitializeCounter());

  std::cout << "Test finished." << std::endl;
  return EXIT_SUCCESS;
}
<commit_msg>BUG: Fix variable typo `MultiphaseDenseFiniteDifferenceImageFilter` test<commit_after>/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  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
 *
 *         https://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  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 "itkMultiphaseDenseFiniteDifferenceImageFilter.h"
#include "itkScalarChanAndVeseLevelSetFunction.h"
#include "itkTestingMacros.h"

namespace itk
{

template <typename TInputImage,
          typename TFeatureImage,
          typename TOutputImage,
          typename TFiniteDifferenceFunction,
          typename TIdCell>
class MultiphaseDenseFiniteDifferenceImageFilterTestHelper
  : public MultiphaseDenseFiniteDifferenceImageFilter<TInputImage,
                                                      TFeatureImage,
                                                      TOutputImage,
                                                      TFiniteDifferenceFunction,
                                                      TIdCell>
{
public:
  /** Standard class type aliases. */
  using Self = MultiphaseDenseFiniteDifferenceImageFilterTestHelper;
  using Superclass = MultiphaseDenseFiniteDifferenceImageFilter<TInputImage,
                                                                TFeatureImage,
                                                                TOutputImage,
                                                                TFiniteDifferenceFunction,
                                                                TIdCell>;
  using Pointer = SmartPointer<Self>;
  using ConstPointer = SmartPointer<const Self>;

  /** Run-time type information (and related methods) */
  itkTypeMacro(MultiphaseDenseFiniteDifferenceImageFilterTestHelper, MultiphaseDenseFiniteDifferenceImageFilter);

  itkNewMacro(Self);

  void
  AllocateUpdateBuffer() override
  {}

  using typename Superclass::TimeStepType;

  void
  ApplyUpdate(TimeStepType itkNotUsed(dt)) override
  {}

  TimeStepType
  CalculateChange() override
  {
    return TimeStepType(1.0);
  }

  void
  CopyInputToOutput() override
  {}
};

} // namespace itk

int
itkMultiphaseDenseFiniteDifferenceImageFilterTest(int, char *[])
{
  constexpr unsigned int Dimension = 3;

  using LevelSetImageType = itk::Image<double, Dimension>;
  using FeatureImageType = itk::Image<float, Dimension>;
  using OutputImageType = itk::Image<unsigned char, Dimension>;

  using DataHelperType = itk::ScalarChanAndVeseLevelSetFunctionData<LevelSetImageType, FeatureImageType>;
  using SharedDataHelperType =
    itk::ConstrainedRegionBasedLevelSetFunctionSharedData<LevelSetImageType, FeatureImageType, DataHelperType>;

  using RegionBasedLevelSetFunctionType =
    itk::ScalarChanAndVeseLevelSetFunction<LevelSetImageType, FeatureImageType, SharedDataHelperType>;

  auto function = RegionBasedLevelSetFunctionType::New();
  if (function.IsNull())
  {
    return EXIT_FAILURE;
  }

  using IdCellType = unsigned long;

  using FilterType = itk::MultiphaseDenseFiniteDifferenceImageFilterTestHelper<LevelSetImageType,
                                                                               FeatureImageType,
                                                                               OutputImageType,
                                                                               RegionBasedLevelSetFunctionType,
                                                                               IdCellType>;

  auto filter = FilterType::New();

  ITK_EXERCISE_BASIC_OBJECT_METHODS(
    filter, MultiphaseDenseFiniteDifferenceImageFilterTestHelper, MultiphaseDenseFiniteDifferenceImageFilter);

  unsigned int reinitializeCounter = 1;
  filter->SetReinitializeCounter(reinitializeCounter);
  ITK_TEST_SET_GET_VALUE(reinitializeCounter, filter->GetReinitializeCounter());

  std::cout << "Test finished." << std::endl;
  return EXIT_SUCCESS;
}
<|endoftext|>