<commit_before>#include <vltPort.h>

#include <cdbDALaccess.h>
#include <logging.h>
#include <ace/ARGV.h>

#ifdef MAKE_VXWORKS
#	include "rebootLib.h"
#	include "acsutilArgUnpack.h"
//******************************************************************************
//*    ALMA - Atacama Large Millimiter Array
//*    (c) European Southern Observatory, 2002
//*    Copyright by ESO (in the framework of the ALMA collaboration)
//*    and Cosylab 2002, All rights reserved
//*
//*    This library is free software; you can redistribute it and/or
//*    modify it under the terms of the GNU Lesser General Public
//*    License as published by the Free Software Foundation; either
//*    version 2.1 of the License, or (at your option) any later version.
//*
//*    This library is distributed in the hope that it will be useful,
//*    but WITHOUT ANY WARRANTY; without even the implied warranty of
//*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//*    Lesser General Public License for more details.
//*
//*    You should have received a copy of the GNU Lesser General Public
//*    License along with this library; if not, write to the Free Software
//*    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
//*
int startCdbTest (char *szCmdLn)
{
    int  argc;
    char *argv[100];

    argc = argUnpack(szCmdLn, argv);
    argv[0] = "cdbTest";
#else
int main(int argc, char* argv[])
{
#endif // defined( MAKE_VXWORKS )

try{
    CORBA::ULong ul;
    Field dbFld;
    Table* dataBase;
    ACE_CString curl, field;
    ACE_CString strCmdLine;
    ACE_CString strField;
    LoggingProxy logger(0, 0, 31);

    // initialize the rest of LoggingProxy
    LoggingProxy::ProcessName(argv[0]);
    LoggingProxy::ThreadName("main");

    LoggingProxy::init (&logger);

    ACS_SHORT_LOG ((LM_INFO, "cdbTestClient starts"));

    dataBase = getDatabase( argc, argv );

    curl = "MACI/Managers/Manager";

    // something for sure
    field = "CentralizedLogger";
    if (dataBase->GetField(curl, field, dbFld))
	{
	dbFld.GetString(strCmdLine);
        ACS_SHORT_LOG((LM_INFO, "Curl %s field %s OK", curl.c_str(), field.c_str() ));
        dbFld.ToString(strField, true);
        ACS_SHORT_LOG((LM_INFO, "ToString: %s", strField.c_str()));
        ACE_CString strFrom = "<String>maci::test";
        ACS_SHORT_LOG((LM_INFO, "FromString output : %d", dbFld.FromString(strFrom)));
	}
    else
	ACS_SHORT_LOG((LM_ERROR, "Curl %s does not have %s field", curl.c_str(), field.c_str() ));

    // litle bit conversion
    field = "MaxCachePriority";
    if (dataBase->GetField(curl, field, dbFld))
	{
	if( dbFld.GetULong(ul) )
	    {
	    ACS_SHORT_LOG((LM_INFO, "Curl %s field %s OK", curl.c_str(), field.c_str() ));
	    }
	else
	    {
	    ACS_SHORT_LOG((LM_ERROR, "Curl %s : invalid value in %s field", curl.c_str(), field.c_str() ));
	    }
	}
    else
	ACS_SHORT_LOG((LM_ERROR, "Curl %s does not have %s field", curl.c_str(), field.c_str() ));

    // try something ilegal
    field = "StrangeFieldName";
    if (dataBase->GetField(curl, field, dbFld))
	{
	// will hardly get here
	}
    else
	ACS_SHORT_LOG((LM_ERROR, "Curl %s does not have %s field", curl.c_str(), field.c_str() ));

    // Container
    curl = "MACI/Containers/Container";

    // Container should have
    field = "UseIFR";
    if (dataBase->GetField(curl, field, dbFld))
	{
	dbFld.GetString(strCmdLine);
	ACS_SHORT_LOG((LM_INFO, "Curl %s field %s OK", curl.c_str(), field.c_str() ));
	}
    else
	ACS_SHORT_LOG((LM_ERROR, "Curl %s does not have %s field", curl.c_str(), field.c_str() ));

    ACS_SHORT_LOG((LM_INFO, "The test will create database with remote DAOs"));

    // this is done by local parser now try it with remote DAO
    ACE_ARGV myArgs;
    myArgs.add (argv);
    myArgs.add("-DAOremote");

    destroyDatabase(dataBase);
    dataBase = getDatabase( myArgs.argc(), myArgs.argv(), CORBA::ORB::_nil(), NULL, 1 );
    //dataBase = getDatabase( myArgs.argc(), myArgs.argv() );

    // PS
    curl = "alma/TEST_PS_1";

    field = "current/graph_max";
    if (dataBase->GetField(curl, field, dbFld))
	{
	if( dbFld.GetULong(ul) )
	    {
	    ACS_SHORT_LOG((LM_INFO, "Curl %s field %s OK", curl.c_str(), field.c_str() ));
	    }
	else
	    {
	    ACS_SHORT_LOG((LM_ERROR, "Curl %s : invalid value in %s field", curl.c_str(), field.c_str() ));
	    }
	}
    else
	ACS_SHORT_LOG((LM_ERROR, "Curl %s does not have %s field", curl.c_str(), field.c_str() ));

    // make an exception on remote DAO

    field = "StrangeFieldName";
    if (dataBase->GetField(curl, field, dbFld))
	{
	// will hardly get here
	}
    else
	ACS_SHORT_LOG((LM_ERROR, "Curl %s does not have %s field", curl.c_str(), field.c_str() ));

    ACS_SHORT_LOG((LM_INFO, "Test arrays"));
    // array
    curl = curl + "/status";
    field = "bitDescription";
    if (!dataBase->GetField(curl, field, dbFld))
	{
	ACS_SHORT_LOG((LM_ERROR, "Curl %s does not have %s field", curl.c_str(), field.c_str() ));
	}
    else
	{
	int nElements = 0;
	StringArray * strarray = dbFld.GetStringArray();
	StringArray::const_iterator iter = strarray->begin();
	for (; iter != strarray->end(); iter++)
	    {
	    ACS_SHORT_LOG((LM_ERROR, "Curl %s in %s field values: %s", curl.c_str(), field.c_str(), iter->c_str() ));
	    nElements++;
	    }
	if( nElements == 9 )
	    {
	    ACS_SHORT_LOG((LM_INFO, "Test arrays OK"));
	    }
	else
	    {
	    ACS_SHORT_LOG((LM_INFO, "Test arrays FAILED"));
	    }
	}

    destroyDatabase(dataBase);
    dataBase = 0;

    // test database free by singleton
/*    for( int i=0; i<100; i++ )
	{
	dataBase = getDatabase();
	}
*/
    ACE_OS::sleep(1);

    ACS_SHORT_LOG ((LM_INFO, "cdbTestClient ends"));

    }
catch(CORBA::Exception ex)
    {
    ACE_PRINT_EXCEPTION(ACE_ANY_EXCEPTION, "main");
    }

return 0;
}

<commit_msg>An & that forget for handling the exception on the previous commit.<commit_after>#include <vltPort.h>

#include <cdbDALaccess.h>
#include <logging.h>
#include <ace/ARGV.h>

#ifdef MAKE_VXWORKS
#	include "rebootLib.h"
#	include "acsutilArgUnpack.h"
//******************************************************************************
//*    ALMA - Atacama Large Millimiter Array
//*    (c) European Southern Observatory, 2002
//*    Copyright by ESO (in the framework of the ALMA collaboration)
//*    and Cosylab 2002, All rights reserved
//*
//*    This library is free software; you can redistribute it and/or
//*    modify it under the terms of the GNU Lesser General Public
//*    License as published by the Free Software Foundation; either
//*    version 2.1 of the License, or (at your option) any later version.
//*
//*    This library is distributed in the hope that it will be useful,
//*    but WITHOUT ANY WARRANTY; without even the implied warranty of
//*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//*    Lesser General Public License for more details.
//*
//*    You should have received a copy of the GNU Lesser General Public
//*    License along with this library; if not, write to the Free Software
//*    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
//*
int startCdbTest (char *szCmdLn)
{
    int  argc;
    char *argv[100];

    argc = argUnpack(szCmdLn, argv);
    argv[0] = "cdbTest";
#else
int main(int argc, char* argv[])
{
#endif // defined( MAKE_VXWORKS )

try{
    CORBA::ULong ul;
    Field dbFld;
    Table* dataBase;
    ACE_CString curl, field;
    ACE_CString strCmdLine;
    ACE_CString strField;
    LoggingProxy logger(0, 0, 31);

    // initialize the rest of LoggingProxy
    LoggingProxy::ProcessName(argv[0]);
    LoggingProxy::ThreadName("main");

    LoggingProxy::init (&logger);

    ACS_SHORT_LOG ((LM_INFO, "cdbTestClient starts"));

    dataBase = getDatabase( argc, argv );

    curl = "MACI/Managers/Manager";

    // something for sure
    field = "CentralizedLogger";
    if (dataBase->GetField(curl, field, dbFld))
	{
	dbFld.GetString(strCmdLine);
        ACS_SHORT_LOG((LM_INFO, "Curl %s field %s OK", curl.c_str(), field.c_str() ));
        dbFld.ToString(strField, true);
        ACS_SHORT_LOG((LM_INFO, "ToString: %s", strField.c_str()));
        ACE_CString strFrom = "<String>maci::test";
        ACS_SHORT_LOG((LM_INFO, "FromString output : %d", dbFld.FromString(strFrom)));
	}
    else
	ACS_SHORT_LOG((LM_ERROR, "Curl %s does not have %s field", curl.c_str(), field.c_str() ));

    // litle bit conversion
    field = "MaxCachePriority";
    if (dataBase->GetField(curl, field, dbFld))
	{
	if( dbFld.GetULong(ul) )
	    {
	    ACS_SHORT_LOG((LM_INFO, "Curl %s field %s OK", curl.c_str(), field.c_str() ));
	    }
	else
	    {
	    ACS_SHORT_LOG((LM_ERROR, "Curl %s : invalid value in %s field", curl.c_str(), field.c_str() ));
	    }
	}
    else
	ACS_SHORT_LOG((LM_ERROR, "Curl %s does not have %s field", curl.c_str(), field.c_str() ));

    // try something ilegal
    field = "StrangeFieldName";
    if (dataBase->GetField(curl, field, dbFld))
	{
	// will hardly get here
	}
    else
	ACS_SHORT_LOG((LM_ERROR, "Curl %s does not have %s field", curl.c_str(), field.c_str() ));

    // Container
    curl = "MACI/Containers/Container";

    // Container should have
    field = "UseIFR";
    if (dataBase->GetField(curl, field, dbFld))
	{
	dbFld.GetString(strCmdLine);
	ACS_SHORT_LOG((LM_INFO, "Curl %s field %s OK", curl.c_str(), field.c_str() ));
	}
    else
	ACS_SHORT_LOG((LM_ERROR, "Curl %s does not have %s field", curl.c_str(), field.c_str() ));

    ACS_SHORT_LOG((LM_INFO, "The test will create database with remote DAOs"));

    // this is done by local parser now try it with remote DAO
    ACE_ARGV myArgs;
    myArgs.add (argv);
    myArgs.add("-DAOremote");

    destroyDatabase(dataBase);
    dataBase = getDatabase( myArgs.argc(), myArgs.argv(), CORBA::ORB::_nil(), NULL, 1 );
    //dataBase = getDatabase( myArgs.argc(), myArgs.argv() );

    // PS
    curl = "alma/TEST_PS_1";

    field = "current/graph_max";
    if (dataBase->GetField(curl, field, dbFld))
	{
	if( dbFld.GetULong(ul) )
	    {
	    ACS_SHORT_LOG((LM_INFO, "Curl %s field %s OK", curl.c_str(), field.c_str() ));
	    }
	else
	    {
	    ACS_SHORT_LOG((LM_ERROR, "Curl %s : invalid value in %s field", curl.c_str(), field.c_str() ));
	    }
	}
    else
	ACS_SHORT_LOG((LM_ERROR, "Curl %s does not have %s field", curl.c_str(), field.c_str() ));

    // make an exception on remote DAO

    field = "StrangeFieldName";
    if (dataBase->GetField(curl, field, dbFld))
	{
	// will hardly get here
	}
    else
	ACS_SHORT_LOG((LM_ERROR, "Curl %s does not have %s field", curl.c_str(), field.c_str() ));

    ACS_SHORT_LOG((LM_INFO, "Test arrays"));
    // array
    curl = curl + "/status";
    field = "bitDescription";
    if (!dataBase->GetField(curl, field, dbFld))
	{
	ACS_SHORT_LOG((LM_ERROR, "Curl %s does not have %s field", curl.c_str(), field.c_str() ));
	}
    else
	{
	int nElements = 0;
	StringArray * strarray = dbFld.GetStringArray();
	StringArray::const_iterator iter = strarray->begin();
	for (; iter != strarray->end(); iter++)
	    {
	    ACS_SHORT_LOG((LM_ERROR, "Curl %s in %s field values: %s", curl.c_str(), field.c_str(), iter->c_str() ));
	    nElements++;
	    }
	if( nElements == 9 )
	    {
	    ACS_SHORT_LOG((LM_INFO, "Test arrays OK"));
	    }
	else
	    {
	    ACS_SHORT_LOG((LM_INFO, "Test arrays FAILED"));
	    }
	}

    destroyDatabase(dataBase);
    dataBase = 0;

    // test database free by singleton
/*    for( int i=0; i<100; i++ )
	{
	dataBase = getDatabase();
	}
*/
    ACE_OS::sleep(1);

    ACS_SHORT_LOG ((LM_INFO, "cdbTestClient ends"));

    }
catch(CORBA::Exception &ex)
    {
    ACE_PRINT_EXCEPTION(ACE_ANY_EXCEPTION, "main");
    }

return 0;
}

<|endoftext|>


<commit_before>#include <stddef.h>
#include <kernel/ext2.h>
#include <kernel/ata.h>
#include <kernel/itoa.h>
#include <kernel/panic.h>

Ext2::Ext2(uint32_t lba) : lba(lba) {
	ata_pio_read(lba + 1024 / 512, 2, &this->superblock);
	if(this->superblock.base.signature != 0xef53) {
		kernel_panic("invalid ext2 superblock signature");
	}
}

uint32_t Ext2::GetBlockSize(void) {
	return 1024u << this->superblock.base.block_shift;
}

uint32_t Ext2::GetBlockAddr(uint32_t block_id) {
	return block_id * this->GetBlockSize();
}

uint32_t Ext2::GetInodeAddr(uint32_t inode_id) {
	uint32_t group_id = (inode_id - 1) / this->superblock.base.group_inode_count;
	GroupDesc gd = this->GetGroupDesc(group_id);

	uint32_t index = (inode_id - 1) % this->superblock.base.group_inode_count;
	return gd.inode_table_addr * this->GetBlockSize() + index * sizeof(Inode);
}

Ext2::GroupDesc Ext2::GetGroupDesc(uint32_t group_id) {
	uint32_t block_id = this->superblock.base.superblock_id + 1;
	uint32_t offset = this->GetBlockAddr(block_id) + group_id * sizeof(GroupDesc);

	char buffer[1024];
	ata_pio_read(this->lba + offset / 512, 2, &buffer);

	GroupDesc *gd = reinterpret_cast<GroupDesc *>(&buffer[offset % 512]);
	return *gd;
}

Maybe<Ext2::Inode> Ext2::GetInode(uint32_t inode_id) {
	uint32_t inode_addr = this->GetInodeAddr(inode_id);

	char buffer[1024];
	ata_pio_read(this->lba + inode_addr / 512, 2, &buffer);

	Inode *inode = reinterpret_cast<Inode *>(&buffer[inode_addr % 512]);
	return Maybe<Inode>(*inode);
}

Maybe<Ext2::Inode> Ext2::GetInode(Inode &pwd, const char *name) {
	auto mDirent = this->GetDirectoryEntry(pwd.block_ptr[0], name);
	if(mDirent.IsNothing()) {
		return Maybe<Inode>();
	} else {
		return this->GetInode(mDirent.FromJust().inode_id);
	}
}

Maybe<Ext2::DirectoryEntry> Ext2::GetDirectoryEntry(uint32_t block_id, const char *name) {
	uint32_t block_size = this->GetBlockSize();
	uint32_t block_addr = this->GetBlockAddr(block_id);

	// XXX: this assumes a block is 1024 bytes, which is very bad if it's not
	char buffer[1024];
	ata_pio_read(this->lba + block_addr / 512, 2, &buffer);

	DirectoryEntry *dirent;
	for(size_t offset = 0; offset < block_size; offset += dirent->size) {
		dirent = reinterpret_cast<DirectoryEntry *>(&buffer[offset]);
		if(dirent->inode_id == 0) { break; }

		bool match = true;
		for(size_t n = 0; n < dirent->len; ++n) {
			if(buffer[offset + sizeof(DirectoryEntry) + n] != name[n]) {
				match = false;
				break;
			}
		}
		if(match) {
			return Maybe<DirectoryEntry>(*dirent);
		}
	}
	return Maybe<DirectoryEntry>();
}
<commit_msg>Add extra XXX comment<commit_after>#include <stddef.h>
#include <kernel/ext2.h>
#include <kernel/ata.h>
#include <kernel/itoa.h>
#include <kernel/panic.h>

Ext2::Ext2(uint32_t lba) : lba(lba) {
	ata_pio_read(lba + 1024 / 512, 2, &this->superblock);
	if(this->superblock.base.signature != 0xef53) {
		kernel_panic("invalid ext2 superblock signature");
	}
}

uint32_t Ext2::GetBlockSize(void) {
	return 1024u << this->superblock.base.block_shift;
}

uint32_t Ext2::GetBlockAddr(uint32_t block_id) {
	return block_id * this->GetBlockSize();
}

uint32_t Ext2::GetInodeAddr(uint32_t inode_id) {
	uint32_t group_id = (inode_id - 1) / this->superblock.base.group_inode_count;
	GroupDesc gd = this->GetGroupDesc(group_id);

	uint32_t index = (inode_id - 1) % this->superblock.base.group_inode_count;
	return gd.inode_table_addr * this->GetBlockSize() + index * sizeof(Inode);
}

Ext2::GroupDesc Ext2::GetGroupDesc(uint32_t group_id) {
	uint32_t block_id = this->superblock.base.superblock_id + 1;
	uint32_t offset = this->GetBlockAddr(block_id) + group_id * sizeof(GroupDesc);

	char buffer[1024];
	ata_pio_read(this->lba + offset / 512, 2, &buffer);

	GroupDesc *gd = reinterpret_cast<GroupDesc *>(&buffer[offset % 512]);
	return *gd;
}

Maybe<Ext2::Inode> Ext2::GetInode(uint32_t inode_id) {
	uint32_t inode_addr = this->GetInodeAddr(inode_id);

	char buffer[1024];
	ata_pio_read(this->lba + inode_addr / 512, 2, &buffer);

	Inode *inode = reinterpret_cast<Inode *>(&buffer[inode_addr % 512]);
	return Maybe<Inode>(*inode);
}

Maybe<Ext2::Inode> Ext2::GetInode(Inode &pwd, const char *name) {
	// XXX: this assumes all dir entries will fit in a single block, which is bad
	auto mDirent = this->GetDirectoryEntry(pwd.block_ptr[0], name);
	if(mDirent.IsNothing()) {
		return Maybe<Inode>();
	} else {
		return this->GetInode(mDirent.FromJust().inode_id);
	}
}

Maybe<Ext2::DirectoryEntry> Ext2::GetDirectoryEntry(uint32_t block_id, const char *name) {
	uint32_t block_size = this->GetBlockSize();
	uint32_t block_addr = this->GetBlockAddr(block_id);

	// XXX: this assumes a block is 1024 bytes, which is very bad if it's not
	char buffer[1024];
	ata_pio_read(this->lba + block_addr / 512, 2, &buffer);

	DirectoryEntry *dirent;
	for(size_t offset = 0; offset < block_size; offset += dirent->size) {
		dirent = reinterpret_cast<DirectoryEntry *>(&buffer[offset]);
		if(dirent->inode_id == 0) { break; }

		bool match = true;
		for(size_t n = 0; n < dirent->len; ++n) {
			if(buffer[offset + sizeof(DirectoryEntry) + n] != name[n]) {
				match = false;
				break;
			}
		}
		if(match) {
			return Maybe<DirectoryEntry>(*dirent);
		}
	}
	return Maybe<DirectoryEntry>();
}
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author:  Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------

#include "IncrementalParser.h"
#include "ASTDumper.h"
#include "ASTNodeEraser.h"
#include "DeclCollector.h"
#include "DeclExtractor.h"
#include "DynamicLookup.h"
#include "ReturnSynthesizer.h"
#include "ValuePrinterSynthesizer.h"
#include "cling/Interpreter/CIFactory.h"
#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/Transaction.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclGroup.h"
#include "clang/Basic/FileManager.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Parse/Parser.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Serialization/ASTWriter.h"

#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_os_ostream.h"

#include <ctime>
#include <iostream>
#include <stdio.h>
#include <sstream>

using namespace clang;

namespace cling {
  IncrementalParser::IncrementalParser(Interpreter* interp,
                                       int argc, const char* const *argv,
                                       const char* llvmdir):
    m_Interpreter(interp), m_Consumer(0) {

    CompilerInstance* CI
      = CIFactory::createCI(llvm::MemoryBuffer::getMemBuffer("", "CLING"),
                            argc, argv, llvmdir);
    assert(CI && "CompilerInstance is (null)!");

    m_Consumer = dyn_cast<DeclCollector>(&CI->getASTConsumer());
    assert(m_Consumer && "Expected ChainedConsumer!");

    m_CI.reset(CI);

    if (CI->getFrontendOpts().ProgramAction != clang::frontend::ParseSyntaxOnly){
      m_CodeGen.reset(CreateLLVMCodeGen(CI->getDiagnostics(), "cling input",
                                        CI->getCodeGenOpts(),
                                        *m_Interpreter->getLLVMContext()
                                        ));
    }

    CreateSLocOffsetGenerator();

    // Add transformers to the IncrementalParser, which owns them
    m_TTransformers.push_back(new EvaluateTSynthesizer(&CI->getSema()));

    m_TTransformers.push_back(new DeclExtractor(&getCI()->getSema()));
    m_TTransformers.push_back(new ValuePrinterSynthesizer(&CI->getSema(), 0));
    m_TTransformers.push_back(new ReturnSynthesizer(&CI->getSema()));
    m_TTransformers.push_back(new ASTDumper());

    m_Parser.reset(new Parser(CI->getPreprocessor(), CI->getSema(),
                              false /*skipFuncBodies*/));
    CI->getPreprocessor().EnterMainSourceFile();
    // Initialize the parser after we have entered the main source file.
    m_Parser->Initialize();
    // Perform initialization that occurs after the parser has been initialized
    // but before it parses anything. Initializes the consumers too.
    CI->getSema().Initialize();
  }

  IncrementalParser::~IncrementalParser() {
     if (hasCodeGenerator()) {
       getCodeGenerator()->ReleaseModule();
     }
     for (size_t i = 0; i < m_Transactions.size(); ++i)
       delete m_Transactions[i];

     for (size_t i = 0; i < m_TTransformers.size(); ++i)
       delete m_TTransformers[i];
  }

  // pin the vtable here since there is no point to create dedicated to that
  // cpp file.
  TransactionTransformer::~TransactionTransformer() {}

  void IncrementalParser::beginTransaction(const CompilationOptions& Opts) {
    llvm::Module* M = 0;
    if (hasCodeGenerator())
      M = getCodeGenerator()->GetModule();

    Transaction* NewCurT = new Transaction(Opts, M);
    Transaction* OldCurT = m_Consumer->getTransaction();
    m_Consumer->setTransaction(NewCurT);
    // If we are in the middle of transaction and we see another begin
    // transaction - it must be nested transaction.
    if (OldCurT && !OldCurT->isCompleted()) {
      OldCurT->addNestedTransaction(NewCurT); // takes the ownership
      return;
    }

    m_Transactions.push_back(NewCurT);
  }

  void IncrementalParser::endTransaction() const {
    Transaction* CurT = m_Consumer->getTransaction();
    CurT->setCompleted();
    const DiagnosticsEngine& Diags = getCI()->getSema().getDiagnostics();

    //TODO: Make the enum orable.
    if (Diags.getNumWarnings() > 0)
      CurT->setIssuedDiags(Transaction::kWarnings);

    if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
      CurT->setIssuedDiags(Transaction::kErrors);

    if (CurT->isNestedTransaction()) {
      assert(!CurT->getParent()->isCompleted()
             && "Parent transaction completed!?");
      // FIXME: Not sure what I meant :) REVISIT
      //CurT = m_Consumer->getTransaction()->getParent();
    }
  }

  void IncrementalParser::commitCurrentTransaction() {
    Transaction* CurT = m_Consumer->getTransaction();
    assert(CurT->isCompleted() && "Transaction not ended!?");

    // Check for errors...
    if (CurT->getIssuedDiags() == Transaction::kErrors) {
      rollbackTransaction(CurT);
      return;
    }

    // We are sure it's safe to pipe it through the transformers
    for (size_t i = 0; i < m_TTransformers.size(); ++i)
      if (!m_TTransformers[i]->TransformTransaction(*CurT)) {
        // Roll back on error in a transformer
        rollbackTransaction(CurT);
        return;
      }

    // Pull all template instantiations in that came from the consumers.
    getCI()->getSema().PerformPendingInstantiations();

    m_Consumer->HandleTranslationUnit(getCI()->getASTContext());

    if (CurT->getCompilationOpts().CodeGeneration && hasCodeGenerator()) {
      // Reset the module builder to clean up global initializers, c'tors, d'tors
      getCodeGenerator()->Initialize(getCI()->getASTContext());

      // codegen the transaction
      for (Transaction::const_iterator I = CurT->decls_begin(),
             E = CurT->decls_end(); I != E; ++I) {
        getCodeGenerator()->HandleTopLevelDecl(*I);
      }
      getCodeGenerator()->HandleTranslationUnit(getCI()->getASTContext());
      // run the static initializers that came from codegenning
      m_Interpreter->runStaticInitializersOnce();
    }

    CurT->setState(Transaction::kCommitted);
    InterpreterCallbacks* callbacks = m_Interpreter->getCallbacks();
    if (callbacks)
      callbacks->TransactionCommitted(*CurT);
  }

  void IncrementalParser::rollbackTransaction(Transaction* T) const {
    ASTNodeEraser NodeEraser(&getCI()->getSema());

    if (NodeEraser.RevertTransaction(T))
      T->setState(Transaction::kRolledBack);
    else
      T->setState(Transaction::kRolledBackWithErrors);
  }

  // Each input line is contained in separate memory buffer. The SourceManager
  // assigns sort-of invalid FileID for each buffer, i.e there is no FileEntry
  // for the MemoryBuffer's FileID. That in turn is problem because invalid
  // SourceLocations are given to the diagnostics. Thus the diagnostics cannot
  // order the overloads, for example
  //
  // Our work-around is creating a virtual file, which doesn't exist on the disk
  // with enormous size (no allocation is done). That file has valid FileEntry
  // and so on... We use it for generating valid SourceLocations with valid
  // offsets so that it doesn't cause any troubles to the diagnostics.
  //
  // +---------------------+
  // | Main memory buffer  |
  // +---------------------+
  // |  Virtual file SLoc  |
  // |    address space    |<-----------------+
  // |         ...         |<------------+    |
  // |         ...         |             |    |
  // |         ...         |<----+       |    |
  // |         ...         |     |       |    |
  // +~~~~~~~~~~~~~~~~~~~~~+     |       |    |
  // |     input_line_1    | ....+.......+..--+
  // +---------------------+     |       |
  // |     input_line_2    | ....+.....--+
  // +---------------------+     |
  // |          ...        |     |
  // +---------------------+     |
  // |     input_line_N    | ..--+
  // +---------------------+
  //
  void IncrementalParser::CreateSLocOffsetGenerator() {
    SourceManager& SM = getCI()->getSourceManager();
    FileManager& FM = SM.getFileManager();
    const FileEntry* FE
      = FM.getVirtualFile("Interactive/InputLineIncluder", 1U << 15U, time(0));
    m_VirtualFileID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User);

    assert(!m_VirtualFileID.isInvalid() && "No VirtualFileID created?");
  }

  IncrementalParser::EParseResult
  IncrementalParser::Compile(llvm::StringRef input,
                             const CompilationOptions& Opts) {

    beginTransaction(Opts);
    EParseResult Result = ParseInternal(input);
    endTransaction();

    // Check for errors coming from our custom consumers.
    DiagnosticConsumer& DClient = m_CI->getDiagnosticClient();
    DClient.BeginSourceFile(getCI()->getLangOpts(),
                            &getCI()->getPreprocessor());
    commitCurrentTransaction();

    DClient.EndSourceFile();
    m_CI->getDiagnostics().Reset();

    return Result;
  }

  Transaction* IncrementalParser::Parse(llvm::StringRef input,
                                        const CompilationOptions& Opts) {
    beginTransaction(Opts);
    ParseInternal(input);
    endTransaction();

    return getLastTransaction();
  }

  // Add the input to the memory buffer, parse it, and add it to the AST.
  IncrementalParser::EParseResult
  IncrementalParser::ParseInternal(llvm::StringRef input) {
    if (input.empty()) return IncrementalParser::kSuccess;

    PrettyStackTraceParserEntry CrashInfo(*m_Parser.get());

    // Recover resources if we crash before exiting this method.
    llvm::CrashRecoveryContextCleanupRegistrar<Parser>
      CleanupParser(m_Parser.get());

    Sema& S = getCI()->getSema();
    // Recover resources if we crash before exiting this method.
    llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S);

    Preprocessor& PP = m_CI->getPreprocessor();
    DiagnosticConsumer& DClient = m_CI->getDiagnosticClient();

    if (!PP.getCurrentLexer()) {
       PP.EnterSourceFile(m_CI->getSourceManager().getMainFileID(),
                          0, SourceLocation());
    }
    assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?");
    PP.enableIncrementalProcessing();

    DClient.BeginSourceFile(m_CI->getLangOpts(), &PP);

    std::ostringstream source_name;
    source_name << "input_line_" << (m_MemoryBuffers.size() + 1);

    // Create an uninitialized memory buffer, copy code in and append "\n"
    size_t InputSize = input.size(); // don't include trailing 0
    // MemBuffer size should *not* include terminating zero
    llvm::MemoryBuffer* MB
      = llvm::MemoryBuffer::getNewUninitMemBuffer(InputSize + 1,
                                                  source_name.str());
    char* MBStart = const_cast<char*>(MB->getBufferStart());
    memcpy(MBStart, input.data(), InputSize);
    memcpy(MBStart + InputSize, "\n", 2);

    m_MemoryBuffers.push_back(MB);
    SourceManager& SM = getCI()->getSourceManager();

    // Create SourceLocation, which will allow clang to order the overload
    // candidates for example
    SourceLocation NewLoc = SM.getLocForStartOfFile(m_VirtualFileID);
    NewLoc = NewLoc.getLocWithOffset(m_MemoryBuffers.size() + 1);

    // Create FileID for the current buffer
    FileID FID = SM.createFileIDForMemBuffer(m_MemoryBuffers.back(),
                                             /*LoadedID*/0,
                                             /*LoadedOffset*/0, NewLoc);

    PP.EnterSourceFile(FID, /*DirLookup*/0, NewLoc);

    Parser::DeclGroupPtrTy ADecl;

    while (!m_Parser->ParseTopLevelDecl(ADecl)) {
      // If we got a null return and something *was* parsed, ignore it.  This
      // is due to a top-level semicolon, an action override, or a parse error
      // skipping something.
      if (ADecl)
        m_Consumer->HandleTopLevelDecl(ADecl.getAsVal<DeclGroupRef>());
    };

    // Process any TopLevelDecls generated by #pragma weak.
    for (llvm::SmallVector<Decl*,2>::iterator I = S.WeakTopLevelDecls().begin(),
           E = S.WeakTopLevelDecls().end(); I != E; ++I) {
      m_Consumer->HandleTopLevelDecl(DeclGroupRef(*I));
    }

    S.PerformPendingInstantiations();

    DClient.EndSourceFile();

    DiagnosticsEngine& Diag = S.getDiagnostics();
    if (Diag.hasErrorOccurred())
      return IncrementalParser::kFailed;
    else if (Diag.getNumWarnings())
      return IncrementalParser::kSuccessWithWarnings;

    return IncrementalParser::kSuccess;
  }

  void IncrementalParser::unloadTransaction(Transaction* T) {
    if (!T)
      T = getLastTransaction();

    assert(T->getState() == Transaction::kCommitted &&
           "Unloading not commited transaction?");
    assert(T->getModule() &&
           "Trying to uncodegen transaction taken in syntax only mode. ");

    ASTNodeEraser NodeEraser(&getCI()->getSema());
    NodeEraser.RevertTransaction(T);

    InterpreterCallbacks* callbacks = m_Interpreter->getCallbacks();
    if (callbacks)
      callbacks->TransactionUnloaded(*T);
  }

} // namespace cling
<commit_msg>Remove the / because the FileManager thinks it is a directory prefix and asserts that the directory doesn't exist in its cache. Bring us one step further in importing AST nodes.<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author:  Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------

