<commit_before>// @(#)root/io:$Id$
// Author: Elvin Sindrilaru   19/05/2011

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

#include "TFilePrefetch.h"
#include "TTimeStamp.h"
#include "TVirtualPerfStats.h"
#include "TVirtualMonitoring.h"

#include <iostream>
#include <string>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cctype>

static const int kMAX_READ_SIZE    = 2;   //maximum size of the read list of blocks

inline int xtod(char c) { return (c>='0' && c<='9') ? c-'0' : ((c>='A' && c<='F') ? c-'A'+10 : ((c>='a' && c<='f') ? c-'a'+10 : 0)); }

using namespace std;

ClassImp(TFilePrefetch)

//____________________________________________________________________________________________
TFilePrefetch::TFilePrefetch(TFile* file)
{
   // Constructor.

   fConsumer = 0;
   fFile = file;
   fPendingBlocks    = new TList();
   fReadBlocks       = new TList();
   fMutexReadList    = new TMutex();
   fMutexPendingList = new TMutex();
   fNewBlockAdded    = new TCondition(0);
   fReadBlockAdded   = new TCondition(0);
   fCondNextFile     = new TCondition(0);
   fSemMasterWorker  = new TSemaphore(0);
   fSemWorkerMaster  = new TSemaphore(0);
}

//____________________________________________________________________________________________
TFilePrefetch::~TFilePrefetch()
{
   // Destructor.

   //killing consumer thread
   fSemMasterWorker->Post();

   TMutex *mutexCond = fNewBlockAdded->GetMutex();
   while ( fSemWorkerMaster->Wait(10) != 0 ) {
      mutexCond->Lock();
      fNewBlockAdded->Signal();
      mutexCond->UnLock();
   }

   fConsumer->Join();

   SafeDelete(fConsumer);
   SafeDelete(fPendingBlocks);
   SafeDelete(fReadBlocks);
   SafeDelete(fMutexReadList);
   SafeDelete(fMutexPendingList);
   SafeDelete(fNewBlockAdded);
   SafeDelete(fReadBlockAdded);
   SafeDelete(fCondNextFile);
   SafeDelete(fSemMasterWorker);
   SafeDelete(fSemWorkerMaster);
}

//____________________________________________________________________________________________
void TFilePrefetch::ReadAsync(TFPBlock* block, Bool_t &inCache)
{
   // Read one block and insert it in prefetchBuffers list.

   char* path = 0;

   if (CheckBlockInCache(path, block)){
      block->SetBuffer(GetBlockFromCache(path, block->GetDataSize()));
      inCache = kTRUE;
   }
   else{
      fFile->ReadBuffers(block->GetBuffer(), block->GetPos(), block->GetLen(), block->GetNoElem());
      if (fFile->GetArchive()) {
         for (Int_t i = 0; i < block->GetNoElem(); i++)
            block->SetPos(i, block->GetPos(i) - fFile->GetArchiveOffset());
      }
      inCache =kFALSE;
   }
   delete[] path;
}

//____________________________________________________________________________________________
void TFilePrefetch::ReadListOfBlocks()
{
   // Get blocks specified in prefetchBlocks.

   Bool_t inCache = kFALSE;
   TFPBlock*  block = 0;

   while((block = GetPendingBlock())){
      ReadAsync(block, inCache);
      AddReadBlock(block);
      if (!inCache)
         SaveBlockInCache(block);
   }
}

//____________________________________________________________________________________________
Bool_t TFilePrefetch::BinarySearchReadList(TFPBlock* blockObj, Long64_t offset, Int_t len, Int_t* index)
{
   // Search for a requested element in a block and return the index.

   Int_t first = 0, last = -1, mid = -1;
   last = (Int_t) blockObj->GetNoElem()-1;

   while (first <= last){
      mid = first + (last - first) / 2;
      if ((offset >= blockObj->GetPos(mid) && offset <= (blockObj->GetPos(mid) + blockObj->GetLen(mid))
           && ( (offset + len) <= blockObj->GetPos(mid) + blockObj->GetLen(mid)))){

         *index = mid;
         return true;
      }
      else if (blockObj->GetPos(mid) < offset){
         first = mid + 1;
      }
      else{
         last = mid - 1;
      }
   }
   return false;
}

//____________________________________________________________________________________________
Long64_t TFilePrefetch::GetWaitTime()
{
   // Return the time spent wating for buffer to be read in microseconds.

   return Long64_t(fWaitTime.RealTime()*1.e+6);
}

//____________________________________________________________________________________________
Bool_t TFilePrefetch::ReadBuffer(char* buf, Long64_t offset, Int_t len)
{
   // Return a prefetched element.

   Bool_t found = false;
   TFPBlock* blockObj = 0;
   TMutex *mutexBlocks = fMutexReadList;
   Int_t index = -1;

   while (1){
      mutexBlocks->Lock();
      TIter iter(fReadBlocks);
      while ((blockObj = (TFPBlock*) iter.Next())){
         index = -1;
         if (BinarySearchReadList(blockObj, offset, len, &index)){
            found = true;
            break;
         }
      }
      if (found)
         break;
      else{
         mutexBlocks->UnLock();

         fWaitTime.Start(kFALSE);
         fReadBlockAdded->Wait(); //wait for a new block to be added
         fWaitTime.Stop();
      }
   }

   if (found){
      char *pBuff = blockObj->GetPtrToPiece(index);
      pBuff += (offset - blockObj->GetPos(index));
      memcpy(buf, pBuff, len);
   }
   mutexBlocks->UnLock();
   return found;
}

//____________________________________________________________________________________________
void TFilePrefetch::ReadBlock(Long64_t* offset, Int_t* len, Int_t nblock)
{
   // Create a TFPBlock object or recycle one and add it to the prefetchBlocks list.

   TFPBlock* block = CreateBlockObj(offset, len, nblock);
   AddPendingBlock(block);
}

//____________________________________________________________________________________________
void TFilePrefetch::AddPendingBlock(TFPBlock* block)
{
   // Safe method to add a block to the pendingList.

   TMutex *mutexBlocks = fMutexPendingList;
   TMutex *mutexCond = fNewBlockAdded->GetMutex();

   mutexBlocks->Lock();
   fPendingBlocks->Add(block);
   mutexBlocks->UnLock();

   mutexCond->Lock();
   fNewBlockAdded->Signal();
   mutexCond->UnLock();
}

//____________________________________________________________________________________________
TFPBlock* TFilePrefetch::GetPendingBlock()
{
   // Safe method to remove a block from the pendingList.

   TFPBlock* block = 0;
   TMutex *mutex = fMutexPendingList;
   mutex->Lock();

   if (fPendingBlocks->GetSize()){
      block = (TFPBlock*)fPendingBlocks->First();
      block = (TFPBlock*)fPendingBlocks->Remove(block);
   }
   mutex->UnLock();
   return block;
}

//____________________________________________________________________________________________
void TFilePrefetch::AddReadBlock(TFPBlock* block)
{
   // Safe method to add a block to the readList.

   TMutex *mutexCond = fReadBlockAdded->GetMutex();
   TMutex *mutex = fMutexReadList;
   mutex->Lock();

   if (fReadBlocks->GetSize() >= kMAX_READ_SIZE){
      TFPBlock* movedBlock = (TFPBlock*) fReadBlocks->First();
      movedBlock = (TFPBlock*)fReadBlocks->Remove(movedBlock);
      delete movedBlock;
      movedBlock = 0;
   }

   fReadBlocks->Add(block);
   mutex->UnLock();

   //signal the addition of a new block
   mutexCond->Lock();
   fReadBlockAdded->Signal();
   mutexCond->UnLock();
}

//____________________________________________________________________________________________
TFPBlock* TFilePrefetch::CreateBlockObj(Long64_t* offset, Int_t* len, Int_t noblock)
{
   // Create a new block or recycle an old one.

   TFPBlock* blockObj = 0;
   TMutex *mutex = fMutexReadList;

   mutex->Lock();

   if (fReadBlocks->GetSize() >= kMAX_READ_SIZE){
      blockObj = static_cast<TFPBlock*>(fReadBlocks->First());
      fReadBlocks->Remove(blockObj);
      mutex->UnLock();
      blockObj->ReallocBlock(offset, len, noblock);
   }
   else{
      mutex->UnLock();
      blockObj = new TFPBlock(offset, len, noblock);
   }
   return blockObj;
}

//____________________________________________________________________________________________
TThread* TFilePrefetch::GetThread() const
{
   // Return reference to the consumer thread.

   return fConsumer;
}

//____________________________________________________________________________________________
void TFilePrefetch::SetFile(TFile *file)
{
   // Change the file

   fFile = file;
}

//____________________________________________________________________________________________
Int_t TFilePrefetch::ThreadStart()
{
   // Used to start the consumer thread.
   int rc;
   fConsumer= new TThread((TThread::VoidRtnFunc_t) ThreadProc,
                          (void*) this);
   rc = fConsumer->Run();
   return rc;
}

//____________________________________________________________________________________________
TThread::VoidRtnFunc_t TFilePrefetch::ThreadProc(void* arg)
{
   // Execution loop of the consumer thread.

   TFilePrefetch* pClass = (TFilePrefetch*) arg;
   TMutex *mutex = pClass->fCondNextFile->GetMutex();

   pClass->fNewBlockAdded->Wait();

   while( pClass->fSemMasterWorker->TryWait() != 0 ) {

      pClass->ReadListOfBlocks();

      //need to signal TChain that we finished work
      //in the previous file, before we move on
      mutex->Lock();
      pClass->fCondNextFile->Signal();
      mutex->UnLock();

      pClass->fNewBlockAdded->Wait();
   }

   pClass->fSemWorkerMaster->Post();
   return (TThread::VoidRtnFunc_t) 1;
}

//############################# CACHING PART ###################################

//____________________________________________________________________________________________
Int_t TFilePrefetch::SumHex(const char *hex)
{
   // Sum up individual hex values to obtain a decimal value.

   Int_t result = 0;
   const char* ptr = hex;

   for(Int_t i=0; i < (Int_t)strlen(hex); i++)
      result += xtod(ptr[i]);

   return result;
}

