<commit_before>//  **************************************************************************
//  * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
//  *                                                                        *
//  * Author: The ALICE Off-line Project.                                    *
//  * Contributors are mentioned in the code where appropriate.              *
//  *                                                                        *
//  * Permission to use, copy, modify and distribute this software and its   *
//  * documentation strictly for non-commercial purposes is hereby granted   *
//  * without fee, provided that the above copyright notice appears in all   *
//  * copies and that both the copyright notice and this permission notice   *
//  * appear in the supporting documentation. The authors make no claims     *
//  * about the suitability of this software for any purpose. It is          *
//  * provided "as is" without express or implied warranty.                  *
//  **************************************************************************

#include "AliHMPID.h"
#include "AliHMPIDHit.h"   //OccupancyPrint(), HitQa()
#include "AliHMPIDDigit.h" //
#include <TParticle.h>  //SummaryOfEvent(), HitQa()
#include <TBenchmark.h>  //HitQA()
#include <TPDGCode.h>    //HitQA()
#include <AliStack.h>   //SummaryOfEvent(), HitQa()
#include <AliRun.h>     //HitQa()
#include <AliMC.h>       //ctor
#include <AliHeader.h>
#include <TH1F.h>        //HitQA()
#include <AliLog.h>      //in many methods to print AliInfo
ClassImp(AliHMPID)
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AliHMPID::AliHMPID(const char *name, const char *title):AliDetector(name,title),fSdi(0),fDig(0),fClu(0)
{
//Named ctor
  AliDebug(1,"Start.");
//AliDetector ctor deals with Hits and Digits (reset them to 0, does not create them)
  HitCreate();          gAlice->GetMCApp()->AddHitList(fHits);
  AliDebug(1,"Stop.");
}//AliHMPID::AliHMPID(const char *name, const char *title)
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AliHMPID::~AliHMPID()
{
//dtor
  AliDebug(1,"Start.");

  if(fHits)      delete fHits;
  if(fDigits)    delete fDigits;
  if(fSdi)       delete fSdi;
  if(fDig)      {fDig->Delete();   delete fDig;}
  if(fClu)      {fClu->Delete();   delete fClu;}
  AliDebug(1,"Stop.");
}//AliHMPID::~AliHMPID()
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void AliHMPID::MakeBranch(Option_t* option)
{
//Create Tree branches for the HMPID.
  AliDebug(1,Form("Start with option= %s.",option));

  const Int_t kBufSize = 4000;

  const char *cH = strstr(option,"H");
  const char *cD = strstr(option,"D");
  const char *cR = strstr(option,"R");
  const char *cS = strstr(option,"S");

  if(cH&&         TreeH()){HitCreate();                       MakeBranchInTree(         TreeH(),     "HMPID"     ,&fHits       ,kBufSize,0);}
  if(cS&&fLoader->TreeS()){SdiCreate();                       MakeBranchInTree(fLoader->TreeS(),     "HMPID"     ,&fSdi        ,kBufSize,0);}
  if(cD&&fLoader->TreeD()){DigCreate();for(Int_t i=0;i<7;i++) MakeBranchInTree(fLoader->TreeD(),Form("HMPID%d",i),&((*fDig)[i]),kBufSize,0);}
  if(cR&&fLoader->TreeR()){CluCreate();for(Int_t i=0;i<7;i++) MakeBranchInTree(fLoader->TreeR(),Form("HMPID%d",i),&((*fClu)[i]),kBufSize,0);}

  AliDebug(1,"Stop.");
}//void AliHMPID::MakeBranch(Option_t* option)
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void AliHMPID::SetTreeAddress()
{
//Set branch address for the Hits and Digits Tree.
  AliDebug(1,"Start.");
  if(fLoader->TreeH() && fLoader->TreeH()->GetBranch("HMPID" )){HitCreate();                      fLoader->TreeH()->SetBranchAddress(     "HMPID"     ,&fHits       );}
  if(fLoader->TreeS() && fLoader->TreeS()->GetBranch("HMPID" )){SdiCreate();                      fLoader->TreeS()->SetBranchAddress(     "HMPID"     ,&fSdi        );}
  if(fLoader->TreeD() && fLoader->TreeD()->GetBranch("HMPID0")){DigCreate(); for(int i=0;i<7;i++) fLoader->TreeD()->SetBranchAddress(Form("HMPID%d",i),&((*fDig)[i]));}
  if(fLoader->TreeR() && fLoader->TreeR()->GetBranch("HMPID0")){CluCreate(); for(int i=0;i<7;i++) fLoader->TreeR()->SetBranchAddress(Form("HMPID%d",i),&((*fClu)[i]));}
  AliDebug(1,"Stop.");
}//void AliHMPID::SetTreeAddress()
//__________________________________________________________________________________________________
// AliHMPIDHit* AliHMPID::Hit(Int_t tid)const
// {
// // Search for the first HMPID hit belonging to the given tid
//   GetLoader()->LoadHits();
//   for(Int_t iPrimN=0;iPrimN<GetLoader()->TreeH()->GetEntries();iPrimN++){//prims loop
//     GetLoader()->TreeH()->GetEntry(iPrimN);
//     for(Int_t iHitN=0;iHitN<Hits()->GetEntries();iHitN++){
//       AliHMPIDHit *pHit=(AliHMPIDHit*)Hits()->At(iHitN);
//       if(tid==pHit->Track()) {GetLoader()->UnloadHits();return pHit;}
//     }//hits
//   }//prims loop
//   GetLoader()->UnloadHits();
//   return 0;
// }
//__________________________________________________________________________________________________
void AliHMPID::HitPrint(Int_t iEvtN)const
{
//Prints a list of HMPID hits for a given event. Default is event number 0.
  if(GetLoader()->GetRunLoader()->GetEvent(iEvtN)) return;
  AliInfo(Form("List of HMPID hits for event %i",iEvtN));
  if(GetLoader()->LoadHits()) return;

  Int_t iTotalHits=0;
  for(Int_t iPrimN=0;iPrimN<GetLoader()->TreeH()->GetEntries();iPrimN++){//prims loop
    GetLoader()->TreeH()->GetEntry(iPrimN);
    Hits()->Print();
    iTotalHits+=Hits()->GetEntries();
  }
  GetLoader()->UnloadHits();
  AliInfo(Form("totally %i hits",iTotalHits));
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void AliHMPID::SdiPrint(Int_t iEvt)const
{
//prints a list of HMPID sdigits  for a given event
  if(GetLoader()->GetRunLoader()->GetEvent(iEvt)) return;
  Info("PrintSDigits","List of HMPID sdigits for event %i",iEvt);
  if(GetLoader()->LoadSDigits()) return;

  GetLoader()->TreeS()->GetEntry(0);
  SdiLst()->Print();
  GetLoader()->UnloadSDigits();
  Printf("totally %i sdigits",SdiLst()->GetEntries());
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void AliHMPID::DigPrint(Int_t iEvt)const
{
//prints a list of HMPID digits  for a given event
  if(GetLoader()->GetRunLoader()->GetEvent(iEvt)) return;
  Printf("List of HMPID digits for event %i",iEvt);
  if(GetLoader()->LoadDigits()) return;

  GetLoader()->TreeD()->GetEntry(0);
  DigLst()->Print();
  Int_t totDigs=0;
  for(Int_t i=0;i<7;i++) {totDigs+=DigLst(i)->GetEntries();}
  Printf("totally %i Digits",totDigs);
  GetLoader()->UnloadDigits();
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void AliHMPID::CluPrint(Int_t iEvtN)const
{
//prints a list of HMPID clusters  for a given event
  Printf("List of HMPID clusters for event %i",iEvtN);
  GetLoader()->GetRunLoader()->GetEvent(iEvtN);
  if(GetLoader()->LoadRecPoints()) return;

  Int_t iCluCnt=0;
  GetLoader()->TreeR()->GetEntry(0);
  for(Int_t iCh=0;iCh<7;iCh++){
    TClonesArray *pCluLst=(TClonesArray*)fClu->At(iCh);    iCluCnt+=pCluLst->GetEntries();    pCluLst->Print();
  }
  GetLoader()->UnloadRecPoints();
  Printf("totally %i clusters for event %i",iCluCnt,iEvtN);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
<commit_msg>Minor protection<commit_after>//  **************************************************************************
//  * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
//  *                                                                        *
//  * Author: The ALICE Off-line Project.                                    *
//  * Contributors are mentioned in the code where appropriate.              *
//  *                                                                        *
//  * Permission to use, copy, modify and distribute this software and its   *
//  * documentation strictly for non-commercial purposes is hereby granted   *
//  * without fee, provided that the above copyright notice appears in all   *
//  * copies and that both the copyright notice and this permission notice   *
//  * appear in the supporting documentation. The authors make no claims     *
//  * about the suitability of this software for any purpose. It is          *
//  * provided "as is" without express or implied warranty.                  *
//  **************************************************************************

#include "AliHMPID.h"
#include "AliHMPIDHit.h"   //OccupancyPrint(), HitQa()
#include "AliHMPIDDigit.h" //
#include <TParticle.h>  //SummaryOfEvent(), HitQa()
#include <TBenchmark.h>  //HitQA()
#include <TPDGCode.h>    //HitQA()
#include <AliStack.h>   //SummaryOfEvent(), HitQa()
#include <AliRun.h>     //HitQa()
#include <AliMC.h>       //ctor
#include <AliHeader.h>
#include <TH1F.h>        //HitQA()
#include <AliLog.h>      //in many methods to print AliInfo
ClassImp(AliHMPID)
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AliHMPID::AliHMPID(const char *name, const char *title):AliDetector(name,title),fSdi(0),fDig(0),fClu(0)
{
//Named ctor
  AliDebug(1,"Start.");
//AliDetector ctor deals with Hits and Digits (reset them to 0, does not create them)
  HitCreate();          gAlice->GetMCApp()->AddHitList(fHits);
  AliDebug(1,"Stop.");
}//AliHMPID::AliHMPID(const char *name, const char *title)
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AliHMPID::~AliHMPID()
{
//dtor
  AliDebug(1,"Start.");

  if(fHits)      delete fHits;
  if(fDigits)    delete fDigits;
  if(fSdi)       delete fSdi;
  if(fDig)      {fDig->Delete();   delete fDig;}
  if(fClu)      {fClu->Delete();   delete fClu;}
  AliDebug(1,"Stop.");
}//AliHMPID::~AliHMPID()
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void AliHMPID::MakeBranch(Option_t* option)
{
//Create Tree branches for the HMPID.
  AliDebug(1,Form("Start with option= %s.",option));

  const Int_t kBufSize = 4000;

  const char *cH = strstr(option,"H");
  const char *cD = strstr(option,"D");
  const char *cR = strstr(option,"R");
  const char *cS = strstr(option,"S");

  if(cH&&         TreeH()){HitCreate();                       MakeBranchInTree(         TreeH(),     "HMPID"     ,&fHits       ,kBufSize,0);}
  if(cS&&fLoader->TreeS()){SdiCreate();                       MakeBranchInTree(fLoader->TreeS(),     "HMPID"     ,&fSdi        ,kBufSize,0);}
  if(cD&&fLoader->TreeD()){DigCreate();for(Int_t i=0;i<7;i++) MakeBranchInTree(fLoader->TreeD(),Form("HMPID%d",i),&((*fDig)[i]),kBufSize,0);}
  if(cR&&fLoader->TreeR()){CluCreate();for(Int_t i=0;i<7;i++) MakeBranchInTree(fLoader->TreeR(),Form("HMPID%d",i),&((*fClu)[i]),kBufSize,0);}

  AliDebug(1,"Stop.");
}//void AliHMPID::MakeBranch(Option_t* option)
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void AliHMPID::SetTreeAddress()
{
//Set branch address for the Hits and Digits Tree.
  AliDebug(1,"Start.");
  if(fLoader->TreeH() && fLoader->TreeH()->GetBranch("HMPID" )){HitCreate();                      fLoader->TreeH()->SetBranchAddress(     "HMPID"     ,&fHits       );}
  if(fLoader->TreeS() && fLoader->TreeS()->GetBranch("HMPID" )){SdiCreate();                      fLoader->TreeS()->SetBranchAddress(     "HMPID"     ,&fSdi        );}
  if(fLoader->TreeD() && fLoader->TreeD()->GetBranch("HMPID0")){DigCreate(); for(int i=0;i<7;i++) fLoader->TreeD()->SetBranchAddress(Form("HMPID%d",i),&((*fDig)[i]));}
  if(fLoader->TreeR() && fLoader->TreeR()->GetBranch("HMPID0")){CluCreate(); for(int i=0;i<7;i++) fLoader->TreeR()->SetBranchAddress(Form("HMPID%d",i),&((*fClu)[i]));}
  AliDebug(1,"Stop.");
}//void AliHMPID::SetTreeAddress()
//__________________________________________________________________________________________________
// AliHMPIDHit* AliHMPID::Hit(Int_t tid)const
// {
// // Search for the first HMPID hit belonging to the given tid
//   GetLoader()->LoadHits();
//   for(Int_t iPrimN=0;iPrimN<GetLoader()->TreeH()->GetEntries();iPrimN++){//prims loop
//     GetLoader()->TreeH()->GetEntry(iPrimN);
//     for(Int_t iHitN=0;iHitN<Hits()->GetEntries();iHitN++){
//       AliHMPIDHit *pHit=(AliHMPIDHit*)Hits()->At(iHitN);
//       if(tid==pHit->Track()) {GetLoader()->UnloadHits();return pHit;}
//     }//hits
//   }//prims loop
//   GetLoader()->UnloadHits();
//   return 0;
// }
//__________________________________________________________________________________________________
void AliHMPID::HitPrint(Int_t iEvtN)const
{
//Prints a list of HMPID hits for a given event. Default is event number 0.
  if(GetLoader()->GetRunLoader()->GetEvent(iEvtN)) return;
  AliInfo(Form("List of HMPID hits for event %i",iEvtN));
  if(GetLoader()->LoadHits()) return;

  Int_t iTotalHits=0;
  for(Int_t iPrimN=0;iPrimN<GetLoader()->TreeH()->GetEntries();iPrimN++){//prims loop
    GetLoader()->TreeH()->GetEntry(iPrimN);
    Hits()->Print();
    iTotalHits+=Hits()->GetEntries();
  }
  GetLoader()->UnloadHits();
  AliInfo(Form("totally %i hits",iTotalHits));
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void AliHMPID::SdiPrint(Int_t iEvt)const
{
//prints a list of HMPID sdigits  for a given event
  if(GetLoader()->GetRunLoader()->GetEvent(iEvt)) return;
  Info("PrintSDigits","List of HMPID sdigits for event %i",iEvt);
  if(GetLoader()->LoadSDigits()) return;

  GetLoader()->TreeS()->GetEntry(0);
  SdiLst()->Print();
  GetLoader()->UnloadSDigits();
  Printf("totally %i sdigits",SdiLst()->GetEntries());
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void AliHMPID::DigPrint(Int_t iEvt)const
{
//prints a list of HMPID digits  for a given event
  if(GetLoader()->GetRunLoader()->GetEvent(iEvt)) return;
  Printf("List of HMPID digits for event %i",iEvt);
  if(GetLoader()->LoadDigits()) return;

  GetLoader()->TreeD()->GetEntry(0);
  DigLst()->Print();
  Int_t totDigs=0;
  for(Int_t i=0;i<7;i++) {totDigs+=DigLst(i)->GetEntries();}
  Printf("totally %i Digits",totDigs);
  GetLoader()->UnloadDigits();
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void AliHMPID::CluPrint(Int_t iEvtN)const
{
//prints a list of HMPID clusters  for a given event
  Printf("List of HMPID clusters for event %i",iEvtN);
  if(GetLoader()->GetRunLoader()->GetEvent(iEvtN)) return;
  if(GetLoader()->LoadRecPoints()) return;

  Int_t iCluCnt=0;
  GetLoader()->TreeR()->GetEntry(0);
  for(Int_t iCh=0;iCh<7;iCh++){
    TClonesArray *pCluLst=(TClonesArray*)fClu->At(iCh);    iCluCnt+=pCluLst->GetEntries();    pCluLst->Print();
  }
  GetLoader()->UnloadRecPoints();
  Printf("totally %i clusters for event %i",iCluCnt,iEvtN);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
<|endoftext|>


<commit_before>#include "ImportMatchers.h"
#include "clang/Basic/SourceManager.h"
#include "clang/ASTMatchers/ASTMatchersInternal.h"
#include "clang/AST/ExprObjC.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>

using namespace clang;
using namespace clang::ast_matchers;
using namespace clang::tooling;
using namespace import_tidy;

namespace clang {
namespace ast_matchers {
  static const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCMessageExpr> message;
  static const internal::VariadicDynCastAllOfMatcher<Decl, ObjCInterfaceDecl> interface;
  static const internal::VariadicDynCastAllOfMatcher<Decl, ObjCMethodDecl> method;
  static const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCProtocolExpr> protocol;
  static const internal::VariadicDynCastAllOfMatcher<Decl, ImportDecl> import;

  AST_MATCHER(ObjCInterfaceDecl, isImplementationInMainFile) {
    if (auto *ImpDecl = Node.getImplementation()) {
      auto &SourceManager = Finder->getASTContext().getSourceManager();
      return SourceManager.isInMainFile(SourceManager.getExpansionLoc(ImpDecl->getLocStart()));
    } else {
      return false;
    }
  }

  AST_MATCHER(ObjCInterfaceDecl, isForwardDeclare) {
    return !Node.hasDefinition();
  }

  AST_MATCHER(Decl, isNotInSystemHeader) {
    auto Loc = Node.getLocation();
    if (!Loc.isValid())
      return false;

    auto &SM = Finder->getASTContext().getSourceManager();
    return !SM.isInSystemHeader(Loc);
  }

  AST_MATCHER(ObjCMethodDecl, isDefinedInHeader) {
    auto *ID = Node.getClassInterface();
    if (!ID || !ID->getImplementation())
      return false;

    auto &SM = Finder->getASTContext().getSourceManager();
    if (SM.isInMainFile(SM.getExpansionLoc(Node.getLocStart())))
      return false;

    auto ImpLoc = SM.getExpansionLoc(ID->getImplementation()->getLocStart());
    return SM.isInMainFile(ImpLoc);
  }

} // end namespace ast_matchers
} // end namespace clang

namespace {

  static Import importForFileLoc(const SourceLocation L, const SourceManager &SM) {
    if (SM.isLoadedSourceLocation(L)) {
      return Import(ImportType::Module, SM.getModuleImportLoc(L).second);
    }

    auto Path = SM.getFilename(L);
    if (SM.isInSystemHeader(L)) {
      return Import(ImportType::Library, Path);
    }

    auto Filename = Path.drop_front(Path.find_last_of('/') + 1);
    return Import(ImportType::File, Filename);
  }

  static const clang::FileEntry*
  fileIncludingFile(std::map<const FileEntry*,
                    std::set<const FileEntry*>> &Map,
                    const FileEntry *F) {
    for (auto I : Map) {
      if (I.second.count(F)) {
        return I.first;
      }
    }
    return F;
  }

  static FileID
  topFileIncludingFile(std::map<const FileEntry*,
                       std::set<const FileEntry*>> &Map,
                       FileID File,
                       const SourceManager &SM) {
    auto *FE = SM.getFileEntryForID(File);
    const FileEntry *PE;
    do {
      PE = FE;
      FE = fileIncludingFile(Map, PE);
    } while (PE != FE);

    auto F = SM.translateFile(FE);
    return F.isInvalid() ? File : F;
  }

  static SourceLocation
  findCategoryImportForMethod(const ObjCInterfaceDecl *ID, const ObjCMethodDecl *MD) {
    SourceLocation InvalidLoc;
    if (ID->lookupPrivateMethod(MD->getSelector(), MD->isInstanceMethod())) {
      return InvalidLoc;
    }

    auto *Interface = ID;
    do {
      for (auto *Cat : Interface->visible_categories()) {
        for (auto *Ctx : Cat->noload_decls()) {
          if (auto *CatMD = dyn_cast<ObjCMethodDecl>(Ctx)) {
            if (CatMD->getSelector() == MD->getSelector()) {
              return CatMD->getLocStart();
            }
          }
        }
      }
    } while ((Interface = Interface->getSuperClass()));

    return InvalidLoc;
  }

  class ImportCallbacks : public clang::PPCallbacks {
  public:
    ImportCallbacks(const SourceManager &SM, ImportMatcher &M) :
      SM(SM), Matcher(M) { };

    void InclusionDirective(SourceLocation HashLoc,
                            const Token &IncludeTok,
                            StringRef FileName,
                            bool IsAngled,
                            CharSourceRange FilenameRange,
                            const FileEntry *File,
                            StringRef SearchPath,
                            StringRef RelativePath,
                            const Module *Imported) override {
      if (File && SM.isInSystemHeader(HashLoc)) {
        Matcher.addLibraryInclude(SM.getFileEntryForID(SM.getFileID(HashLoc)), File);
      } else {
        Matcher.removeImport(HashLoc, SM);
      }
    }
  private:
    const SourceManager &SM;
    ImportMatcher &Matcher;
  };

} // end anonymous namespace

namespace import_tidy {
  static const StringRef nodeKey = "key";

  bool FileCallbacks::handleBeginSource(CompilerInstance &CI, StringRef Filename) {
    llvm::outs() << "Compiling " << Filename << "\n";
    auto &SM = CI.getSourceManager();
    auto &PP = CI.getPreprocessor();
    PP.addPPCallbacks(std::unique_ptr<ImportCallbacks>(new ImportCallbacks(SM, Matcher)));
    SourceMgr = &SM;
    Matcher.setSysroot(CI.getHeaderSearchOpts().Sysroot);

    return true;
  }

  void FileCallbacks::handleEndSource() {
    Matcher.flush(*SourceMgr);
  }

  void CallExprCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *FD = Result.Nodes.getNodeAs<CallExpr>(nodeKey)->getDirectCallee()) {
      auto &SM = *Result.SourceManager;
      Matcher.addImport(SM.getMainFileID(), FD->getLocation(), SM);
    }
  }

  void InterfaceCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *ID = Result.Nodes.getNodeAs<ObjCInterfaceDecl>(nodeKey)) {
      auto &SM = *Result.SourceManager;
      auto InFile = SM.getFileID(ID->getLocation());

      if (auto *SC = ID->getSuperClass()) {
        Matcher.addImport(InFile, SC->getLocation(), SM);
      }

      if (InFile != SM.getMainFileID()) {
        Matcher.addImport(SM.getMainFileID(), ID->getLocation(), SM);
      }

      for (auto *P : ID->protocols()) {
        Matcher.addImport(InFile, P->getLocation(), SM);

        for (auto *M : P->methods()) {
          if (M->getImplementationControl() != ObjCMethodDecl::Required)
            continue;

          auto Loc = findCategoryImportForMethod(ID, M);
          if (Loc.isValid()) {
            Matcher.addImport(InFile, Loc, SM);
          }
        }
      }

      for (auto *C : ID->visible_categories()) {
        auto CategoryFile = SM.getFileID(C->getLocation());

        for (auto *P : C->protocols()) {
          Matcher.addImport(CategoryFile, P->getLocation(), SM);
        }
      }
    }
  }

  void MessageExprCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *E = Result.Nodes.getNodeAs<ObjCMessageExpr>(nodeKey)) {
      auto &SM = *Result.SourceManager;

      if (auto *ID = E->getReceiverInterface()) {
        Matcher.addImport(SM.getMainFileID(), ID->getLocation(), SM);
      } else if (auto *Ptr = E->getReceiverType()->getAs<ObjCObjectPointerType>()) {
        for (auto i = Ptr->qual_begin(); i != Ptr->qual_end(); i++) {
          Matcher.addImport(SM.getMainFileID(), (*i)->getLocation(), SM);
        }
      } else {
        llvm::outs() << "failed to unpack expr of kind " << E->getReceiverKind();
        assert(0);
      }
    }
  }

  void MethodCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *M = Result.Nodes.getNodeAs<ObjCMethodDecl>(nodeKey)) {
      auto FID = Result.SourceManager->getFileID(M->getLocation());
      addType(FID, M->getReturnType(), *Result.SourceManager);

      for (auto i = M->param_begin(); i != M->param_end(); i++) {
        addType(FID, (*i)->getType(), *Result.SourceManager);
      }
    }
  }

  void MethodCallback::addType(const FileID InFile, QualType T, const SourceManager &SM) {
    if (auto *PT = T->getAs<ObjCObjectPointerType>()) {
      if (auto *ID = PT->getInterfaceDecl()) {
        auto Filename = SM.getFilename(ID->getLocation());
        if (Filename.startswith(Matcher.getSysroot())) {
          Matcher.addImport(InFile, ID->getLocation(), SM);
        } else {
          Matcher.addForwardDeclare(InFile, ID->getName());
        }
      }
    }
  }

  void ProtocolCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *PD = Result.Nodes.getNodeAs<ObjCProtocolExpr>(nodeKey)->getProtocol()) {
      auto &SM = *Result.SourceManager;
      Matcher.addImport(SM.getMainFileID(), PD->getLocation(), SM);
    }
  }

  void StripCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *D = Result.Nodes.getNodeAs<Decl>(nodeKey)) {
      Matcher.removeImport(D->getLocation(), *Result.SourceManager);
    }
  }

  std::unique_ptr<FrontendActionFactory>
  ImportMatcher::getActionFactory(MatchFinder& Finder) {
    auto CallMatcher = callExpr(isExpansionInMainFile()).bind(nodeKey);
    auto InterfaceMatcher = interface(isImplementationInMainFile()).bind(nodeKey);
    auto ForwardDeclareMatcher = interface(isNotInSystemHeader(), isForwardDeclare()).bind(nodeKey);
    auto ImportMatcher = import(isNotInSystemHeader()).bind(nodeKey);
    auto MsgMatcher = message(isExpansionInMainFile()).bind(nodeKey);
    auto MtdMatcher = method(isDefinedInHeader()).bind(nodeKey);
    auto ProtoMatcher = protocol(isExpansionInMainFile()).bind(nodeKey);
    Finder.addMatcher(CallMatcher, &CallCallback);
    Finder.addMatcher(InterfaceMatcher, &InterfaceCallback);
    Finder.addMatcher(ForwardDeclareMatcher, &StripCallback);
    Finder.addMatcher(ImportMatcher, &StripCallback);
    Finder.addMatcher(MsgMatcher, &MsgCallback);
    Finder.addMatcher(MtdMatcher, &MtdCallback);
    Finder.addMatcher(ProtoMatcher, &ProtoCallback);

    return newFrontendActionFactory(&Finder, &FileCallbacks);
  }

  void ImportMatcher::addLibraryInclude(const FileEntry *HE, const FileEntry *FE) {
    // some framework headers import themselves
    if (HE == FE)
      return;

    // don't allow circular imports
    if (LibraryImportMap.count(FE) > 0)
      return;

    // don't track includes across libraries
    if (HE->getDir() != FE->getDir())
      return;

    LibraryImportMap[HE].insert(FE);
  }

  void ImportMatcher::addForwardDeclare(const FileID InFile, llvm::StringRef Name) {
    ImportMap[InFile].insert(Import(ImportType::ForwardDeclare, Name));
  }

  void ImportMatcher::addImport(const FileID InFile,
                                const SourceLocation Loc,
                                const SourceManager &SM) {
    auto OfFile = SM.getFileID(Loc);
    auto ImportedFile = topFileIncludingFile(LibraryImportMap, OfFile, SM);
    auto ImportedLoc = SM.getLocForStartOfFile(ImportedFile);

    Import Imp = importForFileLoc(ImportedLoc, SM);
    if (ImportMap[InFile].count(Imp) == 0) {
      ImportMap[InFile].insert(Imp);
      if (Imp.getType() == ImportType::Library) {
        LibraryImportCount[Imp]++;
      }
    }
  }

  void ImportMatcher::removeImport(const SourceLocation Loc, const SourceManager &SM) {
    auto fid = SM.getFileID(Loc);
    auto *fileStart = SM.getBuffer(fid)->getBufferStart();
    unsigned start = SM.getFileOffset(Loc);
    unsigned end = strchr(fileStart + start, '\n') - fileStart + 1;

    // TODO: this is a dangerous assumption, should be modelled as a set of ranges
    ImportOffset[fid] = std::max(ImportOffset[fid], end);
  }

  void ImportMatcher::flush(const SourceManager &SM) {
    auto &out = llvm::outs();

    for (auto &Pair : ImportMap) {
      std::string import;
      llvm::raw_string_ostream ImportStr(import);
      for (auto &Import : Pair.second) {
        ImportStr << Import << '\n';
      }

      auto fid = Pair.first;
      auto start = SM.getLocForStartOfFile(fid);
      auto replacementLength = ImportOffset[fid];
      if (replacementLength == 0) {
        // make sure there is at least a line of whitespace after the new imports
        ImportStr << '\n';
      }

      Replacements.insert(Replacement(SM, start, replacementLength, ImportStr.str()));
      out << "File: " << SM.getFilename(start) << "\n";
      out << ImportStr.str() << "\n";
    }
    ImportMap.clear();
    ImportOffset.clear();
  }

  void ImportMatcher::printLibraryCounts(llvm::raw_ostream &OS) {
    using ImpPair = std::pair<Import, unsigned>;
    std::vector<ImpPair> counts(LibraryImportCount.begin(), LibraryImportCount.end());
    std::sort(counts.begin(), counts.end(), [](const ImpPair &L, const ImpPair &R) {
      return L.second < R.second;
    });

    for (auto I = counts.rbegin(); I != counts.rend(); I++) {
      OS << I->first << " : " << I->second << " times\n";
    }
  }

} // end namespace import_tidy
<commit_msg>don't apply multiple replacements to the same file, each pass should be correct (but isn't, so isn't de-duplicating)<commit_after>#include "ImportMatchers.h"
#include "clang/Basic/SourceManager.h"
#include "clang/ASTMatchers/ASTMatchersInternal.h"
#include "clang/AST/ExprObjC.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>