#include "IncrementalParser.h"
#include "ASTDumper.h"
#include "ASTNodeEraser.h"
#include "DeclCollector.h"
#include "DeclExtractor.h"
#include "DynamicLookup.h"
#include "ReturnSynthesizer.h"
#include "ValuePrinterSynthesizer.h"
#include "cling/Interpreter/CIFactory.h"
#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/Transaction.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclGroup.h"
#include "clang/Basic/FileManager.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Parse/Parser.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Serialization/ASTWriter.h"

#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_os_ostream.h"

#include <ctime>
#include <iostream>
#include <stdio.h>
#include <sstream>

using namespace clang;

namespace cling {
  IncrementalParser::IncrementalParser(Interpreter* interp,
                                       int argc, const char* const *argv,
                                       const char* llvmdir):
    m_Interpreter(interp), m_Consumer(0) {

    CompilerInstance* CI
      = CIFactory::createCI(llvm::MemoryBuffer::getMemBuffer("", "CLING"),
                            argc, argv, llvmdir);
    assert(CI && "CompilerInstance is (null)!");

    m_Consumer = dyn_cast<DeclCollector>(&CI->getASTConsumer());
    assert(m_Consumer && "Expected ChainedConsumer!");

    m_CI.reset(CI);

    if (CI->getFrontendOpts().ProgramAction != clang::frontend::ParseSyntaxOnly){
      m_CodeGen.reset(CreateLLVMCodeGen(CI->getDiagnostics(), "cling input",
                                        CI->getCodeGenOpts(),
                                        *m_Interpreter->getLLVMContext()
                                        ));
    }

    CreateSLocOffsetGenerator();

    // Add transformers to the IncrementalParser, which owns them
    m_TTransformers.push_back(new EvaluateTSynthesizer(&CI->getSema()));

    m_TTransformers.push_back(new DeclExtractor(&getCI()->getSema()));
    m_TTransformers.push_back(new ValuePrinterSynthesizer(&CI->getSema(), 0));
    m_TTransformers.push_back(new ReturnSynthesizer(&CI->getSema()));
    m_TTransformers.push_back(new ASTDumper());

    m_Parser.reset(new Parser(CI->getPreprocessor(), CI->getSema(),
                              false /*skipFuncBodies*/));
    CI->getPreprocessor().EnterMainSourceFile();
    // Initialize the parser after we have entered the main source file.
    m_Parser->Initialize();
    // Perform initialization that occurs after the parser has been initialized
    // but before it parses anything. Initializes the consumers too.
    CI->getSema().Initialize();
  }