//____________________________________________________________________________________________
Bool_t TFilePrefetch::CheckBlockInCache(char*& path, TFPBlock* block)
{
   // Test if the block is in cache.

   if (fPathCache == "")
      return false;

   Bool_t found = false;
   TString fullPath(fPathCache); // path of the cached files.

   Int_t value = 0;

   if (gSystem->OpenDirectory(fullPath) == 0)
      gSystem->mkdir(fullPath);

   //dir is SHA1 value modulo 16; filename is the value of the SHA1(offset+len)
   TMD5* md = new TMD5();

   TString concatStr;
   for (Int_t i=0; i < block->GetNoElem(); i++){
      concatStr.Form("%lld", block->GetPos(i));
      md->Update((UChar_t*)concatStr.Data(), concatStr.Length());
   }

   md->Final();
   TString fileName( md->AsString() );
   value = SumHex(fileName);
   value = value % 16;
   TString dirName;
   dirName.Form("%i", value);

   fullPath += "/" + dirName + "/" + fileName;

   FileStat_t stat;
   if (gSystem->GetPathInfo(fullPath, stat) == 0) {
      path = new char[fullPath.Length() + 1];
      strlcpy(path, fullPath,fullPath.Length() + 1);
      found = true;
   } else
      found = false;

   delete md;
   return found;
}

//____________________________________________________________________________________________
char* TFilePrefetch::GetBlockFromCache(const char* path, Int_t length)
{
   // Return a buffer from cache.

   char *buffer = 0;
   TString strPath = path;

   strPath += "?filetype=raw";
   TFile* file = new TFile(strPath);

   Double_t start = 0;
   if (gPerfStats != 0) start = TTimeStamp();

   buffer = (char*) calloc(length, sizeof(char));
   file->ReadBuffer(buffer, 0, length);

   fFile->fBytesRead  += length;
   fFile->fgBytesRead += length;
   fFile->SetReadCalls(fFile->GetReadCalls() + 1);
   fFile->fgReadCalls++;

   if (gMonitoringWriter)
      gMonitoringWriter->SendFileReadProgress(fFile);
   if (gPerfStats != 0) {
      gPerfStats->FileReadEvent(fFile, length, start);
   }

   file->Close();
   delete file;
   return buffer;
}

//____________________________________________________________________________________________
void TFilePrefetch::SaveBlockInCache(TFPBlock* block)
{
   // Save the block content in cache.

   if (fPathCache == "")
      return;

   //dir is SHA1 value modulo 16; filename is the value of the SHA1
   TMD5* md = new TMD5();

   TString concatStr;
   for(Int_t i=0; i< block->GetNoElem(); i++){
      concatStr.Form("%lld", block->GetPos(i));
      md->Update((UChar_t*)concatStr.Data(), concatStr.Length());
   }
   md->Final();

   TString fileName( md->AsString() );
   Int_t value = SumHex(fileName);
   value = value % 16;

   TString fullPath( fPathCache );
   TString dirName;
   dirName.Form("%i", value);
   fullPath += ("/" + dirName);

   if (gSystem->OpenDirectory(fullPath) == false)
      gSystem->mkdir(fullPath);

   TFile* file = 0;
   fullPath += ("/" + fileName);
   FileStat_t stat;
   if (gSystem->GetPathInfo(fullPath, stat) == 0) {
      fullPath += "?filetype=raw";
      file = TFile::Open(fullPath, "update");
   } else{
      fullPath += "?filetype=raw";
      file = TFile::Open(fullPath, "new");
   }

   if (file) {
      // coverity[unchecked_value] We do not print error message, have not error
      // return code and close the file anyway, not need to check the return value.
      file->WriteBuffer(block->GetBuffer(), block->GetDataSize());
      file->Close();
      delete file;
   }
   delete md;
}

//____________________________________________________________________________________________
Bool_t TFilePrefetch::CheckCachePath(const char* locationCache)
{
   // Validate the input file cache path.

   Bool_t found = true;
   TString path = locationCache;
   Ssiz_t pos = path.Index(":/");

   if (pos > 0) {
      TSubString prot   = path(0, pos);
      TSubString dir  = path(pos + 2, path.Length());
      TString protocol(prot);
      TString directory(dir);

      for(Int_t i=0; i < directory.Sizeof()-1; i++)
         if (!isdigit(directory[i]) && !isalpha(directory[i]) && directory[i] !='/' && directory[i] != ':'){
            found = false;
            break;
         }
   } else
      found = false;

   return found;
}

//____________________________________________________________________________________________
Bool_t TFilePrefetch::SetCache(const char* path)
{
   // Set the path of the cache directory.

   if (CheckCachePath(path)){
      fPathCache = path;

      if (!gSystem->OpenDirectory(path)){
         gSystem->mkdir(path);
      }
   } else
      return false;
   return true;
}

<commit_msg>From Elvin:<commit_after>// @(#)root/io:$Id$
// Author: Elvin Sindrilaru   19/05/2011

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

#include "TFilePrefetch.h"
#include "TTimeStamp.h"
#include "TVirtualPerfStats.h"
#include "TVirtualMonitoring.h"

#include <iostream>
#include <string>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cctype>

static const int kMAX_READ_SIZE    = 2;   //maximum size of the read list of blocks

inline int xtod(char c) { return (c>='0' && c<='9') ? c-'0' : ((c>='A' && c<='F') ? c-'A'+10 : ((c>='a' && c<='f') ? c-'a'+10 : 0)); }

using namespace std;

ClassImp(TFilePrefetch)

//____________________________________________________________________________________________
TFilePrefetch::TFilePrefetch(TFile* file)
{
   // Constructor.

   fConsumer = 0;
   fFile = file;
   fPendingBlocks    = new TList();
   fReadBlocks       = new TList();
   fMutexReadList    = new TMutex();
   fMutexPendingList = new TMutex();
   fNewBlockAdded    = new TCondition(0);
   fReadBlockAdded   = new TCondition(0);
   fCondNextFile     = new TCondition(0);
   fSemMasterWorker  = new TSemaphore(0);
   fSemWorkerMaster  = new TSemaphore(0);
}

//____________________________________________________________________________________________
TFilePrefetch::~TFilePrefetch()
{
   // Destructor.

   //killing consumer thread
   fSemMasterWorker->Post();

   TMutex *mutexCond = fNewBlockAdded->GetMutex();
   while ( fSemWorkerMaster->Wait(10) != 0 ) {
      mutexCond->Lock();
      fNewBlockAdded->Signal();
      mutexCond->UnLock();
   }

   fConsumer->Join();

   SafeDelete(fConsumer);
   SafeDelete(fPendingBlocks);
   SafeDelete(fReadBlocks);
   SafeDelete(fMutexReadList);
   SafeDelete(fMutexPendingList);
   SafeDelete(fNewBlockAdded);
   SafeDelete(fReadBlockAdded);
   SafeDelete(fCondNextFile);
   SafeDelete(fSemMasterWorker);
   SafeDelete(fSemWorkerMaster);
}

//____________________________________________________________________________________________
void TFilePrefetch::ReadAsync(TFPBlock* block, Bool_t &inCache)
{
   // Read one block and insert it in prefetchBuffers list.

   char* path = 0;

   if (CheckBlockInCache(path, block)){
      block->SetBuffer(GetBlockFromCache(path, block->GetDataSize()));
      inCache = kTRUE;
   }
   else{
      fFile->ReadBuffers(block->GetBuffer(), block->GetPos(), block->GetLen(), block->GetNoElem());
      if (fFile->GetArchive()) {
         for (Int_t i = 0; i < block->GetNoElem(); i++)
            block->SetPos(i, block->GetPos(i) - fFile->GetArchiveOffset());
      }
      inCache =kFALSE;
   }
   delete[] path;
}

//____________________________________________________________________________________________
void TFilePrefetch::ReadListOfBlocks()
{
   // Get blocks specified in prefetchBlocks.

   Bool_t inCache = kFALSE;
   TFPBlock*  block = 0;

   while((block = GetPendingBlock())){
      ReadAsync(block, inCache);
      AddReadBlock(block);
      if (!inCache)
         SaveBlockInCache(block);
   }
}

//____________________________________________________________________________________________
Bool_t TFilePrefetch::BinarySearchReadList(TFPBlock* blockObj, Long64_t offset, Int_t len, Int_t* index)
{
   // Search for a requested element in a block and return the index.

   Int_t first = 0, last = -1, mid = -1;
   last = (Int_t) blockObj->GetNoElem()-1;

   while (first <= last){
      mid = first + (last - first) / 2;
      if ((offset >= blockObj->GetPos(mid) && offset <= (blockObj->GetPos(mid) + blockObj->GetLen(mid))
           && ( (offset + len) <= blockObj->GetPos(mid) + blockObj->GetLen(mid)))){

         *index = mid;
         return true;
      }
      else if (blockObj->GetPos(mid) < offset){
         first = mid + 1;
      }
      else{
         last = mid - 1;
      }
   }
   return false;
}

//____________________________________________________________________________________________
Long64_t TFilePrefetch::GetWaitTime()
{
   // Return the time spent wating for buffer to be read in microseconds.

   return Long64_t(fWaitTime.RealTime()*1.e+6);
}

//____________________________________________________________________________________________
Bool_t TFilePrefetch::ReadBuffer(char* buf, Long64_t offset, Int_t len)
{
   // Return a prefetched element.

   Bool_t found = false;
   TFPBlock* blockObj = 0;
   TMutex *mutexBlocks = fMutexReadList;
   Int_t index = -1;

   while (1){
      mutexBlocks->Lock();
      TIter iter(fReadBlocks);
      while ((blockObj = (TFPBlock*) iter.Next())){
         index = -1;
         if (BinarySearchReadList(blockObj, offset, len, &index)){
            found = true;
            break;
         }
      }
      if (found)
         break;
      else{
         mutexBlocks->UnLock();

         fWaitTime.Start(kFALSE);
         fReadBlockAdded->Wait(); //wait for a new block to be added
         fWaitTime.Stop();
      }
   }

   if (found){
      char *pBuff = blockObj->GetPtrToPiece(index);
      pBuff += (offset - blockObj->GetPos(index));
      memcpy(buf, pBuff, len);
   }
   mutexBlocks->UnLock();
   return found;
}

