<commit_before>#include "Jitter.h"
#include <iostream>
#include <set>

#ifdef _DEBUG
//#define DUMP_STATEMENTS
#endif

#ifdef DUMP_STATEMENTS
#include <iostream>
#endif

using namespace Jitter;

void CJitter::AllocateRegisters(BASIC_BLOCK& basicBlock)
{
	auto& symbolTable = basicBlock.symbolTable;

	std::multimap<unsigned int, STATEMENT> loadStatements;
	std::multimap<unsigned int, STATEMENT> spillStatements;
#ifdef DUMP_STATEMENTS
	DumpStatementList(basicBlock.statements);
	std::cout << std::endl;
#endif

	auto allocRanges = ComputeAllocationRanges(basicBlock);
	for(const auto& allocRange : allocRanges)
	{
		SymbolRegAllocInfo symbolRegAllocs;
		ComputeLivenessForRange(basicBlock, allocRange, symbolRegAllocs);

		MarkAliasedSymbols(basicBlock, allocRange, symbolRegAllocs);

		AssociateSymbolsToRegisters(symbolRegAllocs);

		//Replace all references to symbols by references to allocated registers
		for(const auto& statementInfo : IndexedStatementList(basicBlock.statements))
		{
			auto& statement(statementInfo.statement);
			const auto& statementIdx(statementInfo.index);
			if(statementIdx < allocRange.first) continue;
			if(statementIdx > allocRange.second) break;

			statement.VisitOperands(
				[&] (SymbolRefPtr& symbolRef, bool)
				{
					auto symbol = symbolRef->GetSymbol();
					auto symbolRegAllocIterator = symbolRegAllocs.find(symbol);
					if(symbolRegAllocIterator != std::end(symbolRegAllocs))
					{
						const auto& symbolRegAlloc = symbolRegAllocIterator->second;
						if(symbolRegAlloc.registerId != -1)
						{
							symbolRef = MakeSymbolRef(
								symbolTable.MakeSymbol(symbolRegAlloc.registerType, symbolRegAlloc.registerId));
						}
					}
				}
			);
		}

		//Prepare load and spills
		for(const auto& symbolRegAllocPair : symbolRegAllocs)
		{
			const auto& symbol = symbolRegAllocPair.first;
			const auto& symbolRegAlloc = symbolRegAllocPair.second;

			//Check if it's actually allocated
			if(symbolRegAlloc.registerId == -1) continue;

			//firstUse == -1 means it is written to but never used afterwards in this block

			//Do we need to load register at the beginning?
			//If symbol is read and we use this symbol before we define it, so we need to load it first
			if((symbolRegAlloc.firstUse != -1) && (symbolRegAlloc.firstUse <= symbolRegAlloc.firstDef))
			{
				STATEMENT statement;
				statement.op	= OP_MOV;
				statement.dst	= std::make_shared<CSymbolRef>(
					symbolTable.MakeSymbol(symbolRegAlloc.registerType, symbolRegAlloc.registerId));
				statement.src1	= std::make_shared<CSymbolRef>(symbol);

				loadStatements.insert(std::make_pair(allocRange.first, statement));
			}

			//If symbol is defined, we need to save it at the end
			if(symbolRegAlloc.firstDef != -1)
			{
				STATEMENT statement;
				statement.op	= OP_MOV;
				statement.dst	= std::make_shared<CSymbolRef>(symbol);
				statement.src1	= std::make_shared<CSymbolRef>(
					symbolTable.MakeSymbol(symbolRegAlloc.registerType, symbolRegAlloc.registerId));

				spillStatements.insert(std::make_pair(allocRange.second, statement));
			}
		}
	}

#ifdef DUMP_STATEMENTS
	DumpStatementList(basicBlock.statements);
	std::cout << std::endl;
#endif

	std::map<unsigned int, StatementList::const_iterator> loadPoints;
	std::map<unsigned int, StatementList::const_iterator> spillPoints;

	//Load
	for(const auto& statementInfo : ConstIndexedStatementList(basicBlock.statements))
	{
		const auto& statementIdx(statementInfo.index);
		if(loadStatements.find(statementIdx) != std::end(loadStatements))
		{
			loadPoints.insert(std::make_pair(statementIdx, statementInfo.iterator));
		}
	}

	//Spill
	for(const auto& statementInfo : ConstIndexedStatementList(basicBlock.statements))
	{
		const auto& statementIdx(statementInfo.index);
		if(spillStatements.find(statementIdx) != std::end(spillStatements))
		{
			const auto& statement = statementInfo.statement;
			auto statementIterator = statementInfo.iterator;
			if((statement.op != OP_CONDJMP) && (statement.op != OP_JMP))
			{
				statementIterator++;
			}
			spillPoints.insert(std::make_pair(statementIdx, statementIterator));
		}
	}

	//Loads
	for(const auto& loadPoint : loadPoints)
	{
		unsigned int statementIndex = loadPoint.first;
		for(auto statementIterator = loadStatements.lower_bound(statementIndex);
			statementIterator != loadStatements.upper_bound(statementIndex);
			statementIterator++)
		{
			const auto& statement(statementIterator->second);
			basicBlock.statements.insert(loadPoint.second, statement);
		}
	}

	//Spills
	for(const auto& spillPoint : spillPoints)
	{
		unsigned int statementIndex = spillPoint.first;
		for(auto statementIterator = spillStatements.lower_bound(statementIndex);
			statementIterator != spillStatements.upper_bound(statementIndex);
			statementIterator++)
		{
			const auto& statement(statementIterator->second);
			basicBlock.statements.insert(spillPoint.second, statement);
		}
	}

#ifdef DUMP_STATEMENTS
	DumpStatementList(basicBlock.statements);
	std::cout << std::endl;
#endif
}

void CJitter::AssociateSymbolsToRegisters(SymbolRegAllocInfo& symbolRegAllocs) const
{
	std::multimap<SYM_TYPE, unsigned int> availableRegisters;
	{
		unsigned int regCount = m_codeGen->GetAvailableRegisterCount();
		for(unsigned int i = 0; i < regCount; i++)
		{
			availableRegisters.insert(std::make_pair(SYM_REGISTER, i));
		}
	}

	{
		unsigned int regCount = m_codeGen->GetAvailableMdRegisterCount();
		for(unsigned int i = 0; i < regCount; i++)
		{
			availableRegisters.insert(std::make_pair(SYM_REGISTER128, i));
		}
	}

	auto isRegisterAllocatable =
		[] (SYM_TYPE symbolType)
		{
			return
				(symbolType == SYM_RELATIVE) || (symbolType == SYM_TEMPORARY) ||
				(symbolType == SYM_RELATIVE128) || (symbolType == SYM_TEMPORARY128);
		};

	//Sort symbols by usage count
	std::list<SymbolRegAllocInfo::value_type*> sortedSymbols;
	for(auto& symbolRegAllocPair : symbolRegAllocs)
	{
		const auto& symbol(symbolRegAllocPair.first);
		const auto& symbolRegAlloc(symbolRegAllocPair.second);
		if(!isRegisterAllocatable(symbol->m_type)) continue;
		if(symbolRegAlloc.aliased) continue;
		sortedSymbols.push_back(&symbolRegAllocPair);
	}
	sortedSymbols.sort(
		[] (SymbolRegAllocInfo::value_type* symbolRegAllocPair1, SymbolRegAllocInfo::value_type* symbolRegAllocPair2)
		{
			return symbolRegAllocPair1->second.useCount > symbolRegAllocPair2->second.useCount;
		}
	);

	for(auto& symbolRegAllocPair : sortedSymbols)
	{
		if(availableRegisters.empty()) break;

		const auto& symbol = symbolRegAllocPair->first;
		auto& symbolRegAlloc = symbolRegAllocPair->second;

		//Find suitable register for this symbol
		auto registerIterator = std::end(availableRegisters);
		auto registerIteratorEnd = std::end(availableRegisters);
		if((symbol->m_type == SYM_RELATIVE) || (symbol->m_type == SYM_TEMPORARY))
		{
			registerIterator = availableRegisters.lower_bound(SYM_REGISTER);
			registerIteratorEnd = availableRegisters.upper_bound(SYM_REGISTER);
		}
		else if((symbol->m_type == SYM_RELATIVE128) || (symbol->m_type == SYM_TEMPORARY128))
		{
			registerIterator = availableRegisters.lower_bound(SYM_REGISTER128);
			registerIteratorEnd = availableRegisters.upper_bound(SYM_REGISTER128);
		}
		if(registerIterator != registerIteratorEnd)
		{
			symbolRegAlloc.registerType = registerIterator->first;
			symbolRegAlloc.registerId = registerIterator->second;
			availableRegisters.erase(registerIterator);
		}
	}
}

CJitter::AllocationRangeArray CJitter::ComputeAllocationRanges(const BASIC_BLOCK& basicBlock)
{
	AllocationRangeArray result;
	unsigned int currentStart = 0;
	for(const auto& statementInfo : ConstIndexedStatementList(basicBlock.statements))
	{
		const auto& statement(statementInfo.statement);
		const auto& statementIdx(statementInfo.index);
		if(statement.op == OP_CALL)
		{
			//Gotta split here
			result.push_back(std::make_pair(currentStart, statementIdx));
			currentStart = statementIdx + 1;
		}
	}
	result.push_back(std::make_pair(currentStart, basicBlock.statements.size() - 1));
	return result;
}

void CJitter::ComputeLivenessForRange(const BASIC_BLOCK& basicBlock, const AllocationRange& allocRange, SymbolRegAllocInfo& symbolRegAllocs) const
{
	auto& symbolTable(basicBlock.symbolTable);
	const auto& statements(basicBlock.statements);

	for(const auto& statementInfo : ConstIndexedStatementList(basicBlock.statements))
	{
		const auto& statement(statementInfo.statement);
		unsigned int statementIdx(statementInfo.index);
		if(statementIdx < allocRange.first) continue;
		if(statementIdx > allocRange.second) continue;

		statement.VisitDestination(
			[&] (const SymbolRefPtr& symbolRef, bool)
			{
				auto symbol(symbolRef->GetSymbol());
				auto& symbolRegAlloc = symbolRegAllocs[symbol];
				symbolRegAlloc.useCount++;
				if(symbolRegAlloc.firstDef == -1)
				{
					symbolRegAlloc.firstDef = statementIdx;
				}
				if((symbolRegAlloc.lastDef == -1) || (statementIdx > symbolRegAlloc.lastDef))
				{
					symbolRegAlloc.lastDef = statementIdx;
				}
			}
		);

		statement.VisitSources(
			[&] (const SymbolRefPtr& symbolRef, bool)
			{
				auto symbol(symbolRef->GetSymbol());
				auto& symbolRegAlloc = symbolRegAllocs[symbol];
				symbolRegAlloc.useCount++;
				if(symbolRegAlloc.firstUse == -1)
				{
					symbolRegAlloc.firstUse = statementIdx;
				}
			}
		);
	}
}