using namespace clang;
using namespace clang::ast_matchers;
using namespace clang::tooling;
using namespace import_tidy;

namespace clang {
namespace ast_matchers {
  static const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCMessageExpr> message;
  static const internal::VariadicDynCastAllOfMatcher<Decl, ObjCInterfaceDecl> interface;
  static const internal::VariadicDynCastAllOfMatcher<Decl, ObjCMethodDecl> method;
  static const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCProtocolExpr> protocol;
  static const internal::VariadicDynCastAllOfMatcher<Decl, ImportDecl> import;

  AST_MATCHER(ObjCInterfaceDecl, isImplementationInMainFile) {
    if (auto *ImpDecl = Node.getImplementation()) {
      auto &SourceManager = Finder->getASTContext().getSourceManager();
      return SourceManager.isInMainFile(SourceManager.getExpansionLoc(ImpDecl->getLocStart()));
    } else {
      return false;
    }
  }

  AST_MATCHER(ObjCInterfaceDecl, isForwardDeclare) {
    return !Node.hasDefinition();
  }

  AST_MATCHER(Decl, isNotInSystemHeader) {
    auto Loc = Node.getLocation();
    if (!Loc.isValid())
      return false;

    auto &SM = Finder->getASTContext().getSourceManager();
    return !SM.isInSystemHeader(Loc);
  }