//____________________________________________________________________________________________
void TFilePrefetch::ReadBlock(Long64_t* offset, Int_t* len, Int_t nblock)
{
   // Create a TFPBlock object or recycle one and add it to the prefetchBlocks list.

   TFPBlock* block = CreateBlockObj(offset, len, nblock);
   AddPendingBlock(block);
}

//____________________________________________________________________________________________
void TFilePrefetch::AddPendingBlock(TFPBlock* block)
{
   // Safe method to add a block to the pendingList.

   TMutex *mutexBlocks = fMutexPendingList;
   TMutex *mutexCond = fNewBlockAdded->GetMutex();

   mutexBlocks->Lock();
   fPendingBlocks->Add(block);
   mutexBlocks->UnLock();

   mutexCond->Lock();
   fNewBlockAdded->Signal();
   mutexCond->UnLock();
}

//____________________________________________________________________________________________
TFPBlock* TFilePrefetch::GetPendingBlock()
{
   // Safe method to remove a block from the pendingList.

   TFPBlock* block = 0;
   TMutex *mutex = fMutexPendingList;
   mutex->Lock();

   if (fPendingBlocks->GetSize()){
      block = (TFPBlock*)fPendingBlocks->First();
      block = (TFPBlock*)fPendingBlocks->Remove(block);
   }
   mutex->UnLock();
   return block;
}

//____________________________________________________________________________________________
void TFilePrefetch::AddReadBlock(TFPBlock* block)
{
   // Safe method to add a block to the readList.

   TMutex *mutexCond = fReadBlockAdded->GetMutex();
   TMutex *mutex = fMutexReadList;
   mutex->Lock();

   if (fReadBlocks->GetSize() >= kMAX_READ_SIZE){
      TFPBlock* movedBlock = (TFPBlock*) fReadBlocks->First();
      movedBlock = (TFPBlock*)fReadBlocks->Remove(movedBlock);
      delete movedBlock;
      movedBlock = 0;
   }

   fReadBlocks->Add(block);
   mutex->UnLock();

   //signal the addition of a new block
   mutexCond->Lock();
   fReadBlockAdded->Signal();
   mutexCond->UnLock();
}

//____________________________________________________________________________________________
TFPBlock* TFilePrefetch::CreateBlockObj(Long64_t* offset, Int_t* len, Int_t noblock)
{
   // Create a new block or recycle an old one.

   TFPBlock* blockObj = 0;
   TMutex *mutex = fMutexReadList;

   mutex->Lock();

   if (fReadBlocks->GetSize() >= kMAX_READ_SIZE){
      blockObj = static_cast<TFPBlock*>(fReadBlocks->First());
      fReadBlocks->Remove(blockObj);
      mutex->UnLock();
      blockObj->ReallocBlock(offset, len, noblock);
   }
   else{
      mutex->UnLock();
      blockObj = new TFPBlock(offset, len, noblock);
   }
   return blockObj;
}

//____________________________________________________________________________________________
TThread* TFilePrefetch::GetThread() const
{
   // Return reference to the consumer thread.

   return fConsumer;
}

//____________________________________________________________________________________________
void TFilePrefetch::SetFile(TFile *file)
{
   // Change the file

   fFile = file;
}

//____________________________________________________________________________________________
Int_t TFilePrefetch::ThreadStart()
{
   // Used to start the consumer thread.
   int rc;
   fConsumer= new TThread((TThread::VoidRtnFunc_t) ThreadProc,
                          (void*) this);
   rc = fConsumer->Run();
   return rc;
}

//____________________________________________________________________________________________
TThread::VoidRtnFunc_t TFilePrefetch::ThreadProc(void* arg)
{
   // Execution loop of the consumer thread.

   int val = 0;
   TFilePrefetch* pClass = (TFilePrefetch*) arg;
   TMutex *mutexNextFile = pClass->fCondNextFile->GetMutex();
   TMutex *mutexPendingList = pClass->fMutexPendingList;

   while ( pClass->fNewBlockAdded->TimedWaitRelative(50) != 0 ) {
      // Deal with the situation in which the signal was already sent
      // before entering the wait logic
      mutexPendingList->Lock();
      val = pClass->fPendingBlocks->GetSize();
      mutexPendingList->UnLock();

      if ( val ) {
         break;
      }
   }

   while( pClass->fSemMasterWorker->TryWait() != 0 ) {
      pClass->ReadListOfBlocks();

     // Need to signal TChain that we finished work
     // in the previous file, before we move on
     mutexNextFile->Lock();
     pClass->fCondNextFile->Signal();
     mutexNextFile->UnLock();

     while ( pClass->fNewBlockAdded->TimedWaitRelative(50) != 0 ) {
        // Deal with the situation in which the signal was already sent
        // before entering the wait logic
        mutexPendingList->Lock();
        val = pClass->fPendingBlocks->GetSize();
        mutexPendingList->UnLock();
        if ( val ) {
           break;
       }
     }
   }

   pClass->fSemWorkerMaster->Post();
   return (TThread::VoidRtnFunc_t) 1;
}

//############################# CACHING PART ###################################

//____________________________________________________________________________________________
Int_t TFilePrefetch::SumHex(const char *hex)
{
   // Sum up individual hex values to obtain a decimal value.

   Int_t result = 0;
   const char* ptr = hex;

   for(Int_t i=0; i < (Int_t)strlen(hex); i++)
      result += xtod(ptr[i]);

   return result;
}

//____________________________________________________________________________________________
Bool_t TFilePrefetch::CheckBlockInCache(char*& path, TFPBlock* block)
{
   // Test if the block is in cache.

   if (fPathCache == "")
      return false;

   Bool_t found = false;
   TString fullPath(fPathCache); // path of the cached files.

   Int_t value = 0;

   if (gSystem->OpenDirectory(fullPath) == 0)
      gSystem->mkdir(fullPath);

   //dir is SHA1 value modulo 16; filename is the value of the SHA1(offset+len)
   TMD5* md = new TMD5();

   TString concatStr;
   for (Int_t i=0; i < block->GetNoElem(); i++){
      concatStr.Form("%lld", block->GetPos(i));
      md->Update((UChar_t*)concatStr.Data(), concatStr.Length());
   }

   md->Final();
   TString fileName( md->AsString() );
   value = SumHex(fileName);
   value = value % 16;
   TString dirName;
   dirName.Form("%i", value);

   fullPath += "/" + dirName + "/" + fileName;

   FileStat_t stat;
   if (gSystem->GetPathInfo(fullPath, stat) == 0) {
      path = new char[fullPath.Length() + 1];
      strlcpy(path, fullPath,fullPath.Length() + 1);
      found = true;
   } else
      found = false;

   delete md;
   return found;
}

//____________________________________________________________________________________________
char* TFilePrefetch::GetBlockFromCache(const char* path, Int_t length)
{
   // Return a buffer from cache.

   char *buffer = 0;
   TString strPath = path;

   strPath += "?filetype=raw";
   TFile* file = new TFile(strPath);

   Double_t start = 0;
   if (gPerfStats != 0) start = TTimeStamp();

   buffer = (char*) calloc(length, sizeof(char));
   file->ReadBuffer(buffer, 0, length);

   fFile->fBytesRead  += length;
   fFile->fgBytesRead += length;
   fFile->SetReadCalls(fFile->GetReadCalls() + 1);
   fFile->fgReadCalls++;

   if (gMonitoringWriter)
      gMonitoringWriter->SendFileReadProgress(fFile);
   if (gPerfStats != 0) {
      gPerfStats->FileReadEvent(fFile, length, start);
   }

   file->Close();
   delete file;
   return buffer;
}

//____________________________________________________________________________________________
void TFilePrefetch::SaveBlockInCache(TFPBlock* block)
{
   // Save the block content in cache.

   if (fPathCache == "")
      return;

   //dir is SHA1 value modulo 16; filename is the value of the SHA1
   TMD5* md = new TMD5();

   TString concatStr;
   for(Int_t i=0; i< block->GetNoElem(); i++){
      concatStr.Form("%lld", block->GetPos(i));
      md->Update((UChar_t*)concatStr.Data(), concatStr.Length());
   }
   md->Final();

   TString fileName( md->AsString() );
   Int_t value = SumHex(fileName);
   value = value % 16;

   TString fullPath( fPathCache );
   TString dirName;
   dirName.Form("%i", value);
   fullPath += ("/" + dirName);

   if (gSystem->OpenDirectory(fullPath) == false)
      gSystem->mkdir(fullPath);

   TFile* file = 0;
   fullPath += ("/" + fileName);
   FileStat_t stat;
   if (gSystem->GetPathInfo(fullPath, stat) == 0) {
      fullPath += "?filetype=raw";
      file = TFile::Open(fullPath, "update");
   } else{
      fullPath += "?filetype=raw";
      file = TFile::Open(fullPath, "new");
   }

   if (file) {
      // coverity[unchecked_value] We do not print error message, have not error
      // return code and close the file anyway, not need to check the return value.
      file->WriteBuffer(block->GetBuffer(), block->GetDataSize());
      file->Close();
      delete file;
   }
   delete md;
}

//____________________________________________________________________________________________
Bool_t TFilePrefetch::CheckCachePath(const char* locationCache)
{
   // Validate the input file cache path.

   Bool_t found = true;
   TString path = locationCache;
   Ssiz_t pos = path.Index(":/");

   if (pos > 0) {
      TSubString prot   = path(0, pos);
      TSubString dir  = path(pos + 2, path.Length());
      TString protocol(prot);
      TString directory(dir);

      for(Int_t i=0; i < directory.Sizeof()-1; i++)
         if (!isdigit(directory[i]) && !isalpha(directory[i]) && directory[i] !='/' && directory[i] != ':'){
            found = false;
            break;
         }
   } else
      found = false;

   return found;
}