void CJitter::MarkAliasedSymbols(const BASIC_BLOCK& basicBlock, const AllocationRange& allocRange, SymbolRegAllocInfo& symbolRegAllocs) const
{
	for(const auto& statementInfo : ConstIndexedStatementList(basicBlock.statements))
	{
		auto& statement(statementInfo.statement);
		const auto& statementIdx(statementInfo.index);
		if(statementIdx < allocRange.first) continue;
		if(statementIdx > allocRange.second) break;
		if(statement.op == OP_PARAM_RET)
		{
			auto& symbolRegAlloc = symbolRegAllocs[statement.src1->GetSymbol()];
			symbolRegAlloc.aliased = true;
		}
		for(auto& symbolRegAlloc : symbolRegAllocs)
		{
			if(symbolRegAlloc.second.aliased) continue;
			auto testedSymbol = symbolRegAlloc.first;
			statement.VisitOperands(
				[&](const SymbolRefPtr& symbolRef, bool)
				{
					auto symbol = symbolRef->GetSymbol();
					if(symbol->Equals(testedSymbol.get())) return;
					if(symbol->Aliases(testedSymbol.get()))
					{
						symbolRegAlloc.second.aliased = true;
					}
				}
			);
		}
	}
}
<commit_msg>Added clarification.<commit_after>#include "Jitter.h"
#include <iostream>
#include <set>

#ifdef _DEBUG
//#define DUMP_STATEMENTS
#endif

#ifdef DUMP_STATEMENTS
#include <iostream>
#endif

using namespace Jitter;

void CJitter::AllocateRegisters(BASIC_BLOCK& basicBlock)
{
	auto& symbolTable = basicBlock.symbolTable;

	std::multimap<unsigned int, STATEMENT> loadStatements;
	std::multimap<unsigned int, STATEMENT> spillStatements;
#ifdef DUMP_STATEMENTS
	DumpStatementList(basicBlock.statements);
	std::cout << std::endl;
#endif

	auto allocRanges = ComputeAllocationRanges(basicBlock);
	for(const auto& allocRange : allocRanges)
	{
		SymbolRegAllocInfo symbolRegAllocs;
		ComputeLivenessForRange(basicBlock, allocRange, symbolRegAllocs);

		MarkAliasedSymbols(basicBlock, allocRange, symbolRegAllocs);

		AssociateSymbolsToRegisters(symbolRegAllocs);

		//Replace all references to symbols by references to allocated registers
		for(const auto& statementInfo : IndexedStatementList(basicBlock.statements))
		{
			auto& statement(statementInfo.statement);
			const auto& statementIdx(statementInfo.index);
			if(statementIdx < allocRange.first) continue;
			if(statementIdx > allocRange.second) break;

			statement.VisitOperands(
				[&] (SymbolRefPtr& symbolRef, bool)
				{
					auto symbol = symbolRef->GetSymbol();
					auto symbolRegAllocIterator = symbolRegAllocs.find(symbol);
					if(symbolRegAllocIterator != std::end(symbolRegAllocs))
					{
						const auto& symbolRegAlloc = symbolRegAllocIterator->second;
						if(symbolRegAlloc.registerId != -1)
						{
							symbolRef = MakeSymbolRef(
								symbolTable.MakeSymbol(symbolRegAlloc.registerType, symbolRegAlloc.registerId));
						}
					}
				}
			);
		}

		//Prepare load and spills
		for(const auto& symbolRegAllocPair : symbolRegAllocs)
		{
			const auto& symbol = symbolRegAllocPair.first;
			const auto& symbolRegAlloc = symbolRegAllocPair.second;

			//Check if it's actually allocated
			if(symbolRegAlloc.registerId == -1) continue;

			//firstUse == -1 means it is written to but never used afterwards in this block

			//Do we need to load register at the beginning?
			//If symbol is read and we use this symbol before we define it, so we need to load it first
			if((symbolRegAlloc.firstUse != -1) && (symbolRegAlloc.firstUse <= symbolRegAlloc.firstDef))
			{
				STATEMENT statement;
				statement.op	= OP_MOV;
				statement.dst	= std::make_shared<CSymbolRef>(
					symbolTable.MakeSymbol(symbolRegAlloc.registerType, symbolRegAlloc.registerId));
				statement.src1	= std::make_shared<CSymbolRef>(symbol);

				loadStatements.insert(std::make_pair(allocRange.first, statement));
			}

			//If symbol is defined, we need to save it at the end
			if(symbolRegAlloc.firstDef != -1)
			{
				STATEMENT statement;
				statement.op	= OP_MOV;
				statement.dst	= std::make_shared<CSymbolRef>(symbol);
				statement.src1	= std::make_shared<CSymbolRef>(
					symbolTable.MakeSymbol(symbolRegAlloc.registerType, symbolRegAlloc.registerId));

				spillStatements.insert(std::make_pair(allocRange.second, statement));
			}
		}
	}

#ifdef DUMP_STATEMENTS
	DumpStatementList(basicBlock.statements);
	std::cout << std::endl;
#endif

	std::map<unsigned int, StatementList::const_iterator> loadPoints;
	std::map<unsigned int, StatementList::const_iterator> spillPoints;

	//Load
	for(const auto& statementInfo : ConstIndexedStatementList(basicBlock.statements))
	{
		const auto& statementIdx(statementInfo.index);
		if(loadStatements.find(statementIdx) != std::end(loadStatements))
		{
			loadPoints.insert(std::make_pair(statementIdx, statementInfo.iterator));
		}
	}

	//Spill
	for(const auto& statementInfo : ConstIndexedStatementList(basicBlock.statements))
	{
		const auto& statementIdx(statementInfo.index);
		if(spillStatements.find(statementIdx) != std::end(spillStatements))
		{
			const auto& statement = statementInfo.statement;
			auto statementIterator = statementInfo.iterator;
			if((statement.op != OP_CONDJMP) && (statement.op != OP_JMP))
			{
				statementIterator++;
			}
			spillPoints.insert(std::make_pair(statementIdx, statementIterator));
		}
	}

	//Loads
	for(const auto& loadPoint : loadPoints)
	{
		unsigned int statementIndex = loadPoint.first;
		for(auto statementIterator = loadStatements.lower_bound(statementIndex);
			statementIterator != loadStatements.upper_bound(statementIndex);
			statementIterator++)
		{
			const auto& statement(statementIterator->second);
			basicBlock.statements.insert(loadPoint.second, statement);
		}
	}

	//Spills
	for(const auto& spillPoint : spillPoints)
	{
		unsigned int statementIndex = spillPoint.first;
		for(auto statementIterator = spillStatements.lower_bound(statementIndex);
			statementIterator != spillStatements.upper_bound(statementIndex);
			statementIterator++)
		{
			const auto& statement(statementIterator->second);
			basicBlock.statements.insert(spillPoint.second, statement);
		}
	}

#ifdef DUMP_STATEMENTS
	DumpStatementList(basicBlock.statements);
	std::cout << std::endl;
#endif
}

void CJitter::AssociateSymbolsToRegisters(SymbolRegAllocInfo& symbolRegAllocs) const
{
	std::multimap<SYM_TYPE, unsigned int> availableRegisters;
	{
		unsigned int regCount = m_codeGen->GetAvailableRegisterCount();
		for(unsigned int i = 0; i < regCount; i++)
		{
			availableRegisters.insert(std::make_pair(SYM_REGISTER, i));
		}
	}

	{
		unsigned int regCount = m_codeGen->GetAvailableMdRegisterCount();
		for(unsigned int i = 0; i < regCount; i++)
		{
			availableRegisters.insert(std::make_pair(SYM_REGISTER128, i));
		}
	}

	auto isRegisterAllocatable =
		[] (SYM_TYPE symbolType)
		{
			return
				(symbolType == SYM_RELATIVE) || (symbolType == SYM_TEMPORARY) ||
				(symbolType == SYM_RELATIVE128) || (symbolType == SYM_TEMPORARY128);
		};

	//Sort symbols by usage count
	std::list<SymbolRegAllocInfo::value_type*> sortedSymbols;
	for(auto& symbolRegAllocPair : symbolRegAllocs)
	{
		const auto& symbol(symbolRegAllocPair.first);
		const auto& symbolRegAlloc(symbolRegAllocPair.second);
		if(!isRegisterAllocatable(symbol->m_type)) continue;
		if(symbolRegAlloc.aliased) continue;
		sortedSymbols.push_back(&symbolRegAllocPair);
	}
	sortedSymbols.sort(
		[] (SymbolRegAllocInfo::value_type* symbolRegAllocPair1, SymbolRegAllocInfo::value_type* symbolRegAllocPair2)
		{
			return symbolRegAllocPair1->second.useCount > symbolRegAllocPair2->second.useCount;
		}
	);

	for(auto& symbolRegAllocPair : sortedSymbols)
	{
		if(availableRegisters.empty()) break;

		const auto& symbol = symbolRegAllocPair->first;
		auto& symbolRegAlloc = symbolRegAllocPair->second;

		//Find suitable register for this symbol
		auto registerIterator = std::end(availableRegisters);
		auto registerIteratorEnd = std::end(availableRegisters);
		if((symbol->m_type == SYM_RELATIVE) || (symbol->m_type == SYM_TEMPORARY))
		{
			registerIterator = availableRegisters.lower_bound(SYM_REGISTER);
			registerIteratorEnd = availableRegisters.upper_bound(SYM_REGISTER);
		}
		else if((symbol->m_type == SYM_RELATIVE128) || (symbol->m_type == SYM_TEMPORARY128))
		{
			registerIterator = availableRegisters.lower_bound(SYM_REGISTER128);
			registerIteratorEnd = availableRegisters.upper_bound(SYM_REGISTER128);
		}
		if(registerIterator != registerIteratorEnd)
		{
			symbolRegAlloc.registerType = registerIterator->first;
			symbolRegAlloc.registerId = registerIterator->second;
			availableRegisters.erase(registerIterator);
		}
	}
}

CJitter::AllocationRangeArray CJitter::ComputeAllocationRanges(const BASIC_BLOCK& basicBlock)
{
	AllocationRangeArray result;
	unsigned int currentStart = 0;
	for(const auto& statementInfo : ConstIndexedStatementList(basicBlock.statements))
	{
		const auto& statement(statementInfo.statement);
		const auto& statementIdx(statementInfo.index);
		if(statement.op == OP_CALL)
		{
			//Gotta split here
			result.push_back(std::make_pair(currentStart, statementIdx));
			currentStart = statementIdx + 1;
		}
	}
	result.push_back(std::make_pair(currentStart, basicBlock.statements.size() - 1));
	return result;
}

void CJitter::ComputeLivenessForRange(const BASIC_BLOCK& basicBlock, const AllocationRange& allocRange, SymbolRegAllocInfo& symbolRegAllocs) const
{
	auto& symbolTable(basicBlock.symbolTable);
	const auto& statements(basicBlock.statements);

	for(const auto& statementInfo : ConstIndexedStatementList(basicBlock.statements))
	{
		const auto& statement(statementInfo.statement);
		unsigned int statementIdx(statementInfo.index);
		if(statementIdx < allocRange.first) continue;
		if(statementIdx > allocRange.second) continue;

		statement.VisitDestination(
			[&] (const SymbolRefPtr& symbolRef, bool)
			{
				auto symbol(symbolRef->GetSymbol());
				auto& symbolRegAlloc = symbolRegAllocs[symbol];
				symbolRegAlloc.useCount++;
				if(symbolRegAlloc.firstDef == -1)
				{
					symbolRegAlloc.firstDef = statementIdx;
				}
				if((symbolRegAlloc.lastDef == -1) || (statementIdx > symbolRegAlloc.lastDef))
				{
					symbolRegAlloc.lastDef = statementIdx;
				}
			}
		);

		statement.VisitSources(
			[&] (const SymbolRefPtr& symbolRef, bool)
			{
				auto symbol(symbolRef->GetSymbol());
				auto& symbolRegAlloc = symbolRegAllocs[symbol];
				symbolRegAlloc.useCount++;
				if(symbolRegAlloc.firstUse == -1)
				{
					symbolRegAlloc.firstUse = statementIdx;
				}
			}
		);
	}
}