  AST_MATCHER(ObjCMethodDecl, isDefinedInHeader) {
    auto *ID = Node.getClassInterface();
    if (!ID || !ID->getImplementation())
      return false;

    auto &SM = Finder->getASTContext().getSourceManager();
    if (SM.isInMainFile(SM.getExpansionLoc(Node.getLocStart())))
      return false;

    auto ImpLoc = SM.getExpansionLoc(ID->getImplementation()->getLocStart());
    return SM.isInMainFile(ImpLoc);
  }

} // end namespace ast_matchers
} // end namespace clang

namespace {

  static Import importForFileLoc(const SourceLocation L, const SourceManager &SM) {
    if (SM.isLoadedSourceLocation(L)) {
      return Import(ImportType::Module, SM.getModuleImportLoc(L).second);
    }

    auto Path = SM.getFilename(L);
    if (SM.isInSystemHeader(L)) {
      return Import(ImportType::Library, Path);
    }

    auto Filename = Path.drop_front(Path.find_last_of('/') + 1);
    return Import(ImportType::File, Filename);
  }

  static const clang::FileEntry*
  fileIncludingFile(std::map<const FileEntry*,
                    std::set<const FileEntry*>> &Map,
                    const FileEntry *F) {
    for (auto I : Map) {
      if (I.second.count(F)) {
        return I.first;
      }
    }
    return F;
  }

  static FileID
  topFileIncludingFile(std::map<const FileEntry*,
                       std::set<const FileEntry*>> &Map,
                       FileID File,
                       const SourceManager &SM) {
    auto *FE = SM.getFileEntryForID(File);
    const FileEntry *PE;
    do {
      PE = FE;
      FE = fileIncludingFile(Map, PE);
    } while (PE != FE);

    auto F = SM.translateFile(FE);
    return F.isInvalid() ? File : F;
  }

  static SourceLocation
  findCategoryImportForMethod(const ObjCInterfaceDecl *ID, const ObjCMethodDecl *MD) {
    SourceLocation InvalidLoc;
    if (ID->lookupPrivateMethod(MD->getSelector(), MD->isInstanceMethod())) {
      return InvalidLoc;
    }

    auto *Interface = ID;
    do {
      for (auto *Cat : Interface->visible_categories()) {
        for (auto *Ctx : Cat->noload_decls()) {
          if (auto *CatMD = dyn_cast<ObjCMethodDecl>(Ctx)) {
            if (CatMD->getSelector() == MD->getSelector()) {
              return CatMD->getLocStart();
            }
          }
        }
      }
    } while ((Interface = Interface->getSuperClass()));

    return InvalidLoc;
  }

  class ImportCallbacks : public clang::PPCallbacks {
  public:
    ImportCallbacks(const SourceManager &SM, ImportMatcher &M) :
      SM(SM), Matcher(M) { };

    void InclusionDirective(SourceLocation HashLoc,
                            const Token &IncludeTok,
                            StringRef FileName,
                            bool IsAngled,
                            CharSourceRange FilenameRange,
                            const FileEntry *File,
                            StringRef SearchPath,
                            StringRef RelativePath,
                            const Module *Imported) override {
      if (File && SM.isInSystemHeader(HashLoc)) {
        Matcher.addLibraryInclude(SM.getFileEntryForID(SM.getFileID(HashLoc)), File);
      } else {
        Matcher.removeImport(HashLoc, SM);
      }
    }
  private:
    const SourceManager &SM;
    ImportMatcher &Matcher;
  };

} // end anonymous namespace

namespace import_tidy {
  static const StringRef nodeKey = "key";

  bool FileCallbacks::handleBeginSource(CompilerInstance &CI, StringRef Filename) {
    llvm::outs() << "Compiling " << Filename << "\n";
    auto &SM = CI.getSourceManager();
    auto &PP = CI.getPreprocessor();
    PP.addPPCallbacks(std::unique_ptr<ImportCallbacks>(new ImportCallbacks(SM, Matcher)));
    SourceMgr = &SM;
    Matcher.setSysroot(CI.getHeaderSearchOpts().Sysroot);

    return true;
  }

  void FileCallbacks::handleEndSource() {
    Matcher.flush(*SourceMgr);
  }