  IncrementalParser::~IncrementalParser() {
     if (hasCodeGenerator()) {
       getCodeGenerator()->ReleaseModule();
     }
     for (size_t i = 0; i < m_Transactions.size(); ++i)
       delete m_Transactions[i];

     for (size_t i = 0; i < m_TTransformers.size(); ++i)
       delete m_TTransformers[i];
  }

  // pin the vtable here since there is no point to create dedicated to that
  // cpp file.
  TransactionTransformer::~TransactionTransformer() {}

  void IncrementalParser::beginTransaction(const CompilationOptions& Opts) {
    llvm::Module* M = 0;
    if (hasCodeGenerator())
      M = getCodeGenerator()->GetModule();

    Transaction* NewCurT = new Transaction(Opts, M);
    Transaction* OldCurT = m_Consumer->getTransaction();
    m_Consumer->setTransaction(NewCurT);
    // If we are in the middle of transaction and we see another begin
    // transaction - it must be nested transaction.
    if (OldCurT && !OldCurT->isCompleted()) {
      OldCurT->addNestedTransaction(NewCurT); // takes the ownership
      return;
    }

    m_Transactions.push_back(NewCurT);
  }

  void IncrementalParser::endTransaction() const {
    Transaction* CurT = m_Consumer->getTransaction();
    CurT->setCompleted();
    const DiagnosticsEngine& Diags = getCI()->getSema().getDiagnostics();

    //TODO: Make the enum orable.
    if (Diags.getNumWarnings() > 0)
      CurT->setIssuedDiags(Transaction::kWarnings);

    if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
      CurT->setIssuedDiags(Transaction::kErrors);

    if (CurT->isNestedTransaction()) {
      assert(!CurT->getParent()->isCompleted()
             && "Parent transaction completed!?");
      // FIXME: Not sure what I meant :) REVISIT
      //CurT = m_Consumer->getTransaction()->getParent();
    }
  }