void CJitter::MarkAliasedSymbols(const BASIC_BLOCK& basicBlock, const AllocationRange& allocRange, SymbolRegAllocInfo& symbolRegAllocs) const
{
	for(const auto& statementInfo : ConstIndexedStatementList(basicBlock.statements))
	{
		auto& statement(statementInfo.statement);
		const auto& statementIdx(statementInfo.index);
		if(statementIdx < allocRange.first) continue;
		if(statementIdx > allocRange.second) break;
		if(statement.op == OP_PARAM_RET)
		{
			//This symbol will end up being written to by the callee, thus will be aliased
			auto& symbolRegAlloc = symbolRegAllocs[statement.src1->GetSymbol()];
			symbolRegAlloc.aliased = true;
		}
		for(auto& symbolRegAlloc : symbolRegAllocs)
		{
			if(symbolRegAlloc.second.aliased) continue;
			auto testedSymbol = symbolRegAlloc.first;
			statement.VisitOperands(
				[&](const SymbolRefPtr& symbolRef, bool)
				{
					auto symbol = symbolRef->GetSymbol();
					if(symbol->Equals(testedSymbol.get())) return;
					if(symbol->Aliases(testedSymbol.get()))
					{
						symbolRegAlloc.second.aliased = true;
					}
				}
			);
		}
	}
}
<|endoftext|>


<commit_before>#include <muduo/base/BlockingQueue.h>
#include <muduo/base/CountDownLatch.h>
#include <muduo/base/Thread.h>
#include <muduo/base/Timestamp.h>

#include <boost/bind.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <map>
#include <string>
#include <stdio.h>

class Bench
{
 public:
  Bench(int numThreads)
    : latch_(numThreads),
      threads_(numThreads)
  {
    for (int i = 0; i < numThreads; ++i)
    {
      char name[32];
      snprintf(name, sizeof name, "work thread %d", i);
      threads_.push_back(new muduo::Thread(
            boost::bind(&Bench::threadFunc, this), muduo::string(name)));
    }
    for_each(threads_.begin(), threads_.end(), boost::bind(&muduo::Thread::start, _1));
  }

  void run(int times)
  {
    printf("waiting for count down latch\n");
    latch_.wait();
    printf("all threads started\n");
    for (int i = 0; i < times; ++i)
    {
      muduo::Timestamp now(muduo::Timestamp::now());
      queue_.put(now);
      usleep(1000);
    }
  }

  void joinAll()
  {
    for (size_t i = 0; i < threads_.size(); ++i)
    {
      queue_.put(muduo::Timestamp::invalid());
    }

    for_each(threads_.begin(), threads_.end(), boost::bind(&muduo::Thread::join, _1));
  }

 private:

  void threadFunc()
  {
    printf("tid=%d, %s started\n",
           muduo::CurrentThread::tid(),
           muduo::CurrentThread::name());

    std::map<int, int> delays;
    latch_.countDown();
    bool running = true;
    while (running)
    {
      muduo::Timestamp t(queue_.take());
      muduo::Timestamp now(muduo::Timestamp::now());
      if (t.valid())
      {
        int delay = static_cast<int>(timeDifference(now, t) * 1000000);
        printf("tid=%d, latency = %d us\n",
               muduo::CurrentThread::tid(), delay);
        ++delays[delay];
      }
      running = t.valid();
    }

    printf("tid=%d, %s stopped\n",
           muduo::CurrentThread::tid(),
           muduo::CurrentThread::name());
    for (std::map<int, int>::iterator it = delays.begin();
        it != delays.end(); ++it)
    {
      printf("tid = %d, delay = %d, count = %d\n",
             muduo::CurrentThread::tid(),
             it->first, it->second);
    }
  }

  muduo::BlockingQueue<muduo::Timestamp> queue_;
  muduo::CountDownLatch latch_;
  boost::ptr_vector<muduo::Thread> threads_;
};

int main(int argc, char* argv[])
{
  int threads = argc > 1 ? atoi(argv[1]) : 1;

  Bench t(threads);
  t.run(1000);
  t.joinAll();
}
<commit_msg>no IO in calc thread.<commit_after>#include <muduo/base/BlockingQueue.h>
#include <muduo/base/CountDownLatch.h>
#include <muduo/base/Thread.h>
#include <muduo/base/Timestamp.h>

#include <boost/bind.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <map>
#include <string>
#include <stdio.h>

class Bench
{
 public:
  Bench(int numThreads)
    : latch_(numThreads),
      threads_(numThreads)
  {
    for (int i = 0; i < numThreads; ++i)
    {
      char name[32];
      snprintf(name, sizeof name, "work thread %d", i);
      threads_.push_back(new muduo::Thread(
            boost::bind(&Bench::threadFunc, this), muduo::string(name)));
    }
    for_each(threads_.begin(), threads_.end(), boost::bind(&muduo::Thread::start, _1));
  }

  void run(int times)
  {
    printf("waiting for count down latch\n");
    latch_.wait();
    printf("all threads started\n");
    for (int i = 0; i < times; ++i)
    {
      muduo::Timestamp now(muduo::Timestamp::now());
      queue_.put(now);
      usleep(1000);
    }
  }

  void joinAll()
  {
    for (size_t i = 0; i < threads_.size(); ++i)
    {
      queue_.put(muduo::Timestamp::invalid());
    }

    for_each(threads_.begin(), threads_.end(), boost::bind(&muduo::Thread::join, _1));
  }

 private:

  void threadFunc()
  {
    printf("tid=%d, %s started\n",
           muduo::CurrentThread::tid(),
           muduo::CurrentThread::name());

    std::map<int, int> delays;
    latch_.countDown();
    bool running = true;
    while (running)
    {
      muduo::Timestamp t(queue_.take());
      muduo::Timestamp now(muduo::Timestamp::now());
      if (t.valid())
      {
        int delay = static_cast<int>(timeDifference(now, t) * 1000000);
        // printf("tid=%d, latency = %d us\n",
        //        muduo::CurrentThread::tid(), delay);
        ++delays[delay];
      }
      running = t.valid();
    }

    printf("tid=%d, %s stopped\n",
           muduo::CurrentThread::tid(),
           muduo::CurrentThread::name());
    for (std::map<int, int>::iterator it = delays.begin();
        it != delays.end(); ++it)
    {
      printf("tid = %d, delay = %d, count = %d\n",
             muduo::CurrentThread::tid(),
             it->first, it->second);
    }
  }

  muduo::BlockingQueue<muduo::Timestamp> queue_;
  muduo::CountDownLatch latch_;
  boost::ptr_vector<muduo::Thread> threads_;
};

int main(int argc, char* argv[])
{
  int threads = argc > 1 ? atoi(argv[1]) : 1;

  Bench t(threads);
  t.run(1000);
  t.joinAll();
}
<|endoftext|>