  void CallExprCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *FD = Result.Nodes.getNodeAs<CallExpr>(nodeKey)->getDirectCallee()) {
      auto &SM = *Result.SourceManager;
      Matcher.addImport(SM.getMainFileID(), FD->getLocation(), SM);
    }
  }

  void InterfaceCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *ID = Result.Nodes.getNodeAs<ObjCInterfaceDecl>(nodeKey)) {
      auto &SM = *Result.SourceManager;
      auto InFile = SM.getFileID(ID->getLocation());

      if (auto *SC = ID->getSuperClass()) {
        Matcher.addImport(InFile, SC->getLocation(), SM);
      }

      if (InFile != SM.getMainFileID()) {
        Matcher.addImport(SM.getMainFileID(), ID->getLocation(), SM);
      }

      for (auto *P : ID->protocols()) {
        Matcher.addImport(InFile, P->getLocation(), SM);

        for (auto *M : P->methods()) {
          if (M->getImplementationControl() != ObjCMethodDecl::Required)
            continue;

          auto Loc = findCategoryImportForMethod(ID, M);
          if (Loc.isValid()) {
            Matcher.addImport(InFile, Loc, SM);
          }
        }
      }

      for (auto *C : ID->visible_categories()) {
        auto CategoryFile = SM.getFileID(C->getLocation());

        for (auto *P : C->protocols()) {
          Matcher.addImport(CategoryFile, P->getLocation(), SM);
        }
      }
    }
  }

  void MessageExprCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *E = Result.Nodes.getNodeAs<ObjCMessageExpr>(nodeKey)) {
      auto &SM = *Result.SourceManager;

      if (auto *ID = E->getReceiverInterface()) {
        Matcher.addImport(SM.getMainFileID(), ID->getLocation(), SM);
      } else if (auto *Ptr = E->getReceiverType()->getAs<ObjCObjectPointerType>()) {
        for (auto i = Ptr->qual_begin(); i != Ptr->qual_end(); i++) {
          Matcher.addImport(SM.getMainFileID(), (*i)->getLocation(), SM);
        }
      } else {
        llvm::outs() << "failed to unpack expr of kind " << E->getReceiverKind();
        assert(0);
      }
    }
  }

  void MethodCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *M = Result.Nodes.getNodeAs<ObjCMethodDecl>(nodeKey)) {
      auto FID = Result.SourceManager->getFileID(M->getLocation());
      addType(FID, M->getReturnType(), *Result.SourceManager);

      for (auto i = M->param_begin(); i != M->param_end(); i++) {
        addType(FID, (*i)->getType(), *Result.SourceManager);
      }
    }
  }

  void MethodCallback::addType(const FileID InFile, QualType T, const SourceManager &SM) {
    if (auto *PT = T->getAs<ObjCObjectPointerType>()) {
      if (auto *ID = PT->getInterfaceDecl()) {
        auto Filename = SM.getFilename(ID->getLocation());
        if (Filename.startswith(Matcher.getSysroot())) {
          Matcher.addImport(InFile, ID->getLocation(), SM);
        } else {
          Matcher.addForwardDeclare(InFile, ID->getName());
        }
      }
    }
  }

  void ProtocolCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *PD = Result.Nodes.getNodeAs<ObjCProtocolExpr>(nodeKey)->getProtocol()) {
      auto &SM = *Result.SourceManager;
      Matcher.addImport(SM.getMainFileID(), PD->getLocation(), SM);
    }
  }

  void StripCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *D = Result.Nodes.getNodeAs<Decl>(nodeKey)) {
      Matcher.removeImport(D->getLocation(), *Result.SourceManager);
    }
  }

  std::unique_ptr<FrontendActionFactory>
  ImportMatcher::getActionFactory(MatchFinder& Finder) {
    auto CallMatcher = callExpr(isExpansionInMainFile()).bind(nodeKey);
    auto InterfaceMatcher = interface(isImplementationInMainFile()).bind(nodeKey);
    auto ForwardDeclareMatcher = interface(isNotInSystemHeader(), isForwardDeclare()).bind(nodeKey);
    auto ImportMatcher = import(isNotInSystemHeader()).bind(nodeKey);
    auto MsgMatcher = message(isExpansionInMainFile()).bind(nodeKey);
    auto MtdMatcher = method(isDefinedInHeader()).bind(nodeKey);
    auto ProtoMatcher = protocol(isExpansionInMainFile()).bind(nodeKey);
    Finder.addMatcher(CallMatcher, &CallCallback);
    Finder.addMatcher(InterfaceMatcher, &InterfaceCallback);
    Finder.addMatcher(ForwardDeclareMatcher, &StripCallback);
    Finder.addMatcher(ImportMatcher, &StripCallback);
    Finder.addMatcher(MsgMatcher, &MsgCallback);
    Finder.addMatcher(MtdMatcher, &MtdCallback);
    Finder.addMatcher(ProtoMatcher, &ProtoCallback);

    return newFrontendActionFactory(&Finder, &FileCallbacks);
  }

  void ImportMatcher::addLibraryInclude(const FileEntry *HE, const FileEntry *FE) {
    // some framework headers import themselves
    if (HE == FE)
      return;

    // don't allow circular imports
    if (LibraryImportMap.count(FE) > 0)
      return;

    // don't track includes across libraries
    if (HE->getDir() != FE->getDir())
      return;

    LibraryImportMap[HE].insert(FE);
  }

  void ImportMatcher::addForwardDeclare(const FileID InFile, llvm::StringRef Name) {
    ImportMap[InFile].insert(Import(ImportType::ForwardDeclare, Name));
  }

  void ImportMatcher::addImport(const FileID InFile,
                                const SourceLocation Loc,
                                const SourceManager &SM) {
    auto OfFile = SM.getFileID(Loc);
    auto ImportedFile = topFileIncludingFile(LibraryImportMap, OfFile, SM);
    auto ImportedLoc = SM.getLocForStartOfFile(ImportedFile);

    Import Imp = importForFileLoc(ImportedLoc, SM);
    if (ImportMap[InFile].count(Imp) == 0) {
      ImportMap[InFile].insert(Imp);
      if (Imp.getType() == ImportType::Library) {
        LibraryImportCount[Imp]++;
      }
    }
  }

  void ImportMatcher::removeImport(const SourceLocation Loc, const SourceManager &SM) {
    auto fid = SM.getFileID(Loc);
    auto *fileStart = SM.getBuffer(fid)->getBufferStart();
    unsigned start = SM.getFileOffset(Loc);
    unsigned end = strchr(fileStart + start, '\n') - fileStart + 1;

    // TODO: this is a dangerous assumption, should be modelled as a set of ranges
    ImportOffset[fid] = std::max(ImportOffset[fid], end);
  }

  void ImportMatcher::flush(const SourceManager &SM) {
    auto &out = llvm::outs();

    for (auto &Pair : ImportMap) {
      std::string import;
      llvm::raw_string_ostream ImportStr(import);
      for (auto &Import : Pair.second) {
        ImportStr << Import << '\n';
      }

      auto fid = Pair.first;
      auto start = SM.getLocForStartOfFile(fid);
      auto Path = SM.getFilename(start);
      auto Found = std::find_if(Replacements.begin(), Replacements.end(),
                                [Path](const Replacement &R) {
                                  return R.getFilePath() == Path;
                                });
      if (Found != Replacements.end()) {
        out << "Skipping, already cleaned: " << Path << "\n\n";
        continue;
      }

      auto replacementLength = ImportOffset[fid];
      if (replacementLength == 0) {
        // make sure there is at least a line of whitespace after the new imports
        ImportStr << '\n';
      }

      Replacements.insert(Replacement(SM, start, replacementLength, ImportStr.str()));
      out << "File: " << SM.getFilename(start) << "\n";
      out << ImportStr.str() << "\n";
    }
    ImportMap.clear();
    ImportOffset.clear();
  }

  void ImportMatcher::printLibraryCounts(llvm::raw_ostream &OS) {
    using ImpPair = std::pair<Import, unsigned>;
    std::vector<ImpPair> counts(LibraryImportCount.begin(), LibraryImportCount.end());
    std::sort(counts.begin(), counts.end(), [](const ImpPair &L, const ImpPair &R) {
      return L.second < R.second;
    });

    for (auto I = counts.rbegin(); I != counts.rend(); I++) {
      OS << I->first << " : " << I->second << " times\n";
    }
  }

} // end namespace import_tidy
<|endoftext|>


<commit_before>#pragma once

#include "types.hpp"

#include <string>
// IPMI commands for Transport net functions.
enum ipmi_netfn_storage_cmds
{
    // Get capability bits
    IPMI_CMD_SET_LAN = 0x01,
    IPMI_CMD_GET_LAN = 0x02,
};

// Command specific completion codes
enum ipmi_transport_return_codes
{
    IPMI_CC_PARM_NOT_SUPPORTED = 0x80,
};

// Parameters
enum class LanParam : uint8_t
{
    INPROGRESS = 0,
    AUTHSUPPORT = 1,
    AUTHENABLES = 2,
    IP = 3,
    IPSRC = 4,
    MAC = 5,
    SUBNET = 6,
    GATEWAY = 12,
    VLAN = 20,
    CIPHER_SUITE_COUNT = 22,
    CIPHER_SUITE_ENTRIES = 23,
};

constexpr uint8_t SET_COMPLETE = 0;
constexpr uint8_t SET_IN_PROGRESS = 1;
constexpr uint8_t SET_COMMIT_WRITE = 2;         // Optional
constexpr uint8_t SET_IN_PROGRESS_RESERVED = 3; // Reserved

const int CHANNEL_MASK = 0x0f;
const int NUM_CHANNELS = 0x0f;

struct ChannelConfig_t
{
    std::string ipaddr;
    ipmi::network::IPOrigin ipsrc = ipmi::network::IPOrigin::UNSPECIFIED;
    std::string netmask;
    std::string gateway;
    std::string macAddress;
    // IPMI stores the vlan info in 16 bits,32 bits is to aligned
    // with phosphor-dbus interfaces.
    // vlan id is in 12 bits and the 16th bit is for enable mask.
    uint32_t vlanID = ipmi::network::VLAN_ID_MASK;
    uint8_t lan_set_in_progress = SET_COMPLETE;
    bool flush = false;

    void clear()
    {
        ipaddr.clear();
        netmask.clear();
        gateway.clear();
        macAddress.clear();
        vlanID = ipmi::network::VLAN_ID_MASK;
        ipsrc = ipmi::network::IPOrigin::UNSPECIFIED;
        lan_set_in_progress = SET_COMPLETE;
        flush = false;
    }
};

// Given a channel, get the corresponding configuration,
// or allocate it first.
//
// @param[in] channel the channel
// @return the ChannelConfig_t pointer.
struct ChannelConfig_t* getChannelConfig(int channel);

/** @brief Iterate over all the channelconfig and if
 *         user has given the data for a channel then
 *         apply the network changes for that channel.
 */
void commitNetworkChanges();

/* @brief  Apply the network changes which is there in the
 *         network cache for a given channel which gets filled
 *         through setLan command. If some of the network
 *         parameter was not given by the setLan then this function
 *         gets the value of that parameter which is already
 *         configured on the system.
 * @param[in] channel: channel number.
 */
void applyChanges(int channel);
<commit_msg>transporthandler: Add IPV6 LAN parameters to enum<commit_after>#pragma once

#include "types.hpp"

#include <string>
// IPMI commands for Transport net functions.
enum ipmi_netfn_storage_cmds
{
    // Get capability bits
    IPMI_CMD_SET_LAN = 0x01,
    IPMI_CMD_GET_LAN = 0x02,
};

// Command specific completion codes
enum ipmi_transport_return_codes
{
    IPMI_CC_PARM_NOT_SUPPORTED = 0x80,
};

// Parameters
enum class LanParam : uint8_t
{
    INPROGRESS = 0,
    AUTHSUPPORT = 1, // Read-only
    AUTHENABLES = 2,
    IP = 3,
    IPSRC = 4,
    MAC = 5,
    SUBNET = 6,
    IPHEADER_PARAMS = 7,
    RMCP_PORT = 8,
    RMCP_SECONDARY_PORT = 9,
    BMC_GENERATED_ARP_CTRL = 10,
    GRATUITOUS_ARP_INTERVAL = 11,
    GATEWAY = 12,
    GATEWAY_MAC = 13,
    GATEWAY_BACKUP = 14,
    GATEWAY_BACKUP_MAC = 15,
    COMMUNITY_STRING = 16,
    LAN_ALERT_DESTINATION_COUNT = 17, // Read-only
    LAN_ALERT_DESTINATION_TYPE = 18,  // Type per destination
    LAN_ALERT_DESTINATIONS = 19,
    VLAN = 20,
    VLAN_PRIORITY = 21,
    CIPHER_SUITE_COUNT = 22,   // Read-only
    CIPHER_SUITE_ENTRIES = 23, // Read-only
    CIPHER_SUITE_PRIVILEGE_LEVELS = 24,
    DESTINATION_ADDR_VLAN_TAGS = 25,
    BAD_PASSWORD_THRESHOLD = 26,
    IPV6_AND_IPV4_SUPPORTED = 50, // Read-only
    IPV6_AND_IPV4_ENABLES = 51,
    IPV6_HEADER_STATIC_TRAFFIC_CLASS = 52,
    IPV6_HEADER_STATIC_HOP_LIMIT = 53,
    IPV6_HEADER_FLOW_LABEL = 54,
    IPV6_STATUS = 55, // Read-only
    IPV6_STATIC_ADDRESSES = 56,
    IPV6_DHCPV6_STATIC_DUID_STORAGE_LENGTH = 57, // Read-only
    IPV6_DHCPV6_STATIC_DUIDS = 58,
    IPV6_DYNAMIC_ADDRESSES = 59,            // Read-only
    IPV6_DHCPV6_DYNAMIC_DUID_STOR_LEN = 60, // Read-only
    IPV6_DHCPV6_DYNAMIC_DUIDS = 61,
    IPV6_DHCPV6_TIMING_CONF_SUPPORT = 62, // Read-only
    IPV6_DHCPV6_TIMING_CONFIGURATION = 63,
    IPV6_ROUTER_ADDRESS_CONF_CTRL = 64,
    IPV6_STATIC_ROUTER_1_IP_ADDR = 65,
    IPV6_STATIC_ROUTER_1_MAC_ADDR = 66,
    IPV6_STATIC_ROUTER_1_PREFIX_LEN = 67,
    IPV6_STATIC_ROUTER_1_PREFIX_VAL = 68,
    IPV6_STATIC_ROUTER_2_IP_ADDR = 69,
    IPV6_STATIC_ROUTER_2_MAC_ADDR = 70,
    IPV6_STATIC_ROUTER_2_PREFIX_LEN = 71,
    IPV6_STATIC_ROUTER_2_PREFIX_VAL = 72,
    DYNAMIC_ROUTER_INFO_SET_COUNT = 73,       // Read-only
    IPV6_DYNAMIC_ROUTER_INFO_IP_ADDR = 74,    // Read-only
    IPV6_DYNAMIC_ROUTER_INFO_MAC = 75,        // Read-only
    IPV6_DYNAMIC_ROUTER_INFO_PREFIX_LEN = 76, // Read-only
    IPV6_DYNAMIC_ROUTER_INFO_PREFIX_VAL = 77, // Read-only
    IPV6_DYNAMIC_ROUTER_RECV_HOP_LIMIT = 78,
    IPV6_NEIGHBOR_TIMING_CONF_SUPPORT = 79, // Read-only
    IPV6_NEIGHBOR_TIMING_CONFIGURATION = 80,
};