  void IncrementalParser::commitCurrentTransaction() {
    Transaction* CurT = m_Consumer->getTransaction();
    assert(CurT->isCompleted() && "Transaction not ended!?");

    // Check for errors...
    if (CurT->getIssuedDiags() == Transaction::kErrors) {
      rollbackTransaction(CurT);
      return;
    }

    // We are sure it's safe to pipe it through the transformers
    for (size_t i = 0; i < m_TTransformers.size(); ++i)
      if (!m_TTransformers[i]->TransformTransaction(*CurT)) {
        // Roll back on error in a transformer
        rollbackTransaction(CurT);
        return;
      }

    // Pull all template instantiations in that came from the consumers.
    getCI()->getSema().PerformPendingInstantiations();

    m_Consumer->HandleTranslationUnit(getCI()->getASTContext());

    if (CurT->getCompilationOpts().CodeGeneration && hasCodeGenerator()) {
      // Reset the module builder to clean up global initializers, c'tors, d'tors
      getCodeGenerator()->Initialize(getCI()->getASTContext());

      // codegen the transaction
      for (Transaction::const_iterator I = CurT->decls_begin(),
             E = CurT->decls_end(); I != E; ++I) {
        getCodeGenerator()->HandleTopLevelDecl(*I);
      }
      getCodeGenerator()->HandleTranslationUnit(getCI()->getASTContext());
      // run the static initializers that came from codegenning
      m_Interpreter->runStaticInitializersOnce();
    }

    CurT->setState(Transaction::kCommitted);
    InterpreterCallbacks* callbacks = m_Interpreter->getCallbacks();
    if (callbacks)
      callbacks->TransactionCommitted(*CurT);
  }

  void IncrementalParser::rollbackTransaction(Transaction* T) const {
    ASTNodeEraser NodeEraser(&getCI()->getSema());

    if (NodeEraser.RevertTransaction(T))
      T->setState(Transaction::kRolledBack);
    else
      T->setState(Transaction::kRolledBackWithErrors);
  }

  // Each input line is contained in separate memory buffer. The SourceManager
  // assigns sort-of invalid FileID for each buffer, i.e there is no FileEntry
  // for the MemoryBuffer's FileID. That in turn is problem because invalid
  // SourceLocations are given to the diagnostics. Thus the diagnostics cannot
  // order the overloads, for example
  //
  // Our work-around is creating a virtual file, which doesn't exist on the disk
  // with enormous size (no allocation is done). That file has valid FileEntry
  // and so on... We use it for generating valid SourceLocations with valid
  // offsets so that it doesn't cause any troubles to the diagnostics.
  //
  // +---------------------+
  // | Main memory buffer  |
  // +---------------------+
  // |  Virtual file SLoc  |
  // |    address space    |<-----------------+
  // |         ...         |<------------+    |
  // |         ...         |             |    |
  // |         ...         |<----+       |    |
  // |         ...         |     |       |    |
  // +~~~~~~~~~~~~~~~~~~~~~+     |       |    |
  // |     input_line_1    | ....+.......+..--+
  // +---------------------+     |       |
  // |     input_line_2    | ....+.....--+
  // +---------------------+     |
  // |          ...        |     |
  // +---------------------+     |
  // |     input_line_N    | ..--+
  // +---------------------+
  //
  void IncrementalParser::CreateSLocOffsetGenerator() {
    SourceManager& SM = getCI()->getSourceManager();
    FileManager& FM = SM.getFileManager();
    const FileEntry* FE
      = FM.getVirtualFile("InteractiveInputLineIncluder.h", 1U << 15U, time(0));
    m_VirtualFileID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User);

    assert(!m_VirtualFileID.isInvalid() && "No VirtualFileID created?");
  }

  IncrementalParser::EParseResult
  IncrementalParser::Compile(llvm::StringRef input,
                             const CompilationOptions& Opts) {

    beginTransaction(Opts);
    EParseResult Result = ParseInternal(input);
    endTransaction();

    // Check for errors coming from our custom consumers.
    DiagnosticConsumer& DClient = m_CI->getDiagnosticClient();
    DClient.BeginSourceFile(getCI()->getLangOpts(),
                            &getCI()->getPreprocessor());
    commitCurrentTransaction();

    DClient.EndSourceFile();
    m_CI->getDiagnostics().Reset();

    return Result;
  }

  Transaction* IncrementalParser::Parse(llvm::StringRef input,
                                        const CompilationOptions& Opts) {
    beginTransaction(Opts);
    ParseInternal(input);
    endTransaction();

    return getLastTransaction();
  }

  // Add the input to the memory buffer, parse it, and add it to the AST.
  IncrementalParser::EParseResult
  IncrementalParser::ParseInternal(llvm::StringRef input) {
    if (input.empty()) return IncrementalParser::kSuccess;

    PrettyStackTraceParserEntry CrashInfo(*m_Parser.get());

    // Recover resources if we crash before exiting this method.
    llvm::CrashRecoveryContextCleanupRegistrar<Parser>
      CleanupParser(m_Parser.get());

    Sema& S = getCI()->getSema();
    // Recover resources if we crash before exiting this method.
    llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S);

    Preprocessor& PP = m_CI->getPreprocessor();
    DiagnosticConsumer& DClient = m_CI->getDiagnosticClient();

    if (!PP.getCurrentLexer()) {
       PP.EnterSourceFile(m_CI->getSourceManager().getMainFileID(),
                          0, SourceLocation());
    }
    assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?");
    PP.enableIncrementalProcessing();

    DClient.BeginSourceFile(m_CI->getLangOpts(), &PP);

    std::ostringstream source_name;
    source_name << "input_line_" << (m_MemoryBuffers.size() + 1);

    // Create an uninitialized memory buffer, copy code in and append "\n"
    size_t InputSize = input.size(); // don't include trailing 0
    // MemBuffer size should *not* include terminating zero
    llvm::MemoryBuffer* MB
      = llvm::MemoryBuffer::getNewUninitMemBuffer(InputSize + 1,
                                                  source_name.str());
    char* MBStart = const_cast<char*>(MB->getBufferStart());
    memcpy(MBStart, input.data(), InputSize);
    memcpy(MBStart + InputSize, "\n", 2);

    m_MemoryBuffers.push_back(MB);
    SourceManager& SM = getCI()->getSourceManager();

    // Create SourceLocation, which will allow clang to order the overload
    // candidates for example
    SourceLocation NewLoc = SM.getLocForStartOfFile(m_VirtualFileID);
    NewLoc = NewLoc.getLocWithOffset(m_MemoryBuffers.size() + 1);

    // Create FileID for the current buffer
    FileID FID = SM.createFileIDForMemBuffer(m_MemoryBuffers.back(),
                                             /*LoadedID*/0,
                                             /*LoadedOffset*/0, NewLoc);

    PP.EnterSourceFile(FID, /*DirLookup*/0, NewLoc);

    Parser::DeclGroupPtrTy ADecl;

    while (!m_Parser->ParseTopLevelDecl(ADecl)) {
      // If we got a null return and something *was* parsed, ignore it.  This
      // is due to a top-level semicolon, an action override, or a parse error
      // skipping something.
      if (ADecl)
        m_Consumer->HandleTopLevelDecl(ADecl.getAsVal<DeclGroupRef>());
    };

    // Process any TopLevelDecls generated by #pragma weak.
    for (llvm::SmallVector<Decl*,2>::iterator I = S.WeakTopLevelDecls().begin(),
           E = S.WeakTopLevelDecls().end(); I != E; ++I) {
      m_Consumer->HandleTopLevelDecl(DeclGroupRef(*I));
    }

    S.PerformPendingInstantiations();

    DClient.EndSourceFile();

    DiagnosticsEngine& Diag = S.getDiagnostics();
    if (Diag.hasErrorOccurred())
      return IncrementalParser::kFailed;
    else if (Diag.getNumWarnings())
      return IncrementalParser::kSuccessWithWarnings;

    return IncrementalParser::kSuccess;
  }

  void IncrementalParser::unloadTransaction(Transaction* T) {
    if (!T)
      T = getLastTransaction();

    assert(T->getState() == Transaction::kCommitted &&
           "Unloading not commited transaction?");
    assert(T->getModule() &&
           "Trying to uncodegen transaction taken in syntax only mode. ");

    ASTNodeEraser NodeEraser(&getCI()->getSema());
    NodeEraser.RevertTransaction(T);

    InterpreterCallbacks* callbacks = m_Interpreter->getCallbacks();
    if (callbacks)
      callbacks->TransactionUnloaded(*T);
  }

} // namespace cling
<|endoftext|>


<commit_before>/**
 * @file
 * @brief Implements the handling of the sensitive device
 * @remarks Based on code from John Idarraga
 * @copyright Copyright (c) 2017 CERN and the Allpix Squared authors.
 * This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
 * In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
 * Intergovernmental Organization or submit itself to any jurisdiction.
 */

#include "SensitiveDetectorActionG4.hpp"
#include "TrackInfoG4.hpp"

#include <memory>

#include "G4DecayTable.hh"
#include "G4HCofThisEvent.hh"
#include "G4LogicalVolume.hh"
#include "G4RunManager.hh"
#include "G4SDManager.hh"
#include "G4Step.hh"
#include "G4ThreeVector.hh"
#include "G4Track.hh"
#include "G4VProcess.hh"
#include "G4ios.hh"

#include "TMath.h"
#include "TString.h"

#include "core/utils/log.h"
#include "tools/ROOT.h"
#include "tools/geant4.h"

using namespace allpix;

SensitiveDetectorActionG4::SensitiveDetectorActionG4(Module* module,
                                                     const std::shared_ptr<Detector>& detector,
                                                     Messenger* msg,
                                                     TrackInfoManager* track_info_manager,
                                                     double charge_creation_energy)
    : G4VSensitiveDetector("SensitiveDetector_" + detector->getName()), module_(module), detector_(detector),
      messenger_(msg), track_info_manager_(track_info_manager), charge_creation_energy_(charge_creation_energy) {

    // Add the sensor to the internal sensitive detector manager
    G4SDManager* sd_man_g4 = G4SDManager::GetSDMpointer();
    sd_man_g4->AddNewDetector(this);
}