<commit_before>/*
                            __  __            _
                         ___\ \/ /_ __   __ _| |_
                        / _ \\  /| '_ \ / _` | __|
                       |  __//  \| |_) | (_| | |_
                        \___/_/\_\ .__/ \__,_|\__|
                                 |_| XML parser

   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
   Copyright (c) 2000-2017 Expat development team
   Licensed under the MIT license:

   Permission is  hereby granted,  free of charge,  to any  person obtaining
   a  copy  of  this  software   and  associated  documentation  files  (the
   "Software"),  to  deal in  the  Software  without restriction,  including
   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
   distribute, sublicense, and/or sell copies of the Software, and to permit
   persons  to whom  the Software  is  furnished to  do so,  subject to  the
   following conditions:

   The above copyright  notice and this permission notice  shall be included
   in all copies or substantial portions of the Software.

   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
   USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include "expat.h"
#ifdef XML_UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <urlmon.h>
#include <wininet.h>
#include <stdio.h>
#include <tchar.h>
#include "xmlurl.h"
#include "xmlmime.h"

static int
processURL(XML_Parser parser, IMoniker *baseMoniker, const XML_Char *url);

typedef void (*StopHandler)(void *, HRESULT);

class Callback : public IBindStatusCallback {
public:
  // IUnknown methods
  STDMETHODIMP QueryInterface(REFIID,void **);
  STDMETHODIMP_(ULONG) AddRef();
  STDMETHODIMP_(ULONG) Release();
  // IBindStatusCallback methods
  STDMETHODIMP OnStartBinding(DWORD, IBinding *);
  STDMETHODIMP GetPriority(LONG *);
  STDMETHODIMP OnLowResource(DWORD);
  STDMETHODIMP OnProgress(ULONG, ULONG, ULONG, LPCWSTR);
  STDMETHODIMP OnStopBinding(HRESULT, LPCWSTR);
  STDMETHODIMP GetBindInfo(DWORD *, BINDINFO *);
  STDMETHODIMP OnDataAvailable(DWORD, DWORD, FORMATETC *, STGMEDIUM *);
  STDMETHODIMP OnObjectAvailable(REFIID, IUnknown *);
  Callback(XML_Parser, IMoniker *, StopHandler, void *);
  ~Callback();
  int externalEntityRef(const XML_Char *context,
                        const XML_Char *systemId, const XML_Char *publicId);
private:
  XML_Parser parser_;
  IMoniker *baseMoniker_;
  DWORD totalRead_;
  ULONG ref_;
  IBinding *pBinding_;
  StopHandler stopHandler_;
  void *stopArg_;
};

STDMETHODIMP_(ULONG)
Callback::AddRef()
{
  return ref_++;
}

STDMETHODIMP_(ULONG)
Callback::Release()
{
  if (--ref_ == 0) {
    delete this;
    return 0;
  }
  return ref_;
}

STDMETHODIMP
Callback::QueryInterface(REFIID riid, void** ppv)
{
  if (IsEqualGUID(riid, IID_IUnknown))
    *ppv = (IUnknown *)this;
  else if (IsEqualGUID(riid, IID_IBindStatusCallback))
    *ppv = (IBindStatusCallback *)this;
  else
    return E_NOINTERFACE;
  ((LPUNKNOWN)*ppv)->AddRef();
  return S_OK;
}

STDMETHODIMP
Callback::OnStartBinding(DWORD, IBinding* pBinding)
{
  pBinding_ = pBinding;
  pBinding->AddRef();
  return S_OK;
}

STDMETHODIMP
Callback::GetPriority(LONG *)
{
  return E_NOTIMPL;
}

STDMETHODIMP
Callback::OnLowResource(DWORD)
{
  return E_NOTIMPL;
}

STDMETHODIMP
Callback::OnProgress(ULONG, ULONG, ULONG, LPCWSTR)
{
  return S_OK;
}

STDMETHODIMP
Callback::OnStopBinding(HRESULT hr, LPCWSTR szError)
{
  if (pBinding_) {
    pBinding_->Release();
    pBinding_ = 0;
  }
  if (baseMoniker_) {
    baseMoniker_->Release();
    baseMoniker_ = 0;
  }
  stopHandler_(stopArg_, hr);
  return S_OK;
}

STDMETHODIMP
Callback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindinfo)
{
  *pgrfBINDF = BINDF_ASYNCHRONOUS;
  return S_OK;
}

static void
reportError(XML_Parser parser)
{
  int code = XML_GetErrorCode(parser);
  const XML_Char *message = XML_ErrorString(code);
  if (message)
    _ftprintf(stderr, _T("%s:%d:%ld: %s\n"),
	     XML_GetBase(parser),
	     XML_GetErrorLineNumber(parser),
	     XML_GetErrorColumnNumber(parser),
	     message);
  else
    _ftprintf(stderr, _T("%s: (unknown message %d)\n"),
              XML_GetBase(parser), code);
}

STDMETHODIMP
Callback::OnDataAvailable(DWORD grfBSCF,
                          DWORD dwSize,
                          FORMATETC *pfmtetc,
                          STGMEDIUM* pstgmed)
{
  if (grfBSCF & BSCF_FIRSTDATANOTIFICATION) {
    IWinInetHttpInfo *hp;
    HRESULT hr = pBinding_->QueryInterface(IID_IWinInetHttpInfo,
                                           (void **)&hp);
    if (SUCCEEDED(hr)) {
      char contentType[1024];
      DWORD bufSize = sizeof(contentType);
      DWORD flags = 0;
      contentType[0] = 0;
      hr = hp->QueryInfo(HTTP_QUERY_CONTENT_TYPE, contentType,
                         &bufSize, 0, NULL);
      if (SUCCEEDED(hr)) {
	char charset[CHARSET_MAX];
	getXMLCharset(contentType, charset);
	if (charset[0]) {
#ifdef XML_UNICODE
	  XML_Char wcharset[CHARSET_MAX];
	  XML_Char *p1 = wcharset;
	  const char *p2 = charset;
	  while ((*p1++ = (unsigned char)*p2++) != 0)
	    ;
	  XML_SetEncoding(parser_, wcharset);
#else
	  XML_SetEncoding(parser_, charset);
#endif
	}
      }
      hp->Release();
    }
  }
  if (!parser_)
    return E_ABORT;
  if (pstgmed->tymed == TYMED_ISTREAM) {
    while (totalRead_ < dwSize) {
#define READ_MAX (64*1024)
      DWORD nToRead = dwSize - totalRead_;
      if (nToRead > READ_MAX)
	nToRead = READ_MAX;
      void *buf = XML_GetBuffer(parser_, nToRead);
      if (!buf) {
	_ftprintf(stderr, _T("out of memory\n"));
	return E_ABORT;
      }
      DWORD nRead;
      HRESULT hr = pstgmed->pstm->Read(buf, nToRead, &nRead);
      if (SUCCEEDED(hr)) {
	totalRead_ += nRead;
	if (!XML_ParseBuffer(parser_,
			     nRead,
			     (grfBSCF & BSCF_LASTDATANOTIFICATION) != 0
			     && totalRead_ == dwSize)) {
	  reportError(parser_);
	  return E_ABORT;
	}
      }
    }
  }
  return S_OK;
}

STDMETHODIMP
Callback::OnObjectAvailable(REFIID, IUnknown *)
{
  return S_OK;
}

int
Callback::externalEntityRef(const XML_Char *context,
                            const XML_Char *systemId,
                            const XML_Char *publicId)
{
  XML_Parser entParser = XML_ExternalEntityParserCreate(parser_, context, 0);
  XML_SetBase(entParser, systemId);
  int ret = processURL(entParser, baseMoniker_, systemId);
  XML_ParserFree(entParser);
  return ret;
}

Callback::Callback(XML_Parser parser, IMoniker *baseMoniker,
                   StopHandler stopHandler, void *stopArg)
: parser_(parser),
  baseMoniker_(baseMoniker),
  ref_(0),
  pBinding_(0),
  totalRead_(0),
  stopHandler_(stopHandler),
  stopArg_(stopArg)
{
  if (baseMoniker_)
    baseMoniker_->AddRef();
}

Callback::~Callback()
{
  if (pBinding_)
    pBinding_->Release();
  if (baseMoniker_)
    baseMoniker_->Release();
}

static int
externalEntityRef(void *arg,
                  const XML_Char *context,
                  const XML_Char *base,
                  const XML_Char *systemId,
                  const XML_Char *publicId)
{
  return ((Callback *)arg)->externalEntityRef(context, systemId, publicId);
}

static HRESULT
openStream(XML_Parser parser,
           IMoniker *baseMoniker,
           const XML_Char *uri,
           StopHandler stopHandler, void *stopArg)
{
  if (!XML_SetBase(parser, uri))
    return E_OUTOFMEMORY;
  HRESULT hr;
  IMoniker *m;
#ifdef XML_UNICODE
  hr = CreateURLMoniker(0, uri, &m);
#else
  LPWSTR uriw = new wchar_t[strlen(uri) + 1];
  for (int i = 0;; i++) {
    uriw[i] = uri[i];
    if (uriw[i] == 0)
      break;
  }
  hr = CreateURLMoniker(baseMoniker, uriw, &m);
  delete [] uriw;
#endif
  if (FAILED(hr))
    return hr;
  IBindStatusCallback *cb = new Callback(parser, m, stopHandler, stopArg);
  XML_SetExternalEntityRefHandler(parser, externalEntityRef);
  XML_SetExternalEntityRefHandlerArg(parser, cb);
  cb->AddRef();
  IBindCtx *b;
  if (FAILED(hr = CreateAsyncBindCtx(0, cb, 0, &b))) {
    cb->Release();
    m->Release();
    return hr;
  }
  cb->Release();
  IStream *pStream;
  hr = m->BindToStorage(b, 0, IID_IStream, (void **)&pStream);
  if (SUCCEEDED(hr)) {
    if (pStream)
      pStream->Release();
  }
  if (hr == MK_S_ASYNCHRONOUS)
    hr = S_OK;
  m->Release();
  b->Release();
  return hr;
}

struct QuitInfo {
  const XML_Char *url;
  HRESULT hr;
  int stop;
};

static void
winPerror(const XML_Char *url, HRESULT hr)
{
  LPVOID buf;
  if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
		    | FORMAT_MESSAGE_FROM_HMODULE,
		    GetModuleHandleA("urlmon.dll"),
		    hr,
		    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		    (LPTSTR) &buf,
		    0,
		    NULL)
      || FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
		      | FORMAT_MESSAGE_FROM_SYSTEM,
		      0,
		      hr,
		      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		      (LPTSTR) &buf,
		      0,
		      NULL)) {
    /* The system error messages seem to end with a newline. */
    _ftprintf(stderr, _T("%s: %s"), url, buf);
    fflush(stderr);
    LocalFree(buf);
  }
  else
    _ftprintf(stderr, _T("%s: error %x\n"), url, hr);
}

static void
threadQuit(void *p, HRESULT hr)
{
  QuitInfo *qi = (QuitInfo *)p;
  qi->hr = hr;
  qi->stop = 1;
}

extern "C"
int
XML_URLInit(void)
{
  return SUCCEEDED(CoInitialize(0));
}

extern "C"
void
XML_URLUninit(void)
{
  CoUninitialize();
}

static int
processURL(XML_Parser parser, IMoniker *baseMoniker,
           const XML_Char *url)
{
  QuitInfo qi;
  qi.stop = 0;
  qi.url = url;

  XML_SetBase(parser, url);
  HRESULT hr = openStream(parser, baseMoniker, url, threadQuit, &qi);
  if (FAILED(hr)) {
    winPerror(url, hr);
    return 0;
  }
  else if (FAILED(qi.hr)) {
    winPerror(url, qi.hr);
    return 0;
  }
  MSG msg;
  while (!qi.stop && GetMessage (&msg, NULL, 0, 0)) {
    TranslateMessage (&msg);
    DispatchMessage (&msg);
  }
  return 1;
}

extern "C"
int
XML_ProcessURL(XML_Parser parser,
               const XML_Char *url,
               unsigned flags)
{
  return processURL(parser, 0, url);
}
<commit_msg>xmlwf: Ensure that STDMETHODIMP_ is defined for cppcheck (fixes #285)<commit_after>/*
                            __  __            _
                         ___\ \/ /_ __   __ _| |_
                        / _ \\  /| '_ \ / _` | __|
                       |  __//  \| |_) | (_| | |_
                        \___/_/\_\ .__/ \__,_|\__|
                                 |_| XML parser

   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
   Copyright (c) 2000-2017 Expat development team
   Licensed under the MIT license:

   Permission is  hereby granted,  free of charge,  to any  person obtaining
   a  copy  of  this  software   and  associated  documentation  files  (the
   "Software"),  to  deal in  the  Software  without restriction,  including
   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
   distribute, sublicense, and/or sell copies of the Software, and to permit
   persons  to whom  the Software  is  furnished to  do so,  subject to  the
   following conditions:

   The above copyright  notice and this permission notice  shall be included
   in all copies or substantial portions of the Software.

   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
   USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include "expat.h"
#ifdef XML_UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <urlmon.h>
#include <wininet.h>
#include <stdio.h>
#include <tchar.h>
#include "xmlurl.h"
#include "xmlmime.h"

// Make cppcheck happy:
#ifndef STDMETHODIMP_
# define STDMETHODIMP_(x)  x
#endif

static int
processURL(XML_Parser parser, IMoniker *baseMoniker, const XML_Char *url);

typedef void (*StopHandler)(void *, HRESULT);

class Callback : public IBindStatusCallback {
public:
  // IUnknown methods
  STDMETHODIMP QueryInterface(REFIID,void **);
  STDMETHODIMP_(ULONG) AddRef();
  STDMETHODIMP_(ULONG) Release();
  // IBindStatusCallback methods
  STDMETHODIMP OnStartBinding(DWORD, IBinding *);
  STDMETHODIMP GetPriority(LONG *);
  STDMETHODIMP OnLowResource(DWORD);
  STDMETHODIMP OnProgress(ULONG, ULONG, ULONG, LPCWSTR);
  STDMETHODIMP OnStopBinding(HRESULT, LPCWSTR);
  STDMETHODIMP GetBindInfo(DWORD *, BINDINFO *);
  STDMETHODIMP OnDataAvailable(DWORD, DWORD, FORMATETC *, STGMEDIUM *);
  STDMETHODIMP OnObjectAvailable(REFIID, IUnknown *);
  Callback(XML_Parser, IMoniker *, StopHandler, void *);
  ~Callback();
  int externalEntityRef(const XML_Char *context,
                        const XML_Char *systemId, const XML_Char *publicId);
private:
  XML_Parser parser_;
  IMoniker *baseMoniker_;
  DWORD totalRead_;
  ULONG ref_;
  IBinding *pBinding_;
  StopHandler stopHandler_;
  void *stopArg_;
};

STDMETHODIMP_(ULONG)
Callback::AddRef()
{
  return ref_++;
}

STDMETHODIMP_(ULONG)
Callback::Release()
{
  if (--ref_ == 0) {
    delete this;
    return 0;
  }
  return ref_;
}

STDMETHODIMP
Callback::QueryInterface(REFIID riid, void** ppv)
{
  if (IsEqualGUID(riid, IID_IUnknown))
    *ppv = (IUnknown *)this;
  else if (IsEqualGUID(riid, IID_IBindStatusCallback))
    *ppv = (IBindStatusCallback *)this;
  else
    return E_NOINTERFACE;
  ((LPUNKNOWN)*ppv)->AddRef();
  return S_OK;
}

STDMETHODIMP
Callback::OnStartBinding(DWORD, IBinding* pBinding)
{
  pBinding_ = pBinding;
  pBinding->AddRef();
  return S_OK;
}

STDMETHODIMP
Callback::GetPriority(LONG *)
{
  return E_NOTIMPL;
}

STDMETHODIMP
Callback::OnLowResource(DWORD)
{
  return E_NOTIMPL;
}

STDMETHODIMP
Callback::OnProgress(ULONG, ULONG, ULONG, LPCWSTR)
{
  return S_OK;
}

STDMETHODIMP
Callback::OnStopBinding(HRESULT hr, LPCWSTR szError)
{
  if (pBinding_) {
    pBinding_->Release();
    pBinding_ = 0;
  }
  if (baseMoniker_) {
    baseMoniker_->Release();
    baseMoniker_ = 0;
  }
  stopHandler_(stopArg_, hr);
  return S_OK;
}

STDMETHODIMP
Callback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindinfo)
{
  *pgrfBINDF = BINDF_ASYNCHRONOUS;
  return S_OK;
}