constexpr uint8_t SET_COMPLETE = 0;
constexpr uint8_t SET_IN_PROGRESS = 1;
constexpr uint8_t SET_COMMIT_WRITE = 2;         // Optional
constexpr uint8_t SET_IN_PROGRESS_RESERVED = 3; // Reserved

const int CHANNEL_MASK = 0x0f;
const int NUM_CHANNELS = 0x0f;

struct ChannelConfig_t
{
    std::string ipaddr;
    ipmi::network::IPOrigin ipsrc = ipmi::network::IPOrigin::UNSPECIFIED;
    std::string netmask;
    std::string gateway;
    std::string macAddress;
    // IPMI stores the vlan info in 16 bits,32 bits is to aligned
    // with phosphor-dbus interfaces.
    // vlan id is in 12 bits and the 16th bit is for enable mask.
    uint32_t vlanID = ipmi::network::VLAN_ID_MASK;
    uint8_t lan_set_in_progress = SET_COMPLETE;
    bool flush = false;

    void clear()
    {
        ipaddr.clear();
        netmask.clear();
        gateway.clear();
        macAddress.clear();
        vlanID = ipmi::network::VLAN_ID_MASK;
        ipsrc = ipmi::network::IPOrigin::UNSPECIFIED;
        lan_set_in_progress = SET_COMPLETE;
        flush = false;
    }
};

// Given a channel, get the corresponding configuration,
// or allocate it first.
//
// @param[in] channel the channel
// @return the ChannelConfig_t pointer.
struct ChannelConfig_t* getChannelConfig(int channel);

/** @brief Iterate over all the channelconfig and if
 *         user has given the data for a channel then
 *         apply the network changes for that channel.
 */
void commitNetworkChanges();

/* @brief  Apply the network changes which is there in the
 *         network cache for a given channel which gets filled
 *         through setLan command. If some of the network
 *         parameter was not given by the setLan then this function
 *         gets the value of that parameter which is already
 *         configured on the system.
 * @param[in] channel: channel number.
 */
void applyChanges(int channel);
<|endoftext|>


<commit_before>#include "camerafilter.h"

#include "cameraframegrabber.h"

//#include <QCameraViewfinder>
#include <QCameraInfo>
#include <QTime>
#include <QtDebug>

CameraFilter::CameraFilter(StatisticsInterface *stats, QSize resolution):
  Filter("Camera", stats, false, true),
  camera_(),
  cameraFrameGrabber_(),
  resolution_(resolution)
{
  camera_ = new QCamera(QCameraInfo::defaultCamera());
  cameraFrameGrabber_ = new CameraFrameGrabber();

  Q_ASSERT(camera_ && cameraFrameGrabber_);

  camera_->setViewfinder(cameraFrameGrabber_);

  connect(cameraFrameGrabber_, SIGNAL(frameAvailable(QVideoFrame)), this, SLOT(handleFrame(QVideoFrame)));

  camera_->start();
}

CameraFilter::~CameraFilter()
{
  delete camera_;
  delete cameraFrameGrabber_;
}

void CameraFilter::process()
{
  // do nothing because camera input is handled via camera signal
}

void CameraFilter::stop()
{
  camera_->stop();
  Filter::stop();
}

void CameraFilter::handleFrame(const QVideoFrame &frame)
{
  QVideoFrame cloneFrame(frame);
  cloneFrame.map(QAbstractVideoBuffer::ReadOnly);

  QVideoFrame::PixelFormat pf = cloneFrame.pixelFormat();

  Q_ASSERT(pf == QVideoFrame::Format_RGB32);

  // capture the frame data
  Data * newImage = new Data;

  // set time
  timeval present_time;

  present_time.tv_sec = QDateTime::currentMSecsSinceEpoch()/1000;
  present_time.tv_usec = (QDateTime::currentMSecsSinceEpoch()%1000) * 1000;

  newImage->presentationTime = present_time;
  newImage->type = RGB32VIDEO;
  std::unique_ptr<uchar> uu(new uchar[cloneFrame.mappedBytes()]);
  newImage->data = std::unique_ptr<uchar[]>(new uchar[cloneFrame.mappedBytes()]);

  uchar *bits = cloneFrame.bits();

  memcpy(newImage->data.get(), bits, cloneFrame.mappedBytes());
  newImage->data_size = cloneFrame.mappedBytes();
  newImage->width = cloneFrame.width();
  newImage->height = cloneFrame.height();

  // scale the image and copy to new data
  if(resolution_ != cloneFrame.size())
  {
    QImage image(
          newImage->data.get(),
          newImage->width,
          newImage->height,
          QImage::Format_RGB32);

    QImage image2 = image.scaled(resolution_);
    if(resolution_.width() * resolution_.height()
       > cloneFrame.size().width() * cloneFrame.size().height())
    {
      newImage->data = std::unique_ptr<uchar[]>(new uchar[image2.byteCount()]);
    }
    memcpy(newImage->data.get(), image2.bits(), image2.byteCount());
    newImage->width = resolution_.width();
    newImage->height = resolution_.height();
    newImage->data_size = image2.byteCount();
  }

  //qDebug() << "Frame generated. Format: " << pf
  //         << " width: " << newImage->width << ", height: " << newImage->height;

  std::unique_ptr<Data> u_newImage( newImage );
  cloneFrame.unmap();

  Q_ASSERT(u_newImage->data);
  sendOutput(std::move(u_newImage));
}
<commit_msg>Remove unnecessary mamory allocation from camera filter.<commit_after>#include "camerafilter.h"

#include "cameraframegrabber.h"

//#include <QCameraViewfinder>
#include <QCameraInfo>
#include <QTime>
#include <QtDebug>

CameraFilter::CameraFilter(StatisticsInterface *stats, QSize resolution):
  Filter("Camera", stats, false, true),
  camera_(),
  cameraFrameGrabber_(),
  resolution_(resolution)
{
  camera_ = new QCamera(QCameraInfo::defaultCamera());
  cameraFrameGrabber_ = new CameraFrameGrabber();

  Q_ASSERT(camera_ && cameraFrameGrabber_);

  camera_->setViewfinder(cameraFrameGrabber_);

  connect(cameraFrameGrabber_, SIGNAL(frameAvailable(QVideoFrame)), this, SLOT(handleFrame(QVideoFrame)));

  camera_->start();
}

CameraFilter::~CameraFilter()
{
  delete camera_;
  delete cameraFrameGrabber_;
}

void CameraFilter::process()
{
  // do nothing because camera input is handled via camera signal
}

void CameraFilter::stop()
{
  camera_->stop();
  Filter::stop();
}

void CameraFilter::handleFrame(const QVideoFrame &frame)
{
  QVideoFrame cloneFrame(frame);
  cloneFrame.map(QAbstractVideoBuffer::ReadOnly);

  QVideoFrame::PixelFormat pf = cloneFrame.pixelFormat();

  Q_ASSERT(pf == QVideoFrame::Format_RGB32);

  // capture the frame data
  Data * newImage = new Data;

  // set time
  timeval present_time;

  present_time.tv_sec = QDateTime::currentMSecsSinceEpoch()/1000;
  present_time.tv_usec = (QDateTime::currentMSecsSinceEpoch()%1000) * 1000;

  newImage->presentationTime = present_time;
  newImage->type = RGB32VIDEO;
  newImage->data = std::unique_ptr<uchar[]>(new uchar[cloneFrame.mappedBytes()]);

  uchar *bits = cloneFrame.bits();

  memcpy(newImage->data.get(), bits, cloneFrame.mappedBytes());
  newImage->data_size = cloneFrame.mappedBytes();
  newImage->width = cloneFrame.width();
  newImage->height = cloneFrame.height();

  // scale the image and copy to new data
  if(resolution_ != cloneFrame.size())
  {
    QImage image(
          newImage->data.get(),
          newImage->width,
          newImage->height,
          QImage::Format_RGB32);

    QImage image2 = image.scaled(resolution_);
    if(resolution_.width() * resolution_.height()
       > cloneFrame.size().width() * cloneFrame.size().height())
    {
      newImage->data = std::unique_ptr<uchar[]>(new uchar[image2.byteCount()]);
    }
    memcpy(newImage->data.get(), image2.bits(), image2.byteCount());
    newImage->width = resolution_.width();
    newImage->height = resolution_.height();
    newImage->data_size = image2.byteCount();
  }

  //qDebug() << "Frame generated. Format: " << pf
  //         << " width: " << newImage->width << ", height: " << newImage->height;

  std::unique_ptr<Data> u_newImage( newImage );
  cloneFrame.unmap();

  Q_ASSERT(u_newImage->data);
  sendOutput(std::move(u_newImage));
}
<|endoftext|>


<commit_before>/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2018  Serge Bazanski <q3k@symbioticeda.com>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "lineshader.h"
#include "log.h"

NEXTPNR_NAMESPACE_BEGIN

void PolyLine::buildPoint(LineShaderData *building, const QVector2D *prev, const QVector2D *cur,
                          const QVector2D *next) const
{
    // buildPoint emits two vertices per line point, along with normals to move
    // them the right directio when rendering and miter to compensate for
    // bends.

    if (cur == nullptr) {
        // BUG
        return;
    }

    if (prev == nullptr && next == nullptr) {
        // BUG
        return;
    }

    // TODO(q3k): fast path for vertical/horizontal lines?

    // TODO(q3k): consider moving some of the linear algebra to the GPU,
    // they're better at this than poor old CPUs.

    // Build two unit vectors pointing in the direction of the two segments
    // defined by (prev, cur) and (cur, next)
    QVector2D dprev, dnext;
    if (prev == nullptr) {
        dnext = *next - *cur;
        dprev = dnext;
    } else if (next == nullptr) {
        dprev = *cur - *prev;
        dnext = dprev;
    } else {
        dprev = *cur - *prev;
        dnext = *next - *cur;
    }
    dprev.normalize();
    dnext.normalize();

    // Calculate tangent unit vector.
    QVector2D tangent(dprev + dnext);
    tangent.normalize();

    // Calculate normal to tangent - this is the line on which the vectors need
    // to be pushed to build a thickened line.
    const QVector2D tangent_normal = QVector2D(-tangent.y(), tangent.x());

    // Calculate normal to one of the lines.
    const QVector2D dprev_normal = QVector2D(-dprev.y(), dprev.x());
    // https://people.eecs.berkeley.edu/~sequin/CS184/IMGS/Sweep_PolyLine.jpg
    // (the ^-1 is performed in the shader)
    const float miter = QVector2D::dotProduct(tangent_normal, dprev_normal);

    const float x = cur->x();
    const float y = cur->y();
    const float mx = tangent_normal.x();
    const float my = tangent_normal.y();

    // Push back 'left' vertex.
    building->vertices.push_back(Vertex2DPOD(x, y));
    building->normals.push_back(Vertex2DPOD(mx, my));
    building->miters.push_back(miter);

    // Push back 'right' vertex.
    building->vertices.push_back(Vertex2DPOD(x, y));
    building->normals.push_back(Vertex2DPOD(mx, my));
    building->miters.push_back(-miter);
}

void PolyLine::build(LineShaderData &target) const
{
    if (points_.size() < 2) {
        return;
    }
    const QVector2D *first = &points_.front();
    const QVector2D *last = &points_.back();

    // Index number of vertices, used to build the index buffer.
    unsigned int startIndex = target.vertices.size();
    unsigned int index = startIndex;

    // For every point on the line, call buildPoint with (prev, point, next).
    // If we're building a closed line, prev/next wrap around. Otherwise
    // they are passed as nullptr and buildPoint interprets that accordinglu.
    const QVector2D *prev = nullptr;

    // Loop iterator used to ensure next is valid.
    unsigned int i = 0;
    for (const QVector2D &point : points_) {
        const QVector2D *next = nullptr;
        if (++i < points_.size()) {
            next = (&point + 1);
        }

        // If the line is closed, wrap around. Otherwise, pass nullptr.
        if (prev == nullptr && closed_) {
            buildPoint(&target, last, &point, next);
        } else if (next == nullptr && closed_) {
            buildPoint(&target, prev, &point, first);
        } else {
            buildPoint(&target, prev, &point, next);
        }

        // If we have a prev point relative to cur, build a pair of triangles
        // to render vertices into lines.
        if (prev != nullptr) {
            target.indices.push_back(index);
            target.indices.push_back(index + 1);
            target.indices.push_back(index + 2);

            target.indices.push_back(index + 2);
            target.indices.push_back(index + 1);
            target.indices.push_back(index + 3);

            index += 2;
        }
        prev = &point;
    }

    // If we're closed, build two more vertices that loop the line around.
    if (closed_) {
        target.indices.push_back(index);
        target.indices.push_back(index + 1);
        target.indices.push_back(startIndex);

        target.indices.push_back(startIndex);
        target.indices.push_back(index + 1);
        target.indices.push_back(startIndex + 1);
    }
}