G4bool SensitiveDetectorActionG4::ProcessHits(G4Step* step, G4TouchableHistory*) {
    // Get the step parameters
    auto edep = step->GetTotalEnergyDeposit();
    G4StepPoint* preStepPoint = step->GetPreStepPoint();
    G4StepPoint* postStepPoint = step->GetPostStepPoint();

    // Get Transportaion Matrix
    G4TouchableHandle theTouchable = step->GetPreStepPoint()->GetTouchableHandle();

    // Put the charge deposit in the middle of the step
    G4ThreeVector mid_pos = (preStepPoint->GetPosition() + postStepPoint->GetPosition()) / 2;
    double mid_time = (preStepPoint->GetGlobalTime() + postStepPoint->GetGlobalTime()) / 2;

    // Calculate the charge deposit at a local position
    auto deposit_position = detector_->getLocalPosition(static_cast<ROOT::Math::XYZPoint>(mid_pos));
    auto deposit_position_g4 = theTouchable->GetHistory()->GetTopTransform().TransformPoint(mid_pos);
    auto charge = static_cast<unsigned int>(edep / charge_creation_energy_);

    auto deposit_position_g4loc =
        ROOT::Math::XYZPoint(deposit_position_g4.x() + detector_->getModel()->getSensorCenter().x(),
                             deposit_position_g4.y() + detector_->getModel()->getSensorCenter().y(),
                             deposit_position_g4.z() + detector_->getModel()->getSensorCenter().z());

    const auto userTrackInfo = dynamic_cast<TrackInfoG4*>(step->GetTrack()->GetUserInformation());
    if(userTrackInfo == nullptr) {
        throw ModuleError("No track information attached to track.");
    }
    auto trackID = userTrackInfo->getID();
    auto parentTrackID = userTrackInfo->getParentID();

    // Save begin point when track is seen for the first time
    if(track_begin_.find(trackID) == track_begin_.end()) {
        track_info_manager_->setTrackInfoToBeStored(trackID);
        auto start_position = detector_->getLocalPosition(static_cast<ROOT::Math::XYZPoint>(preStepPoint->GetPosition()));
        track_begin_.emplace(trackID, start_position);
        track_parents_.emplace(trackID, parentTrackID);
        track_time_.emplace(trackID, mid_time);
        track_pdg_.emplace(trackID, step->GetTrack()->GetDynamicParticle()->GetPDGcode());
    }

    // Update current end point with the current last step
    auto end_position = detector_->getLocalPosition(static_cast<ROOT::Math::XYZPoint>(postStepPoint->GetPosition()));
    track_end_[trackID] = end_position;

    // Add new deposit if the charge is more than zero
    if(charge == 0) {
        return false;
    }

    auto global_deposit_position = detector_->getGlobalPosition(deposit_position);

    // Deposit electron
    deposits_.emplace_back(deposit_position, global_deposit_position, CarrierType::ELECTRON, charge, mid_time);
    deposit_to_id_.push_back(trackID);

    // Deposit hole
    deposits_.emplace_back(deposit_position, global_deposit_position, CarrierType::HOLE, charge, mid_time);
    deposit_to_id_.push_back(trackID);

    LOG(DEBUG) << "Created deposit of " << charge << " charges at " << Units::display(mid_pos, {"mm", "um"})
               << " locally on " << Units::display(deposit_position, {"mm", "um"}) << " in " << detector_->getName()
               << " after " << Units::display(mid_time, {"ns", "ps"});

    LOG(DEBUG) << "Geant4 transformation to local: " << Units::display(deposit_position_g4loc, {"mm", "um"});
    if((deposit_position_g4loc - deposit_position).mag2() > 0.001) {
        LOG(ERROR) << "Difference G4 to internal: "
                   << Units::display((deposit_position_g4loc - deposit_position), {"mm", "um"});
    }
    return true;
}

std::string SensitiveDetectorActionG4::getName() {
    return detector_->getName();
}

unsigned int SensitiveDetectorActionG4::getTotalDepositedCharge() {
    return total_deposited_charge_;
}

unsigned int SensitiveDetectorActionG4::getDepositedCharge() {
    return deposited_charge_;
}

void SensitiveDetectorActionG4::dispatchMessages() {
    // Create the mc particles
    std::vector<MCParticle> mc_particles;
    for(auto& track_id_point : track_begin_) {
        auto track_id = track_id_point.first;
        auto local_begin = track_id_point.second;

        ROOT::Math::XYZPoint end_point;
        auto local_end = track_end_.at(track_id);
        auto pdg_code = track_pdg_.at(track_id);
        auto track_time = track_time_.at(track_id);

        auto global_begin = detector_->getGlobalPosition(local_begin);
        auto global_end = detector_->getGlobalPosition(local_end);
        mc_particles.emplace_back(local_begin, global_begin, local_end, global_end, pdg_code, track_time);
        mc_particles.back().setTrack(track_info_manager_->findMCTrack(track_id));
        id_to_particle_[track_id] = mc_particles.size() - 1;

        LOG(DEBUG) << "Found MC particle " << pdg_code << " crossing detector " << detector_->getName() << " from "
                   << Units::display(local_begin, {"mm", "um"}) << " to " << Units::display(local_end, {"mm", "um"})
                   << " (local coordinates) at " << Units::display(track_time, {"us", "ns", "ps"});
    }

    for(auto& track_parent : track_parents_) {
        auto track_id = track_parent.first;
        auto parent_id = track_parent.second;
        if(id_to_particle_.find(parent_id) == id_to_particle_.end()) {
            // Skip tracks without direct parents with deposits
            // FIXME: Geant4 does not allow for an easy way retrieve the whole hierarchy
            continue;
        }
        auto track_idx = id_to_particle_.at(track_id);
        auto parent_idx = id_to_particle_.at(parent_id);
        mc_particles.at(track_idx).setParent(&mc_particles.at(parent_idx));
    }

    // Send the mc particle information
    auto mc_particle_message = std::make_shared<MCParticleMessage>(std::move(mc_particles), detector_);
    messenger_->dispatchMessage(module_, mc_particle_message);

    // Clear track data for the next event
    track_parents_.clear();
    track_begin_.clear();
    track_end_.clear();
    track_pdg_.clear();

    // Send a deposit message if we have any deposits
    if(!deposits_.empty()) {
        unsigned int charges = 0;
        for(auto& ch : deposits_) {
            charges += ch.getCharge();
            total_deposited_charge_ += ch.getCharge();
        }
        LOG(INFO) << "Deposited " << charges << " charges in sensor of detector " << detector_->getName();

        // Store the number of charge carriers:
        deposited_charge_ = charges;

        // Match deposit with mc particle if possible
        for(size_t i = 0; i < deposits_.size(); ++i) {
            auto track_id = deposit_to_id_.at(i);
            deposits_.at(i).setMCParticle(&mc_particle_message->getData().at(id_to_particle_.at(track_id)));
        }

        // Create a new charge deposit message
        auto deposit_message = std::make_shared<DepositedChargeMessage>(std::move(deposits_), detector_);

        // Dispatch the message
        messenger_->dispatchMessage(module_, deposit_message);
    }

    // Clear deposits for next event
    deposits_ = std::vector<DepositedCharge>();

    // Clear link tables for next event
    deposit_to_id_.clear();
    id_to_particle_.clear();
}
<commit_msg>clear the track timings after each event<commit_after>/**
 * @file
 * @brief Implements the handling of the sensitive device
 * @remarks Based on code from John Idarraga
 * @copyright Copyright (c) 2017 CERN and the Allpix Squared authors.
 * This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
 * In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
 * Intergovernmental Organization or submit itself to any jurisdiction.
 */

#include "SensitiveDetectorActionG4.hpp"
#include "TrackInfoG4.hpp"

#include <memory>

#include "G4DecayTable.hh"
#include "G4HCofThisEvent.hh"
#include "G4LogicalVolume.hh"
#include "G4RunManager.hh"
#include "G4SDManager.hh"
#include "G4Step.hh"
#include "G4ThreeVector.hh"
#include "G4Track.hh"
#include "G4VProcess.hh"
#include "G4ios.hh"

#include "TMath.h"
#include "TString.h"

#include "core/utils/log.h"
#include "tools/ROOT.h"
#include "tools/geant4.h"

using namespace allpix;

SensitiveDetectorActionG4::SensitiveDetectorActionG4(Module* module,
                                                     const std::shared_ptr<Detector>& detector,
                                                     Messenger* msg,
                                                     TrackInfoManager* track_info_manager,
                                                     double charge_creation_energy)
    : G4VSensitiveDetector("SensitiveDetector_" + detector->getName()), module_(module), detector_(detector),
      messenger_(msg), track_info_manager_(track_info_manager), charge_creation_energy_(charge_creation_energy) {

    // Add the sensor to the internal sensitive detector manager
    G4SDManager* sd_man_g4 = G4SDManager::GetSDMpointer();
    sd_man_g4->AddNewDetector(this);
}

G4bool SensitiveDetectorActionG4::ProcessHits(G4Step* step, G4TouchableHistory*) {
    // Get the step parameters
    auto edep = step->GetTotalEnergyDeposit();
    G4StepPoint* preStepPoint = step->GetPreStepPoint();
    G4StepPoint* postStepPoint = step->GetPostStepPoint();

    // Get Transportaion Matrix
    G4TouchableHandle theTouchable = step->GetPreStepPoint()->GetTouchableHandle();

    // Put the charge deposit in the middle of the step
    G4ThreeVector mid_pos = (preStepPoint->GetPosition() + postStepPoint->GetPosition()) / 2;
    double mid_time = (preStepPoint->GetGlobalTime() + postStepPoint->GetGlobalTime()) / 2;

    // Calculate the charge deposit at a local position
    auto deposit_position = detector_->getLocalPosition(static_cast<ROOT::Math::XYZPoint>(mid_pos));
    auto deposit_position_g4 = theTouchable->GetHistory()->GetTopTransform().TransformPoint(mid_pos);
    auto charge = static_cast<unsigned int>(edep / charge_creation_energy_);

    auto deposit_position_g4loc =
        ROOT::Math::XYZPoint(deposit_position_g4.x() + detector_->getModel()->getSensorCenter().x(),
                             deposit_position_g4.y() + detector_->getModel()->getSensorCenter().y(),
                             deposit_position_g4.z() + detector_->getModel()->getSensorCenter().z());

    const auto userTrackInfo = dynamic_cast<TrackInfoG4*>(step->GetTrack()->GetUserInformation());
    if(userTrackInfo == nullptr) {
        throw ModuleError("No track information attached to track.");
    }
    auto trackID = userTrackInfo->getID();
    auto parentTrackID = userTrackInfo->getParentID();

    // Save begin point when track is seen for the first time
    if(track_begin_.find(trackID) == track_begin_.end()) {
        track_info_manager_->setTrackInfoToBeStored(trackID);
        auto start_position = detector_->getLocalPosition(static_cast<ROOT::Math::XYZPoint>(preStepPoint->GetPosition()));
        track_begin_.emplace(trackID, start_position);
        track_parents_.emplace(trackID, parentTrackID);
        track_time_.emplace(trackID, mid_time);
        track_pdg_.emplace(trackID, step->GetTrack()->GetDynamicParticle()->GetPDGcode());
    }

    // Update current end point with the current last step
    auto end_position = detector_->getLocalPosition(static_cast<ROOT::Math::XYZPoint>(postStepPoint->GetPosition()));
    track_end_[trackID] = end_position;

    // Add new deposit if the charge is more than zero
    if(charge == 0) {
        return false;
    }

    auto global_deposit_position = detector_->getGlobalPosition(deposit_position);

    // Deposit electron
    deposits_.emplace_back(deposit_position, global_deposit_position, CarrierType::ELECTRON, charge, mid_time);
    deposit_to_id_.push_back(trackID);

    // Deposit hole
    deposits_.emplace_back(deposit_position, global_deposit_position, CarrierType::HOLE, charge, mid_time);
    deposit_to_id_.push_back(trackID);

    LOG(DEBUG) << "Created deposit of " << charge << " charges at " << Units::display(mid_pos, {"mm", "um"})
               << " locally on " << Units::display(deposit_position, {"mm", "um"}) << " in " << detector_->getName()
               << " after " << Units::display(mid_time, {"ns", "ps"});

    LOG(DEBUG) << "Geant4 transformation to local: " << Units::display(deposit_position_g4loc, {"mm", "um"});
    if((deposit_position_g4loc - deposit_position).mag2() > 0.001) {
        LOG(ERROR) << "Difference G4 to internal: "
                   << Units::display((deposit_position_g4loc - deposit_position), {"mm", "um"});
    }
    return true;
}

std::string SensitiveDetectorActionG4::getName() {
    return detector_->getName();
}

unsigned int SensitiveDetectorActionG4::getTotalDepositedCharge() {
    return total_deposited_charge_;
}

unsigned int SensitiveDetectorActionG4::getDepositedCharge() {
    return deposited_charge_;
}