static void
reportError(XML_Parser parser)
{
  int code = XML_GetErrorCode(parser);
  const XML_Char *message = XML_ErrorString(code);
  if (message)
    _ftprintf(stderr, _T("%s:%d:%ld: %s\n"),
	     XML_GetBase(parser),
	     XML_GetErrorLineNumber(parser),
	     XML_GetErrorColumnNumber(parser),
	     message);
  else
    _ftprintf(stderr, _T("%s: (unknown message %d)\n"),
              XML_GetBase(parser), code);
}

STDMETHODIMP
Callback::OnDataAvailable(DWORD grfBSCF,
                          DWORD dwSize,
                          FORMATETC *pfmtetc,
                          STGMEDIUM* pstgmed)
{
  if (grfBSCF & BSCF_FIRSTDATANOTIFICATION) {
    IWinInetHttpInfo *hp;
    HRESULT hr = pBinding_->QueryInterface(IID_IWinInetHttpInfo,
                                           (void **)&hp);
    if (SUCCEEDED(hr)) {
      char contentType[1024];
      DWORD bufSize = sizeof(contentType);
      DWORD flags = 0;
      contentType[0] = 0;
      hr = hp->QueryInfo(HTTP_QUERY_CONTENT_TYPE, contentType,
                         &bufSize, 0, NULL);
      if (SUCCEEDED(hr)) {
	char charset[CHARSET_MAX];
	getXMLCharset(contentType, charset);
	if (charset[0]) {
#ifdef XML_UNICODE
	  XML_Char wcharset[CHARSET_MAX];
	  XML_Char *p1 = wcharset;
	  const char *p2 = charset;
	  while ((*p1++ = (unsigned char)*p2++) != 0)
	    ;
	  XML_SetEncoding(parser_, wcharset);
#else
	  XML_SetEncoding(parser_, charset);
#endif
	}
      }
      hp->Release();
    }
  }
  if (!parser_)
    return E_ABORT;
  if (pstgmed->tymed == TYMED_ISTREAM) {
    while (totalRead_ < dwSize) {
#define READ_MAX (64*1024)
      DWORD nToRead = dwSize - totalRead_;
      if (nToRead > READ_MAX)
	nToRead = READ_MAX;
      void *buf = XML_GetBuffer(parser_, nToRead);
      if (!buf) {
	_ftprintf(stderr, _T("out of memory\n"));
	return E_ABORT;
      }
      DWORD nRead;
      HRESULT hr = pstgmed->pstm->Read(buf, nToRead, &nRead);
      if (SUCCEEDED(hr)) {
	totalRead_ += nRead;
	if (!XML_ParseBuffer(parser_,
			     nRead,
			     (grfBSCF & BSCF_LASTDATANOTIFICATION) != 0
			     && totalRead_ == dwSize)) {
	  reportError(parser_);
	  return E_ABORT;
	}
      }
    }
  }
  return S_OK;
}

STDMETHODIMP
Callback::OnObjectAvailable(REFIID, IUnknown *)
{
  return S_OK;
}

int
Callback::externalEntityRef(const XML_Char *context,
                            const XML_Char *systemId,
                            const XML_Char *publicId)
{
  XML_Parser entParser = XML_ExternalEntityParserCreate(parser_, context, 0);
  XML_SetBase(entParser, systemId);
  int ret = processURL(entParser, baseMoniker_, systemId);
  XML_ParserFree(entParser);
  return ret;
}

Callback::Callback(XML_Parser parser, IMoniker *baseMoniker,
                   StopHandler stopHandler, void *stopArg)
: parser_(parser),
  baseMoniker_(baseMoniker),
  ref_(0),
  pBinding_(0),
  totalRead_(0),
  stopHandler_(stopHandler),
  stopArg_(stopArg)
{
  if (baseMoniker_)
    baseMoniker_->AddRef();
}

Callback::~Callback()
{
  if (pBinding_)
    pBinding_->Release();
  if (baseMoniker_)
    baseMoniker_->Release();
}

static int
externalEntityRef(void *arg,
                  const XML_Char *context,
                  const XML_Char *base,
                  const XML_Char *systemId,
                  const XML_Char *publicId)
{
  return ((Callback *)arg)->externalEntityRef(context, systemId, publicId);
}

static HRESULT
openStream(XML_Parser parser,
           IMoniker *baseMoniker,
           const XML_Char *uri,
           StopHandler stopHandler, void *stopArg)
{
  if (!XML_SetBase(parser, uri))
    return E_OUTOFMEMORY;
  HRESULT hr;
  IMoniker *m;
#ifdef XML_UNICODE
  hr = CreateURLMoniker(0, uri, &m);
#else
  LPWSTR uriw = new wchar_t[strlen(uri) + 1];
  for (int i = 0;; i++) {
    uriw[i] = uri[i];
    if (uriw[i] == 0)
      break;
  }
  hr = CreateURLMoniker(baseMoniker, uriw, &m);
  delete [] uriw;
#endif
  if (FAILED(hr))
    return hr;
  IBindStatusCallback *cb = new Callback(parser, m, stopHandler, stopArg);
  XML_SetExternalEntityRefHandler(parser, externalEntityRef);
  XML_SetExternalEntityRefHandlerArg(parser, cb);
  cb->AddRef();
  IBindCtx *b;
  if (FAILED(hr = CreateAsyncBindCtx(0, cb, 0, &b))) {
    cb->Release();
    m->Release();
    return hr;
  }
  cb->Release();
  IStream *pStream;
  hr = m->BindToStorage(b, 0, IID_IStream, (void **)&pStream);
  if (SUCCEEDED(hr)) {
    if (pStream)
      pStream->Release();
  }
  if (hr == MK_S_ASYNCHRONOUS)
    hr = S_OK;
  m->Release();
  b->Release();
  return hr;
}

struct QuitInfo {
  const XML_Char *url;
  HRESULT hr;
  int stop;
};

static void
winPerror(const XML_Char *url, HRESULT hr)
{
  LPVOID buf;
  if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
		    | FORMAT_MESSAGE_FROM_HMODULE,
		    GetModuleHandleA("urlmon.dll"),
		    hr,
		    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		    (LPTSTR) &buf,
		    0,
		    NULL)
      || FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
		      | FORMAT_MESSAGE_FROM_SYSTEM,
		      0,
		      hr,
		      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		      (LPTSTR) &buf,
		      0,
		      NULL)) {
    /* The system error messages seem to end with a newline. */
    _ftprintf(stderr, _T("%s: %s"), url, buf);
    fflush(stderr);
    LocalFree(buf);
  }
  else
    _ftprintf(stderr, _T("%s: error %x\n"), url, hr);
}

static void
threadQuit(void *p, HRESULT hr)
{
  QuitInfo *qi = (QuitInfo *)p;
  qi->hr = hr;
  qi->stop = 1;
}

extern "C"
int
XML_URLInit(void)
{
  return SUCCEEDED(CoInitialize(0));
}

extern "C"
void
XML_URLUninit(void)
{
  CoUninitialize();
}

static int
processURL(XML_Parser parser, IMoniker *baseMoniker,
           const XML_Char *url)
{
  QuitInfo qi;
  qi.stop = 0;
  qi.url = url;

  XML_SetBase(parser, url);
  HRESULT hr = openStream(parser, baseMoniker, url, threadQuit, &qi);
  if (FAILED(hr)) {
    winPerror(url, hr);
    return 0;
  }
  else if (FAILED(qi.hr)) {
    winPerror(url, qi.hr);
    return 0;
  }
  MSG msg;
  while (!qi.stop && GetMessage (&msg, NULL, 0, 0)) {
    TranslateMessage (&msg);
    DispatchMessage (&msg);
  }
  return 1;
}

extern "C"
int
XML_ProcessURL(XML_Parser parser,
               const XML_Char *url,
               unsigned flags)
{
  return processURL(parser, 0, url);
}
<|endoftext|>


<commit_before>
//----------------------------------------------------------------------------------
// Include
//----------------------------------------------------------------------------------
#include <math.h>
#include "EffekseerSoundOSMixer.SoundImplemented.h"
#include "EffekseerSoundOSMixer.SoundPlayer.h"