bool LineShader::compile(void)
{
    program_ = new QOpenGLShaderProgram(parent_);
    program_->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource_);
    program_->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource_);
    if (!program_->link()) {
        printf("could not link program: %s\n", program_->log().toStdString().c_str());
        return false;
    }

    program_->bind();
    attributes_.position = program_->attributeLocation("position");
    attributes_.normal = program_->attributeLocation("normal");
    attributes_.miter = program_->attributeLocation("miter");
    uniforms_.thickness = program_->uniformLocation("thickness");
    uniforms_.projection = program_->uniformLocation("projection");
    uniforms_.color = program_->uniformLocation("color");
    program_->release();

    for (int style = 0; style < GraphicElement::STYLE_MAX; style++) {
        buffers_[style].position = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
        buffers_[style].normal = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
        buffers_[style].miter = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
        buffers_[style].index = QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);

        if (!buffers_[style].vao.create())
            log_abort();
        buffers_[style].vao.bind();

        if (!buffers_[style].position.create())
            log_abort();
        if (!buffers_[style].normal.create())
            log_abort();
        if (!buffers_[style].miter.create())
            log_abort();
        if (!buffers_[style].index.create())
            log_abort();

        buffers_[style].position.setUsagePattern(QOpenGLBuffer::StaticDraw);
        buffers_[style].normal.setUsagePattern(QOpenGLBuffer::StaticDraw);
        buffers_[style].miter.setUsagePattern(QOpenGLBuffer::StaticDraw);
        buffers_[style].index.setUsagePattern(QOpenGLBuffer::StaticDraw);

        buffers_[style].position.bind();
        buffers_[style].normal.bind();
        buffers_[style].miter.bind();
        buffers_[style].index.bind();

        buffers_[style].vao.release();
    }

    return true;
}

void LineShader::update_vbos(enum GraphicElement::style_t style,
                                     const LineShaderData &line)
{
    if (buffers_[style].last_vbo_update == line.last_render)
        return;
    buffers_[style].last_vbo_update = line.last_render;

    buffers_[style].indices = line.indices.size();
    if (buffers_[style].indices == 0) {
        // invalidate buffers
        buffers_[style].position.allocate(nullptr, 0);
        buffers_[style].normal.allocate(nullptr, 0);
        buffers_[style].miter.allocate(nullptr, 0);
        buffers_[style].index.allocate(nullptr, 0);
        return;
    }

    buffers_[style].position.bind();
    buffers_[style].position.allocate(&line.vertices[0], sizeof(Vertex2DPOD) * line.vertices.size());

    buffers_[style].normal.bind();
    buffers_[style].normal.allocate(&line.normals[0], sizeof(Vertex2DPOD) * line.normals.size());

    buffers_[style].miter.bind();
    buffers_[style].miter.allocate(&line.miters[0], sizeof(GLfloat) * line.miters.size());

    buffers_[style].index.bind();
    buffers_[style].index.allocate(&line.indices[0], sizeof(GLuint) * line.indices.size());
}

void LineShader::draw(enum GraphicElement::style_t style, const QColor &color,
                                float thickness, const QMatrix4x4 &projection)
{
    auto gl = QOpenGLContext::currentContext()->functions();
    if (buffers_[style].indices == 0)
        return;
    program_->bind();
    buffers_[style].vao.bind();

    program_->setUniformValue(uniforms_.projection, projection);
    program_->setUniformValue(uniforms_.thickness, thickness);
    program_->setUniformValue(uniforms_.color, color.redF(), color.greenF(), color.blueF(), color.alphaF());

    buffers_[style].position.bind();
    program_->enableAttributeArray(attributes_.position);
    program_->setAttributeBuffer(attributes_.position, GL_FLOAT, 0, 2);

    buffers_[style].normal.bind();
    program_->enableAttributeArray(attributes_.normal);
    program_->setAttributeBuffer(attributes_.normal, GL_FLOAT, 0, 2);

    buffers_[style].miter.bind();
    program_->enableAttributeArray(attributes_.miter);
    program_->setAttributeBuffer(attributes_.miter, GL_FLOAT, 0, 1);

    buffers_[style].index.bind();
    gl->glDrawElements(GL_TRIANGLES, buffers_[style].indices, GL_UNSIGNED_INT, (void *)0);

    program_->disableAttributeArray(attributes_.position);
    program_->disableAttributeArray(attributes_.normal);
    program_->disableAttributeArray(attributes_.miter);

    buffers_[style].vao.release();
    program_->release();
}

NEXTPNR_NAMESPACE_END
<commit_msg>do not invalidate buffer, leave as is<commit_after>/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2018  Serge Bazanski <q3k@symbioticeda.com>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "lineshader.h"
#include "log.h"

NEXTPNR_NAMESPACE_BEGIN

void PolyLine::buildPoint(LineShaderData *building, const QVector2D *prev, const QVector2D *cur,
                          const QVector2D *next) const
{
    // buildPoint emits two vertices per line point, along with normals to move
    // them the right directio when rendering and miter to compensate for
    // bends.

    if (cur == nullptr) {
        // BUG
        return;
    }

    if (prev == nullptr && next == nullptr) {
        // BUG
        return;
    }

    // TODO(q3k): fast path for vertical/horizontal lines?

    // TODO(q3k): consider moving some of the linear algebra to the GPU,
    // they're better at this than poor old CPUs.

    // Build two unit vectors pointing in the direction of the two segments
    // defined by (prev, cur) and (cur, next)
    QVector2D dprev, dnext;
    if (prev == nullptr) {
        dnext = *next - *cur;
        dprev = dnext;
    } else if (next == nullptr) {
        dprev = *cur - *prev;
        dnext = dprev;
    } else {
        dprev = *cur - *prev;
        dnext = *next - *cur;
    }
    dprev.normalize();
    dnext.normalize();

    // Calculate tangent unit vector.
    QVector2D tangent(dprev + dnext);
    tangent.normalize();

    // Calculate normal to tangent - this is the line on which the vectors need
    // to be pushed to build a thickened line.
    const QVector2D tangent_normal = QVector2D(-tangent.y(), tangent.x());

    // Calculate normal to one of the lines.
    const QVector2D dprev_normal = QVector2D(-dprev.y(), dprev.x());
    // https://people.eecs.berkeley.edu/~sequin/CS184/IMGS/Sweep_PolyLine.jpg
    // (the ^-1 is performed in the shader)
    const float miter = QVector2D::dotProduct(tangent_normal, dprev_normal);

    const float x = cur->x();
    const float y = cur->y();
    const float mx = tangent_normal.x();
    const float my = tangent_normal.y();

    // Push back 'left' vertex.
    building->vertices.push_back(Vertex2DPOD(x, y));
    building->normals.push_back(Vertex2DPOD(mx, my));
    building->miters.push_back(miter);

    // Push back 'right' vertex.
    building->vertices.push_back(Vertex2DPOD(x, y));
    building->normals.push_back(Vertex2DPOD(mx, my));
    building->miters.push_back(-miter);
}

void PolyLine::build(LineShaderData &target) const
{
    if (points_.size() < 2) {
        return;
    }
    const QVector2D *first = &points_.front();
    const QVector2D *last = &points_.back();

    // Index number of vertices, used to build the index buffer.
    unsigned int startIndex = target.vertices.size();
    unsigned int index = startIndex;

    // For every point on the line, call buildPoint with (prev, point, next).
    // If we're building a closed line, prev/next wrap around. Otherwise
    // they are passed as nullptr and buildPoint interprets that accordinglu.
    const QVector2D *prev = nullptr;

    // Loop iterator used to ensure next is valid.
    unsigned int i = 0;
    for (const QVector2D &point : points_) {
        const QVector2D *next = nullptr;
        if (++i < points_.size()) {
            next = (&point + 1);
        }

        // If the line is closed, wrap around. Otherwise, pass nullptr.
        if (prev == nullptr && closed_) {
            buildPoint(&target, last, &point, next);
        } else if (next == nullptr && closed_) {
            buildPoint(&target, prev, &point, first);
        } else {
            buildPoint(&target, prev, &point, next);
        }

        // If we have a prev point relative to cur, build a pair of triangles
        // to render vertices into lines.
        if (prev != nullptr) {
            target.indices.push_back(index);
            target.indices.push_back(index + 1);
            target.indices.push_back(index + 2);

            target.indices.push_back(index + 2);
            target.indices.push_back(index + 1);
            target.indices.push_back(index + 3);

            index += 2;
        }
        prev = &point;
    }

    // If we're closed, build two more vertices that loop the line around.
    if (closed_) {
        target.indices.push_back(index);
        target.indices.push_back(index + 1);
        target.indices.push_back(startIndex);

        target.indices.push_back(startIndex);
        target.indices.push_back(index + 1);
        target.indices.push_back(startIndex + 1);
    }
}

bool LineShader::compile(void)
{
    program_ = new QOpenGLShaderProgram(parent_);
    program_->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource_);
    program_->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource_);
    if (!program_->link()) {
        printf("could not link program: %s\n", program_->log().toStdString().c_str());
        return false;
    }

    program_->bind();
    attributes_.position = program_->attributeLocation("position");
    attributes_.normal = program_->attributeLocation("normal");
    attributes_.miter = program_->attributeLocation("miter");
    uniforms_.thickness = program_->uniformLocation("thickness");
    uniforms_.projection = program_->uniformLocation("projection");
    uniforms_.color = program_->uniformLocation("color");
    program_->release();

    for (int style = 0; style < GraphicElement::STYLE_MAX; style++) {
        buffers_[style].position = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
        buffers_[style].normal = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
        buffers_[style].miter = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
        buffers_[style].index = QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);

        if (!buffers_[style].vao.create())
            log_abort();
        buffers_[style].vao.bind();

        if (!buffers_[style].position.create())
            log_abort();
        if (!buffers_[style].normal.create())
            log_abort();
        if (!buffers_[style].miter.create())
            log_abort();
        if (!buffers_[style].index.create())
            log_abort();

        buffers_[style].position.setUsagePattern(QOpenGLBuffer::StaticDraw);
        buffers_[style].normal.setUsagePattern(QOpenGLBuffer::StaticDraw);
        buffers_[style].miter.setUsagePattern(QOpenGLBuffer::StaticDraw);
        buffers_[style].index.setUsagePattern(QOpenGLBuffer::StaticDraw);

        buffers_[style].position.bind();
        buffers_[style].normal.bind();
        buffers_[style].miter.bind();
        buffers_[style].index.bind();

        buffers_[style].vao.release();
    }

    return true;
}

void LineShader::update_vbos(enum GraphicElement::style_t style,
                                     const LineShaderData &line)
{
    if (buffers_[style].last_vbo_update == line.last_render)
        return;
    buffers_[style].last_vbo_update = line.last_render;

    buffers_[style].indices = line.indices.size();
    if (buffers_[style].indices == 0)
        return;

    buffers_[style].position.bind();
    buffers_[style].position.allocate(&line.vertices[0], sizeof(Vertex2DPOD) * line.vertices.size());

    buffers_[style].normal.bind();
    buffers_[style].normal.allocate(&line.normals[0], sizeof(Vertex2DPOD) * line.normals.size());

    buffers_[style].miter.bind();
    buffers_[style].miter.allocate(&line.miters[0], sizeof(GLfloat) * line.miters.size());

    buffers_[style].index.bind();
    buffers_[style].index.allocate(&line.indices[0], sizeof(GLuint) * line.indices.size());
}