void SensitiveDetectorActionG4::dispatchMessages() {
    // Create the mc particles
    std::vector<MCParticle> mc_particles;
    for(auto& track_id_point : track_begin_) {
        auto track_id = track_id_point.first;
        auto local_begin = track_id_point.second;

        ROOT::Math::XYZPoint end_point;
        auto local_end = track_end_.at(track_id);
        auto pdg_code = track_pdg_.at(track_id);
        auto track_time = track_time_.at(track_id);

        auto global_begin = detector_->getGlobalPosition(local_begin);
        auto global_end = detector_->getGlobalPosition(local_end);
        mc_particles.emplace_back(local_begin, global_begin, local_end, global_end, pdg_code, track_time);
        mc_particles.back().setTrack(track_info_manager_->findMCTrack(track_id));
        id_to_particle_[track_id] = mc_particles.size() - 1;

        LOG(DEBUG) << "Found MC particle " << pdg_code << " crossing detector " << detector_->getName() << " from "
                   << Units::display(local_begin, {"mm", "um"}) << " to " << Units::display(local_end, {"mm", "um"})
                   << " (local coordinates) at " << Units::display(track_time, {"us", "ns", "ps"});
    }

    for(auto& track_parent : track_parents_) {
        auto track_id = track_parent.first;
        auto parent_id = track_parent.second;
        if(id_to_particle_.find(parent_id) == id_to_particle_.end()) {
            // Skip tracks without direct parents with deposits
            // FIXME: Geant4 does not allow for an easy way retrieve the whole hierarchy
            continue;
        }
        auto track_idx = id_to_particle_.at(track_id);
        auto parent_idx = id_to_particle_.at(parent_id);
        mc_particles.at(track_idx).setParent(&mc_particles.at(parent_idx));
    }

    // Send the mc particle information
    auto mc_particle_message = std::make_shared<MCParticleMessage>(std::move(mc_particles), detector_);
    messenger_->dispatchMessage(module_, mc_particle_message);

    // Clear track data for the next event
    track_parents_.clear();
    track_begin_.clear();
    track_end_.clear();
    track_pdg_.clear();
    track_time_.clear();

    // Send a deposit message if we have any deposits
    if(!deposits_.empty()) {
        unsigned int charges = 0;
        for(auto& ch : deposits_) {
            charges += ch.getCharge();
            total_deposited_charge_ += ch.getCharge();
        }
        LOG(INFO) << "Deposited " << charges << " charges in sensor of detector " << detector_->getName();

        // Store the number of charge carriers:
        deposited_charge_ = charges;

        // Match deposit with mc particle if possible
        for(size_t i = 0; i < deposits_.size(); ++i) {
            auto track_id = deposit_to_id_.at(i);
            deposits_.at(i).setMCParticle(&mc_particle_message->getData().at(id_to_particle_.at(track_id)));
        }

        // Create a new charge deposit message
        auto deposit_message = std::make_shared<DepositedChargeMessage>(std::move(deposits_), detector_);

        // Dispatch the message
        messenger_->dispatchMessage(module_, deposit_message);
    }

    // Clear deposits for next event
    deposits_ = std::vector<DepositedCharge>();

    // Clear link tables for next event
    deposit_to_id_.clear();
    id_to_particle_.clear();
}
<|endoftext|>


<commit_before>// For conditions of distribution and use, see copyright notice in LICENSE

#include "DebugOperatorNew.h"

#include "AssetCache.h"
#include "AssetAPI.h"
#include "IAsset.h"

#include "CoreDefines.h"
#include "Framework.h"
#include "LoggingFunctions.h"

#include <QDateTime>
#include <QUrl>
#include <QFile>
#include <QDataStream>
#include <QFileInfo>
#include <QScopedPointer>

#ifdef Q_WS_WIN
#include "Win.h"
#else
#include <sys/stat.h>
#include <utime.h>
#endif

#include "MemoryLeakCheck.h"

AssetCache::AssetCache(AssetAPI *owner, QString assetCacheDirectory) :
    assetAPI(owner),
    cacheDirectory(GuaranteeTrailingSlash(QDir::fromNativeSeparators(assetCacheDirectory)))
{
    LogInfo("* Asset cache directory: " + cacheDirectory);

    // Check that the main directory exists
    QDir assetDir(cacheDirectory);
    if (!assetDir.exists())
    {
        QString dirName = cacheDirectory.split("/", QString::SkipEmptyParts).last();
        QString parentPath = cacheDirectory;
        parentPath.chop(dirName.length()+1);
        QDir parentDir(parentPath);
        parentDir.mkdir(dirName);
    }

    // Check that the needed subfolders exist
    if (!assetDir.exists("data"))
        assetDir.mkdir("data");
    assetDataDir = QDir(cacheDirectory + "data");

    // Check --clear-asset-cache start param
    if (owner->GetFramework()->HasCommandLineParameter("--clear-asset-cache"))
    {
        LogInfo("AssetCache: Removing all data and metadata files from cache, found 'clear-asset-cache' from start params!");
        ClearAssetCache();
    }
}

QString AssetCache::FindInCache(const QString &assetRef)
{
    QString absolutePath = GetDiskSourceByRef(assetRef);
    if (QFile::exists(absolutePath))
        return absolutePath;
    else // The file is not in cache, return an empty string to denote that.
        return "";
}

QString AssetCache::GetDiskSourceByRef(const QString &assetRef)
{
    // Return the path where the given asset ref would be stored, if it was saved in the cache
    // (regardless of whether it now exists in the cache).
    return assetDataDir.absolutePath() + "/" + AssetAPI::SanitateAssetRef(assetRef);
}

QString AssetCache::CacheDirectory() const
{
    return GuaranteeTrailingSlash(assetDataDir.absolutePath());
}

QString AssetCache::StoreAsset(AssetPtr asset)
{
    std::vector<u8> data;
    asset->SerializeTo(data);
    return StoreAsset(&data[0], data.size(), asset->Name());
}

QString AssetCache::StoreAsset(const u8 *data, size_t numBytes, const QString &assetName)
{
    QString absolutePath = GetDiskSourceByRef(assetName);
    bool success = SaveAssetFromMemoryToFile(data, numBytes, absolutePath);
    if (success)
        return absolutePath;
    return "";
}

QDateTime AssetCache::LastModified(const QString &assetRef)
{
    QString absolutePath = FindInCache(assetRef);
    if (absolutePath.isEmpty())
        return QDateTime();

#ifdef Q_WS_WIN
    HANDLE fileHandle = (HANDLE)OpenFileHandle(absolutePath);
    if (fileHandle == INVALID_HANDLE_VALUE)
    {
        LogError("AssetCache: Failed to open cache file to read last modified time: " + assetRef);
        return QDateTime();
    }

    // Get last write time.
    FILETIME fileTime;
    BOOL success = GetFileTime(fileHandle, 0, 0, &fileTime); // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724320(v=VS.85).aspx
    CloseHandle(fileHandle);
    if (!success)
    {
        LogError("AssetCache: Failed to read cache file last modified time: " + assetRef);
        return QDateTime();
    }

    // Convert to UTC.
    SYSTEMTIME sysTime;
    if (!FileTimeToSystemTime(&fileTime, &sysTime)) // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724280(v=VS.85).aspx
    {
        LogError("Win32 FileTimeToSystemTime failed for asset ref " + assetRef);
        return QDateTime();
    }

    // Ignore msec
    QDateTime dateTime;
    dateTime.setTimeSpec(Qt::UTC);
    dateTime.setDate(QDate((int)sysTime.wYear, (int)sysTime.wMonth, (int)sysTime.wDay));
    dateTime.setTime(QTime((int)sysTime.wHour, (int)sysTime.wMinute, (int)sysTime.wSecond, 0));
    return dateTime;
#else
    QDateTime dateTime;
    QString nativePath = QDir::toNativeSeparators(absolutePath);
    struct stat fileStats;
    if (stat(nativePath.toStdString().c_str(), &fileStats) == 0)
    {
        qint64 msecFromEpoch = (qint64)fileStats.st_mtime * 1000;
        dateTime.setMSecsSinceEpoch(msecFromEpoch);
    }
    else
        LogError("AssetCache: Failed to read cache file last modified time: " + assetRef);
    return dateTime;
#endif
}

bool AssetCache::SetLastModified(const QString &assetRef, const QDateTime &dateTime)
{
    if (!dateTime.isValid())
    {
        LogError("SetLastModified() DateTime is invalid: " + assetRef);
        return false;
    }

    QString absolutePath = FindInCache(assetRef);
    if (absolutePath.isEmpty())
        return false;

    QDate date = dateTime.date();
    QTime time = dateTime.time();

#ifdef Q_WS_WIN
    HANDLE fileHandle = (HANDLE)OpenFileHandle(absolutePath);
    if (fileHandle == INVALID_HANDLE_VALUE)
    {
        LogError("AssetCache: Failed to open cache file to update last modified time: " + assetRef);
        return false;
    }

    // Notes: For SYSTEMTIME Sunday is 0 and ignore msec.
    SYSTEMTIME sysTime;
    sysTime.wDay = (WORD)date.day();
    sysTime.wDayOfWeek = (WORD)date.dayOfWeek();
    if (sysTime.wDayOfWeek == 7)
        sysTime.wDayOfWeek = 0;
    sysTime.wMonth = (WORD)date.month();
    sysTime.wYear = (WORD)date.year();
    sysTime.wHour = (WORD)time.hour();
    sysTime.wMinute = (WORD)time.minute();
    sysTime.wSecond = (WORD)time.second();
    sysTime.wMilliseconds = 0;

    // Set last write time
    FILETIME fileTime;
    BOOL success = SystemTimeToFileTime(&sysTime, &fileTime);
    if (success)
        success = SetFileTime(fileHandle, 0, 0, &fileTime);
    CloseHandle(fileHandle);
    if (!success)
    {
        LogError("AssetCache: Failed to update cache file last modified time: " + assetRef);
        return false;
    }
    return true;
#else
    QString nativePath = QDir::toNativeSeparators(absolutePath);
    utimbuf modTime;
    modTime.actime = (time_t)(dateTime.toMSecsSinceEpoch() / 1000);
    modTime.modtime = (time_t)(dateTime.toMSecsSinceEpoch() / 1000);
    if (utime(nativePath.toStdString().c_str(), &modTime) == -1)
    {
        LogError("AssetCache: Failed to read cache file last modified time: " + assetRef);
        return false;
    }
    else
        return true;
#endif
}