//-----------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------
namespace EffekseerSound
{
//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
SoundPlayer::SoundPlayer( SoundImplemented* sound )
	: m_sound	( sound )
{
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
SoundPlayer::~SoundPlayer()
{
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
::Effekseer::SoundHandle SoundPlayer::Play( ::Effekseer::SoundTag tag,
		const ::Effekseer::SoundPlayer::InstanceParameter& parameter )
{
	if( m_sound->GetMute() )
	{
		return nullptr;
	}
	SoundData* soundData = (SoundData*)parameter.Data;
	if( soundData == nullptr )
	{
		return nullptr;
	}

	auto device = m_sound->GetDevice();
	int32_t id = device->Play( soundData );

	device->SetPlaybackSpeed( id, powf(2.0f, parameter.Pitch) );

	if( parameter.Mode3D )
	{
		float rolloff, pan;
		m_sound->Calculate3DSound( parameter.Position, parameter.Distance, rolloff, pan );
		device->SetVolume( id, rolloff * parameter.Volume );
		device->SetPanningPosition( id, pan );
	}
	else
	{
		device->SetVolume( id, parameter.Volume );
		device->SetPanningPosition( id, parameter.Pan );
	}

	SoundImplemented::Instance instance;
	instance.id = id;
	instance.tag = tag;
	instance.data = soundData;
	m_sound->AddInstance( instance );

	return reinterpret_cast<Effekseer::SoundHandle>( id );
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
void SoundPlayer::Stop( ::Effekseer::SoundHandle handle, ::Effekseer::SoundTag tag )
{
	int32_t id = (int32_t)(intptr_t)handle;
	m_sound->GetDevice()->Stop( id );
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
void SoundPlayer::Pause( ::Effekseer::SoundHandle handle, ::Effekseer::SoundTag tag, bool pause )
{
	int32_t id = (int32_t)(intptr_t)handle;
	if( pause )
	{
		m_sound->GetDevice()->Pause( id );
	}
	else
	{
		m_sound->GetDevice()->Resume( id );
	}
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
bool SoundPlayer::CheckPlaying( ::Effekseer::SoundHandle handle, ::Effekseer::SoundTag tag )
{
	int32_t id = (int32_t)(intptr_t)handle;
	return m_sound->GetDevice()->IsPlaying( id );
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
void SoundPlayer::StopTag( ::Effekseer::SoundTag tag )
{
	m_sound->StopTag( tag );
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
void SoundPlayer::PauseTag( ::Effekseer::SoundTag tag, bool pause )
{
	m_sound->PauseTag( tag, pause );
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
bool SoundPlayer::CheckPlayingTag( ::Effekseer::SoundTag tag )
{
	return m_sound->CheckPlayingTag( tag );
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
void SoundPlayer::StopAll()
{
	m_sound->StopAll();
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
}
//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
<commit_msg>Fixed a bug makes a pitch disabled<commit_after>
//----------------------------------------------------------------------------------
// Include
//----------------------------------------------------------------------------------
#include <math.h>
#include "EffekseerSoundOSMixer.SoundImplemented.h"
#include "EffekseerSoundOSMixer.SoundPlayer.h"

//-----------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------
namespace EffekseerSound
{
//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
SoundPlayer::SoundPlayer( SoundImplemented* sound )
	: m_sound	( sound )
{
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
SoundPlayer::~SoundPlayer()
{
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
::Effekseer::SoundHandle SoundPlayer::Play( ::Effekseer::SoundTag tag,
		const ::Effekseer::SoundPlayer::InstanceParameter& parameter )
{
	if( m_sound->GetMute() )
	{
		return nullptr;
	}
	SoundData* soundData = (SoundData*)parameter.Data;
	if( soundData == nullptr )
	{
		return nullptr;
	}

	auto device = m_sound->GetDevice();
	int32_t id = device->Play( soundData );

	if (parameter.Pitch != 0.0f)
	{
		device->SetIsPlaybackSpeedEnabled(id, true);
		device->SetPlaybackSpeed(id, powf(2.0f, parameter.Pitch));
	}
	else
	{
		device->SetIsPlaybackSpeedEnabled(id, false);
		device->SetPlaybackSpeed(id, 1.0f);
	}

	if( parameter.Mode3D )
	{
		float rolloff, pan;
		m_sound->Calculate3DSound( parameter.Position, parameter.Distance, rolloff, pan );
		device->SetVolume( id, rolloff * parameter.Volume );
		device->SetPanningPosition( id, pan );
	}
	else
	{
		device->SetVolume( id, parameter.Volume );
		device->SetPanningPosition( id, parameter.Pan );
	}

	SoundImplemented::Instance instance;
	instance.id = id;
	instance.tag = tag;
	instance.data = soundData;
	m_sound->AddInstance( instance );

	return reinterpret_cast<Effekseer::SoundHandle>( id );
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
void SoundPlayer::Stop( ::Effekseer::SoundHandle handle, ::Effekseer::SoundTag tag )
{
	int32_t id = (int32_t)(intptr_t)handle;
	m_sound->GetDevice()->Stop( id );
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
void SoundPlayer::Pause( ::Effekseer::SoundHandle handle, ::Effekseer::SoundTag tag, bool pause )
{
	int32_t id = (int32_t)(intptr_t)handle;
	if( pause )
	{
		m_sound->GetDevice()->Pause( id );
	}
	else
	{
		m_sound->GetDevice()->Resume( id );
	}
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
bool SoundPlayer::CheckPlaying( ::Effekseer::SoundHandle handle, ::Effekseer::SoundTag tag )
{
	int32_t id = (int32_t)(intptr_t)handle;
	return m_sound->GetDevice()->IsPlaying( id );
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
void SoundPlayer::StopTag( ::Effekseer::SoundTag tag )
{
	m_sound->StopTag( tag );
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
void SoundPlayer::PauseTag( ::Effekseer::SoundTag tag, bool pause )
{
	m_sound->PauseTag( tag, pause );
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
bool SoundPlayer::CheckPlayingTag( ::Effekseer::SoundTag tag )
{
	return m_sound->CheckPlayingTag( tag );
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
void SoundPlayer::StopAll()
{
	m_sound->StopAll();
}

//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
}
//----------------------------------------------------------------------------------
//
//----------------------------------------------------------------------------------
<|endoftext|>


<commit_before>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2014 Srinath Ravichandran, The appleseedhq Organization
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "curveobject.h"

// appleseed.renderer headers.
#include "renderer/global/globallogger.h"
#include "renderer/utility/paramarray.h"

// appleseed.foundation headers.
#include "foundation/math/sampling/mappings.h"
#include "foundation/math/aabb.h"
#include "foundation/math/qmc.h"
#include "foundation/math/rng.h"
#include "foundation/math/scalar.h"
#include "foundation/math/vector.h"
#include "foundation/platform/defaulttimers.h"
#include "foundation/utility/searchpaths.h"
#include "foundation/utility/stopwatch.h"
#include "foundation/utility/string.h"

// Standard headers.
#include <cassert>
#include <cmath>
#include <fstream>
#include <string>
#include <vector>

using namespace foundation;
using namespace std;

namespace renderer
{

//
// CurveObject class implementation.
//

struct CurveObject::Impl
{
    RegionKit               m_region_kit;
    Lazy<RegionKit>         m_lazy_region_kit;
    vector<BezierCurve3d>   m_curves;
    vector<string>          m_material_slots;

    Impl()
      : m_lazy_region_kit(&m_region_kit)
    {
    }

    GAABB3 compute_bounds() const
    {
        AABB3d bbox;
        bbox.invalidate();

        const size_t curve_count = m_curves.size();

        for (size_t i = 0; i < curve_count; ++i)
            bbox.insert(m_curves[i].compute_bbox());

        return bbox;
    }

    template <typename RNG>
    static Vector2d rand_vector2d(RNG& rng)
    {
        Vector2d v;
        v[0] = rand_double2(rng);
        v[1] = rand_double2(rng);
        return v;
    }

    void create_hair_ball(const ParamArray& params)
    {
        const size_t ControlPointCount = 4;

        const size_t curve_count = params.get_optional<size_t>("curves", 100);
        const double curve_width = params.get_optional<double>("width", 0.002);

        Vector3d points[ControlPointCount];
        MersenneTwister rng;

        m_curves.reserve(curve_count);

        for (size_t c = 0; c < curve_count; ++c)
        {
            for (size_t p = 0; p < ControlPointCount; ++p)
            {
                // http://math.stackexchange.com/questions/87230/picking-random-points-in-the-volume-of-sphere-with-uniform-probability
                const double r = pow(1.0 - rand_double2(rng), 1.0 / 3);
                const Vector3d d = sample_sphere_uniform(rand_vector2d(rng));
                points[p] = r * d;
            }

            const BezierCurve3d curve(&points[0], curve_width);
            m_curves.push_back(curve);
        }
    }

    void create_furry_ball(const ParamArray& params)
    {
        const size_t ControlPointCount = 4;

        const size_t curve_count = params.get_optional<size_t>("curves", 100);
        const double curve_length = params.get_optional<double>("length", 0.1);
        const double base_width = params.get_optional<double>("base_width", 0.001);
        const double tip_width = params.get_optional<double>("tip_width", 0.0001);
        const double length_fuzziness = params.get_optional<double>("length_fuzziness", 0.3);
        const double curliness = params.get_optional<double>("curliness", 0.5);

        Vector3d points[ControlPointCount];
        double widths[ControlPointCount];

        MersenneTwister rng;

        m_curves.reserve(curve_count);

        for (size_t c = 0; c < curve_count; ++c)
        {
            static const size_t Bases[] = { 2 };
            const Vector2d s = hammersley_sequence<double, 2>(Bases, c, curve_count);
            const Vector3d d = sample_sphere_uniform(s);
            points[0] = d;
            widths[0] = base_width;

            const double f = rand_double1(rng, -length_fuzziness, +length_fuzziness);
            const double length = curve_length * (1.0 + f);

            for (size_t p = 1; p < ControlPointCount; ++p)
            {
                const double r = static_cast<double>(p) / (ControlPointCount - 1);
                const Vector3d f = curliness * sample_sphere_uniform(rand_vector2d(rng));
                points[p] = points[0] + length * (r * d + f);
                widths[p] = lerp(base_width, tip_width, r);
            }

            const BezierCurve3d curve(&points[0], &widths[0]);
            m_curves.push_back(curve);
        }
    }

    void split_and_store(vector<BezierCurve3d>& curves, const BezierCurve3d& curve, const size_t split_count)
    {
        if (split_count > 0)
        {
            BezierCurve3d child1, child2;
            curve.split(child1, child2);
            split_and_store(curves, child1, split_count - 1);
            split_and_store(curves, child2, split_count - 1);
        }
        else
        {
            curves.push_back(curve);
        }
    }

    void load_curve_file(const char* filepath, const ParamArray& params)
    {
        const size_t presplit_count = params.get_optional<size_t>("presplits", 0);

        ifstream input;
        input.open(filepath);

        if (input.good())
        {
            Stopwatch<DefaultWallclockTimer> stopwatch;
            stopwatch.start();

            // Read the number of curves.
            size_t curve_count;
            input >> curve_count;

            // Read the number of control points per curve.
            size_t control_point_count;
            input >> control_point_count;

            if (control_point_count != 4)
            {
                RENDERER_LOG_ERROR(
                    "while loading curve file %s: only curves with 4 control points are currently supported.",
                    filepath);
                return;
            }

            vector<Vector3d> points(control_point_count);
            vector<double> widths(control_point_count);

            m_curves.reserve(curve_count);

            for (size_t c = 0; c < curve_count; ++c)
            {
                for (size_t p = 0; p < control_point_count; ++p)
                {
                    input >> points[p].x >> points[p].y >> points[p].z;
                    input >> widths[p];
                }

                const BezierCurve3d curve(&points[0], &widths[0]);
                split_and_store(m_curves, curve, presplit_count);
            }

            input.close();

            stopwatch.measure();

            RENDERER_LOG_INFO(
                "loaded curve file %s (%s curves) in %s.",
                filepath,
                pretty_uint(curve_count).c_str(),
                pretty_time(stopwatch.get_seconds()).c_str());
        }
        else
        {
            RENDERER_LOG_ERROR("failed to load curve file %s.", filepath);
        }
    }
};

CurveObject::CurveObject(
    const SearchPaths&  search_paths,
    const char*         name,
    const ParamArray&   params)
  : Object(name, params)
  , impl(new Impl())
{
    const string filepath = params.get<string>("filepath");

    if (filepath == "builtin:hairball")
        impl->create_hair_ball(params);
    else if (filepath == "builtin:furryball")
        impl->create_furry_ball(params);
    else
    {
        impl->load_curve_file(
            search_paths.qualify(filepath).c_str(),
            params);
    }
}

CurveObject::~CurveObject()
{
    delete impl;
}

void CurveObject::release()
{
    delete this;
}

const char* CurveObject::get_model() const
{
    return CurveObjectFactory::get_model();
}

GAABB3 CurveObject::compute_local_bbox() const
{
    return impl->compute_bounds();
}

Lazy<RegionKit>& CurveObject::get_region_kit()
{
    return impl->m_lazy_region_kit;
}

size_t CurveObject::get_material_slot_count() const
{
    return impl->m_material_slots.size();
}

const char* CurveObject::get_material_slot(const size_t index) const
{
    return impl->m_material_slots[index].c_str();
}

size_t CurveObject::get_curve_count() const
{
    return impl->m_curves.size();
}

const BezierCurve3d& CurveObject::get_curve(const size_t index) const
{
    assert(index < impl->m_curves.size());
    return impl->m_curves[index];
}

//
// CurveObjectFactory class implementation.
//

const char* CurveObjectFactory::get_model()
{
    return "curve_object";
}

auto_release_ptr<CurveObject> CurveObjectFactory::create(
    const SearchPaths&  search_paths,
    const char*         name,
    const ParamArray&   params)
{
    return
        auto_release_ptr<CurveObject>(
            new CurveObject(search_paths, name, params));
}

}   // namespace renderer
<commit_msg>presplitting is now available for hair ball and furry ball built-in curve objects.<commit_after>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2014 Srinath Ravichandran, The appleseedhq Organization
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "curveobject.h"

// appleseed.renderer headers.
#include "renderer/global/globallogger.h"
#include "renderer/utility/paramarray.h"

// appleseed.foundation headers.
#include "foundation/math/sampling/mappings.h"
#include "foundation/math/aabb.h"
#include "foundation/math/qmc.h"
#include "foundation/math/rng.h"
#include "foundation/math/scalar.h"
#include "foundation/math/vector.h"
#include "foundation/platform/defaulttimers.h"
#include "foundation/utility/searchpaths.h"
#include "foundation/utility/stopwatch.h"
#include "foundation/utility/string.h"

// Standard headers.
#include <cassert>
#include <cmath>
#include <fstream>
#include <string>
#include <vector>

using namespace foundation;
using namespace std;

namespace renderer
{

//
// CurveObject class implementation.
//

struct CurveObject::Impl
{
    RegionKit               m_region_kit;
    Lazy<RegionKit>         m_lazy_region_kit;
    vector<BezierCurve3d>   m_curves;
    vector<string>          m_material_slots;

    Impl()
      : m_lazy_region_kit(&m_region_kit)
    {
    }

    GAABB3 compute_bounds() const
    {
        AABB3d bbox;
        bbox.invalidate();

        const size_t curve_count = m_curves.size();

        for (size_t i = 0; i < curve_count; ++i)
            bbox.insert(m_curves[i].compute_bbox());

        return bbox;
    }

    template <typename RNG>
    static Vector2d rand_vector2d(RNG& rng)
    {
        Vector2d v;
        v[0] = rand_double2(rng);
        v[1] = rand_double2(rng);
        return v;
    }

    void split_and_store(vector<BezierCurve3d>& curves, const BezierCurve3d& curve, const size_t split_count)
    {
        if (split_count > 0)
        {
            BezierCurve3d child1, child2;
            curve.split(child1, child2);
            split_and_store(curves, child1, split_count - 1);
            split_and_store(curves, child2, split_count - 1);
        }
        else curves.push_back(curve);
    }

    void create_hair_ball(const ParamArray& params)
    {
        const size_t ControlPointCount = 4;

        const size_t curve_count = params.get_optional<size_t>("curves", 100);
        const double curve_width = params.get_optional<double>("width", 0.002);
        const size_t split_count = params.get_optional<size_t>("presplits", 0);

        Vector3d points[ControlPointCount];
        MersenneTwister rng;

        m_curves.reserve(curve_count);

        for (size_t c = 0; c < curve_count; ++c)
        {
            for (size_t p = 0; p < ControlPointCount; ++p)
            {
                // http://math.stackexchange.com/questions/87230/picking-random-points-in-the-volume-of-sphere-with-uniform-probability
                const double r = pow(1.0 - rand_double2(rng), 1.0 / 3);
                const Vector3d d = sample_sphere_uniform(rand_vector2d(rng));
                points[p] = r * d;
            }

            const BezierCurve3d curve(&points[0], curve_width);
            split_and_store(m_curves, curve, split_count);
        }
    }

    void create_furry_ball(const ParamArray& params)
    {
        const size_t ControlPointCount = 4;

        const size_t curve_count = params.get_optional<size_t>("curves", 100);
        const double curve_length = params.get_optional<double>("length", 0.1);
        const double base_width = params.get_optional<double>("base_width", 0.001);
        const double tip_width = params.get_optional<double>("tip_width", 0.0001);
        const double length_fuzziness = params.get_optional<double>("length_fuzziness", 0.3);
        const double curliness = params.get_optional<double>("curliness", 0.5);
        const size_t split_count = params.get_optional<size_t>("presplits", 0);

        Vector3d points[ControlPointCount];
        double widths[ControlPointCount];

        MersenneTwister rng;

        m_curves.reserve(curve_count);

        for (size_t c = 0; c < curve_count; ++c)
        {
            static const size_t Bases[] = { 2 };
            const Vector2d s = hammersley_sequence<double, 2>(Bases, c, curve_count);
            const Vector3d d = sample_sphere_uniform(s);
            points[0] = d;
            widths[0] = base_width;

            const double f = rand_double1(rng, -length_fuzziness, +length_fuzziness);
            const double length = curve_length * (1.0 + f);

            for (size_t p = 1; p < ControlPointCount; ++p)
            {
                const double r = static_cast<double>(p) / (ControlPointCount - 1);
                const Vector3d f = curliness * sample_sphere_uniform(rand_vector2d(rng));
                points[p] = points[0] + length * (r * d + f);
                widths[p] = lerp(base_width, tip_width, r);
            }

            const BezierCurve3d curve(&points[0], &widths[0]);
            split_and_store(m_curves, curve, split_count);
        }
    }

    void load_curve_file(const char* filepath, const ParamArray& params)
    {
        const size_t split_count = params.get_optional<size_t>("presplits", 0);

        ifstream input;
        input.open(filepath);

        if (input.good())
        {
            Stopwatch<DefaultWallclockTimer> stopwatch;
            stopwatch.start();

            // Read the number of curves.
            size_t curve_count;
            input >> curve_count;

            // Read the number of control points per curve.
            size_t control_point_count;
            input >> control_point_count;

            if (control_point_count != 4)
            {
                RENDERER_LOG_ERROR(
                    "while loading curve file %s: only curves with 4 control points are currently supported.",
                    filepath);
                return;
            }

            vector<Vector3d> points(control_point_count);
            vector<double> widths(control_point_count);

            m_curves.reserve(curve_count);

            for (size_t c = 0; c < curve_count; ++c)
            {
                for (size_t p = 0; p < control_point_count; ++p)
                {
                    input >> points[p].x >> points[p].y >> points[p].z;
                    input >> widths[p];
                }

                const BezierCurve3d curve(&points[0], &widths[0]);
                split_and_store(m_curves, curve, split_count);
            }

            input.close();

            stopwatch.measure();

            RENDERER_LOG_INFO(
                "loaded curve file %s (%s curves) in %s.",
                filepath,
                pretty_uint(curve_count).c_str(),
                pretty_time(stopwatch.get_seconds()).c_str());
        }
        else
        {
            RENDERER_LOG_ERROR("failed to load curve file %s.", filepath);
        }
    }
};

CurveObject::CurveObject(
    const SearchPaths&  search_paths,
    const char*         name,
    const ParamArray&   params)
  : Object(name, params)
  , impl(new Impl())
{
    const string filepath = params.get<string>("filepath");

    if (filepath == "builtin:hairball")
        impl->create_hair_ball(params);
    else if (filepath == "builtin:furryball")
        impl->create_furry_ball(params);
    else
    {
        impl->load_curve_file(
            search_paths.qualify(filepath).c_str(),
            params);
    }
}

CurveObject::~CurveObject()
{
    delete impl;
}

void CurveObject::release()
{
    delete this;
}

const char* CurveObject::get_model() const
{
    return CurveObjectFactory::get_model();
}

GAABB3 CurveObject::compute_local_bbox() const
{
    return impl->compute_bounds();
}

Lazy<RegionKit>& CurveObject::get_region_kit()
{
    return impl->m_lazy_region_kit;
}

size_t CurveObject::get_material_slot_count() const
{
    return impl->m_material_slots.size();
}

const char* CurveObject::get_material_slot(const size_t index) const
{
    return impl->m_material_slots[index].c_str();
}

size_t CurveObject::get_curve_count() const
{
    return impl->m_curves.size();
}

const BezierCurve3d& CurveObject::get_curve(const size_t index) const
{
    assert(index < impl->m_curves.size());
    return impl->m_curves[index];
}

//
// CurveObjectFactory class implementation.
//

const char* CurveObjectFactory::get_model()
{
    return "curve_object";
}

auto_release_ptr<CurveObject> CurveObjectFactory::create(
    const SearchPaths&  search_paths,
    const char*         name,
    const ParamArray&   params)
{
    return
        auto_release_ptr<CurveObject>(
            new CurveObject(search_paths, name, params));
}

}   // namespace renderer
<|endoftext|>


<commit_before>/***
      __   __  _  _  __     __    ___
     / _\ (  )( \/ )(  )   /  \  / __)
    /    \ )(  )  ( / (_/\(  O )( (_ \
    \_/\_/(__)(_/\_)\____/ \__/  \___/

    This file is part of aixlog
    Copyright (C) 2017  Johannes Pohl

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
***/

#include "aixlog.hpp"

using namespace std;

class Test
{
public:
	Test()
	{
	}

	void do_something(const std::string& some_string)
	{
		LOG(INFO) << "LOG(INFO): " << some_string << "\n";
	}
};

int main(int argc, char** argv)
{
	Log::init(
		{
			/// Log normal (i.e. non-special) logs to LogSinkCout
			make_shared<LogSinkCout>(LogPriority::debug, LogSink::Type::normal, "cout: %Y-%m-%d %H-%M-%S.#ms [#prio] (#tag) #logline"),
			/// Log error and higher prio messages to cerr
			make_shared<LogSinkCerr>(LogPriority::error, LogSink::Type::all, "cerr: %Y-%m-%d %H-%M-%S.#ms [#prio] (#tag)"),
			/// Log special logs to native log (Syslog on Linux, Android Log on Android, EventLog on Windows, Unified logging on Apple)
			make_shared<LogSinkNative>("aixlog", LogPriority::debug, LogSink::Type::special),
			/// Callback log sink with cout logging in a lambda function
			make_shared<LogSinkCallback>(LogPriority::debug, LogSink::Type::all,
				[](const time_point_sys_clock& timestamp, LogPriority priority, LogType type, const Tag& tag, const std::string& message)
				{
					cout << "Callback:\n\tmsg:  " << message << "\n\ttag:  " << tag.tag << "\n\tprio: " << Log::toString(priority) << " (" << (int)priority << ")\n\ttype: " << (type == LogType::normal?"normal":"special") << "\n";
				}
			)
		}
	);

	/// Log with info prio
	LOG(INFO) << "LOG(INFO)\n";
	/// Log with info prio and tag
	LOG(INFO, "guten tag") << "LOG(INFO, \"guten tag\")\n";
	/// Log with info prio and explicit tag (same result as above)
	LOG(INFO) << TAG("guten tag") << "LOGI << TAG(\"guten tag\")\n";
	/// Log "special" with info prio
	SLOG(INFO) << "SLOG(INFO)\n";
	/// Log "special" with info prio
	LOG(INFO) << LogType::special << "LOGI << LogType::special\n";
	/// Log "special" with info prio and explicit tag
	LOG(INFO) << LogType::special << TAG("guten tag") << "LOGI << LogType::special << TAG(\"guten tag\")\n";

	/// Different log priorities
	LOG(EMERG) << "LOG(EMERG)\nLOG(EMERG) Second line\n";
	LOG(EMERG) << TAG("hello") << "LOG(EMERG) << TAG(\"hello\") no line break";
	LOG(EMERG) << "LOG(EMERG) 2 no line break";
	LOG(ALERT) << "LOG(ALERT): change in loglevel will add a line break";
	LOG(ERROR) << "LOG(ERROR)";
	LOG(INFO) << TAG("my tag") << "LOG(INFO) << TAG(\"my tag\")n";
	LOG(NOTICE) << "LOG(NOTICE)\n";
	LOG(INFO) << "LOG(INFO)\n";
	LOG(DEBUG) << "LOG(DEBUG)\n";

	/// Conditional logging
	LOG(DEBUG) << COND(1 == 1) << "LOGD will be logged\n";
	LOG(DEBUG) << COND(1 == 2) << "LOGD will not be logged\n";

	/// Colors :-)
	LOG(CRIT) << "LOG(CRIT) " << Color::red << "red" << Color::none << " default color\n";
	LOG(CRIT) << "LOG(CRIT) " << LogColor(Color::yellow, Color::blue) << "yellow on blue background" << Color::none << " default color\n";

	Test test;
	test.do_something("doing something...");
}

<commit_msg>update example code<commit_after>/***
      __   __  _  _  __     __    ___
     / _\ (  )( \/ )(  )   /  \  / __)
    /    \ )(  )  ( / (_/\(  O )( (_ \
    \_/\_/(__)(_/\_)\____/ \__/  \___/

    This file is part of aixlog
    Copyright (C) 2017  Johannes Pohl

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
***/

#include "aixlog.hpp"

using namespace std;

int main(int argc, char** argv)
{
	Log::init(
		{
			/// Log normal (i.e. non-special) logs to LogSinkCout
			make_shared<LogSinkCout>(LogPriority::debug, LogSink::Type::normal, "cout: %Y-%m-%d %H-%M-%S.#ms [#prio] (#tag) #logline"),
			/// Log error and higher prio messages to cerr
			make_shared<LogSinkCerr>(LogPriority::error, LogSink::Type::all, "cerr: %Y-%m-%d %H-%M-%S.#ms [#prio] (#tag)"),
			/// Log special logs to native log (Syslog on Linux, Android Log on Android, EventLog on Windows, Unified logging on Apple)
			make_shared<LogSinkNative>("aixlog", LogPriority::debug, LogSink::Type::special),
			/// Callback log sink with cout logging in a lambda function
			/// Could also do file logging
			make_shared<LogSinkCallback>(LogPriority::debug, LogSink::Type::all,
				[](const time_point_sys_clock& timestamp, LogPriority priority, LogType type, const Tag& tag, const std::string& message)
				{
					cout << "Callback:\n\tmsg:  " << message << "\n\ttag:  " << tag.tag << "\n\tprio: " << Log::toString(priority) << " (" << (int)priority << ")\n\ttype: " << (type == LogType::normal?"normal":"special") << "\n";
				}
			)
		}
	);

	/// Log with info priority
	LOG(INFO) << "LOG(INFO)\n";
	/// ... with a tag
	LOG(INFO, "guten tag") << "LOG(INFO, \"guten tag\")\n";
	/// ... with an explicit tag (same result as above)
	LOG(INFO) << TAG("guten tag") << "LOGI << TAG(\"guten tag\")\n";
	/// Log "special" with info prio
	SLOG(INFO) << "SLOG(INFO)\n";
	/// Log with explicit "special" type
	LOG(INFO) << LogType::special << "LOGI << LogType::special\n";
	/// ... with explicit "special" type and explicit tag
	LOG(INFO) << LogType::special << TAG("guten tag") << "LOGI << LogType::special << TAG(\"guten tag\")\n";

	/// Different log priorities
	LOG(EMERG) << "LOG(EMERG)\nLOG(EMERG) Second line\n";
	LOG(EMERG) << TAG("hello") << "LOG(EMERG) << TAG(\"hello\") no line break";
	LOG(EMERG) << "LOG(EMERG) 2 no line break";
	LOG(ALERT) << "LOG(ALERT): change in loglevel will add a line break";
	LOG(CRIT) << "LOG(CRIT)";
	LOG(ERROR) << "LOG(ERROR)";
	LOG(WARNING) << "LOG(WARNING)";
	LOG(NOTICE) << "LOG(NOTICE)\n";
	LOG(INFO) << "LOG(INFO)\n";
	LOG(INFO) << TAG("my tag") << "LOG(INFO) << TAG(\"my tag\")n";
	LOG(DEBUG) << "LOG(DEBUG)\n";

	/// Conditional logging
	LOG(DEBUG) << COND(1 == 1) << "LOG(DEBUG) will be logged\n";
	LOG(DEBUG) << COND(1 == 2) << "LOG(DEBUG) will not be logged\n";

	/// Colors :-)
	LOG(CRIT) << "LOG(CRIT) " << Color::red << "red" << Color::none << " default color\n";
	LOG(CRIT) << "LOG(CRIT) " << LogColor(Color::yellow, Color::blue) << "yellow on blue background" << Color::none << " default color\n";
}

<|endoftext|>


<commit_before>#include "array.hpp"
#include "slice.hpp"

#include <gtest/gtest.h>

using namespace MultidimensionalArray;

class SliceTest: public ::testing::Test {
  protected:
    Size::SizeType sizes;
    int *values;

    virtual void SetUp() {
      sizes = Size::SizeType({2, 3, 4, 5});
      values = new int[2*3*4*5];
      unsigned int index = 0;
      for (int i1 = 0; i1 < 2; i1++)
        for (int i2 = 0; i2 < 3; i2++)
          for (int i3 = 0; i3 < 4; i3++)
            for (int i4 = 0; i4 < 5; i4++) {
              values[index] = index;
              index++;
            }
    }

    virtual void TearDown() {
      delete[] values;
    }

    void check_sizes(Size::SizeType const& sizes1,
        Size::SizeType const& sizes2) {
      EXPECT_EQ(sizes1.size(), sizes2.size());
      for (unsigned int i = 0; i < sizes1.size(); i++)
        EXPECT_EQ(sizes1[i], sizes2[i]);
    }
};

TEST_F(SliceTest, Elements) {
  Array<int> array(sizes, values);
  Slice<int> slice(array, 1);

  unsigned int index = 0, element_index = 0, inner_index = 0;
  for (int i1 = 0; i1 < 2; i1++)
    for (int i2 = 0; i2 < 3; i2++) {
      Array<int> temp(slice.get_element(element_index));
      int* values = temp.get_pointer();
      inner_index = 0;
      for (int i3 = 0; i3 < 4; i3++)
        for (int i4 = 0; i4 < 5; i4++) {
          EXPECT_EQ(index, values[inner_index]);
          EXPECT_EQ(index, temp(i3, i4));
          index++;
          inner_index++;
        }
      element_index++;
    }
}

TEST_F(SliceTest, ElementsSet) {
  Array<int> array(sizes, values);
  Slice<int> slice(array, 1);

  unsigned int index = 0, element_index = 0, inner_index = 0;
  for (int i1 = 0; i1 < 2; i1++)
    for (int i2 = 0; i2 < 3; i2++) {
      Array<int> temp(slice.get_element(element_index));
      int* values = temp.get_pointer();
      inner_index = 0;
      for (int i3 = 0; i3 < 4; i3++)
        for (int i4 = 0; i4 < 5; i4++) {
          temp(i3, i4) *= 2;;
          EXPECT_EQ(2*index, values[inner_index]);
          index++;
          inner_index++;
        }
      element_index++;
    }
}

TEST_F(SliceTest, Sizes) {
  Array<int> array(sizes, values);

  {
    Slice<int> slice(array, 0);
    check_sizes(slice.left_size(), {2});
    check_sizes(slice.right_size(), {3, 4, 5});
  }

  {
    Slice<int> slice(array, 1);
    check_sizes(slice.left_size(), {2, 3});
    check_sizes(slice.right_size(), {4, 5});
  }

  {
    Slice<int> slice(array, 2);
    check_sizes(slice.left_size(), {2, 3, 4});
    check_sizes(slice.right_size(), {5});
  }
}
<commit_msg>Changed some types in Slice tests.<commit_after>#include "array.hpp"
#include "slice.hpp"

#include <gtest/gtest.h>

using namespace MultidimensionalArray;

class SliceTest: public ::testing::Test {
  protected:
    Size::SizeType sizes;
    int *values;

    virtual void SetUp() {
      sizes = Size::SizeType({2, 3, 4, 5});
      values = new int[2*3*4*5];
      size_t index = 0;
      for (Size::SizeType::value_type i1 = 0; i1 < 2; i1++)
        for (Size::SizeType::value_type i2 = 0; i2 < 3; i2++)
          for (Size::SizeType::value_type i3 = 0; i3 < 4; i3++)
            for (Size::SizeType::value_type i4 = 0; i4 < 5; i4++) {
              values[index] = index;
              index++;
            }
    }

    virtual void TearDown() {
      delete[] values;
    }

    void check_sizes(Size::SizeType const& sizes1,
        Size::SizeType const& sizes2) {
      EXPECT_EQ(sizes1.size(), sizes2.size());
      for (size_t i = 0; i < sizes1.size(); i++)
        EXPECT_EQ(sizes1[i], sizes2[i]);
    }
};

TEST_F(SliceTest, Elements) {
  Array<int> array(sizes, values);
  Slice<int> slice(array, 1);

  Size::SizeType::value_type index = 0, element_index = 0, inner_index = 0;
  for (Size::SizeType::value_type i1 = 0; i1 < 2; i1++)
    for (Size::SizeType::value_type i2 = 0; i2 < 3; i2++) {
      Array<int> temp(slice.get_element(element_index));
      int* values = temp.get_pointer();
      inner_index = 0;
      for (Size::SizeType::value_type i3 = 0; i3 < 4; i3++)
        for (Size::SizeType::value_type i4 = 0; i4 < 5; i4++) {
          EXPECT_EQ(index, values[inner_index]);
          EXPECT_EQ(index, temp(i3, i4));
          index++;
          inner_index++;
        }
      element_index++;
    }
}

TEST_F(SliceTest, ElementsSet) {
  Array<int> array(sizes, values);
  Slice<int> slice(array, 1);

  Size::SizeType::value_type index = 0, element_index = 0, inner_index = 0;
  for (Size::SizeType::value_type i1 = 0; i1 < 2; i1++)
    for (Size::SizeType::value_type i2 = 0; i2 < 3; i2++) {
      Array<int> temp(slice.get_element(element_index));
      int* values = temp.get_pointer();
      inner_index = 0;
      for (Size::SizeType::value_type i3 = 0; i3 < 4; i3++)
        for (Size::SizeType::value_type i4 = 0; i4 < 5; i4++) {
          temp(i3, i4) *= 2;;
          EXPECT_EQ(2*index, values[inner_index]);
          index++;
          inner_index++;
        }
      element_index++;
    }
}

TEST_F(SliceTest, Sizes) {
  Array<int> array(sizes, values);

  {
    Slice<int> slice(array, 0);
    check_sizes(slice.left_size(), {2});
    check_sizes(slice.right_size(), {3, 4, 5});
  }

  {
    Slice<int> slice(array, 1);
    check_sizes(slice.left_size(), {2, 3});
    check_sizes(slice.right_size(), {4, 5});
  }

  {
    Slice<int> slice(array, 2);
    check_sizes(slice.left_size(), {2, 3, 4});
    check_sizes(slice.right_size(), {5});
  }
}
<|endoftext|>