void LineShader::draw(enum GraphicElement::style_t style, const QColor &color,
                                float thickness, const QMatrix4x4 &projection)
{
    auto gl = QOpenGLContext::currentContext()->functions();
    if (buffers_[style].indices == 0)
        return;
    program_->bind();
    buffers_[style].vao.bind();

    program_->setUniformValue(uniforms_.projection, projection);
    program_->setUniformValue(uniforms_.thickness, thickness);
    program_->setUniformValue(uniforms_.color, color.redF(), color.greenF(), color.blueF(), color.alphaF());

    buffers_[style].position.bind();
    program_->enableAttributeArray(attributes_.position);
    program_->setAttributeBuffer(attributes_.position, GL_FLOAT, 0, 2);

    buffers_[style].normal.bind();
    program_->enableAttributeArray(attributes_.normal);
    program_->setAttributeBuffer(attributes_.normal, GL_FLOAT, 0, 2);

    buffers_[style].miter.bind();
    program_->enableAttributeArray(attributes_.miter);
    program_->setAttributeBuffer(attributes_.miter, GL_FLOAT, 0, 1);

    buffers_[style].index.bind();
    gl->glDrawElements(GL_TRIANGLES, buffers_[style].indices, GL_UNSIGNED_INT, (void *)0);

    program_->disableAttributeArray(attributes_.position);
    program_->disableAttributeArray(attributes_.normal);
    program_->disableAttributeArray(attributes_.miter);

    buffers_[style].vao.release();
    program_->release();
}

NEXTPNR_NAMESPACE_END
<|endoftext|>


<commit_before>//  Copyright (c) 2013, Facebook, Inc.  All rights reserved.
//  This source code is licensed under the BSD-style license found in the
//  LICENSE file in the root directory of this source tree. An additional grant
//  of patent rights can be found in the PATENTS file in the same directory.
//
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.

#include "rocksdb/db.h"

#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "rocksdb/cache.h"
#include "rocksdb/env.h"
#include "rocksdb/table.h"
#include "rocksdb/write_batch.h"
#include "db/db_impl.h"
#include "db/filename.h"
#include "db/log_format.h"
#include "db/version_set.h"
#include "util/logging.h"
#include "util/testharness.h"
#include "util/testutil.h"

namespace rocksdb {

static const int kValueSize = 1000;

class CorruptionTest {
 public:
  test::ErrorEnv env_;
  std::string dbname_;
  shared_ptr<Cache> tiny_cache_;
  Options options_;
  DB* db_;

  CorruptionTest() {
    tiny_cache_ = NewLRUCache(100);
    options_.env = &env_;
    dbname_ = test::TmpDir() + "/db_test";
    DestroyDB(dbname_, options_);

    db_ = nullptr;
    options_.create_if_missing = true;
    options_.block_size_deviation = 0; // make unit test pass for now
    Reopen();
    options_.create_if_missing = false;
  }

  ~CorruptionTest() {
     delete db_;
     DestroyDB(dbname_, Options());
  }

  Status TryReopen(Options* options = nullptr) {
    delete db_;
    db_ = nullptr;
    Options opt = (options ? *options : options_);
    opt.env = &env_;
    opt.block_cache = tiny_cache_;
    opt.block_size_deviation = 0;
    opt.arena_block_size = 4096;
    return DB::Open(opt, dbname_, &db_);
  }

  void Reopen(Options* options = nullptr) {
    ASSERT_OK(TryReopen(options));
  }

  void RepairDB() {
    delete db_;
    db_ = nullptr;
    ASSERT_OK(::rocksdb::RepairDB(dbname_, options_));
  }

  void Build(int n) {
    std::string key_space, value_space;
    WriteBatch batch;
    for (int i = 0; i < n; i++) {
      //if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n);
      Slice key = Key(i, &key_space);
      batch.Clear();
      batch.Put(key, Value(i, &value_space));
      ASSERT_OK(db_->Write(WriteOptions(), &batch));
    }
  }

  void Check(int min_expected, int max_expected) {
    unsigned int next_expected = 0;
    int missed = 0;
    int bad_keys = 0;
    int bad_values = 0;
    int correct = 0;
    std::string value_space;
    // Do not verify checksums. If we verify checksums then the
    // db itself will raise errors because data is corrupted.
    // Instead, we want the reads to be successful and this test
    // will detect whether the appropriate corruptions have
    // occured.
    Iterator* iter = db_->NewIterator(ReadOptions(false, true));
    for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
      uint64_t key;
      Slice in(iter->key());
      if (!ConsumeDecimalNumber(&in, &key) ||
          !in.empty() ||
          key < next_expected) {
        bad_keys++;
        continue;
      }
      missed += (key - next_expected);
      next_expected = key + 1;
      if (iter->value() != Value(key, &value_space)) {
        bad_values++;
      } else {
        correct++;
      }
    }
    delete iter;

    fprintf(stderr,
            "expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%d\n",
            min_expected, max_expected, correct, bad_keys, bad_values, missed);
    ASSERT_LE(min_expected, correct);
    ASSERT_GE(max_expected, correct);
  }

  void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
    // Pick file to corrupt
    std::vector<std::string> filenames;
    ASSERT_OK(env_.GetChildren(dbname_, &filenames));
    uint64_t number;
    FileType type;
    std::string fname;
    int picked_number = -1;
    for (unsigned int i = 0; i < filenames.size(); i++) {
      if (ParseFileName(filenames[i], &number, &type) &&
          type == filetype &&
          int(number) > picked_number) {  // Pick latest file
        fname = dbname_ + "/" + filenames[i];
        picked_number = number;
      }
    }
    ASSERT_TRUE(!fname.empty()) << filetype;

    struct stat sbuf;
    if (stat(fname.c_str(), &sbuf) != 0) {
      const char* msg = strerror(errno);
      ASSERT_TRUE(false) << fname << ": " << msg;
    }

    if (offset < 0) {
      // Relative to end of file; make it absolute
      if (-offset > sbuf.st_size) {
        offset = 0;
      } else {
        offset = sbuf.st_size + offset;
      }
    }
    if (offset > sbuf.st_size) {
      offset = sbuf.st_size;
    }
    if (offset + bytes_to_corrupt > sbuf.st_size) {
      bytes_to_corrupt = sbuf.st_size - offset;
    }

    // Do it
    std::string contents;
    Status s = ReadFileToString(Env::Default(), fname, &contents);
    ASSERT_TRUE(s.ok()) << s.ToString();
    for (int i = 0; i < bytes_to_corrupt; i++) {
      contents[i + offset] ^= 0x80;
    }
    s = WriteStringToFile(Env::Default(), contents, fname);
    ASSERT_TRUE(s.ok()) << s.ToString();
  }

  int Property(const std::string& name) {
    std::string property;
    int result;
    if (db_->GetProperty(name, &property) &&
        sscanf(property.c_str(), "%d", &result) == 1) {
      return result;
    } else {
      return -1;
    }
  }

  // Return the ith key
  Slice Key(int i, std::string* storage) {
    char buf[100];
    snprintf(buf, sizeof(buf), "%016d", i);
    storage->assign(buf, strlen(buf));
    return Slice(*storage);
  }

  // Return the value to associate with the specified key
  Slice Value(int k, std::string* storage) {
    Random r(k);
    return test::RandomString(&r, kValueSize, storage);
  }
};

TEST(CorruptionTest, Recovery) {
  Build(100);
  Check(100, 100);
  Corrupt(kLogFile, 19, 1);      // WriteBatch tag for first record
  Corrupt(kLogFile, log::kBlockSize + 1000, 1);  // Somewhere in second block
  Reopen();

  // The 64 records in the first two log blocks are completely lost.
  Check(36, 36);
}

TEST(CorruptionTest, RecoverWriteError) {
  env_.writable_file_error_ = true;
  Status s = TryReopen();
  ASSERT_TRUE(!s.ok());
}

TEST(CorruptionTest, NewFileErrorDuringWrite) {
  // Do enough writing to force minor compaction
  env_.writable_file_error_ = true;
  const int num = 3 + (Options().write_buffer_size / kValueSize);
  std::string value_storage;
  Status s;
  bool failed = false;
  for (int i = 0; i < num; i++) {
    WriteBatch batch;
    batch.Put("a", Value(100, &value_storage));
    s = db_->Write(WriteOptions(), &batch);
    if (!s.ok()) {
      failed = true;
    }
    ASSERT_TRUE(!failed || !s.ok());
  }
  ASSERT_TRUE(!s.ok());
  ASSERT_GE(env_.num_writable_file_errors_, 1);
  env_.writable_file_error_ = false;
  Reopen();
}

TEST(CorruptionTest, TableFile) {
  Build(100);
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
  dbi->TEST_FlushMemTable();
  dbi->TEST_CompactRange(0, nullptr, nullptr);
  dbi->TEST_CompactRange(1, nullptr, nullptr);

  Corrupt(kTableFile, 100, 1);
  Check(99, 99);
}

TEST(CorruptionTest, TableFileIndexData) {
  Build(10000);  // Enough to build multiple Tables
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
  dbi->TEST_FlushMemTable();

  Corrupt(kTableFile, -2000, 500);
  Reopen();
  Check(5000, 9999);
}

TEST(CorruptionTest, MissingDescriptor) {
  Build(1000);
  RepairDB();
  Reopen();
  Check(1000, 1000);
}

TEST(CorruptionTest, SequenceNumberRecovery) {
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1"));
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2"));
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v3"));
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v4"));
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v5"));
  RepairDB();
  Reopen();
  std::string v;
  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
  ASSERT_EQ("v5", v);
  // Write something.  If sequence number was not recovered properly,
  // it will be hidden by an earlier write.
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v6"));
  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
  ASSERT_EQ("v6", v);
  Reopen();
  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
  ASSERT_EQ("v6", v);
}

TEST(CorruptionTest, CorruptedDescriptor) {
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello"));
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
  dbi->TEST_FlushMemTable();
  dbi->TEST_CompactRange(0, nullptr, nullptr);

  Corrupt(kDescriptorFile, 0, 1000);
  Status s = TryReopen();
  ASSERT_TRUE(!s.ok());

  RepairDB();
  Reopen();
  std::string v;
  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
  ASSERT_EQ("hello", v);
}

TEST(CorruptionTest, CompactionInputError) {
  Build(10);
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
  dbi->TEST_FlushMemTable();
  const int last = dbi->MaxMemCompactionLevel();
  ASSERT_EQ(1, Property("rocksdb.num-files-at-level" + NumberToString(last)));

  Corrupt(kTableFile, 100, 1);
  Check(9, 9);

  // Force compactions by writing lots of values
  Build(10000);
  Check(10000, 10000);
}

TEST(CorruptionTest, CompactionInputErrorParanoid) {
  Options options;
  options.paranoid_checks = true;
  options.write_buffer_size = 131072;
  options.max_write_buffer_number = 2;
  Reopen(&options);
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);

  // Fill levels >= 1 so memtable compaction outputs to level 1
  for (int level = 1; level < dbi->NumberLevels(); level++) {
    dbi->Put(WriteOptions(), "", "begin");
    dbi->Put(WriteOptions(), "~", "end");
    dbi->TEST_FlushMemTable();
  }

  Build(10);
  dbi->TEST_FlushMemTable();
  dbi->TEST_WaitForCompact();
  ASSERT_EQ(1, Property("rocksdb.num-files-at-level0"));

  Corrupt(kTableFile, 100, 1);
  Check(9, 9);

  // Write must eventually fail because of corrupted table
  Status s;
  std::string tmp1, tmp2;
  bool failed = false;
  for (int i = 0; i < 10000; i++) {
    s = db_->Put(WriteOptions(), Key(i, &tmp1), Value(i, &tmp2));
    if (!s.ok()) {
      failed = true;
    }
    // if one write failed, every subsequent write must fail, too
    ASSERT_TRUE(!failed || !s.ok()) << "write did not fail in a corrupted db";
  }
  ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db";
}

TEST(CorruptionTest, UnrelatedKeys) {
  Build(10);
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
  dbi->TEST_FlushMemTable();
  Corrupt(kTableFile, 100, 1);

  std::string tmp1, tmp2;
  ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2)));
  std::string v;
  ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
  ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
  dbi->TEST_FlushMemTable();
  ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
  ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
}

TEST(CorruptionTest, FileSystemStateCorrupted) {
  for (int iter = 0; iter < 2; ++iter) {
    Options options;
    options.paranoid_checks = true;
    options.create_if_missing = true;
    Reopen(&options);
    Build(10);
    ASSERT_OK(db_->Flush(FlushOptions()));
    DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
    std::vector<LiveFileMetaData> metadata;
    dbi->GetLiveFilesMetaData(&metadata);
    ASSERT_GT(metadata.size(), size_t(0));
    std::string filename = dbname_ + metadata[0].name;

    delete db_;
    db_ = nullptr;

    if (iter == 0) {  // corrupt file size
      unique_ptr<WritableFile> file;
      env_.NewWritableFile(filename, &file, EnvOptions());
      file->Append(Slice("corrupted sst"));
      file.reset();
    } else {  // delete the file
      env_.DeleteFile(filename);
    }

    Status x = TryReopen(&options);
    ASSERT_TRUE(x.IsCorruption());
    DestroyDB(dbname_, options_);
    Reopen(&options);
  }
}

}  // namespace rocksdb