#ifdef Q_WS_WIN
void *AssetCache::OpenFileHandle(const QString &absolutePath)
{
    QString nativePath = QDir::toNativeSeparators(absolutePath);
    QByteArray fileBA = nativePath.toLocal8Bit();
    WCHAR szFilePath[MAX_PATH];
    MultiByteToWideChar(CP_ACP, 0, fileBA.data(), -1, szFilePath, NUMELEMS(szFilePath));
    return CreateFile(szFilePath, GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
}
#endif

void AssetCache::DeleteAsset(const QString &assetRef)
{
    QString absolutePath = GetDiskSourceByRef(assetRef);
    if (QFile::exists(absolutePath))
        QFile::remove(absolutePath);
}

void AssetCache::ClearAssetCache()
{
    if (!assetDataDir.exists())
        return;
    QFileInfoList entries = assetDataDir.entryInfoList(QDir::Files|QDir::NoSymLinks|QDir::NoDotAndDotDot);
    foreach(QFileInfo entry, entries)
    {
        if (entry.isFile())
        {
            if (!assetDataDir.remove(entry.fileName()))
                LogWarning("AssetCache::ClearAssetCache could not remove file " + entry.absoluteFilePath());
        }
    }
}
<commit_msg>AssetCache: Fix opening/creating files via windows api that filename is longer than 260 characters. Fixes #347.<commit_after>// For conditions of distribution and use, see copyright notice in LICENSE

#include "DebugOperatorNew.h"

#include "AssetCache.h"
#include "AssetAPI.h"
#include "IAsset.h"

#include "CoreDefines.h"
#include "Framework.h"
#include "LoggingFunctions.h"

#include <QDateTime>
#include <QUrl>
#include <QFile>
#include <QDataStream>
#include <QFileInfo>
#include <QScopedPointer>

#ifdef Q_WS_WIN
#include "Win.h"
#else
#include <sys/stat.h>
#include <utime.h>
#endif

#include "MemoryLeakCheck.h"

AssetCache::AssetCache(AssetAPI *owner, QString assetCacheDirectory) :
    assetAPI(owner),
    cacheDirectory(GuaranteeTrailingSlash(QDir::fromNativeSeparators(assetCacheDirectory)))
{
    LogInfo("* Asset cache directory: " + cacheDirectory);

    // Check that the main directory exists
    QDir assetDir(cacheDirectory);
    if (!assetDir.exists())
    {
        QString dirName = cacheDirectory.split("/", QString::SkipEmptyParts).last();
        QString parentPath = cacheDirectory;
        parentPath.chop(dirName.length()+1);
        QDir parentDir(parentPath);
        parentDir.mkdir(dirName);
    }

    // Check that the needed subfolders exist
    if (!assetDir.exists("data"))
        assetDir.mkdir("data");
    assetDataDir = QDir(cacheDirectory + "data");

    // Check --clear-asset-cache start param
    if (owner->GetFramework()->HasCommandLineParameter("--clear-asset-cache"))
    {
        LogInfo("AssetCache: Removing all data and metadata files from cache, found 'clear-asset-cache' from start params!");
        ClearAssetCache();
    }
}

QString AssetCache::FindInCache(const QString &assetRef)
{
    QString absolutePath = GetDiskSourceByRef(assetRef);
    if (QFile::exists(absolutePath))
        return absolutePath;
    else // The file is not in cache, return an empty string to denote that.
        return "";
}

QString AssetCache::GetDiskSourceByRef(const QString &assetRef)
{
    // Return the path where the given asset ref would be stored, if it was saved in the cache
    // (regardless of whether it now exists in the cache).
    return assetDataDir.absolutePath() + "/" + AssetAPI::SanitateAssetRef(assetRef);
}

QString AssetCache::CacheDirectory() const
{
    return GuaranteeTrailingSlash(assetDataDir.absolutePath());
}

QString AssetCache::StoreAsset(AssetPtr asset)
{
    std::vector<u8> data;
    asset->SerializeTo(data);
    return StoreAsset(&data[0], data.size(), asset->Name());
}

QString AssetCache::StoreAsset(const u8 *data, size_t numBytes, const QString &assetName)
{
    QString absolutePath = GetDiskSourceByRef(assetName);
    bool success = SaveAssetFromMemoryToFile(data, numBytes, absolutePath);
    if (success)
        return absolutePath;
    return "";
}

QDateTime AssetCache::LastModified(const QString &assetRef)
{
    QString absolutePath = FindInCache(assetRef);
    if (absolutePath.isEmpty())
        return QDateTime();

#ifdef Q_WS_WIN
    HANDLE fileHandle = (HANDLE)OpenFileHandle(absolutePath);
    if (fileHandle == INVALID_HANDLE_VALUE)
    {
        LogError("AssetCache: Failed to open cache file to read last modified time: " + assetRef);
        return QDateTime();
    }

    // Get last write time.
    FILETIME fileTime;
    BOOL success = GetFileTime(fileHandle, 0, 0, &fileTime); // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724320(v=VS.85).aspx
    CloseHandle(fileHandle);
    if (!success)
    {
        LogError("AssetCache: Failed to read cache file last modified time: " + assetRef);
        return QDateTime();
    }

    // Convert to UTC.
    SYSTEMTIME sysTime;
    if (!FileTimeToSystemTime(&fileTime, &sysTime)) // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724280(v=VS.85).aspx
    {
        LogError("Win32 FileTimeToSystemTime failed for asset ref " + assetRef);
        return QDateTime();
    }

    // Ignore msec
    QDateTime dateTime;
    dateTime.setTimeSpec(Qt::UTC);
    dateTime.setDate(QDate((int)sysTime.wYear, (int)sysTime.wMonth, (int)sysTime.wDay));
    dateTime.setTime(QTime((int)sysTime.wHour, (int)sysTime.wMinute, (int)sysTime.wSecond, 0));
    return dateTime;
#else
    QDateTime dateTime;
    QString nativePath = QDir::toNativeSeparators(absolutePath);
    struct stat fileStats;
    if (stat(nativePath.toStdString().c_str(), &fileStats) == 0)
    {
        qint64 msecFromEpoch = (qint64)fileStats.st_mtime * 1000;
        dateTime.setMSecsSinceEpoch(msecFromEpoch);
    }
    else
        LogError("AssetCache: Failed to read cache file last modified time: " + assetRef);
    return dateTime;
#endif
}

bool AssetCache::SetLastModified(const QString &assetRef, const QDateTime &dateTime)
{
    if (!dateTime.isValid())
    {
        LogError("SetLastModified() DateTime is invalid: " + assetRef);
        return false;
    }

    QString absolutePath = FindInCache(assetRef);
    if (absolutePath.isEmpty())
        return false;

    QDate date = dateTime.date();
    QTime time = dateTime.time();

#ifdef Q_WS_WIN
    HANDLE fileHandle = (HANDLE)OpenFileHandle(absolutePath);
    if (fileHandle == INVALID_HANDLE_VALUE)
    {
        LogError("AssetCache: Failed to open cache file to update last modified time: " + assetRef);
        return false;
    }

    // Notes: For SYSTEMTIME Sunday is 0 and ignore msec.
    SYSTEMTIME sysTime;
    sysTime.wDay = (WORD)date.day();
    sysTime.wDayOfWeek = (WORD)date.dayOfWeek();
    if (sysTime.wDayOfWeek == 7)
        sysTime.wDayOfWeek = 0;
    sysTime.wMonth = (WORD)date.month();
    sysTime.wYear = (WORD)date.year();
    sysTime.wHour = (WORD)time.hour();
    sysTime.wMinute = (WORD)time.minute();
    sysTime.wSecond = (WORD)time.second();
    sysTime.wMilliseconds = 0;

    // Set last write time
    FILETIME fileTime;
    BOOL success = SystemTimeToFileTime(&sysTime, &fileTime);
    if (success)
        success = SetFileTime(fileHandle, 0, 0, &fileTime);
    CloseHandle(fileHandle);
    if (!success)
    {
        LogError("AssetCache: Failed to update cache file last modified time: " + assetRef);
        return false;
    }
    return true;
#else
    QString nativePath = QDir::toNativeSeparators(absolutePath);
    utimbuf modTime;
    modTime.actime = (time_t)(dateTime.toMSecsSinceEpoch() / 1000);
    modTime.modtime = (time_t)(dateTime.toMSecsSinceEpoch() / 1000);
    if (utime(nativePath.toStdString().c_str(), &modTime) == -1)
    {
        LogError("AssetCache: Failed to read cache file last modified time: " + assetRef);
        return false;
    }
    else
        return true;
#endif
}

#ifdef Q_WS_WIN
void *AssetCache::OpenFileHandle(const QString &absolutePath)
{
    QString nativePath = QDir::toNativeSeparators(absolutePath);
    QByteArray fileBA = nativePath.toLocal8Bit();
    if (fileBA.size() < MAX_PATH)
    {
        WCHAR szFilePath[MAX_PATH];
        MultiByteToWideChar(CP_ACP, 0, fileBA.data(), -1, szFilePath, NUMELEMS(szFilePath));
        return CreateFile(szFilePath, GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    }
    else
    {
        // Prefixing file path with \\?\ is windows magic, see more from
        // http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
        // This allows use to open >MAX_PATH length file paths which do happen in asset cache if the
        // source url is long! Assume 260*3 will be enough as it still very rarely goes over 260.
        fileBA = "\\\\?\\" + fileBA;
        WCHAR szFilePath[MAX_PATH*3];
        MultiByteToWideChar(CP_ACP, 0, fileBA.data(), -1, szFilePath, NUMELEMS(szFilePath));
        return CreateFileW(szFilePath, GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    }
}
#endif

void AssetCache::DeleteAsset(const QString &assetRef)
{
    QString absolutePath = GetDiskSourceByRef(assetRef);
    if (QFile::exists(absolutePath))
        QFile::remove(absolutePath);
}

void AssetCache::ClearAssetCache()
{
    if (!assetDataDir.exists())
        return;
    QFileInfoList entries = assetDataDir.entryInfoList(QDir::Files|QDir::NoSymLinks|QDir::NoDotAndDotDot);
    foreach(QFileInfo entry, entries)
    {
        if (entry.isFile())
        {
            if (!assetDataDir.remove(entry.fileName()))
                LogWarning("AssetCache::ClearAssetCache could not remove file " + entry.absoluteFilePath());
        }
    }
}
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "hex_map.h"

#include <array>

#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include "asdf_multiplat/data/gl_resources.h"
#include "asdf_multiplat/data/content_manager.h"

using namespace std;
using namespace glm;

namespace asdf
{
    using namespace util;
    using namespace data;

namespace hexmap
{
namespace ui
{
    //const glm::vec4 grid_color(0.0f, 0.0f, 0.0f, 1.0f);
    const glm::vec4 grid_color(1.0f, 1.0f, 1.0f, 1.0f);
    constexpr float grid_overlay_thickness = 2.0f;

    constexpr char imported_textures_json_filename[] = "imported_textures.json";

    constexpr int apply_hexagon_textures = 1;

    gl_vertex_spec_<vertex_attrib::position3_t/*, vertex_attrib::color_t*/> hexagon_vertex_t::vertex_spec;

    hex_map_t::hex_map_t(data::hex_grid_t& _hex_grid)
    : hex_grid(_hex_grid)
    {

        shader = Content.shaders["hexmap"];

        //camera_controller.position.x = 5; //5 hexes right
        camera_controller.position.z = 10.0; // zoom is camera.position.z ^ 2

        std::vector<hexagon_vertex_t> verts(6);

        for(size_t i = 0; i < 6; ++i)
        {
            verts[i].position.x = hexagon_points[i*3 + 0];
            verts[i].position.y = hexagon_points[i*3 + 1];
            verts[i].position.z = hexagon_points[i*3 + 2];

            //verts[i].color = grid_color;
        }

        hexagon.set_data(verts, shader);
        hexagon.draw_mode = GL_LINE_LOOP;

        ASSERT(!CheckGLError(), "");
        GL_State->bind(hexagons_vao);

            GL_State->bind(hexagon.vbo);
            hexagon_vertex_t::vertex_spec.set_vertex_attribs(shader);
            ASSERT(!CheckGLError(), "Error setting vertex attributes");

            GL_State->bind(hex_gl_data);
            GLint attrib_loc = glGetAttribLocation(shader->shader_program_id, "TileID");
            glEnableVertexAttribArray(attrib_loc); //tell the location
            glVertexAttribIPointer(attrib_loc, 1, GL_UNSIGNED_INT, sizeof(data::hex_tile_id_t), 0);  // must use Atrib ** I ** for unsigned int to be used as a uint in glsl
            glVertexAttribDivisor(attrib_loc, 1); //second arg is 1, which means the vertex buffer for hexagon tile ID is incremented every instance, instead of every vertex

        GL_State->unbind_vao();

        ASSERT(!CheckGLError(), "");

        std::array<glm::vec4, num_tile_colors> colors =
        {
              COLOR_RED
            , COLOR_GREEN
            , COLOR_BLUE
            , COLOR_CYAN
            , COLOR_YELLOW
            , COLOR_MAGENTA
            , COLOR_TEAL
            , COLOR_ORANGE
            , COLOR_LIGHTGREY
            , COLOR_GREY
        };

        set_tile_colors(colors);
        ASSERT(!CheckGLError(), "");

        auto dir = find_folder("data");
        ASSERT(dir.length() > 0, "Could not find data folder");

        auto imported_textures_json_filepath = dir + "/" + string(imported_textures_json_filename);
        texture_bank.load_from_list_file(imported_textures_json_filepath);

        ojects_atlas = make_unique<texture_atlas_t>(string(dir + "/../assets/Objects/objects_atlas_data.json"));
    }

    void hex_map_t::set_tile_colors(std::array<glm::vec4, num_tile_colors> const& colors)
    {
        GL_State->bind(shader);
        glUniform4fv(shader->uniform("TileColors[0]"), num_tile_colors, reinterpret_cast<const GLfloat*>(colors.data()));
        ASSERT(!CheckGLError(), "");
    }

    void hex_map_t::update(float dt)
    {
        camera_controller.update(dt);
        camera.position = camera_controller.position;
    }

    void hex_map_t::render()
    {
        ASSERT(hex_grid.chunks.size(), "");
        ASSERT(hex_grid.chunks[0].size(), "");

        auto w = static_cast<float>(app.settings.resolution_width);
        auto h = static_cast<float>(app.settings.resolution_height);  ///FIXME subtract size of window title bar if necessary

        camera.viewport.size_d2 = vec2(w,h) / 2.0f;
        camera.viewport.bottom_left = -camera.viewport.size_d2;

        shader->view_matrix       = camera.view_matrix();
        shader->projection_matrix = camera.projection_ortho();

        GL_State->bind(shader);
        glUniform1i(shader->uniform("ApplyTexture"), apply_hexagon_textures);

        hex_grid.for_each_chunk( [this](data::hex_grid_chunk_t& chunk) -> void
        {
            render_chunk(chunk);
        });

        glUniform1i(shader->uniform("ApplyTexture"), 0);  //turn of textures for the grid
        render_grid_overlay(hex_grid.size);

        //TEST
        // re-importing every frame so I can capture it with nvidia's gfx debugger
        // GL_State->bind(texture_bank.atlas_fbo);
        // glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // auto dir = find_folder("data");
        // auto imported_textures_json_filepath = dir + "/" + string(imported_textures_json_filename);
        // texture_bank.saved_textures.clear(); //reset so I'm not infinitely piling stuff on
        // texture_bank.load_from_list_file(imported_textures_json_filepath);
        // glBindTexture(GL_TEXTURE_2D, texture_bank.atlas_texture.texture_id);
        //---
    }

    void hex_map_t::on_event(SDL_Event* event)
    {
        camera_controller.on_event(event);
        camera.position = camera_controller.position;
    }

    void hex_map_t::render_chunk(data::hex_grid_chunk_t const& chunk)
    {
        GL_State->bind(hexagons_vao);

        hex_gl_data.set_data(chunk);

        float chunk_width_cells = hex_width_d4 * 3 * data::max_chunk_width;
        float chunk_height_cells = hex_height * data::max_chunk_height;

        shader->world_matrix[3][0] = chunk.position.x * chunk_width_cells;
        shader->world_matrix[3][1] = chunk.position.y * chunk_height_cells;
        shader->update_wvp_uniform();

        glUniform4f(shader->uniform("Color"), 1.0f, 1.0f, 1.0f, 1.0f);

        glBindTexture(GL_TEXTURE_2D, texture_bank.atlas_texture.texture_id);

        render_hexagons(glm::ivec2(chunk.size.x, chunk.size.y), GL_TRIANGLE_FAN);
        GL_State->unbind_vao();
    }

    void hex_map_t::render_grid_overlay(glm::ivec2 grid_size)
    {
        GL_State->bind(shader);
        GL_State->bind(hexagon.vao);

        glLineWidth(grid_overlay_thickness);

        shader->world_matrix[3][0] = 0.0f;
        shader->world_matrix[3][1] = 0.0f;
        shader->update_wvp_uniform();

        glUniform4fv( shader->uniform("Color"), 1, glm::value_ptr(grid_color) );

        render_hexagons(grid_size, hexagon.draw_mode);
        GL_State->unbind_vao();
    }

    void hex_map_t::render_hexagons(glm::ivec2 grid_size, GLuint draw_mode)
    {
        auto loc = shader->uniform("CHUNK_HEIGHT");
        glUniform1i(loc, grid_size.y);

        size_t n = grid_size.x * grid_size.y;
        glDrawArraysInstanced(draw_mode, 0, 6, n); //start at 0th, draw 6 points per shape, draw (width/2)
    }

    void hex_buffer_data_t::set_data(data::hex_grid_chunk_t const& chunk)
    {
        std::array<data::hex_tile_id_t, data::max_chunk_width * data::max_chunk_height> cell_data;

        for(size_t x = 0; x < chunk.size.x; ++x)
        {
            for(size_t y = 0; y < chunk.size.y; ++y)
            {
                //cell_data[x*chunk.size.y + y] = rand() % 10;
                //cell_data[x*chunk.size.y + y] = chunk.position.x + abs(chunk.position.y);  //set id to chunk pos for debugging chunks

                cell_data[x*chunk.size.y + y] = chunk.cells[x][y].tile_id;
            }
        }

        //GL_State->bind(*this);
        GL_State->buffer_data(*this, (cell_data.size() * sizeof(data::hex_tile_id_t)), reinterpret_cast<GLvoid*>(cell_data.data()) );

        ASSERT(!CheckGLError(), "Error setting hexagon buffer data");
    }
}
}
}<commit_msg>minor fix<commit_after>#include "stdafx.h"
#include "hex_map.h"

#include <array>

#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include "asdf_multiplat/data/gl_resources.h"
#include "asdf_multiplat/data/content_manager.h"

using namespace std;
using namespace glm;

namespace asdf
{
    using namespace util;
    using namespace data;

namespace hexmap
{
namespace ui
{
    //const glm::vec4 grid_color(0.0f, 0.0f, 0.0f, 1.0f);
    const glm::vec4 grid_color(1.0f, 1.0f, 1.0f, 1.0f);
    constexpr float grid_overlay_thickness = 2.0f;

    constexpr char imported_textures_json_filename[] = "imported_textures.json";

    constexpr int apply_hexagon_textures = 1;

    gl_vertex_spec_<vertex_attrib::position3_t/*, vertex_attrib::color_t*/> hexagon_vertex_t::vertex_spec;

    hex_map_t::hex_map_t(data::hex_grid_t& _hex_grid)
    : hex_grid(_hex_grid)
    {

        shader = Content.shaders["hexmap"];

        //camera_controller.position.x = 5; //5 hexes right
        camera_controller.position.z = 10.0; // zoom is camera.position.z ^ 2

        std::vector<hexagon_vertex_t> verts(6);

        for(size_t i = 0; i < 6; ++i)
        {
            verts[i].position.x = hexagon_points[i*3 + 0];
            verts[i].position.y = hexagon_points[i*3 + 1];
            verts[i].position.z = hexagon_points[i*3 + 2];

            //verts[i].color = grid_color;
        }

        hexagon.set_data(verts, shader);
        hexagon.draw_mode = GL_LINE_LOOP;

        ASSERT(!CheckGLError(), "");
        GL_State->bind(hexagons_vao);

            GL_State->bind(hexagon.vbo);
            hexagon_vertex_t::vertex_spec.set_vertex_attribs(shader);
            ASSERT(!CheckGLError(), "Error setting vertex attributes");

            GL_State->bind(hex_gl_data);
            GLint attrib_loc = glGetAttribLocation(shader->shader_program_id, "TileID");
            glEnableVertexAttribArray(attrib_loc); //tell the location
            glVertexAttribIPointer(attrib_loc, 1, GL_UNSIGNED_INT, sizeof(data::hex_tile_id_t), 0);  // must use Atrib ** I ** for unsigned int to be used as a uint in glsl
            glVertexAttribDivisor(attrib_loc, 1); //second arg is 1, which means the vertex buffer for hexagon tile ID is incremented every instance, instead of every vertex

        GL_State->unbind_vao();

        ASSERT(!CheckGLError(), "");

        std::array<glm::vec4, num_tile_colors> colors =
        {
              COLOR_RED
            , COLOR_GREEN
            , COLOR_BLUE
            , COLOR_CYAN
            , COLOR_YELLOW
            , COLOR_MAGENTA
            , COLOR_TEAL
            , COLOR_ORANGE
            , COLOR_LIGHTGREY
            , COLOR_GREY
        };

        set_tile_colors(colors);
        ASSERT(!CheckGLError(), "");

        auto dir = find_folder("data");
        ASSERT(dir.length() > 0, "Could not find data folder");

        auto imported_textures_json_filepath = dir + "/" + string(imported_textures_json_filename);
        texture_bank.load_from_list_file(imported_textures_json_filepath);

        ojects_atlas = make_unique<texture_atlas_t>(string(dir + "/../assets/Objects/objects_atlas_data.json"));
    }

    void hex_map_t::set_tile_colors(std::array<glm::vec4, num_tile_colors> const& colors)
    {
        GL_State->bind(shader);
        glUniform4fv(shader->uniform("TileColors[0]"), num_tile_colors, reinterpret_cast<const GLfloat*>(colors.data()));
        ASSERT(!CheckGLError(), "");
    }

    void hex_map_t::update(float dt)
    {
        camera_controller.update(dt);
        camera.position = camera_controller.position;
    }

    void hex_map_t::render()
    {
        ASSERT(hex_grid.chunks.size(), "");
        ASSERT(hex_grid.chunks[0].size(), "");

        auto w = static_cast<float>(app.settings.resolution_width);
        auto h = static_cast<float>(app.settings.resolution_height);  ///FIXME subtract size of window title bar if necessary

        camera.viewport.size_d2 = vec2(w,h) / 2.0f;
        camera.viewport.bottom_left = -camera.viewport.size_d2;

        shader->view_matrix       = camera.view_matrix();
        shader->projection_matrix = camera.projection_ortho();

        GL_State->bind(shader);
        glUniform1i(shader->uniform("ApplyTexture"), apply_hexagon_textures);

        hex_grid.for_each_chunk( [this](data::hex_grid_chunk_t& chunk) -> void
        {
            render_chunk(chunk);
        });

        glUniform1i(shader->uniform("ApplyTexture"), 0);  //turn of textures for the grid
        render_grid_overlay(hex_grid.size);

        //TEST
        // re-importing every frame so I can capture it with nvidia's gfx debugger
        // GL_State->bind(texture_bank.atlas_fbo);
        // glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // auto dir = find_folder("data");
        // auto imported_textures_json_filepath = dir + "/" + string(imported_textures_json_filename);
        // texture_bank.saved_textures.clear(); //reset so I'm not infinitely piling stuff on
        // texture_bank.load_from_list_file(imported_textures_json_filepath);
        // glBindTexture(GL_TEXTURE_2D, texture_bank.atlas_texture.texture_id);
        //---
    }

    void hex_map_t::on_event(SDL_Event* event)
    {
        camera_controller.on_event(event);
        camera.position = camera_controller.position;
    }

    void hex_map_t::render_chunk(data::hex_grid_chunk_t const& chunk)
    {
        GL_State->bind(hexagons_vao);

        hex_gl_data.set_data(chunk);

        float chunk_width_cells = hex_width_d4 * 3 * chunk.allocated_size.x;
        float chunk_height_cells = hex_height * chunk.allocated_size.y;

        shader->world_matrix[3][0] = chunk.position.x * chunk_width_cells;
        shader->world_matrix[3][1] = chunk.position.y * chunk_height_cells;
        shader->update_wvp_uniform();

        glUniform4f(shader->uniform("Color"), 1.0f, 1.0f, 1.0f, 1.0f);

        glBindTexture(GL_TEXTURE_2D, texture_bank.atlas_texture.texture_id);

        render_hexagons(glm::ivec2(chunk.size.x, chunk.size.y), GL_TRIANGLE_FAN);
        GL_State->unbind_vao();
    }

    void hex_map_t::render_grid_overlay(glm::ivec2 grid_size)
    {
        GL_State->bind(shader);
        GL_State->bind(hexagon.vao);

        glLineWidth(grid_overlay_thickness);

        shader->world_matrix[3][0] = 0.0f;
        shader->world_matrix[3][1] = 0.0f;
        shader->update_wvp_uniform();

        glUniform4fv( shader->uniform("Color"), 1, glm::value_ptr(grid_color) );

        render_hexagons(grid_size, hexagon.draw_mode);
        GL_State->unbind_vao();
    }

    void hex_map_t::render_hexagons(glm::ivec2 grid_size, GLuint draw_mode)
    {
        auto loc = shader->uniform("CHUNK_HEIGHT");
        glUniform1i(loc, grid_size.y);

        size_t n = grid_size.x * grid_size.y;
        glDrawArraysInstanced(draw_mode, 0, 6, n); //start at 0th, draw 6 points per shape, draw (width/2)
    }

    void hex_buffer_data_t::set_data(data::hex_grid_chunk_t const& chunk)
    {
        //std::array<data::hex_tile_id_t, data::max_chunk_width * data::max_chunk_height> cell_data;
        std::vector<data::hex_tile_id_t> cell_data(chunk.size.x * chunk.size.y);

        for(size_t x = 0; x < chunk.size.x; ++x)
        {
            for(size_t y = 0; y < chunk.size.y; ++y)
            {
                //cell_data[x*chunk.size.y + y] = rand() % 10;
                //cell_data[x*chunk.size.y + y] = chunk.position.x + abs(chunk.position.y);  //set id to chunk pos for debugging chunks

                cell_data[x*chunk.size.y + y] = chunk.cells[x][y].tile_id;
            }
        }

        //GL_State->bind(*this);
        GL_State->buffer_data(*this, (cell_data.size() * sizeof(data::hex_tile_id_t)), reinterpret_cast<GLvoid*>(cell_data.data()) );

        ASSERT(!CheckGLError(), "Error setting hexagon buffer data");
    }
}
}
}<|endoftext|>