//____________________________________________________________________________________________
Bool_t TFilePrefetch::SetCache(const char* path)
{
   // Set the path of the cache directory.

   if (CheckCachePath(path)){
      fPathCache = path;

      if (!gSystem->OpenDirectory(path)){
         gSystem->mkdir(path);
      }
   } else
      return false;
   return true;
}

<|endoftext|>


<commit_before>// @(#)root/reflex:$Name:  $:$Id: ScopeBase.cxx,v 1.9 2006/04/20 17:18:23 roiser Exp $
// Author: Stefan Roiser 2004

// Copyright CERN, CH-1211 Geneva 23, 2004-2006, All rights reserved.
//
// Permission to use, copy, modify, and distribute this software for any
// purpose is hereby granted without fee, provided that this copyright and
// permissions notice appear in all copies and derivatives.
//
// This software is provided "as is" without express or implied warranty.

#ifndef REFLEX_BUILD
#define REFLEX_BUILD
#endif

#include "Reflex/Scope.h"

#include "Reflex/ScopeBase.h"

#include "Reflex/PropertyList.h"
#include "Reflex/Type.h"
#include "Reflex/Member.h"
#include "Reflex/ScopeName.h"
#include "Reflex/TypeTemplate.h"
#include "Reflex/MemberTemplate.h"
#include "Reflex/Tools.h"

#include "Class.h"
#include "Namespace.h"
#include "DataMember.h"
#include "FunctionMember.h"
#include "Union.h"
#include "Enum.h"
#include "NameLookup.h"

//-------------------------------------------------------------------------------
ROOT::Reflex::ScopeBase::ScopeBase( const char * scope,
                                    TYPE scopeType )
   : fMembers( Members() ),
     fDataMembers( Members() ),
     fFunctionMembers( Members() ),
     fScopeName( 0 ),
     fScopeType( scopeType ),
     fDeclaringScope( Scope() ),
     fSubScopes( std::vector<Scope>() ),
     fSubTypes( std::vector<Type>() ),
     fTypeTemplates( std::vector<TypeTemplate>() ),
     fMemberTemplates( std::vector<MemberTemplate>() ),
     fPropertyList( PropertyList( new PropertyListImpl())),
     fBasePosition( Tools::GetBasePosition( scope )) {
//-------------------------------------------------------------------------------

   std::string sname(scope);

   std::string declScope = "";
   std::string currScope = sname;

   if ( fBasePosition ) {
      declScope = sname.substr( 0, fBasePosition-2);
      currScope = std::string(sname, fBasePosition);
   }

   // Construct Scope
   Scope scopePtr = Scope::ByName(sname);
   if ( scopePtr.Id() == 0 ) {
      // create a new Scope
      fScopeName = new ScopeName(scope, this);
   }
   else {
      fScopeName = (ScopeName*)scopePtr.Id();
      fScopeName->fScopeBase = this;
   }

   Scope declScopePtr = Scope::ByName(declScope);
   if ( declScopePtr.Id() == 0 ) {
      if ( scopeType == NAMESPACE ) declScopePtr = (new Namespace( declScope.c_str() ))->ThisScope();
      else                          declScopePtr = (new ScopeName( declScope.c_str(), 0 ))->ThisScope();
   }

   // Set declaring Scope and sub-scopes
   fDeclaringScope = declScopePtr;
   if ( fDeclaringScope )  fDeclaringScope.AddSubScope( this->ThisScope() );
}

//-------------------------------------------------------------------------------
ROOT::Reflex::ScopeBase::ScopeBase()
   : fMembers( Members()),
     fDataMembers( Members()),
     fFunctionMembers( Members()),
     fScopeName( 0 ),
     fScopeType( NAMESPACE ),
     fDeclaringScope( Scope::fg__NIRVANA__ ),
     fSubScopes( std::vector<Scope>()),
     fSubTypes( std::vector<Type>()),
     fPropertyList( PropertyList() ),
     fBasePosition( 0 ) {
//-------------------------------------------------------------------------------
   fScopeName = new ScopeName("", this);
   fPropertyList.AddProperty("Description", "global namespace");
}