int main(int argc, char** argv) {
  return rocksdb::test::RunAllTests();
}
<commit_msg>Make CompactionInputErrorParanoid less flakey<commit_after>//  Copyright (c) 2013, Facebook, Inc.  All rights reserved.
//  This source code is licensed under the BSD-style license found in the
//  LICENSE file in the root directory of this source tree. An additional grant
//  of patent rights can be found in the PATENTS file in the same directory.
//
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.

#include "rocksdb/db.h"

#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "rocksdb/cache.h"
#include "rocksdb/env.h"
#include "rocksdb/table.h"
#include "rocksdb/write_batch.h"
#include "db/db_impl.h"
#include "db/filename.h"
#include "db/log_format.h"
#include "db/version_set.h"
#include "util/logging.h"
#include "util/testharness.h"
#include "util/testutil.h"

namespace rocksdb {

static const int kValueSize = 1000;

class CorruptionTest {
 public:
  test::ErrorEnv env_;
  std::string dbname_;
  shared_ptr<Cache> tiny_cache_;
  Options options_;
  DB* db_;

  CorruptionTest() {
    tiny_cache_ = NewLRUCache(100);
    options_.env = &env_;
    dbname_ = test::TmpDir() + "/corruption_test";
    DestroyDB(dbname_, options_);

    db_ = nullptr;
    options_.create_if_missing = true;
    options_.block_size_deviation = 0; // make unit test pass for now
    Reopen();
    options_.create_if_missing = false;
  }

  ~CorruptionTest() {
     delete db_;
     DestroyDB(dbname_, Options());
  }

  Status TryReopen(Options* options = nullptr) {
    delete db_;
    db_ = nullptr;
    Options opt = (options ? *options : options_);
    opt.env = &env_;
    opt.block_cache = tiny_cache_;
    opt.block_size_deviation = 0;
    opt.arena_block_size = 4096;
    return DB::Open(opt, dbname_, &db_);
  }

  void Reopen(Options* options = nullptr) {
    ASSERT_OK(TryReopen(options));
  }

  void RepairDB() {
    delete db_;
    db_ = nullptr;
    ASSERT_OK(::rocksdb::RepairDB(dbname_, options_));
  }

  void Build(int n) {
    std::string key_space, value_space;
    WriteBatch batch;
    for (int i = 0; i < n; i++) {
      //if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n);
      Slice key = Key(i, &key_space);
      batch.Clear();
      batch.Put(key, Value(i, &value_space));
      ASSERT_OK(db_->Write(WriteOptions(), &batch));
    }
  }

  void Check(int min_expected, int max_expected) {
    unsigned int next_expected = 0;
    int missed = 0;
    int bad_keys = 0;
    int bad_values = 0;
    int correct = 0;
    std::string value_space;
    // Do not verify checksums. If we verify checksums then the
    // db itself will raise errors because data is corrupted.
    // Instead, we want the reads to be successful and this test
    // will detect whether the appropriate corruptions have
    // occured.
    Iterator* iter = db_->NewIterator(ReadOptions(false, true));
    for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
      uint64_t key;
      Slice in(iter->key());
      if (!ConsumeDecimalNumber(&in, &key) ||
          !in.empty() ||
          key < next_expected) {
        bad_keys++;
        continue;
      }
      missed += (key - next_expected);
      next_expected = key + 1;
      if (iter->value() != Value(key, &value_space)) {
        bad_values++;
      } else {
        correct++;
      }
    }
    delete iter;

    fprintf(stderr,
            "expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%d\n",
            min_expected, max_expected, correct, bad_keys, bad_values, missed);
    ASSERT_LE(min_expected, correct);
    ASSERT_GE(max_expected, correct);
  }

  void CorruptFile(const std::string fname, int offset, int bytes_to_corrupt) {
    struct stat sbuf;
    if (stat(fname.c_str(), &sbuf) != 0) {
      const char* msg = strerror(errno);
      ASSERT_TRUE(false) << fname << ": " << msg;
    }

    if (offset < 0) {
      // Relative to end of file; make it absolute
      if (-offset > sbuf.st_size) {
        offset = 0;
      } else {
        offset = sbuf.st_size + offset;
      }
    }
    if (offset > sbuf.st_size) {
      offset = sbuf.st_size;
    }
    if (offset + bytes_to_corrupt > sbuf.st_size) {
      bytes_to_corrupt = sbuf.st_size - offset;
    }

    // Do it
    std::string contents;
    Status s = ReadFileToString(Env::Default(), fname, &contents);
    ASSERT_TRUE(s.ok()) << s.ToString();
    for (int i = 0; i < bytes_to_corrupt; i++) {
      contents[i + offset] ^= 0x80;
    }
    s = WriteStringToFile(Env::Default(), contents, fname);
    ASSERT_TRUE(s.ok()) << s.ToString();
  }

  void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
    // Pick file to corrupt
    std::vector<std::string> filenames;
    ASSERT_OK(env_.GetChildren(dbname_, &filenames));
    uint64_t number;
    FileType type;
    std::string fname;
    int picked_number = -1;
    for (unsigned int i = 0; i < filenames.size(); i++) {
      if (ParseFileName(filenames[i], &number, &type) &&
          type == filetype &&
          static_cast<int>(number) > picked_number) {  // Pick latest file
        fname = dbname_ + "/" + filenames[i];
        picked_number = number;
      }
    }
    ASSERT_TRUE(!fname.empty()) << filetype;

    CorruptFile(fname, offset, bytes_to_corrupt);
  }

  // corrupts exactly one file at level `level`. if no file found at level,
  // asserts
  void CorruptTableFileAtLevel(int level, int offset, int bytes_to_corrupt) {
    std::vector<LiveFileMetaData> metadata;
    db_->GetLiveFilesMetaData(&metadata);
    for (const auto& m : metadata) {
      if (m.level == level) {
        CorruptFile(dbname_ + "/" + m.name, offset, bytes_to_corrupt);
        return;
      }
    }
    ASSERT_TRUE(false) << "no file found at level";
  }

  int Property(const std::string& name) {
    std::string property;
    int result;
    if (db_->GetProperty(name, &property) &&
        sscanf(property.c_str(), "%d", &result) == 1) {
      return result;
    } else {
      return -1;
    }
  }

  // Return the ith key
  Slice Key(int i, std::string* storage) {
    char buf[100];
    snprintf(buf, sizeof(buf), "%016d", i);
    storage->assign(buf, strlen(buf));
    return Slice(*storage);
  }

  // Return the value to associate with the specified key
  Slice Value(int k, std::string* storage) {
    Random r(k);
    return test::RandomString(&r, kValueSize, storage);
  }
};

TEST(CorruptionTest, Recovery) {
  Build(100);
  Check(100, 100);
  Corrupt(kLogFile, 19, 1);      // WriteBatch tag for first record
  Corrupt(kLogFile, log::kBlockSize + 1000, 1);  // Somewhere in second block
  Reopen();

  // The 64 records in the first two log blocks are completely lost.
  Check(36, 36);
}

TEST(CorruptionTest, RecoverWriteError) {
  env_.writable_file_error_ = true;
  Status s = TryReopen();
  ASSERT_TRUE(!s.ok());
}

TEST(CorruptionTest, NewFileErrorDuringWrite) {
  // Do enough writing to force minor compaction
  env_.writable_file_error_ = true;
  const int num = 3 + (Options().write_buffer_size / kValueSize);
  std::string value_storage;
  Status s;
  bool failed = false;
  for (int i = 0; i < num; i++) {
    WriteBatch batch;
    batch.Put("a", Value(100, &value_storage));
    s = db_->Write(WriteOptions(), &batch);
    if (!s.ok()) {
      failed = true;
    }
    ASSERT_TRUE(!failed || !s.ok());
  }
  ASSERT_TRUE(!s.ok());
  ASSERT_GE(env_.num_writable_file_errors_, 1);
  env_.writable_file_error_ = false;
  Reopen();
}

TEST(CorruptionTest, TableFile) {
  Build(100);
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
  dbi->TEST_FlushMemTable();
  dbi->TEST_CompactRange(0, nullptr, nullptr);
  dbi->TEST_CompactRange(1, nullptr, nullptr);

  Corrupt(kTableFile, 100, 1);
  Check(99, 99);
}

TEST(CorruptionTest, TableFileIndexData) {
  Build(10000);  // Enough to build multiple Tables
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
  dbi->TEST_FlushMemTable();

  Corrupt(kTableFile, -2000, 500);
  Reopen();
  Check(5000, 9999);
}

TEST(CorruptionTest, MissingDescriptor) {
  Build(1000);
  RepairDB();
  Reopen();
  Check(1000, 1000);
}

TEST(CorruptionTest, SequenceNumberRecovery) {
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1"));
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2"));
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v3"));
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v4"));
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v5"));
  RepairDB();
  Reopen();
  std::string v;
  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
  ASSERT_EQ("v5", v);
  // Write something.  If sequence number was not recovered properly,
  // it will be hidden by an earlier write.
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "v6"));
  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
  ASSERT_EQ("v6", v);
  Reopen();
  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
  ASSERT_EQ("v6", v);
}

TEST(CorruptionTest, CorruptedDescriptor) {
  ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello"));
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
  dbi->TEST_FlushMemTable();
  dbi->TEST_CompactRange(0, nullptr, nullptr);

  Corrupt(kDescriptorFile, 0, 1000);
  Status s = TryReopen();
  ASSERT_TRUE(!s.ok());

  RepairDB();
  Reopen();
  std::string v;
  ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));
  ASSERT_EQ("hello", v);
}

TEST(CorruptionTest, CompactionInputError) {
  Build(10);
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
  dbi->TEST_FlushMemTable();
  const int last = dbi->MaxMemCompactionLevel();
  ASSERT_EQ(1, Property("rocksdb.num-files-at-level" + NumberToString(last)));

  Corrupt(kTableFile, 100, 1);
  Check(9, 9);

  // Force compactions by writing lots of values
  Build(10000);
  Check(10000, 10000);
}

TEST(CorruptionTest, CompactionInputErrorParanoid) {
  Options options;
  options.paranoid_checks = true;
  options.write_buffer_size = 131072;
  options.max_write_buffer_number = 2;
  Reopen(&options);
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);

  // Fill levels >= 1 so memtable flush outputs to level 0
  for (int level = 1; level < dbi->NumberLevels(); level++) {
    dbi->Put(WriteOptions(), "", "begin");
    dbi->Put(WriteOptions(), "~", "end");
    dbi->TEST_FlushMemTable();
  }

  options.max_mem_compaction_level = 0;
  Reopen(&options);

  Build(10);
  dbi->TEST_FlushMemTable();
  dbi->TEST_WaitForCompact();
  ASSERT_EQ(1, Property("rocksdb.num-files-at-level0"));

  CorruptTableFileAtLevel(0, 100, 1);
  Check(9, 9);

  // Write must eventually fail because of corrupted table
  Status s;
  std::string tmp1, tmp2;
  bool failed = false;
  for (int i = 0; i < 10000; i++) {
    s = db_->Put(WriteOptions(), Key(i, &tmp1), Value(i, &tmp2));
    if (!s.ok()) {
      failed = true;
    }
    // if one write failed, every subsequent write must fail, too
    ASSERT_TRUE(!failed || !s.ok()) << "write did not fail in a corrupted db";
  }
  ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db";
}

TEST(CorruptionTest, UnrelatedKeys) {
  Build(10);
  DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
  dbi->TEST_FlushMemTable();
  Corrupt(kTableFile, 100, 1);

  std::string tmp1, tmp2;
  ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2)));
  std::string v;
  ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
  ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
  dbi->TEST_FlushMemTable();
  ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));
  ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
}

TEST(CorruptionTest, FileSystemStateCorrupted) {
  for (int iter = 0; iter < 2; ++iter) {
    Options options;
    options.paranoid_checks = true;
    options.create_if_missing = true;
    Reopen(&options);
    Build(10);
    ASSERT_OK(db_->Flush(FlushOptions()));
    DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
    std::vector<LiveFileMetaData> metadata;
    dbi->GetLiveFilesMetaData(&metadata);
    ASSERT_GT(metadata.size(), size_t(0));
    std::string filename = dbname_ + metadata[0].name;

    delete db_;
    db_ = nullptr;

    if (iter == 0) {  // corrupt file size
      unique_ptr<WritableFile> file;
      env_.NewWritableFile(filename, &file, EnvOptions());
      file->Append(Slice("corrupted sst"));
      file.reset();
    } else {  // delete the file
      env_.DeleteFile(filename);
    }

    Status x = TryReopen(&options);
    ASSERT_TRUE(x.IsCorruption());
    DestroyDB(dbname_, options_);
    Reopen(&options);
  }
}

}  // namespace rocksdb

int main(int argc, char** argv) {
  return rocksdb::test::RunAllTests();
}
<|endoftext|>