//-------------------------------------------------------------------------------
ROOT::Reflex::ScopeBase::~ScopeBase( ) {
//-------------------------------------------------------------------------------
   // Informing Scope that I am going away
   if ( fScopeName->fScopeBase == this ) fScopeName->fScopeBase = 0;

   // Informing declaring Scope that I am going to do away
   if ( fDeclaringScope ) {
      fDeclaringScope.RemoveSubScope(ThisScope());
   }

   fPropertyList.ClearProperties();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::ScopeBase::operator ROOT::Reflex::Scope () const {
//-------------------------------------------------------------------------------
   return Scope( fScopeName );
}

//-------------------------------------------------------------------------------
ROOT::Reflex::ScopeBase::operator ROOT::Reflex::Type() const {
//-------------------------------------------------------------------------------
   switch ( fScopeType ) {
   case CLASS:
   case TYPETEMPLATEINSTANCE:
   case UNION:
   case ENUM:
      return *(dynamic_cast<const TypeBase*>(this));
   default:
      return Type();
   }
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Base ROOT::Reflex::ScopeBase::BaseAt( size_t /* nth */ ) const {
//-------------------------------------------------------------------------------
   return Base();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Member
ROOT::Reflex::ScopeBase::DataMemberAt( size_t nth ) const {
//-------------------------------------------------------------------------------
   if ( nth < fDataMembers.size() ) return fDataMembers[ nth ];
   return Member();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Member
ROOT::Reflex::ScopeBase::DataMemberByName( const std::string & nam ) const {
//-------------------------------------------------------------------------------
   for ( Members::const_iterator it = fDataMembers.begin(); it != fDataMembers.end(); ++it) {
      if (it->Name() == nam) return (*it);
   }
   return Member();
}

//-------------------------------------------------------------------------------
size_t ROOT::Reflex::ScopeBase::DataMemberSize() const {
//-------------------------------------------------------------------------------
   return fDataMembers.size();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Member
ROOT::Reflex::ScopeBase::FunctionMemberAt( size_t nth ) const {
//-------------------------------------------------------------------------------
   if ( nth < fFunctionMembers.size() ) return fFunctionMembers[ nth ];
   return Member();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Member
ROOT::Reflex::ScopeBase::FunctionMemberByName( const std::string & name,
                                               const Type & signature ) const {
//-------------------------------------------------------------------------------
   for (Members::const_iterator it = fFunctionMembers.begin(); it != fFunctionMembers.end(); ++it ) {
      if (it->Name() == name) {
         if (signature) {
           if (signature.IsEquivalentTo(it->TypeOf())) return (*it);
         }
         else {
            return (*it);
         }
      }
   }
   return Member();
}

//-------------------------------------------------------------------------------
size_t ROOT::Reflex::ScopeBase::FunctionMemberSize() const {
//-------------------------------------------------------------------------------
   return fFunctionMembers.size();
}

//-------------------------------------------------------------------------------
bool ROOT::Reflex::ScopeBase::IsTopScope() const {
//-------------------------------------------------------------------------------
   if ( fDeclaringScope == Scope::fg__NIRVANA__ ) return true;
   return false;
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Member
ROOT::Reflex::ScopeBase::LookupMember( const std::string & nam,
                                       const Scope & current ) const {
//-------------------------------------------------------------------------------
  return NameLookup::LookupMember( nam, current );
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Type
ROOT::Reflex::ScopeBase::LookupType( const std::string & nam,
                                     const Scope & current ) const {
//-------------------------------------------------------------------------------
   return NameLookup::LookupType( nam, current );
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Member ROOT::Reflex::ScopeBase::MemberAt( size_t nth ) const {
//-------------------------------------------------------------------------------
   if ( nth < fMembers.size() ) { return fMembers[ nth ]; };
   return Member();
}

//-------------------------------------------------------------------------------
size_t ROOT::Reflex::ScopeBase::MemberSize() const {
//-------------------------------------------------------------------------------
   return fMembers.size();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Member
ROOT::Reflex::ScopeBase::MemberByName( const std::string & name,
                                       const Type & signature ) const {
//-------------------------------------------------------------------------------
   if (signature) return FunctionMemberByName(name, signature);
   for ( size_t i = 0; i < fMembers.size() ; i++ ) {
      if ( fMembers[i].Name() == name ) return fMembers[i];
   }
   return Member();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::MemberTemplate ROOT::Reflex::ScopeBase::MemberTemplateAt( size_t nth ) const {
//-------------------------------------------------------------------------------
   if ( nth < fMemberTemplates.size() ) { return MemberTemplate(fMemberTemplates[ nth ]); }
   return MemberTemplate();
}

//-------------------------------------------------------------------------------
size_t ROOT::Reflex::ScopeBase::MemberTemplateSize() const {
//-------------------------------------------------------------------------------
   return fMemberTemplates.size();
}

//-------------------------------------------------------------------------------
std::string ROOT::Reflex::ScopeBase::Name( unsigned int mod ) const {
//-------------------------------------------------------------------------------
   if ( 0 != ( mod & ( SCOPED | S ))) return fScopeName->Name();
   return std::string(fScopeName->Name(), fBasePosition);
}

//-------------------------------------------------------------------------------
ROOT::Reflex::PropertyList ROOT::Reflex::ScopeBase::Properties() const {
//-------------------------------------------------------------------------------
   return fPropertyList;
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Scope ROOT::Reflex::ScopeBase::ThisScope() const {
//-------------------------------------------------------------------------------
   return fScopeName->ThisScope();
}

//-------------------------------------------------------------------------------
std::string ROOT::Reflex::ScopeBase::ScopeTypeAsString() const {
//-------------------------------------------------------------------------------
   switch ( fScopeType ) {
   case CLASS:
      return "CLASS";
      break;
   case TYPETEMPLATEINSTANCE:
      return "TYPETEMPLATEINSTANCE";
      break;
   case NAMESPACE:
      return "NAMESPACE";
      break;
   case ENUM:
      return "ENUM";
      break;
   case UNION:
      return "UNION";
      break;
   case UNRESOLVED:
      return "UNRESOLVED";
      break;
   default:
      return "Scope " + Name() + "is not assigned to a SCOPE";
   }
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Type ROOT::Reflex::ScopeBase::SubTypeAt( size_t nth ) const {
//-------------------------------------------------------------------------------
   if ( nth < fSubTypes.size() ) { return fSubTypes[ nth ]; }
   return Type();
}

//-------------------------------------------------------------------------------
size_t ROOT::Reflex::ScopeBase::SubTypeSize() const {
//-------------------------------------------------------------------------------
   return fSubTypes.size();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Type ROOT::Reflex::ScopeBase::TemplateArgumentAt( size_t /* nth */ ) const {
//-------------------------------------------------------------------------------
   return Type();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::TypeTemplate ROOT::Reflex::ScopeBase::SubTypeTemplateAt( size_t nth ) const {
//-------------------------------------------------------------------------------
   if ( nth < fTypeTemplates.size() ) { return TypeTemplate(fTypeTemplates[ nth ]); }
   return TypeTemplate();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::TypeTemplate ROOT::Reflex::ScopeBase::TemplateFamily() const {
//-------------------------------------------------------------------------------
   return TypeTemplate();
}

//-------------------------------------------------------------------------------
size_t ROOT::Reflex::ScopeBase::SubTypeTemplateSize() const {
//-------------------------------------------------------------------------------
   return fTypeTemplates.size();
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddDataMember( const Member & dm ) const {
//-------------------------------------------------------------------------------
   dm.SetScope( ThisScope() );
   fDataMembers.push_back( dm );
   fMembers.push_back( dm );
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddDataMember( const char * name,
                                             const Type & type,
                                             size_t offset,
                                             unsigned int modifiers ) const {
//-------------------------------------------------------------------------------
   AddDataMember(Member(new DataMember(name, type, offset, modifiers)));
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::RemoveDataMember( const Member & dm ) const {
//-------------------------------------------------------------------------------
   std::vector< Member >::iterator it;
   for ( it = fDataMembers.begin(); it != fDataMembers.end(); ++it) {
      if ( *it == dm ) fDataMembers.erase(it); break;
   }
   std::vector< Member >::iterator im;
   for ( im = fMembers.begin(); im != fMembers.end(); ++im) {
      if ( *im == dm ) fMembers.erase(im); break;
   }
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddFunctionMember( const Member & fm ) const {
//-------------------------------------------------------------------------------
   fm.SetScope( ThisScope());
   fFunctionMembers.push_back( fm );
   fMembers.push_back( fm );
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddFunctionMember( const char * name,
                                                 const Type & type,
                                                 StubFunction stubFP,
                                                 void * stubCtx,
                                                 const char * params,
                                                 unsigned int modifiers ) const {
//-------------------------------------------------------------------------------
   AddFunctionMember(Member(new FunctionMember(name, type, stubFP, stubCtx, params, modifiers)));
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::RemoveFunctionMember( const Member & fm ) const {
//-------------------------------------------------------------------------------
   std::vector< Member >::iterator it;
   for ( it = fFunctionMembers.begin(); it != fFunctionMembers.end(); ++it) {
      if ( *it == fm ) fFunctionMembers.erase(it); break;
   }
   std::vector< Member >::iterator im;
   for ( im = fMembers.begin(); im != fMembers.end(); ++im) {
      if ( *im == fm ) fMembers.erase(im); break;
   }
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddMemberTemplate( const MemberTemplate & mt ) const {
//-------------------------------------------------------------------------------
   fMemberTemplates.push_back( mt );
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::RemoveMemberTemplate( const MemberTemplate & mt ) const {
//-------------------------------------------------------------------------------
   std::vector< MemberTemplate >::iterator it;
   for ( it = fMemberTemplates.begin(); it != fMemberTemplates.end(); ++it ) {
      if ( *it == mt ) fMemberTemplates.erase(it); break;
   }
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddSubScope( const Scope & subscope ) const {
//-------------------------------------------------------------------------------
   RemoveSubScope(subscope);
   fSubScopes.push_back(subscope);
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddSubScope( const char * scope,
                                           TYPE scopeType ) const {
//-------------------------------------------------------------------------------
   AddSubScope(*(new ScopeBase( scope, scopeType )));
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::RemoveSubScope( const Scope & subscope ) const {
//-------------------------------------------------------------------------------
   std::vector< Scope >::iterator it;
   for ( it = fSubScopes.begin(); it != fSubScopes.end(); ++it) {
      if ( *it == subscope ) {
         fSubScopes.erase(it); break;
      }
   }
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddSubType( const Type & At ) const {
//-------------------------------------------------------------------------------
   RemoveSubType(At);
   fSubTypes.push_back(At);
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddSubType( const char * type,
                                          size_t size,
                                          TYPE typeType,
                                          const std::type_info & ti,
                                          unsigned int modifiers ) const {
//-------------------------------------------------------------------------------
   TypeBase * tb = 0;
   switch ( typeType ) {
   case CLASS:
      tb = new Class(type,size,ti,modifiers,STRUCT);
      break;
   case STRUCT:
      tb = new Class(type,size,ti,modifiers);
      break;
   case ENUM:
      tb = new Enum(type,ti);
      break;
   case FUNCTION:
      break;
   case ARRAY:
      break;
   case FUNDAMENTAL:
      break;
   case  POINTER:
      break;
   case POINTERTOMEMBER:
      break;
   case TYPEDEF:
      break;
   case UNION:
      tb = new Union(type,size,ti);
      break;
   default:
      tb = new TypeBase( type, size, typeType, ti );
   }
   if ( tb ) AddSubType( * tb );
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::RemoveSubType( const Type & At ) const {
//-------------------------------------------------------------------------------
   std::vector< Type >::iterator it;
   for ( it = fSubTypes.begin(); it != fSubTypes.end(); ++it) {
      if ( *it == At ) {
         fSubTypes.erase(it); break;
      }
   }
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddSubTypeTemplate( const TypeTemplate & tt ) const {
//-------------------------------------------------------------------------------
   fTypeTemplates.push_back( tt );
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::RemoveSubTypeTemplate( const TypeTemplate & tt ) const {
//-------------------------------------------------------------------------------
   std::vector< TypeTemplate >::iterator it;
   for ( it = fTypeTemplates.begin(); it != fTypeTemplates.end(); ++it) {
      if (*it == tt) {
         fTypeTemplates.erase(it); break;
      }
   }
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddUsingDirective( const Scope & ud ) const {
//-------------------------------------------------------------------------------
   fUsingDirectives.push_back( ud );
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::RemoveUsingDirective( const Scope & ud ) const {
//-------------------------------------------------------------------------------
   for ( Scope_Iterator it = UsingDirective_Begin(); it != UsingDirective_End(); ++ it ) {
      if ( *it == ud ) {
         fUsingDirectives.erase( it );
         break;
      }
   }
}

<commit_msg>fix conding conventions<commit_after>// @(#)root/reflex:$Name:  $:$Id: ScopeBase.cxx,v 1.10 2006/06/08 16:05:14 roiser Exp $
// Author: Stefan Roiser 2004

// Copyright CERN, CH-1211 Geneva 23, 2004-2006, All rights reserved.
//
// Permission to use, copy, modify, and distribute this software for any
// purpose is hereby granted without fee, provided that this copyright and
// permissions notice appear in all copies and derivatives.
//
// This software is provided "as is" without express or implied warranty.

#ifndef REFLEX_BUILD
#define REFLEX_BUILD
#endif

#include "Reflex/Scope.h"

#include "Reflex/ScopeBase.h"

#include "Reflex/PropertyList.h"
#include "Reflex/Type.h"
#include "Reflex/Member.h"
#include "Reflex/ScopeName.h"
#include "Reflex/TypeTemplate.h"
#include "Reflex/MemberTemplate.h"
#include "Reflex/Tools.h"

#include "Class.h"
#include "Namespace.h"
#include "DataMember.h"
#include "FunctionMember.h"
#include "Union.h"
#include "Enum.h"
#include "NameLookup.h"

//-------------------------------------------------------------------------------
ROOT::Reflex::ScopeBase::ScopeBase( const char * scope,
                                    TYPE scopeType )
   : fMembers( Members() ),
     fDataMembers( Members() ),
     fFunctionMembers( Members() ),
     fScopeName( 0 ),
     fScopeType( scopeType ),
     fDeclaringScope( Scope() ),
     fSubScopes( std::vector<Scope>() ),
     fSubTypes( std::vector<Type>() ),
     fTypeTemplates( std::vector<TypeTemplate>() ),
     fMemberTemplates( std::vector<MemberTemplate>() ),
     fPropertyList( PropertyList( new PropertyListImpl())),
     fBasePosition( Tools::GetBasePosition( scope )) {
//-------------------------------------------------------------------------------

   std::string sname(scope);

   std::string declScope = "";
   std::string currScope = sname;

   if ( fBasePosition ) {
      declScope = sname.substr( 0, fBasePosition-2);
      currScope = std::string(sname, fBasePosition);
   }

   // Construct Scope
   Scope scopePtr = Scope::ByName(sname);
   if ( scopePtr.Id() == 0 ) {
      // create a new Scope
      fScopeName = new ScopeName(scope, this);
   }
   else {
      fScopeName = (ScopeName*)scopePtr.Id();
      fScopeName->fScopeBase = this;
   }

   Scope declScopePtr = Scope::ByName(declScope);
   if ( declScopePtr.Id() == 0 ) {
      if ( scopeType == NAMESPACE ) declScopePtr = (new Namespace( declScope.c_str() ))->ThisScope();
      else                          declScopePtr = (new ScopeName( declScope.c_str(), 0 ))->ThisScope();
   }

   // Set declaring Scope and sub-scopes
   fDeclaringScope = declScopePtr;
   if ( fDeclaringScope )  fDeclaringScope.AddSubScope( this->ThisScope() );
}

//-------------------------------------------------------------------------------
ROOT::Reflex::ScopeBase::ScopeBase()
   : fMembers( Members()),
     fDataMembers( Members()),
     fFunctionMembers( Members()),
     fScopeName( 0 ),
     fScopeType( NAMESPACE ),
     fDeclaringScope( Scope::fg__NIRVANA__ ),
     fSubScopes( std::vector<Scope>()),
     fSubTypes( std::vector<Type>()),
     fPropertyList( PropertyList() ),
     fBasePosition( 0 ) {
//-------------------------------------------------------------------------------
   fScopeName = new ScopeName("", this);
   fPropertyList.AddProperty("Description", "global namespace");
}

//-------------------------------------------------------------------------------
ROOT::Reflex::ScopeBase::~ScopeBase( ) {
//-------------------------------------------------------------------------------
   // Informing Scope that I am going away
   if ( fScopeName->fScopeBase == this ) fScopeName->fScopeBase = 0;

   // Informing declaring Scope that I am going to do away
   if ( fDeclaringScope ) {
      fDeclaringScope.RemoveSubScope(ThisScope());
   }

   fPropertyList.ClearProperties();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::ScopeBase::operator ROOT::Reflex::Scope () const {
//-------------------------------------------------------------------------------
   return Scope( fScopeName );
}

//-------------------------------------------------------------------------------
ROOT::Reflex::ScopeBase::operator ROOT::Reflex::Type() const {
//-------------------------------------------------------------------------------
   switch ( fScopeType ) {
   case CLASS:
   case TYPETEMPLATEINSTANCE:
   case UNION:
   case ENUM:
      return *(dynamic_cast<const TypeBase*>(this));
   default:
      return Type();
   }
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Base ROOT::Reflex::ScopeBase::BaseAt( size_t /* nth */ ) const {
//-------------------------------------------------------------------------------
   return Base();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Member
ROOT::Reflex::ScopeBase::DataMemberAt( size_t nth ) const {
//-------------------------------------------------------------------------------
   if ( nth < fDataMembers.size() ) return fDataMembers[ nth ];
   return Member();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Member
ROOT::Reflex::ScopeBase::DataMemberByName( const std::string & nam ) const {
//-------------------------------------------------------------------------------
   for ( Members::const_iterator it = fDataMembers.begin(); it != fDataMembers.end(); ++it) {
      if (it->Name() == nam) return (*it);
   }
   return Member();
}

//-------------------------------------------------------------------------------
size_t ROOT::Reflex::ScopeBase::DataMemberSize() const {
//-------------------------------------------------------------------------------
   return fDataMembers.size();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Member
ROOT::Reflex::ScopeBase::FunctionMemberAt( size_t nth ) const {
//-------------------------------------------------------------------------------
   if ( nth < fFunctionMembers.size() ) return fFunctionMembers[ nth ];
   return Member();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Member
ROOT::Reflex::ScopeBase::FunctionMemberByName( const std::string & name,
                                               const Type & signature ) const {
//-------------------------------------------------------------------------------
   for (Members::const_iterator it = fFunctionMembers.begin(); it != fFunctionMembers.end(); ++it ) {
      if (it->Name() == name) {
         if (signature) {
           if (signature.IsEquivalentTo(it->TypeOf())) return (*it);
         }
         else {
            return (*it);
         }
      }
   }
   return Member();
}

//-------------------------------------------------------------------------------
size_t ROOT::Reflex::ScopeBase::FunctionMemberSize() const {
//-------------------------------------------------------------------------------
   return fFunctionMembers.size();
}

//-------------------------------------------------------------------------------
bool ROOT::Reflex::ScopeBase::IsTopScope() const {
//-------------------------------------------------------------------------------
   if ( fDeclaringScope == Scope::fg__NIRVANA__ ) return true;
   return false;
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Member
ROOT::Reflex::ScopeBase::LookupMember( const std::string & nam,
                                       const Scope & current ) const {
//-------------------------------------------------------------------------------
   return NameLookup::LookupMember( nam, current );
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Type
ROOT::Reflex::ScopeBase::LookupType( const std::string & nam,
                                     const Scope & current ) const {
//-------------------------------------------------------------------------------
   return NameLookup::LookupType( nam, current );
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Member ROOT::Reflex::ScopeBase::MemberAt( size_t nth ) const {
//-------------------------------------------------------------------------------
   if ( nth < fMembers.size() ) { return fMembers[ nth ]; };
   return Member();
}

//-------------------------------------------------------------------------------
size_t ROOT::Reflex::ScopeBase::MemberSize() const {
//-------------------------------------------------------------------------------
   return fMembers.size();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Member
ROOT::Reflex::ScopeBase::MemberByName( const std::string & name,
                                       const Type & signature ) const {
//-------------------------------------------------------------------------------
   if (signature) return FunctionMemberByName(name, signature);
   for ( size_t i = 0; i < fMembers.size() ; i++ ) {
      if ( fMembers[i].Name() == name ) return fMembers[i];
   }
   return Member();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::MemberTemplate ROOT::Reflex::ScopeBase::MemberTemplateAt( size_t nth ) const {
//-------------------------------------------------------------------------------
   if ( nth < fMemberTemplates.size() ) { return MemberTemplate(fMemberTemplates[ nth ]); }
   return MemberTemplate();
}

//-------------------------------------------------------------------------------
size_t ROOT::Reflex::ScopeBase::MemberTemplateSize() const {
//-------------------------------------------------------------------------------
   return fMemberTemplates.size();
}

//-------------------------------------------------------------------------------
std::string ROOT::Reflex::ScopeBase::Name( unsigned int mod ) const {
//-------------------------------------------------------------------------------
   if ( 0 != ( mod & ( SCOPED | S ))) return fScopeName->Name();
   return std::string(fScopeName->Name(), fBasePosition);
}

//-------------------------------------------------------------------------------
ROOT::Reflex::PropertyList ROOT::Reflex::ScopeBase::Properties() const {
//-------------------------------------------------------------------------------
   return fPropertyList;
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Scope ROOT::Reflex::ScopeBase::ThisScope() const {
//-------------------------------------------------------------------------------
   return fScopeName->ThisScope();
}

//-------------------------------------------------------------------------------
std::string ROOT::Reflex::ScopeBase::ScopeTypeAsString() const {
//-------------------------------------------------------------------------------
   switch ( fScopeType ) {
   case CLASS:
      return "CLASS";
      break;
   case TYPETEMPLATEINSTANCE:
      return "TYPETEMPLATEINSTANCE";
      break;
   case NAMESPACE:
      return "NAMESPACE";
      break;
   case ENUM:
      return "ENUM";
      break;
   case UNION:
      return "UNION";
      break;
   case UNRESOLVED:
      return "UNRESOLVED";
      break;
   default:
      return "Scope " + Name() + "is not assigned to a SCOPE";
   }
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Type ROOT::Reflex::ScopeBase::SubTypeAt( size_t nth ) const {
//-------------------------------------------------------------------------------
   if ( nth < fSubTypes.size() ) { return fSubTypes[ nth ]; }
   return Type();
}

//-------------------------------------------------------------------------------
size_t ROOT::Reflex::ScopeBase::SubTypeSize() const {
//-------------------------------------------------------------------------------
   return fSubTypes.size();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::Type ROOT::Reflex::ScopeBase::TemplateArgumentAt( size_t /* nth */ ) const {
//-------------------------------------------------------------------------------
   return Type();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::TypeTemplate ROOT::Reflex::ScopeBase::SubTypeTemplateAt( size_t nth ) const {
//-------------------------------------------------------------------------------
   if ( nth < fTypeTemplates.size() ) { return TypeTemplate(fTypeTemplates[ nth ]); }
   return TypeTemplate();
}

//-------------------------------------------------------------------------------
ROOT::Reflex::TypeTemplate ROOT::Reflex::ScopeBase::TemplateFamily() const {
//-------------------------------------------------------------------------------
   return TypeTemplate();
}

//-------------------------------------------------------------------------------
size_t ROOT::Reflex::ScopeBase::SubTypeTemplateSize() const {
//-------------------------------------------------------------------------------
   return fTypeTemplates.size();
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddDataMember( const Member & dm ) const {
//-------------------------------------------------------------------------------
   dm.SetScope( ThisScope() );
   fDataMembers.push_back( dm );
   fMembers.push_back( dm );
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddDataMember( const char * name,
                                             const Type & type,
                                             size_t offset,
                                             unsigned int modifiers ) const {
//-------------------------------------------------------------------------------
   AddDataMember(Member(new DataMember(name, type, offset, modifiers)));
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::RemoveDataMember( const Member & dm ) const {
//-------------------------------------------------------------------------------
   std::vector< Member >::iterator it;
   for ( it = fDataMembers.begin(); it != fDataMembers.end(); ++it) {
      if ( *it == dm ) fDataMembers.erase(it); break;
   }
   std::vector< Member >::iterator im;
   for ( im = fMembers.begin(); im != fMembers.end(); ++im) {
      if ( *im == dm ) fMembers.erase(im); break;
   }
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddFunctionMember( const Member & fm ) const {
//-------------------------------------------------------------------------------
   fm.SetScope( ThisScope());
   fFunctionMembers.push_back( fm );
   fMembers.push_back( fm );
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddFunctionMember( const char * name,
                                                 const Type & type,
                                                 StubFunction stubFP,
                                                 void * stubCtx,
                                                 const char * params,
                                                 unsigned int modifiers ) const {
//-------------------------------------------------------------------------------
   AddFunctionMember(Member(new FunctionMember(name, type, stubFP, stubCtx, params, modifiers)));
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::RemoveFunctionMember( const Member & fm ) const {
//-------------------------------------------------------------------------------
   std::vector< Member >::iterator it;
   for ( it = fFunctionMembers.begin(); it != fFunctionMembers.end(); ++it) {
      if ( *it == fm ) fFunctionMembers.erase(it); break;
   }
   std::vector< Member >::iterator im;
   for ( im = fMembers.begin(); im != fMembers.end(); ++im) {
      if ( *im == fm ) fMembers.erase(im); break;
   }
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddMemberTemplate( const MemberTemplate & mt ) const {
//-------------------------------------------------------------------------------
   fMemberTemplates.push_back( mt );
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::RemoveMemberTemplate( const MemberTemplate & mt ) const {
//-------------------------------------------------------------------------------
   std::vector< MemberTemplate >::iterator it;
   for ( it = fMemberTemplates.begin(); it != fMemberTemplates.end(); ++it ) {
      if ( *it == mt ) fMemberTemplates.erase(it); break;
   }
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddSubScope( const Scope & subscope ) const {
//-------------------------------------------------------------------------------
   RemoveSubScope(subscope);
   fSubScopes.push_back(subscope);
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddSubScope( const char * scope,
                                           TYPE scopeType ) const {
//-------------------------------------------------------------------------------
   AddSubScope(*(new ScopeBase( scope, scopeType )));
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::RemoveSubScope( const Scope & subscope ) const {
//-------------------------------------------------------------------------------
   std::vector< Scope >::iterator it;
   for ( it = fSubScopes.begin(); it != fSubScopes.end(); ++it) {
      if ( *it == subscope ) {
         fSubScopes.erase(it); break;
      }
   }
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddSubType( const Type & At ) const {
//-------------------------------------------------------------------------------
   RemoveSubType(At);
   fSubTypes.push_back(At);
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddSubType( const char * type,
                                          size_t size,
                                          TYPE typeType,
                                          const std::type_info & ti,
                                          unsigned int modifiers ) const {
//-------------------------------------------------------------------------------
   TypeBase * tb = 0;
   switch ( typeType ) {
   case CLASS:
      tb = new Class(type,size,ti,modifiers,STRUCT);
      break;
   case STRUCT:
      tb = new Class(type,size,ti,modifiers);
      break;
   case ENUM:
      tb = new Enum(type,ti);
      break;
   case FUNCTION:
      break;
   case ARRAY:
      break;
   case FUNDAMENTAL:
      break;
   case  POINTER:
      break;
   case POINTERTOMEMBER:
      break;
   case TYPEDEF:
      break;
   case UNION:
      tb = new Union(type,size,ti);
      break;
   default:
      tb = new TypeBase( type, size, typeType, ti );
   }
   if ( tb ) AddSubType( * tb );
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::RemoveSubType( const Type & At ) const {
//-------------------------------------------------------------------------------
   std::vector< Type >::iterator it;
   for ( it = fSubTypes.begin(); it != fSubTypes.end(); ++it) {
      if ( *it == At ) {
         fSubTypes.erase(it); break;
      }
   }
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddSubTypeTemplate( const TypeTemplate & tt ) const {
//-------------------------------------------------------------------------------
   fTypeTemplates.push_back( tt );
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::RemoveSubTypeTemplate( const TypeTemplate & tt ) const {
//-------------------------------------------------------------------------------
   std::vector< TypeTemplate >::iterator it;
   for ( it = fTypeTemplates.begin(); it != fTypeTemplates.end(); ++it) {
      if (*it == tt) {
         fTypeTemplates.erase(it); break;
      }
   }
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::AddUsingDirective( const Scope & ud ) const {
//-------------------------------------------------------------------------------
   fUsingDirectives.push_back( ud );
}

//-------------------------------------------------------------------------------
void ROOT::Reflex::ScopeBase::RemoveUsingDirective( const Scope & ud ) const {
//-------------------------------------------------------------------------------
   for ( Scope_Iterator it = UsingDirective_Begin(); it != UsingDirective_End(); ++ it ) {
      if ( *it == ud ) {
         fUsingDirectives.erase( it );
         break;
      }
   }
}

<|endoftext|>


<commit_before>#include "terminalpp/terminal.hpp"
#include "expect_sequence.hpp"
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>

using namespace terminalpp::literals;

class terminal_string_test_fixture : public CppUnit::TestFixture
{
public :
    CPPUNIT_TEST_SUITE(terminal_string_test_fixture);
        CPPUNIT_TEST(empty_string_outputs_nothing);
        CPPUNIT_TEST(basic_string_outputs_basic_string);

        CPPUNIT_TEST(changed_charset_outputs_charset_code);
        CPPUNIT_TEST(changed_charset_then_second_charset_outputs_charset_codes);

        CPPUNIT_TEST(bold_intensity_outputs_intensity);
        CPPUNIT_TEST(faint_intensity_outputs_intensity);
        CPPUNIT_TEST(normal_intensity_does_not_output_intensity);
        CPPUNIT_TEST(bold_then_normal_intensity_outputs_intensity);
        CPPUNIT_TEST(default_intensity_is_normal_intensity);

        CPPUNIT_TEST(positive_polarity_does_not_output_polarity);
        CPPUNIT_TEST(negative_polarity_outputs_polarity);
        CPPUNIT_TEST(negative_then_positive_polarity_outputs_polarity);
        CPPUNIT_TEST(default_polarity_is_positive_polarity);

        CPPUNIT_TEST(positive_underlining_outputs_underlining);
        CPPUNIT_TEST(negative_underlining_does_not_output_underlining);
        CPPUNIT_TEST(positive_then_negative_underlining_outputs_underlining);
        CPPUNIT_TEST(default_underlining_is_negative_underlining);

        CPPUNIT_TEST(writing_string_moves_cursor);
        CPPUNIT_TEST(writing_past_terminal_width_moves_cursor_to_next_line);
        CPPUNIT_TEST(writing_far_past_terminal_width_moves_multiple_lines);
        CPPUNIT_TEST(writing_past_last_line_scrolls_last_line);
    CPPUNIT_TEST_SUITE_END();

private :
    void empty_string_outputs_nothing();
    void basic_string_outputs_basic_string();

    void changed_charset_outputs_charset_code();
    void changed_charset_then_second_charset_outputs_charset_codes();

    void bold_intensity_outputs_intensity();
    void faint_intensity_outputs_intensity();
    void normal_intensity_does_not_output_intensity();
    void bold_then_normal_intensity_outputs_intensity();
    void default_intensity_is_normal_intensity();

    void positive_polarity_does_not_output_polarity();
    void negative_polarity_outputs_polarity();
    void negative_then_positive_polarity_outputs_polarity();
    void default_polarity_is_positive_polarity();

    void positive_underlining_outputs_underlining();
    void negative_underlining_does_not_output_underlining();
    void positive_then_negative_underlining_outputs_underlining();
    void default_underlining_is_negative_underlining();

    void writing_string_moves_cursor();
    void writing_past_terminal_width_moves_cursor_to_next_line();
    void writing_far_past_terminal_width_moves_multiple_lines();
    void writing_past_last_line_scrolls_last_line();
};

CPPUNIT_TEST_SUITE_REGISTRATION(terminal_string_test_fixture);

void terminal_string_test_fixture::empty_string_outputs_nothing()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string(""),
        terminal.write(""_ets));
}

void terminal_string_test_fixture::basic_string_outputs_basic_string()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("abcde"),
        terminal.write("abcde"_ets));
}

void terminal_string_test_fixture::changed_charset_outputs_charset_code()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B(0abcde"),
        terminal.write("\\c0abcde"_ets));
}

void terminal_string_test_fixture::changed_charset_then_second_charset_outputs_charset_codes()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B(0abc\x1B(Ade"),
        terminal.write("\\c0abc\\cAde"_ets));
}

void terminal_string_test_fixture::bold_intensity_outputs_intensity()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[1mabcde"),
        terminal.write("\\i>abcde"_ets));
}

void terminal_string_test_fixture::faint_intensity_outputs_intensity()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[2mabcde"),
        terminal.write("\\i<abcde"_ets));
}

void terminal_string_test_fixture::normal_intensity_does_not_output_intensity()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("abcde"),
        terminal.write("\\i=abcde"_ets));
}

void terminal_string_test_fixture::bold_then_normal_intensity_outputs_intensity()
{
    // Note: an alternative possible normal string would be
    // \x1B[22m, but since this is longer, \x1B[0m (all attributes to default)
    // should be chosen.
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[1mabc\x1B[0mde"),
        terminal.write("\\i>abc\\i=de"_ets));
}

void terminal_string_test_fixture::default_intensity_is_normal_intensity()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[1mabc\x1B[0mde"),
        terminal.write("\\i>abc\\ixde"_ets));
}

void terminal_string_test_fixture::positive_polarity_does_not_output_polarity()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("abcde"),
        terminal.write("\\p+abcde"_ets));
}

void terminal_string_test_fixture::negative_polarity_outputs_polarity()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[7mabcde"),
        terminal.write("\\p-abcde"_ets));
}

void terminal_string_test_fixture::negative_then_positive_polarity_outputs_polarity()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[7mabc\x1B[0mde"),
        terminal.write("\\p-abc\\p+de"_ets));
}

void terminal_string_test_fixture::default_polarity_is_positive_polarity()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[7mabc\x1B[0mde"),
        terminal.write("\\p-abc\\p=de"_ets));
}

void terminal_string_test_fixture::positive_underlining_outputs_underlining()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[4mabcde"),
        terminal.write("\\u+abcde"_ets));
}

void terminal_string_test_fixture::negative_underlining_does_not_output_underlining()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("abcde"),
        terminal.write("\\u-abcde"_ets));
}

void terminal_string_test_fixture::positive_then_negative_underlining_outputs_underlining()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[4mabc\x1B[0mde"),
        terminal.write("\\u+abc\\u-de"_ets));
}

void terminal_string_test_fixture::default_underlining_is_negative_underlining()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[4mabc\x1B[0mde"),
        terminal.write("\\u+abc\\u=de"_ets));
}

void terminal_string_test_fixture::writing_string_moves_cursor()
{
    terminalpp::terminal terminal;

    terminal.move_cursor({5, 5});
    terminal.write("abcde");

    expect_sequence(
        std::string(""),
        terminal.move_cursor({10, 5}));
}

void terminal_string_test_fixture::writing_past_terminal_width_moves_cursor_to_next_line()
{
    terminalpp::terminal terminal;
    terminal.set_size({10, 10});
    terminal.move_cursor({8, 8});
    terminal.write("abcde");

    expect_sequence(
        std::string(""),
        terminal.move_cursor({3, 9}));
}

void terminal_string_test_fixture::writing_far_past_terminal_width_moves_multiple_lines()
{
    terminalpp::terminal terminal;
    terminal.set_size({10, 10});
    terminal.move_cursor({8, 8});
    terminal.write("abcdefghijklmno");

    expect_sequence(
        std::string(""),
        terminal.move_cursor({3, 10}));
}

void terminal_string_test_fixture::writing_past_last_line_scrolls_last_line()
{
    terminalpp::terminal terminal;
    terminal.set_size({10, 10});
    terminal.move_cursor({8, 10});
    terminal.write("abcdefghijklmno");

    expect_sequence(
        std::string(""),
        terminal.move_cursor({3, 10}));
}

<commit_msg>Added foreground colour tests.<commit_after>#include "terminalpp/terminal.hpp"
#include "expect_sequence.hpp"
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>

using namespace terminalpp::literals;

class terminal_string_test_fixture : public CppUnit::TestFixture
{
public :
    CPPUNIT_TEST_SUITE(terminal_string_test_fixture);
        CPPUNIT_TEST(empty_string_outputs_nothing);
        CPPUNIT_TEST(basic_string_outputs_basic_string);

        CPPUNIT_TEST(changed_charset_outputs_charset_code);
        CPPUNIT_TEST(changed_charset_then_second_charset_outputs_charset_codes);

        CPPUNIT_TEST(bold_intensity_outputs_intensity);
        CPPUNIT_TEST(faint_intensity_outputs_intensity);
        CPPUNIT_TEST(normal_intensity_does_not_output_intensity);
        CPPUNIT_TEST(bold_then_normal_intensity_outputs_intensity);
        CPPUNIT_TEST(default_intensity_is_normal_intensity);

        CPPUNIT_TEST(positive_polarity_does_not_output_polarity);
        CPPUNIT_TEST(negative_polarity_outputs_polarity);
        CPPUNIT_TEST(negative_then_positive_polarity_outputs_polarity);
        CPPUNIT_TEST(default_polarity_is_positive_polarity);

        CPPUNIT_TEST(positive_underlining_outputs_underlining);
        CPPUNIT_TEST(negative_underlining_does_not_output_underlining);
        CPPUNIT_TEST(positive_then_negative_underlining_outputs_underlining);
        CPPUNIT_TEST(default_underlining_is_negative_underlining);

        CPPUNIT_TEST(foreground_low_colour_outputs_foreground_colour);
        CPPUNIT_TEST(foreground_high_colour_outputs_foreground_colour);
        CPPUNIT_TEST(foreground_greyscale_colour_outputs_foreground_colour);
        CPPUNIT_TEST(default_foreground_colour_does_not_output_foreground_colour);
        CPPUNIT_TEST(multiple_foreground_colour_codes_outputs_foreground_colours);

        CPPUNIT_TEST(writing_string_moves_cursor);
        CPPUNIT_TEST(writing_past_terminal_width_moves_cursor_to_next_line);
        CPPUNIT_TEST(writing_far_past_terminal_width_moves_multiple_lines);
        CPPUNIT_TEST(writing_past_last_line_scrolls_last_line);
    CPPUNIT_TEST_SUITE_END();

private :
    void empty_string_outputs_nothing();
    void basic_string_outputs_basic_string();

    void changed_charset_outputs_charset_code();
    void changed_charset_then_second_charset_outputs_charset_codes();

    void bold_intensity_outputs_intensity();
    void faint_intensity_outputs_intensity();
    void normal_intensity_does_not_output_intensity();
    void bold_then_normal_intensity_outputs_intensity();
    void default_intensity_is_normal_intensity();

    void positive_polarity_does_not_output_polarity();
    void negative_polarity_outputs_polarity();
    void negative_then_positive_polarity_outputs_polarity();
    void default_polarity_is_positive_polarity();

    void positive_underlining_outputs_underlining();
    void negative_underlining_does_not_output_underlining();
    void positive_then_negative_underlining_outputs_underlining();
    void default_underlining_is_negative_underlining();

    void foreground_low_colour_outputs_foreground_colour();
    void foreground_high_colour_outputs_foreground_colour();
    void foreground_greyscale_colour_outputs_foreground_colour();
    void default_foreground_colour_does_not_output_foreground_colour();
    void multiple_foreground_colour_codes_outputs_foreground_colours();

    void writing_string_moves_cursor();
    void writing_past_terminal_width_moves_cursor_to_next_line();
    void writing_far_past_terminal_width_moves_multiple_lines();
    void writing_past_last_line_scrolls_last_line();
};

CPPUNIT_TEST_SUITE_REGISTRATION(terminal_string_test_fixture);

void terminal_string_test_fixture::empty_string_outputs_nothing()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string(""),
        terminal.write(""_ets));
}

void terminal_string_test_fixture::basic_string_outputs_basic_string()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("abcde"),
        terminal.write("abcde"_ets));
}

void terminal_string_test_fixture::changed_charset_outputs_charset_code()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B(0abcde"),
        terminal.write("\\c0abcde"_ets));
}

void terminal_string_test_fixture::changed_charset_then_second_charset_outputs_charset_codes()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B(0abc\x1B(Ade"),
        terminal.write("\\c0abc\\cAde"_ets));
}

void terminal_string_test_fixture::bold_intensity_outputs_intensity()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[1mabcde"),
        terminal.write("\\i>abcde"_ets));
}

void terminal_string_test_fixture::faint_intensity_outputs_intensity()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[2mabcde"),
        terminal.write("\\i<abcde"_ets));
}

void terminal_string_test_fixture::normal_intensity_does_not_output_intensity()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("abcde"),
        terminal.write("\\i=abcde"_ets));
}

void terminal_string_test_fixture::bold_then_normal_intensity_outputs_intensity()
{
    // Note: an alternative possible normal string would be
    // \x1B[22m, but since this is longer, \x1B[0m (all attributes to default)
    // should be chosen.
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[1mabc\x1B[0mde"),
        terminal.write("\\i>abc\\i=de"_ets));
}

void terminal_string_test_fixture::default_intensity_is_normal_intensity()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[1mabc\x1B[0mde"),
        terminal.write("\\i>abc\\ixde"_ets));
}

void terminal_string_test_fixture::positive_polarity_does_not_output_polarity()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("abcde"),
        terminal.write("\\p+abcde"_ets));
}

void terminal_string_test_fixture::negative_polarity_outputs_polarity()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[7mabcde"),
        terminal.write("\\p-abcde"_ets));
}

void terminal_string_test_fixture::negative_then_positive_polarity_outputs_polarity()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[7mabc\x1B[0mde"),
        terminal.write("\\p-abc\\p+de"_ets));
}

void terminal_string_test_fixture::default_polarity_is_positive_polarity()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[7mabc\x1B[0mde"),
        terminal.write("\\p-abc\\p=de"_ets));
}

void terminal_string_test_fixture::positive_underlining_outputs_underlining()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[4mabcde"),
        terminal.write("\\u+abcde"_ets));
}

void terminal_string_test_fixture::negative_underlining_does_not_output_underlining()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("abcde"),
        terminal.write("\\u-abcde"_ets));
}

void terminal_string_test_fixture::positive_then_negative_underlining_outputs_underlining()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[4mabc\x1B[0mde"),
        terminal.write("\\u+abc\\u-de"_ets));
}

void terminal_string_test_fixture::default_underlining_is_negative_underlining()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[4mabc\x1B[0mde"),
        terminal.write("\\u+abc\\u=de"_ets));
}

void terminal_string_test_fixture::foreground_low_colour_outputs_foreground_colour()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[32mabc"),
        terminal.write("\\[2abc"_ets));
}

void terminal_string_test_fixture::foreground_high_colour_outputs_foreground_colour()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[38;5;202mabc"),
        terminal.write("\\<510abc"_ets));
}

void terminal_string_test_fixture::foreground_greyscale_colour_outputs_foreground_colour()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[38;5;244mabc"),
        terminal.write("\\{12abc"_ets));
}

void terminal_string_test_fixture::default_foreground_colour_does_not_output_foreground_colour()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("abc"),
        terminal.write("\\[9abc"_ets));
}

void terminal_string_test_fixture::multiple_foreground_colour_codes_outputs_foreground_colours()
{
    terminalpp::terminal terminal;

    expect_sequence(
        std::string("\x1B[32mab\x1B[38;5;202mcd\x1B[38;5;234mef\x1B[0mgh"),
        terminal.write("\\[2ab\\<510cd\\{02ef\\[9gh"_ets));
}

void terminal_string_test_fixture::writing_string_moves_cursor()
{
    terminalpp::terminal terminal;

    terminal.move_cursor({5, 5});
    terminal.write("abcde");

    expect_sequence(
        std::string(""),
        terminal.move_cursor({10, 5}));
}

void terminal_string_test_fixture::writing_past_terminal_width_moves_cursor_to_next_line()
{
    terminalpp::terminal terminal;
    terminal.set_size({10, 10});
    terminal.move_cursor({8, 8});
    terminal.write("abcde");

    expect_sequence(
        std::string(""),
        terminal.move_cursor({3, 9}));
}

void terminal_string_test_fixture::writing_far_past_terminal_width_moves_multiple_lines()
{
    terminalpp::terminal terminal;
    terminal.set_size({10, 10});
    terminal.move_cursor({8, 8});
    terminal.write("abcdefghijklmno");

    expect_sequence(
        std::string(""),
        terminal.move_cursor({3, 10}));
}

void terminal_string_test_fixture::writing_past_last_line_scrolls_last_line()
{
    terminalpp::terminal terminal;
    terminal.set_size({10, 10});
    terminal.move_cursor({8, 10});
    terminal.write("abcdefghijklmno");

    expect_sequence(
        std::string(""),
        terminal.move_cursor({3, 10}));
}

<|endoftext|>