<commit_before>/*
 * Copyright (c) 2007-2009 Digital Bazaar, Inc. All rights reserved.
 */
#include "db/net/DefaultBandwidthThrottler.h"

#include "db/rt/System.h"
#include "db/rt/Thread.h"
#include "db/util/Math.h"

#include <math.h>

using namespace db::net;
using namespace db::rt;
using namespace db::util;

DefaultBandwidthThrottler::DefaultBandwidthThrottler(int rateLimit)
{
   // initialize the last request time
   mLastRequestTime = System::getCurrentMilliseconds();

   // initialize the available number of bytes
   mAvailableBytes = 0;

   // set the rate limit (will also reset the window time if necessary)
   setRateLimit(rateLimit);
}

DefaultBandwidthThrottler::~DefaultBandwidthThrottler()
{
}

bool DefaultBandwidthThrottler::requestBytes(int count, int& permitted)
{
   bool rval = true;

   if(getRateLimit() > 0)
   {
      mLock.lock();
      {
         // limit the bandwidth
         rval = limitBandwidth();

         // get the available bytes
         int available = (mAvailableBytes >= (uint64_t)Math::MAX_INT_VALUE) ?
            Math::MAX_INT_VALUE : (int)mAvailableBytes;
         permitted = (available > count ? count : available);

         // increment the bytes granted
         mBytesGranted += permitted;

         // subtract the number of permitted bytes from the available bytes
         mAvailableBytes -= permitted;

         // update last request time
         mLastRequestTime = System::getCurrentMilliseconds();
      }
      mLock.unlock();
   }
   else
   {
      // no rate limit, return the count
      permitted = count;
   }

   return rval;
}

int DefaultBandwidthThrottler::getAvailableBytes()
{
   int rval = 0;

   mLock.lock();
   {
      if(mRateLimit > 0)
      {
         rval = mAvailableBytes;
      }
      else
      {
         rval = Math::MAX_INT_VALUE;
      }
   }
   mLock.unlock();

   return rval;
}

void DefaultBandwidthThrottler::setRateLimit(int rateLimit)
{
   mLock.lock();
   {
      // set new rate limit
      mRateLimit = rateLimit;
      if(mRateLimit > 0)
      {
         // reset the window time
         resetWindowTime();

         // update the available byte time
         updateAvailableByteTime();
      }
   }
   mLock.unlock();
}

int DefaultBandwidthThrottler::getRateLimit()
{
   return mRateLimit;
}

void DefaultBandwidthThrottler::resetWindowTime()
{
   // set the current window time
   mWindowTime = System::getCurrentMilliseconds();

   // reset the bytes already granted in this window
   mBytesGranted = 0;
}

void DefaultBandwidthThrottler::updateWindowTime()
{
   // get the current time
   uint64_t now = System::getCurrentMilliseconds();

   // Cap the number of bytes granted per window at maximum uint value
   // so that there isn't any overflow. This should also be a sufficiently
   // large enough number such that rate calculations aren't affected
   // very often at all.
   if(mBytesGranted > 0xffffffff)
   {
      resetWindowTime();
   }
   else if(now - mLastRequestTime > 3000)
   {
      // If it has been more than 3 seconds since the last request
      // for bytes, then reset the time window.
      //
      // 3 seconds was chosen because the minimum rate limit is
      // 1 byte per second. So if a request is made at that rate limit
      // then at least one byte would be available after one second.
      //
      // If it subsequently actually takes one second to transfer the
      // byte, then the next request would be somewhere shortly after
      // 2 seconds.
      //
      // It is assumed that any request more than a second later
      // involves a different transfer so we shouldn't store up a
      // lot of available bytes (by failing to reset the window) for
      // that transfer artificially bloating its rate.
      //
      // If the assumption fails, and, for instance, it takes more
      // than one second for a single byte to be transferred or bytes
      // are only requested ever so often, then the requester will
      // have to wait a maximum of one second to acquire another byte.
      resetWindowTime();
   }
}

inline uint64_t DefaultBandwidthThrottler::getWindowTime()
{
   return mWindowTime;
}

void DefaultBandwidthThrottler::updateAvailableByteTime()
{
   // the amount of time until a byte is available is 1000 milliseconds
   // divided by the rate in bytes/second, with a minimum of 1 millisecond
   mAvailableByteTime = (uint64_t)roundl(1000. / getRateLimit());
   mAvailableByteTime = (1 > mAvailableByteTime) ? 1 : mAvailableByteTime;
}

inline uint64_t DefaultBandwidthThrottler::getAvailableByteTime()
{
   return mAvailableByteTime;
}

void DefaultBandwidthThrottler::updateAvailableBytes()
{
   // get the passed time in the current window
   double passedTime = System::getCurrentMilliseconds() - getWindowTime();

   // determine how many bytes are available given the passed time --
   // use the floor so as not to go over the rate limit
   mAvailableBytes = (uint64_t)floorl(passedTime / 1000. * getRateLimit());

   // subtract the number of bytes already granted in this window
   mAvailableBytes = (mBytesGranted > mAvailableBytes) ?
      0 : mAvailableBytes - mBytesGranted;
}

bool DefaultBandwidthThrottler::limitBandwidth()
{
   bool rval = true;

   // update the window time
   updateWindowTime();

   // update the number of available bytes
   updateAvailableBytes();

   // while there aren't any available bytes, sleep for the available byte time
   while(rval && mAvailableBytes == 0)
   {
      // unlock to sleep
      mLock.unlock();
      rval = Thread::sleep(getAvailableByteTime());
      mLock.lock();

      // update the number of available bytes
      updateAvailableBytes();
   }

   return rval;
}
<commit_msg>Fixed bug.<commit_after>/*
 * Copyright (c) 2007-2009 Digital Bazaar, Inc. All rights reserved.
 */
#include "db/net/DefaultBandwidthThrottler.h"

#include "db/rt/System.h"
#include "db/rt/Thread.h"
#include "db/util/Math.h"

#include <math.h>

using namespace db::net;
using namespace db::rt;
using namespace db::util;

DefaultBandwidthThrottler::DefaultBandwidthThrottler(int rateLimit)
{
   // initialize the last request time
   mLastRequestTime = System::getCurrentMilliseconds();

   // initialize the available number of bytes
   mAvailableBytes = 0;

   // set the rate limit (will also reset the window time if necessary)
   setRateLimit(rateLimit);
}

DefaultBandwidthThrottler::~DefaultBandwidthThrottler()
{
}

bool DefaultBandwidthThrottler::requestBytes(int count, int& permitted)
{
   bool rval = true;

   if(getRateLimit() > 0)
   {
      mLock.lock();
      {
         // limit the bandwidth
         rval = limitBandwidth();

         // get the available bytes
         int available = (mAvailableBytes >= (uint64_t)Math::MAX_INT_VALUE) ?
            Math::MAX_INT_VALUE : (int)mAvailableBytes;
         permitted = (available > count ? count : available);

         // increment the bytes granted
         mBytesGranted += permitted;

         // subtract the number of permitted bytes from the available bytes
         mAvailableBytes -= permitted;

         // update last request time
         mLastRequestTime = System::getCurrentMilliseconds();
      }
      mLock.unlock();
   }
   else
   {
      // no rate limit, return the count
      permitted = count;
   }

   return rval;
}

int DefaultBandwidthThrottler::getAvailableBytes()
{
   int rval = 0;

   mLock.lock();
   {
      if(mRateLimit > 0 && mAvailableBytes < (uint64_t)Math::MAX_INT_VALUE)
      {
         rval = mAvailableBytes;
      }
      else
      {
         rval = Math::MAX_INT_VALUE;
      }
   }
   mLock.unlock();

   return rval;
}

void DefaultBandwidthThrottler::setRateLimit(int rateLimit)
{
   mLock.lock();
   {
      // set new rate limit
      mRateLimit = rateLimit;
      if(mRateLimit > 0)
      {
         // reset the window time
         resetWindowTime();

         // update the available byte time
         updateAvailableByteTime();
      }
   }
   mLock.unlock();
}

int DefaultBandwidthThrottler::getRateLimit()
{
   return mRateLimit;
}

void DefaultBandwidthThrottler::resetWindowTime()
{
   // set the current window time
   mWindowTime = System::getCurrentMilliseconds();

   // reset the bytes already granted in this window
   mBytesGranted = 0;
}

void DefaultBandwidthThrottler::updateWindowTime()
{
   // get the current time
   uint64_t now = System::getCurrentMilliseconds();

   // Cap the number of bytes granted per window at maximum uint value
   // so that there isn't any overflow. This should also be a sufficiently
   // large enough number such that rate calculations aren't affected
   // very often at all.
   if(mBytesGranted > 0xffffffff)
   {
      resetWindowTime();
   }
   else if(now - mLastRequestTime > 3000)
   {
      // If it has been more than 3 seconds since the last request
      // for bytes, then reset the time window.
      //
      // 3 seconds was chosen because the minimum rate limit is
      // 1 byte per second. So if a request is made at that rate limit
      // then at least one byte would be available after one second.
      //
      // If it subsequently actually takes one second to transfer the
      // byte, then the next request would be somewhere shortly after
      // 2 seconds.
      //
      // It is assumed that any request more than a second later
      // involves a different transfer so we shouldn't store up a
      // lot of available bytes (by failing to reset the window) for
      // that transfer artificially bloating its rate.
      //
      // If the assumption fails, and, for instance, it takes more
      // than one second for a single byte to be transferred or bytes
      // are only requested ever so often, then the requester will
      // have to wait a maximum of one second to acquire another byte.
      resetWindowTime();
   }
}

inline uint64_t DefaultBandwidthThrottler::getWindowTime()
{
   return mWindowTime;
}

void DefaultBandwidthThrottler::updateAvailableByteTime()
{
   // the amount of time until a byte is available is 1000 milliseconds
   // divided by the rate in bytes/second, with a minimum of 1 millisecond
   mAvailableByteTime = (uint64_t)roundl(1000. / getRateLimit());
   mAvailableByteTime = (1 > mAvailableByteTime) ? 1 : mAvailableByteTime;
}

inline uint64_t DefaultBandwidthThrottler::getAvailableByteTime()
{
   return mAvailableByteTime;
}

void DefaultBandwidthThrottler::updateAvailableBytes()
{
   // get the passed time in the current window
   double passedTime = System::getCurrentMilliseconds() - getWindowTime();

   // determine how many bytes are available given the passed time --
   // use the floor so as not to go over the rate limit
   mAvailableBytes = (uint64_t)floorl(passedTime / 1000. * getRateLimit());

   // subtract the number of bytes already granted in this window
   mAvailableBytes = (mBytesGranted > mAvailableBytes) ?
      0 : mAvailableBytes - mBytesGranted;
}

bool DefaultBandwidthThrottler::limitBandwidth()
{
   bool rval = true;

   // update the window time
   updateWindowTime();

   // update the number of available bytes
   updateAvailableBytes();

   // while there aren't any available bytes, sleep for the available byte time
   while(rval && mAvailableBytes == 0)
   {
      // unlock to sleep
      mLock.unlock();
      rval = Thread::sleep(getAvailableByteTime());
      mLock.lock();

      // update the number of available bytes
      updateAvailableBytes();
   }

   return rval;
}
<|endoftext|>


<commit_before>#include "WSConnection.h"

#include <boost/bind.hpp>

#include "../Common/BinUtils.h"

#include "IMsgHandler.h"

using namespace HTTP::WebSocket;

Connection::Connection(boost::asio::ip::tcp::socket &&SrcSocket, IMsgHandler *MsgHandler) : HTTP::ConnectionBase(std::move(SrcSocket)),
	SafeStates(SAFE_ALL),
	SilentTime(0), CurrFrameLength(UnknownFrameLength), MyHandler(MsgHandler)
{
	//Start reading for incoming messages.
	ClearSafeState<SAFE_READ>();
	StartAsyncRead();
}

void Connection::Stop()
{
	try { MySock.close(); }
	catch (...) { }

	if (MyHandler)
	{
		MyHandler->OnClose(CR_CONN_ERROR);
		MyHandler=nullptr;
	}
}

bool Connection::OnStep(unsigned int StepInterval, ConnectionBase **OutNextConn)
{
	SilentTime+=StepInterval;
	if (SilentTime>Config::MaxSilentTime)
	{
		if (MyHandler)
		{
			MyHandler->OnClose(CR_CONN_ERROR);
			MyHandler=nullptr;
		}

		try { MySock.close(); }
		catch (...) { }
		return SafeStates!=SAFE_ALL;
	}
	else
		return true;
}

unsigned char *Connection::Allocate(MESSAGETYPE Type, unsigned long long Length)
{
	//SocketMtx is locked externally.

	//Create the frame header.
	unsigned long long FrameLength=Length;
	if (Length<126)
		FrameLength+=2;
	else if (Length<655356)
		FrameLength+=2+2;
	else
		FrameLength+=2+8;

	if (unsigned char *FrameBuff=WriteBuff.Allocate((unsigned int)FrameLength))
	{
		//Set the opcode and the FIN flag (we don't send fragmented messsages).
		*FrameBuff++=(unsigned char)(GetOpcode(Type) | FLAG_FIN);

		//Set the length.
		if (Length<126)
			*FrameBuff++=(unsigned char)Length;
		else if (Length<655356)
		{
			*FrameBuff++=126;
			SET_BIGENDIAN_UNALIGNED2(FrameBuff,(unsigned short)Length);
			FrameBuff+=2;
		}
		else
		{
			*FrameBuff++=127;
			SET_BIGENDIAN_UNALIGNED8(FrameBuff,Length);
			FrameBuff+=8;
		}

		return FrameBuff;
	}
	else
		return nullptr;
}

bool Connection::Deallocate()
{
	//SocketMtx is locked externally.
	WriteBuff.Commit(0);
	return true;
}

bool Connection::Send()
{
	//SocketMtx is locked externally.
	WriteBuff.Commit(~0);
	PostWriteReq();
	return true;
}

bool Connection::SendPing()
{
	//SocketMtx is locked externally.
	SendControlFrame(OCN_PING);
	PostWriteReq();
	return true;
}

void Connection::Close(unsigned short Reason)
{
	//SocketMtx is locked externally.
	SendCloseInternal(Reason);
	PostCloseReq();
}

void Connection::OnRead(const boost::system::error_code &error, std::size_t bytes_transferred)
{
	if (!error)
	{
		ReadBuff.OnNewData(bytes_transferred);
		SilentTime=0;
		CLOSEREASON Reason=ProcessIncoming();
		if (Reason==CR_NONE)
			StartAsyncRead();
		else
		{
			OnProtocolError(Reason);
			SetSafeState<SAFE_READ>();
		}
	}
	else
	{
		SetSafeState<SAFE_READ>();
		if (MyHandler)
		{
			MyHandler->OnClose(CR_CONN_ERROR);
			MyHandler=nullptr;
		}
	}
}

void Connection::OnWrite(const boost::system::error_code &error, std::size_t bytes_transferred)
{
	SetSafeState<SAFE_WRITE>();
	if (!error)
	{
		boost::unique_lock<boost::mutex> lock(SendBuffMtx);

		WriteBuff.Release();
		StartAsyncWrite();
	}
	else
	{
		if (MyHandler)
		{
			MyHandler->OnClose(CR_CONN_ERROR);
			MyHandler=nullptr;
		}
	}
}

CLOSEREASON Connection::ProcessIncoming()
{
	while (true)
	{
		unsigned int DataLength;
		const unsigned char *DataBuff=ReadBuff.GetAvailableData(DataLength);
		if (CurrFrameLength==UnknownFrameLength)
		{
			//Try to determine the length of the frame. At least 2 bytes is a must.
			if (DataLength<2)
			{
				ReadBuff.RequestData(2);
				return CR_NONE;
			}

			unsigned char LengthMarker=DataBuff[1] & 0x7F;
			if (LengthMarker<126)
				CurrFrameLength=LengthMarker + 2;
			else if (LengthMarker==126)
			{
				//Length field is 16 bit wide.
				if (DataLength<2+2)
				{
					ReadBuff.RequestData(2+2);
					return CR_NONE;
				}

				CurrFrameLength=GET_BIGENDIAN_UNALIGNED2(DataBuff+2) + 2 + 2;
			}
			else //if (LengthMarker==127)
			{
				//Length field is 64 bit wide.
				if (DataLength<2+8)
				{
					ReadBuff.RequestData(2+8);
					return CR_NONE;
				}

				CurrFrameLength=GET_BIGENDIAN_UNALIGNED8(DataBuff+2) + 2 + 8;
			}

			if (DataBuff[1] & FLAG_MASK)
				CurrFrameLength+=4; //Include the length of the mask byte, too.
		}

		if (CurrFrameLength>Config::MaxFrameSize)
			return CR_SIZE_LIMIT;

		//At this point, we have determined the full frame length, or exited the function.
		if (DataLength<CurrFrameLength)
		{
			ReadBuff.RequestData((unsigned int)CurrFrameLength);
			return CR_NONE;
		}

		CLOSEREASON RetVal=ProcessFrame(DataBuff,(unsigned int)CurrFrameLength);
		ReadBuff.Consume((unsigned int)CurrFrameLength);

		return RetVal;
	}
}

CLOSEREASON Connection::ProcessFrame(const unsigned char *Buff, unsigned int Length)
{
	bool IsFin=(*Buff & FLAG_FIN)!=0, IsMask=(Buff[1] & FLAG_MASK)!=0;
	OPCODENAME OpCode=(OPCODENAME)(*Buff & OCN_MASK);

	unsigned int MaskKey;
	const unsigned char *PayloadPos;
	{
		unsigned char LengthMarker=Buff[1] & 0x7F;
		if (LengthMarker<126)
			PayloadPos=Buff+2;
		else if (LengthMarker==126)
			PayloadPos=Buff+2+2;
		else //if (LengthMarker==127)
			PayloadPos=Buff+2+8;

		if (IsMask)
		{
			//MaskKey=GET_BIGENDIAN_UNALIGNED4(PayloadPos);
			MaskKey=*(const unsigned int *)PayloadPos;
			PayloadPos+=4;
		}
	}

	Length-=PayloadPos-Buff;

	if (IsMask)
	{
		//Unmask the received data in-place.
		unsigned char *ProcessPos=(unsigned char *)PayloadPos, *EndPos=ProcessPos+Length;
		if (Length>4)
		{
			for ( ; ProcessPos<EndPos; ProcessPos+=4)
				*(unsigned int *)ProcessPos=*(unsigned int *)ProcessPos ^ MaskKey;

			if (ProcessPos>EndPos)
				ProcessPos-=4;
		}

		while (ProcessPos!=EndPos)
		{
			*ProcessPos=*ProcessPos ^ (MaskKey & 0xFF);
			MaskKey>>=8;
		}
	}
	else if (!AllowMaskedOnly)
		return CR_PROT_ERROR;

	if (IsFin)
	{
		if (OpCode<OCN_CONTROL_BEGIN)
		{
			if ((FragOpCode!=OCN_CONTINUATION) && (OpCode==OCN_CONTINUATION))
			{
				if (FragMsgData.length()+Length>Config::MaxFragmentedSize)
					return CR_SIZE_LIMIT;

				//This is the last frame of the fragmented message we're receiving.
				FragMsgData.append(PayloadPos,PayloadPos+Length);
				MyHandler->OnMessage(GetMessageType(FragOpCode),(const unsigned char *)FragMsgData.data(),FragMsgData.length());

				FragMsgData.reserve(Config::ReadBuffSize);
				FragMsgData.clear();
				FragOpCode=OCN_CONTINUATION;
			}
			else if ((FragOpCode==OCN_CONTINUATION) && (OpCode!=OCN_CONTINUATION))
				//This is a single frame, with no fragmented data present.
				MyHandler->OnMessage(GetMessageType(OpCode),PayloadPos,Length);
			else
				//Every other combination is invalid.
				return CR_PROT_ERROR;
		}
		else
			//This is a control frame, which is always allowed.
			return ProcessControlFrame(OpCode,PayloadPos,Length);
	}
	else if (OpCode<OCN_CONTROL_BEGIN)
	{
		if ((FragOpCode!=OCN_CONTINUATION) && (OpCode==OCN_CONTINUATION))
		{
			//This is an intermediate frame of the fragmented message we're receiving.
			if (FragMsgData.length()+Length>Config::MaxFragmentedSize)
				return CR_SIZE_LIMIT;

			FragMsgData.append(PayloadPos,PayloadPos+Length);
		}
		else if ((FragOpCode==OCN_CONTINUATION) && (OpCode!=OCN_CONTINUATION))
		{
			//This is the first frame of a fragmented message.
			FragMsgData.assign(PayloadPos,PayloadPos+Length);
			FragOpCode=OpCode;
		}
		else
			//Every other combination is invalid.
			return CR_PROT_ERROR;
	}
	else
		//A control frame with fragmentation is not allowed.
		return CR_PROT_ERROR;

	return CR_NONE;
}

CLOSEREASON Connection::ProcessControlFrame(OPCODENAME OpCode, const unsigned char *PayloadBuff, unsigned int Length)
{
	switch (OpCode)
	{
	case OCN_PING:
		{
			boost::unique_lock<boost::mutex> lock(SendBuffMtx);
			if (SendControlFrame(OCN_PONG))
				StartAsyncWrite();
		}
		break;
	case OCN_CLOSE:
		if (MyHandler)
		{
			unsigned short ReasonCode;
			if (Length>=2)
				ReasonCode=GET_BIGENDIAN_UNALIGNED2(PayloadBuff);
			else
				ReasonCode=0;

			MyHandler->OnClose(ReasonCode);

			boost::unique_lock<boost::mutex> lock(SendBuffMtx);
			if (SendCloseInternal(ReasonCode))
				StartAsyncWrite();

			MyHandler=nullptr;
		}
		break;
	default:
		//Unknown control frame.
		return CR_PROT_ERROR;
	}

	return CR_NONE;
}

void Connection::OnProtocolError(CLOSEREASON Reason)
{
	if (MyHandler)
	{
		MyHandler->OnClose(Reason);

		boost::unique_lock<boost::mutex> lock(SendBuffMtx);
		if (SendCloseInternal(Reason))
			StartAsyncWrite();

		MyHandler=nullptr;
	}
}

bool Connection::SendControlFrame(OPCODENAME OpCode)
{
	if (unsigned char *Buff=WriteBuff.Allocate(2))
	{
		*Buff=(unsigned char)(FLAG_FIN | OpCode);
		Buff[1]=0; //Emtpy payload.
		WriteBuff.Commit(2);

		return true;
	}
	else
		return false;
}

bool Connection::SendCloseInternal(unsigned short Reason)
{
	if (unsigned char *Buff=WriteBuff.Allocate(4))
	{
		*Buff=(unsigned char)(FLAG_FIN | OCN_CLOSE);
		Buff[1]=2;
		SET_BIGENDIAN_UNALIGNED2(Buff+2,Reason);
		WriteBuff.Commit(4);

		return true;
	}
	else
		return false;
}

void Connection::StartAsyncWriteExternal()
{
	boost::unique_lock<boost::mutex> lock(SendBuffMtx);

	StartAsyncWrite();
}

void Connection::StartAsyncWriteCloseExternal()
{
	boost::unique_lock<boost::mutex> lock(SendBuffMtx);

	MyHandler=nullptr;
	StartAsyncWrite();
}

void Connection::StartAsyncRead()
{
	unsigned int FreeLength;
	unsigned char *ReadPos=ReadBuff.GetReadInfo(FreeLength);

	MySock.async_read_some(boost::asio::buffer(ReadPos,FreeLength),
		boost::bind(&Connection::OnRead,this,boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
}

void Connection::StartAsyncWrite()
{
	if (!IsSafeState<SAFE_WRITE>())
		return;

	unsigned int WriteLength;
	while (const unsigned char *WritePos=WriteBuff.Pop(WriteLength))
	{
		ClearSafeState<SAFE_WRITE>();
		boost::asio::async_write(MySock,boost::asio::buffer(WritePos,WriteLength),
			boost::bind(&Connection::OnWrite,this,boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
	}
}

void Connection::PostWriteReq()
{
	MySock.get_io_service().post(boost::bind(&Connection::StartAsyncWriteExternal,this));
}

void Connection::PostCloseReq()
{
	MySock.get_io_service().post(boost::bind(&Connection::StartAsyncWriteCloseExternal,this));
}
<commit_msg>WebSocket::Connection: fragmented message processing fix<commit_after>#include "WSConnection.h"

#include <boost/bind.hpp>

#include "../Common/BinUtils.h"

#include "IMsgHandler.h"

using namespace HTTP::WebSocket;

Connection::Connection(boost::asio::ip::tcp::socket &&SrcSocket, IMsgHandler *MsgHandler) : HTTP::ConnectionBase(std::move(SrcSocket)),
	SafeStates(SAFE_ALL),
	SilentTime(0), CurrFrameLength(UnknownFrameLength), FragOpCode(OCN_CONTINUATION),
	MyHandler(MsgHandler)
{
	//Start reading for incoming messages.
	ClearSafeState<SAFE_READ>();
	StartAsyncRead();
}

void Connection::Stop()
{
	try { MySock.close(); }
	catch (...) { }

	if (MyHandler)
	{
		MyHandler->OnClose(CR_CONN_ERROR);
		MyHandler=nullptr;
	}
}

bool Connection::OnStep(unsigned int StepInterval, ConnectionBase **OutNextConn)
{
	SilentTime+=StepInterval;
	if (SilentTime>Config::MaxSilentTime)
	{
		if (MyHandler)
		{
			MyHandler->OnClose(CR_CONN_ERROR);
			MyHandler=nullptr;
		}

		try { MySock.close(); }
		catch (...) { }
		return SafeStates!=SAFE_ALL;
	}
	else
		return true;
}

unsigned char *Connection::Allocate(MESSAGETYPE Type, unsigned long long Length)
{
	//SocketMtx is locked externally.

	//Create the frame header.
	unsigned long long FrameLength=Length;
	if (Length<126)
		FrameLength+=2;
	else if (Length<655356)
		FrameLength+=2+2;
	else
		FrameLength+=2+8;

	if (unsigned char *FrameBuff=WriteBuff.Allocate((unsigned int)FrameLength))
	{
		//Set the opcode and the FIN flag (we don't send fragmented messsages).
		*FrameBuff++=(unsigned char)(GetOpcode(Type) | FLAG_FIN);

		//Set the length.
		if (Length<126)
			*FrameBuff++=(unsigned char)Length;
		else if (Length<655356)
		{
			*FrameBuff++=126;
			SET_BIGENDIAN_UNALIGNED2(FrameBuff,(unsigned short)Length);
			FrameBuff+=2;
		}
		else
		{
			*FrameBuff++=127;
			SET_BIGENDIAN_UNALIGNED8(FrameBuff,Length);
			FrameBuff+=8;
		}

		return FrameBuff;
	}
	else
		return nullptr;
}

bool Connection::Deallocate()
{
	//SocketMtx is locked externally.
	WriteBuff.Commit(0);
	return true;
}

bool Connection::Send()
{
	//SocketMtx is locked externally.
	WriteBuff.Commit(~0);
	PostWriteReq();
	return true;
}

bool Connection::SendPing()
{
	//SocketMtx is locked externally.
	SendControlFrame(OCN_PING);
	PostWriteReq();
	return true;
}

void Connection::Close(unsigned short Reason)
{
	//SocketMtx is locked externally.
	SendCloseInternal(Reason);
	PostCloseReq();
}

void Connection::OnRead(const boost::system::error_code &error, std::size_t bytes_transferred)
{
	if (!error)
	{
		ReadBuff.OnNewData(bytes_transferred);
		SilentTime=0;
		CLOSEREASON Reason=ProcessIncoming();
		if (Reason==CR_NONE)
			StartAsyncRead();
		else
		{
			OnProtocolError(Reason);
			SetSafeState<SAFE_READ>();
		}
	}
	else
	{
		SetSafeState<SAFE_READ>();
		if (MyHandler)
		{
			MyHandler->OnClose(CR_CONN_ERROR);
			MyHandler=nullptr;
		}
	}
}

void Connection::OnWrite(const boost::system::error_code &error, std::size_t bytes_transferred)
{
	SetSafeState<SAFE_WRITE>();
	if (!error)
	{
		boost::unique_lock<boost::mutex> lock(SendBuffMtx);

		WriteBuff.Release();
		StartAsyncWrite();
	}
	else
	{
		if (MyHandler)
		{
			MyHandler->OnClose(CR_CONN_ERROR);
			MyHandler=nullptr;
		}
	}
}

CLOSEREASON Connection::ProcessIncoming()
{
	while (true)
	{
		unsigned int DataLength;
		const unsigned char *DataBuff=ReadBuff.GetAvailableData(DataLength);
		if (CurrFrameLength==UnknownFrameLength)
		{
			//Try to determine the length of the frame. At least 2 bytes is a must.
			if (DataLength<2)
			{
				ReadBuff.RequestData(2);
				return CR_NONE;
			}

			unsigned char LengthMarker=DataBuff[1] & 0x7F;
			if (LengthMarker<126)
				CurrFrameLength=LengthMarker + 2;
			else if (LengthMarker==126)
			{
				//Length field is 16 bit wide.
				if (DataLength<2+2)
				{
					ReadBuff.RequestData(2+2);
					return CR_NONE;
				}

				CurrFrameLength=GET_BIGENDIAN_UNALIGNED2(DataBuff+2) + 2 + 2;
			}
			else //if (LengthMarker==127)
			{
				//Length field is 64 bit wide.
				if (DataLength<2+8)
				{
					ReadBuff.RequestData(2+8);
					return CR_NONE;
				}

				CurrFrameLength=GET_BIGENDIAN_UNALIGNED8(DataBuff+2) + 2 + 8;
			}

			if (DataBuff[1] & FLAG_MASK)
				CurrFrameLength+=4; //Include the length of the mask byte, too.
		}

		if (CurrFrameLength>Config::MaxFrameSize)
			return CR_SIZE_LIMIT;

		//At this point, we have determined the full frame length, or exited the function.
		if (DataLength<CurrFrameLength)
		{
			ReadBuff.RequestData((unsigned int)CurrFrameLength);
			return CR_NONE;
		}

		CLOSEREASON RetVal=ProcessFrame(DataBuff,(unsigned int)CurrFrameLength);
		ReadBuff.Consume((unsigned int)CurrFrameLength);

		return RetVal;
	}
}

CLOSEREASON Connection::ProcessFrame(const unsigned char *Buff, unsigned int Length)
{
	bool IsFin=(*Buff & FLAG_FIN)!=0, IsMask=(Buff[1] & FLAG_MASK)!=0;
	OPCODENAME OpCode=(OPCODENAME)(*Buff & OCN_MASK);

	unsigned int MaskKey;
	const unsigned char *PayloadPos;
	{
		unsigned char LengthMarker=Buff[1] & 0x7F;
		if (LengthMarker<126)
			PayloadPos=Buff+2;
		else if (LengthMarker==126)
			PayloadPos=Buff+2+2;
		else //if (LengthMarker==127)
			PayloadPos=Buff+2+8;

		if (IsMask)
		{
			//MaskKey=GET_BIGENDIAN_UNALIGNED4(PayloadPos);
			MaskKey=*(const unsigned int *)PayloadPos;
			PayloadPos+=4;
		}
	}

	Length-=PayloadPos-Buff;

	if (IsMask)
	{
		//Unmask the received data in-place.
		unsigned char *ProcessPos=(unsigned char *)PayloadPos, *EndPos=ProcessPos+Length;
		if (Length>4)
		{
			for ( ; ProcessPos<EndPos; ProcessPos+=4)
				*(unsigned int *)ProcessPos=*(unsigned int *)ProcessPos ^ MaskKey;

			if (ProcessPos>EndPos)
				ProcessPos-=4;
		}

		while (ProcessPos!=EndPos)
		{
			*ProcessPos=*ProcessPos ^ (MaskKey & 0xFF);
			MaskKey>>=8;
		}
	}
	else if (!AllowMaskedOnly)
		return CR_PROT_ERROR;

	if (IsFin)
	{
		if (OpCode<OCN_CONTROL_BEGIN)
		{
			if ((FragOpCode!=OCN_CONTINUATION) && (OpCode==OCN_CONTINUATION))
			{
				if (FragMsgData.length()+Length>Config::MaxFragmentedSize)
					return CR_SIZE_LIMIT;

				//This is the last frame of the fragmented message we're receiving.
				FragMsgData.append(PayloadPos,PayloadPos+Length);
				MyHandler->OnMessage(GetMessageType(FragOpCode),(const unsigned char *)FragMsgData.data(),FragMsgData.length());

				FragMsgData.reserve(Config::ReadBuffSize);
				FragMsgData.clear();
				FragOpCode=OCN_CONTINUATION;
			}
			else if ((FragOpCode==OCN_CONTINUATION) && (OpCode!=OCN_CONTINUATION))
				//This is a single frame, with no fragmented data present.
				MyHandler->OnMessage(GetMessageType(OpCode),PayloadPos,Length);
			else
				//Every other combination is invalid.
				return CR_PROT_ERROR;
		}
		else
			//This is a control frame, which is always allowed.
			return ProcessControlFrame(OpCode,PayloadPos,Length);
	}
	else if (OpCode<OCN_CONTROL_BEGIN)
	{
		if ((FragOpCode!=OCN_CONTINUATION) && (OpCode==OCN_CONTINUATION))
		{
			//This is an intermediate frame of the fragmented message we're receiving.
			if (FragMsgData.length()+Length>Config::MaxFragmentedSize)
				return CR_SIZE_LIMIT;

			FragMsgData.append(PayloadPos,PayloadPos+Length);
		}
		else if ((FragOpCode==OCN_CONTINUATION) && (OpCode!=OCN_CONTINUATION))
		{
			//This is the first frame of a fragmented message.
			FragMsgData.assign(PayloadPos,PayloadPos+Length);
			FragOpCode=OpCode;
		}
		else
			//Every other combination is invalid.
			return CR_PROT_ERROR;
	}
	else
		//A control frame with fragmentation is not allowed.
		return CR_PROT_ERROR;

	return CR_NONE;
}

CLOSEREASON Connection::ProcessControlFrame(OPCODENAME OpCode, const unsigned char *PayloadBuff, unsigned int Length)
{
	switch (OpCode)
	{
	case OCN_PING:
		{
			boost::unique_lock<boost::mutex> lock(SendBuffMtx);
			if (SendControlFrame(OCN_PONG))
				StartAsyncWrite();
		}
		break;
	case OCN_CLOSE:
		if (MyHandler)
		{
			unsigned short ReasonCode;
			if (Length>=2)
				ReasonCode=GET_BIGENDIAN_UNALIGNED2(PayloadBuff);
			else
				ReasonCode=0;

			MyHandler->OnClose(ReasonCode);

			boost::unique_lock<boost::mutex> lock(SendBuffMtx);
			if (SendCloseInternal(ReasonCode))
				StartAsyncWrite();

			MyHandler=nullptr;
		}
		break;
	default:
		//Unknown control frame.
		return CR_PROT_ERROR;
	}

	return CR_NONE;
}

void Connection::OnProtocolError(CLOSEREASON Reason)
{
	if (MyHandler)
	{
		MyHandler->OnClose(Reason);

		boost::unique_lock<boost::mutex> lock(SendBuffMtx);
		if (SendCloseInternal(Reason))
			StartAsyncWrite();

		MyHandler=nullptr;
	}
}

bool Connection::SendControlFrame(OPCODENAME OpCode)
{
	if (unsigned char *Buff=WriteBuff.Allocate(2))
	{
		*Buff=(unsigned char)(FLAG_FIN | OpCode);
		Buff[1]=0; //Emtpy payload.
		WriteBuff.Commit(2);

		return true;
	}
	else
		return false;
}

bool Connection::SendCloseInternal(unsigned short Reason)
{
	if (unsigned char *Buff=WriteBuff.Allocate(4))
	{
		*Buff=(unsigned char)(FLAG_FIN | OCN_CLOSE);
		Buff[1]=2;
		SET_BIGENDIAN_UNALIGNED2(Buff+2,Reason);
		WriteBuff.Commit(4);

		return true;
	}
	else
		return false;
}

void Connection::StartAsyncWriteExternal()
{
	boost::unique_lock<boost::mutex> lock(SendBuffMtx);

	StartAsyncWrite();
}

void Connection::StartAsyncWriteCloseExternal()
{
	boost::unique_lock<boost::mutex> lock(SendBuffMtx);

	MyHandler=nullptr;
	StartAsyncWrite();
}

void Connection::StartAsyncRead()
{
	unsigned int FreeLength;
	unsigned char *ReadPos=ReadBuff.GetReadInfo(FreeLength);

	MySock.async_read_some(boost::asio::buffer(ReadPos,FreeLength),
		boost::bind(&Connection::OnRead,this,boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
}

void Connection::StartAsyncWrite()
{
	if (!IsSafeState<SAFE_WRITE>())
		return;

	unsigned int WriteLength;
	while (const unsigned char *WritePos=WriteBuff.Pop(WriteLength))
	{
		ClearSafeState<SAFE_WRITE>();
		boost::asio::async_write(MySock,boost::asio::buffer(WritePos,WriteLength),
			boost::bind(&Connection::OnWrite,this,boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
	}
}

void Connection::PostWriteReq()
{
	MySock.get_io_service().post(boost::bind(&Connection::StartAsyncWriteExternal,this));
}

void Connection::PostCloseReq()
{
	MySock.get_io_service().post(boost::bind(&Connection::StartAsyncWriteCloseExternal,this));
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2004-2013 ZNC, see the NOTICE file for details.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <znc/Chan.h>
#include <znc/FileUtils.h>
#include <znc/IRCSock.h>
#include <znc/Modules.h>
#include <znc/Nick.h>
#include <znc/User.h>
#include <znc/znc.h>

#include "modperl/module.h"
#include "modperl/swigperlrun.h"
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>

#include "modperl/pstring.h"

using std::set;
using std::vector;

// Allows perl to load .so files when needed by .pm
// For example, it needs to load ZNC.so
extern "C" {
	void boot_DynaLoader (pTHX_ CV* cv);
	static void xs_init(pTHX) {
		newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__);
	}
}

class CModPerl: public CModule {
	PerlInterpreter *m_pPerl;
public:
	MODCONSTRUCTOR(CModPerl) {
		m_pPerl = NULL;
	}

#define PSTART dSP; I32 ax; int ret = 0; ENTER; SAVETMPS; PUSHMARK(SP)
#define PCALL(name) PUTBACK; ret = call_pv(name, G_EVAL|G_ARRAY); SPAGAIN; SP -= ret; ax = (SP - PL_stack_base) + 1
#define PEND ax += 0; PUTBACK; FREETMPS; LEAVE
#define PUSH_STR(s) XPUSHs(PString(s).GetSV())
#define PUSH_PTR(type, p) XPUSHs(SWIG_NewInstanceObj(const_cast<type>(p), SWIG_TypeQuery(#type), SWIG_SHADOW))

	bool OnLoad(const CString& sArgsi, CString& sMessage) {
		CString sModPath, sTmp;
		if (!CModules::FindModPath("modperl/startup.pl", sModPath, sTmp)) {
			sMessage = "startup.pl not found.";
			return false;
		}
		sTmp = CDir::ChangeDir(sModPath, "..");
		int argc = 6;
		char *pArgv[] = {"", "-T", "-w",
			"-I", const_cast<char*>(sTmp.c_str()),
			const_cast<char*>(sModPath.c_str()), NULL};
		char **argv = pArgv;
		PERL_SYS_INIT3(&argc, &argv, &environ);
		m_pPerl = perl_alloc();
		perl_construct(m_pPerl);
		if (perl_parse(m_pPerl, xs_init, argc, argv, environ)) {
			sMessage = "Can't initialize perl. ";
			if (SvTRUE(ERRSV)) {
				sMessage += PString(ERRSV);
			}
			perl_free(m_pPerl);
			PERL_SYS_TERM();
			m_pPerl = NULL;
			DEBUG(__PRETTY_FUNCTION__ << " can't init perl");
			return false;
		}
		PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
		PSTART;
		PCALL("ZNC::Core::Init");
		PEND;
		return true;
	}

	virtual EModRet OnModuleLoading(const CString& sModName, const CString& sArgs,
			CModInfo::EModuleType eType, bool& bSuccess, CString& sRetMsg) {
		EModRet result = HALT;
		PSTART;
		PUSH_STR(sModName);
		PUSH_STR(sArgs);
		mXPUSHi(eType);
		PUSH_PTR(CUser*, GetUser());
		PUSH_PTR(CIRCNetwork*, GetNetwork());
		PCALL("ZNC::Core::LoadModule");

		if (SvTRUE(ERRSV)) {
			sRetMsg = PString(ERRSV);
			bSuccess = false;
			result = HALT;
			DEBUG("Perl ZNC::Core::LoadModule died: " << sRetMsg);
		} else if (ret < 1 || 2 < ret) {
			sRetMsg = "Error: Perl ZNC::Core::LoadModule returned " + CString(ret) + " values.";
			bSuccess = false;
			result = HALT;
		} else {
			ELoadPerlMod eLPM = static_cast<ELoadPerlMod>(SvUV(ST(0)));
			if (Perl_NotFound == eLPM) {
				result = CONTINUE; // Not a Perl module
			} else {
				sRetMsg = PString(ST(1));
				result = HALT;
				bSuccess = eLPM == Perl_Loaded;
			}
		}

		PEND;
		return result;
	}

	virtual EModRet OnModuleUnloading(CModule* pModule, bool& bSuccess, CString& sRetMsg) {
		CPerlModule* pMod = AsPerlModule(pModule);
		if (pMod) {
			EModRet result = HALT;
			CString sModName = pMod->GetModName();
			PSTART;
			XPUSHs(pMod->GetPerlObj());
			PCALL("ZNC::Core::UnloadModule");
			if (SvTRUE(ERRSV)) {
				bSuccess = false;
				sRetMsg = PString(ERRSV);
			} else if (ret < 1 || 2 < ret) {
				sRetMsg = "Error: Perl ZNC::Core::UnloadModule returned " + CString(ret) + " values.";
				bSuccess = false;
				result = HALT;
			} else {
				int bUnloaded = SvUV(ST(0));
				if (bUnloaded) {
					bSuccess = true;
					sRetMsg = "Module [" + sModName + "] unloaded";
					result = HALT;
				} else {
					result = CONTINUE; // module wasn't loaded by modperl. Perhaps a module-provider written in perl did that.
				}
			}
			PEND;
			DEBUG(__PRETTY_FUNCTION__ << " " << sRetMsg);
			return result;
		}
		return CONTINUE;
	}

	virtual EModRet OnGetModInfo(CModInfo& ModInfo, const CString& sModule,
			bool& bSuccess, CString& sRetMsg) {
		PSTART;
		PUSH_STR(sModule);
		PUSH_PTR(CModInfo*, &ModInfo);
		PCALL("ZNC::Core::GetModInfo");
		EModRet result = CONTINUE;
		if (SvTRUE(ERRSV)) {
			bSuccess = false;
			sRetMsg = PString(ERRSV);
		} else if (0 < ret) {
			switch(static_cast<ELoadPerlMod>(SvUV(ST(0)))) {
				case Perl_NotFound:
					result = CONTINUE;
					break;
				case Perl_Loaded:
					result = HALT;
					if (1 == ret) {
						bSuccess = true;
					} else {
						bSuccess = false;
						sRetMsg = "Something weird happened";
					}
					break;
				case Perl_LoadError:
					result = HALT;
					bSuccess = false;
					if (2 == ret) {
						sRetMsg = PString(ST(1));
					} else {
						sRetMsg = "Something weird happened";
					}
			}
		} else {
			result = HALT;
			bSuccess = false;
			sRetMsg = "Something weird happened";
		}
		PEND;
		return result;
	}

	virtual void OnGetAvailableMods(set<CModInfo>& ssMods, CModInfo::EModuleType eType) {

		unsigned int a = 0;
		CDir Dir;

		CModules::ModDirList dirs = CModules::GetModDirs();

		while (!dirs.empty()) {
			Dir.FillByWildcard(dirs.front().first, "*.pm");
			dirs.pop();

			for (a = 0; a < Dir.size(); a++) {
				CFile& File = *Dir[a];
				CString sName = File.GetShortName();
				CString sPath = File.GetLongName();
				CModInfo ModInfo;
				sName.RightChomp(3);
				PSTART;
				PUSH_STR(sPath);
				PUSH_STR(sName);
				PUSH_PTR(CModInfo*, &ModInfo);
				PCALL("ZNC::Core::ModInfoByPath");
				if (SvTRUE(ERRSV)) {
					DEBUG(__PRETTY_FUNCTION__ << ": " << sPath << ": " << PString(ERRSV));
				} else if (ModInfo.SupportsType(eType)) {
					ssMods.insert(ModInfo);
				}
				PEND;
			}
		}
	}

	virtual ~CModPerl() {
		if (m_pPerl) {
			PSTART;
			PCALL("ZNC::Core::UnloadAll");
			PEND;
			perl_destruct(m_pPerl);
			perl_free(m_pPerl);
			PERL_SYS_TERM();
		}
	}

};

#include "modperl/functions.cpp"

VWebSubPages& CPerlModule::GetSubPages() {
	VWebSubPages* result = _GetSubPages();
	if (!result) {
		return CModule::GetSubPages();
	}
	return *result;
}

void CPerlTimer::RunJob() {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		PSTART;
		XPUSHs(GetPerlObj());
		PCALL("ZNC::Core::CallTimer");
		PEND;
	}
}

CPerlTimer::~CPerlTimer() {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		PSTART;
		XPUSHs(sv_2mortal(m_perlObj));
		PCALL("ZNC::Core::RemoveTimer");
		PEND;
	}
}

#define SOCKSTART PSTART; XPUSHs(GetPerlObj())
#define SOCKCBCHECK(OnSuccess) PCALL("ZNC::Core::CallSocket"); if (SvTRUE(ERRSV)) { Close(); DEBUG("Perl socket hook died with: " + PString(ERRSV)); } else { OnSuccess; } PEND
#define CBSOCK(Func) void CPerlSocket::Func() {\
	CPerlModule* pMod = AsPerlModule(GetModule());\
	if (pMod) {\
		SOCKSTART;\
		PUSH_STR("On" #Func);\
		SOCKCBCHECK();\
	}\
}
CBSOCK(Connected);
CBSOCK(Disconnected);
CBSOCK(Timeout);
CBSOCK(ConnectionRefused);

void CPerlSocket::ReadData(const char *data, size_t len) {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		SOCKSTART;
		PUSH_STR("OnReadData");
		XPUSHs(sv_2mortal(newSVpvn(data, len)));
		mXPUSHi(len);
		SOCKCBCHECK();
	}
}
void CPerlSocket::ReadLine(const CString& sLine) {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		SOCKSTART;
		PUSH_STR("OnReadLine");
		PUSH_STR(sLine);
		SOCKCBCHECK();
	}
}
Csock* CPerlSocket::GetSockObj(const CString& sHost, unsigned short uPort) {
	CPerlModule* pMod = AsPerlModule(GetModule());
	Csock* result = NULL;
	if (pMod) {
		SOCKSTART;
		PUSH_STR("_Accepted");
		PUSH_STR(sHost);
		mXPUSHi(uPort);
		SOCKCBCHECK(
				result = SvToPtr<CPerlSocket>("CPerlSocket*")(ST(0));
		);
	}
	return result;
}

CPerlSocket::~CPerlSocket() {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		PSTART;
		XPUSHs(sv_2mortal(m_perlObj));
		PCALL("ZNC::Core::RemoveSocket");
		PEND;
	}
}

template<> void TModInfo<CModPerl>(CModInfo& Info) {
	Info.SetWikiPage("modperl");
}

GLOBALMODULEDEFS(CModPerl, "Loads perl scripts as ZNC modules")
<commit_msg>A bit more debug output on modperl<commit_after>/*
 * Copyright (C) 2004-2013 ZNC, see the NOTICE file for details.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <znc/Chan.h>
#include <znc/FileUtils.h>
#include <znc/IRCSock.h>
#include <znc/Modules.h>
#include <znc/Nick.h>
#include <znc/User.h>
#include <znc/znc.h>

#include "modperl/module.h"
#include "modperl/swigperlrun.h"
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>

#include "modperl/pstring.h"

using std::set;
using std::vector;

// Allows perl to load .so files when needed by .pm
// For example, it needs to load ZNC.so
extern "C" {
	void boot_DynaLoader (pTHX_ CV* cv);
	static void xs_init(pTHX) {
		newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__);
	}
}

class CModPerl: public CModule {
	PerlInterpreter *m_pPerl;
public:
	MODCONSTRUCTOR(CModPerl) {
		m_pPerl = NULL;
	}

#define PSTART dSP; I32 ax; int ret = 0; ENTER; SAVETMPS; PUSHMARK(SP)
#define PCALL(name) PUTBACK; ret = call_pv(name, G_EVAL|G_ARRAY); SPAGAIN; SP -= ret; ax = (SP - PL_stack_base) + 1
#define PEND ax += 0; PUTBACK; FREETMPS; LEAVE
#define PUSH_STR(s) XPUSHs(PString(s).GetSV())
#define PUSH_PTR(type, p) XPUSHs(SWIG_NewInstanceObj(const_cast<type>(p), SWIG_TypeQuery(#type), SWIG_SHADOW))

	bool OnLoad(const CString& sArgsi, CString& sMessage) {
		CString sModPath, sTmp;
		if (!CModules::FindModPath("modperl/startup.pl", sModPath, sTmp)) {
			sMessage = "startup.pl not found.";
			return false;
		}
		sTmp = CDir::ChangeDir(sModPath, "..");
		int argc = 6;
		char *pArgv[] = {"", "-T", "-w",
			"-I", const_cast<char*>(sTmp.c_str()),
			const_cast<char*>(sModPath.c_str()), NULL};
		char **argv = pArgv;
		PERL_SYS_INIT3(&argc, &argv, &environ);
		m_pPerl = perl_alloc();
		perl_construct(m_pPerl);
		if (perl_parse(m_pPerl, xs_init, argc, argv, environ)) {
			sMessage = "Can't initialize perl. ";
			if (SvTRUE(ERRSV)) {
				sMessage += PString(ERRSV);
			}
			perl_free(m_pPerl);
			PERL_SYS_TERM();
			m_pPerl = NULL;
			DEBUG(__PRETTY_FUNCTION__ << " can't init perl");
			return false;
		}
		PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
		PSTART;
		PCALL("ZNC::Core::Init");
		PEND;
		return true;
	}

	virtual EModRet OnModuleLoading(const CString& sModName, const CString& sArgs,
			CModInfo::EModuleType eType, bool& bSuccess, CString& sRetMsg) {
		EModRet result = HALT;
		PSTART;
		PUSH_STR(sModName);
		PUSH_STR(sArgs);
		mXPUSHi(eType);
		PUSH_PTR(CUser*, GetUser());
		PUSH_PTR(CIRCNetwork*, GetNetwork());
		PCALL("ZNC::Core::LoadModule");

		if (SvTRUE(ERRSV)) {
			sRetMsg = PString(ERRSV);
			bSuccess = false;
			result = HALT;
			DEBUG("Perl ZNC::Core::LoadModule died: " << sRetMsg);
		} else if (ret < 1 || 2 < ret) {
			sRetMsg = "Error: Perl ZNC::Core::LoadModule returned " + CString(ret) + " values.";
			bSuccess = false;
			result = HALT;
		} else {
			ELoadPerlMod eLPM = static_cast<ELoadPerlMod>(SvUV(ST(0)));
			if (Perl_NotFound == eLPM) {
				result = CONTINUE; // Not a Perl module
			} else {
				sRetMsg = PString(ST(1));
				result = HALT;
				bSuccess = eLPM == Perl_Loaded;
			}
		}

		PEND;
		return result;
	}

	virtual EModRet OnModuleUnloading(CModule* pModule, bool& bSuccess, CString& sRetMsg) {
		CPerlModule* pMod = AsPerlModule(pModule);
		if (pMod) {
			EModRet result = HALT;
			CString sModName = pMod->GetModName();
			PSTART;
			XPUSHs(pMod->GetPerlObj());
			PCALL("ZNC::Core::UnloadModule");
			if (SvTRUE(ERRSV)) {
				bSuccess = false;
				sRetMsg = PString(ERRSV);
			} else if (ret < 1 || 2 < ret) {
				sRetMsg = "Error: Perl ZNC::Core::UnloadModule returned " + CString(ret) + " values.";
				bSuccess = false;
				result = HALT;
			} else {
				int bUnloaded = SvUV(ST(0));
				if (bUnloaded) {
					bSuccess = true;
					sRetMsg = "Module [" + sModName + "] unloaded";
					result = HALT;
				} else {
					result = CONTINUE; // module wasn't loaded by modperl. Perhaps a module-provider written in perl did that.
				}
			}
			PEND;
			DEBUG(__PRETTY_FUNCTION__ << " " << sRetMsg);
			return result;
		}
		return CONTINUE;
	}

	virtual EModRet OnGetModInfo(CModInfo& ModInfo, const CString& sModule,
			bool& bSuccess, CString& sRetMsg) {
		PSTART;
		PUSH_STR(sModule);
		PUSH_PTR(CModInfo*, &ModInfo);
		PCALL("ZNC::Core::GetModInfo");
		EModRet result = CONTINUE;
		if (SvTRUE(ERRSV)) {
			bSuccess = false;
			sRetMsg = PString(ERRSV);
			DEBUG("Perl ZNC::Core::GetModInfo died: " << sRetMsg);
		} else if (0 < ret) {
			switch(static_cast<ELoadPerlMod>(SvUV(ST(0)))) {
				case Perl_NotFound:
					result = CONTINUE;
					break;
				case Perl_Loaded:
					result = HALT;
					if (1 == ret) {
						bSuccess = true;
					} else {
						bSuccess = false;
						sRetMsg = "Something weird happened";
					}
					break;
				case Perl_LoadError:
					result = HALT;
					bSuccess = false;
					if (2 == ret) {
						sRetMsg = PString(ST(1));
					} else {
						sRetMsg = "Something weird happened";
					}
			}
		} else {
			result = HALT;
			bSuccess = false;
			sRetMsg = "Something weird happened";
		}
		PEND;
		return result;
	}

	virtual void OnGetAvailableMods(set<CModInfo>& ssMods, CModInfo::EModuleType eType) {

		unsigned int a = 0;
		CDir Dir;

		CModules::ModDirList dirs = CModules::GetModDirs();

		while (!dirs.empty()) {
			Dir.FillByWildcard(dirs.front().first, "*.pm");
			dirs.pop();

			for (a = 0; a < Dir.size(); a++) {
				CFile& File = *Dir[a];
				CString sName = File.GetShortName();
				CString sPath = File.GetLongName();
				CModInfo ModInfo;
				sName.RightChomp(3);
				PSTART;
				PUSH_STR(sPath);
				PUSH_STR(sName);
				PUSH_PTR(CModInfo*, &ModInfo);
				PCALL("ZNC::Core::ModInfoByPath");
				if (SvTRUE(ERRSV)) {
					DEBUG(__PRETTY_FUNCTION__ << ": " << sPath << ": " << PString(ERRSV));
				} else if (ModInfo.SupportsType(eType)) {
					ssMods.insert(ModInfo);
				}
				PEND;
			}
		}
	}

	virtual ~CModPerl() {
		if (m_pPerl) {
			PSTART;
			PCALL("ZNC::Core::UnloadAll");
			PEND;
			perl_destruct(m_pPerl);
			perl_free(m_pPerl);
			PERL_SYS_TERM();
		}
	}

};

#include "modperl/functions.cpp"

VWebSubPages& CPerlModule::GetSubPages() {
	VWebSubPages* result = _GetSubPages();
	if (!result) {
		return CModule::GetSubPages();
	}
	return *result;
}

void CPerlTimer::RunJob() {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		PSTART;
		XPUSHs(GetPerlObj());
		PCALL("ZNC::Core::CallTimer");
		PEND;
	}
}

CPerlTimer::~CPerlTimer() {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		PSTART;
		XPUSHs(sv_2mortal(m_perlObj));
		PCALL("ZNC::Core::RemoveTimer");
		PEND;
	}
}

#define SOCKSTART PSTART; XPUSHs(GetPerlObj())
#define SOCKCBCHECK(OnSuccess) PCALL("ZNC::Core::CallSocket"); if (SvTRUE(ERRSV)) { Close(); DEBUG("Perl socket hook died with: " + PString(ERRSV)); } else { OnSuccess; } PEND
#define CBSOCK(Func) void CPerlSocket::Func() {\
	CPerlModule* pMod = AsPerlModule(GetModule());\
	if (pMod) {\
		SOCKSTART;\
		PUSH_STR("On" #Func);\
		SOCKCBCHECK();\
	}\
}
CBSOCK(Connected);
CBSOCK(Disconnected);
CBSOCK(Timeout);
CBSOCK(ConnectionRefused);

void CPerlSocket::ReadData(const char *data, size_t len) {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		SOCKSTART;
		PUSH_STR("OnReadData");
		XPUSHs(sv_2mortal(newSVpvn(data, len)));
		mXPUSHi(len);
		SOCKCBCHECK();
	}
}
void CPerlSocket::ReadLine(const CString& sLine) {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		SOCKSTART;
		PUSH_STR("OnReadLine");
		PUSH_STR(sLine);
		SOCKCBCHECK();
	}
}
Csock* CPerlSocket::GetSockObj(const CString& sHost, unsigned short uPort) {
	CPerlModule* pMod = AsPerlModule(GetModule());
	Csock* result = NULL;
	if (pMod) {
		SOCKSTART;
		PUSH_STR("_Accepted");
		PUSH_STR(sHost);
		mXPUSHi(uPort);
		SOCKCBCHECK(
				result = SvToPtr<CPerlSocket>("CPerlSocket*")(ST(0));
		);
	}
	return result;
}

CPerlSocket::~CPerlSocket() {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		PSTART;
		XPUSHs(sv_2mortal(m_perlObj));
		PCALL("ZNC::Core::RemoveSocket");
		PEND;
	}
}

template<> void TModInfo<CModPerl>(CModInfo& Info) {
	Info.SetWikiPage("modperl");
}

GLOBALMODULEDEFS(CModPerl, "Loads perl scripts as ZNC modules")
<|endoftext|>


<commit_before>#include "RequiredItems.hpp"

#include "common/Utils.hpp"

template <>
std::string Utils::toString(RegularFile::DownloadType val)
{
    switch (val)
    {
        case RegularFile::DownloadType::HTTP: return "HTTP";
        case RegularFile::DownloadType::XMDS: return "XMDS";
        case RegularFile::DownloadType::Invalid: return "Invalid";
    }

    return "unknown";
}

std::ostream& operator<<(std::ostream& out, const RegularFile& file)
{
    out << "FileType: " << file.type << " ID: " << file.id << " Size: " << file.size << std::endl;
    out << "MD5: " << file.hash << " FileName: " << file.name
        << " DownloadType: " << Utils::toString(file.downloadType);
    return out;
}

std::ostream& operator<<(std::ostream& out, const ResourceFile& res)
{
    return out << "Layout ID: " << res.layoutId << " Region ID: " << res.regionId << " Media ID: " << res.mediaId;
}
<commit_msg>fix: trace URL when outputting RequiredFile<commit_after>#include "RequiredItems.hpp"

#include "common/Utils.hpp"

template <>
std::string Utils::toString(RegularFile::DownloadType val)
{
    switch (val)
    {
        case RegularFile::DownloadType::HTTP: return "HTTP";
        case RegularFile::DownloadType::XMDS: return "XMDS";
        case RegularFile::DownloadType::Invalid: return "Invalid";
    }

    return "unknown";
}

std::ostream& operator<<(std::ostream& out, const RegularFile& file)
{
    out << "FileType: " << file.type << " ID: " << file.id << " Size: " << file.size << std::endl;
    out << "MD5: " << file.hash << " FileName: " << file.name << " DownloadType: " << Utils::toString(file.downloadType)
        << " URL: " << file.url;
    return out;
}

std::ostream& operator<<(std::ostream& out, const ResourceFile& res)
{
    return out << "Layout ID: " << res.layoutId << " Region ID: " << res.regionId << " Media ID: " << res.mediaId;
}
<|endoftext|>


<commit_before>/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood,
Vladislav Vaintroub & MariaDB Corporation

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/

#include <gssapi/gssapi.h>
#include <string.h>
#include <stdio.h>
#include <mysql/plugin_auth.h>
#include <mysqld_error.h>
#include <mysql.h>
#include "gssapi_errmsg.h"

extern void log_client_error(MYSQL *mysql,const char *fmt,...);

/* This sends the error to the client */
static void log_error(MYSQL *mysql, OM_uint32 major, OM_uint32 minor, const char *msg)
{
  if (GSS_ERROR(major))
  {
    char sysmsg[1024];
    gssapi_errmsg(major, minor, sysmsg, sizeof(sysmsg));
    log_client_error(mysql,
      "Client GSSAPI error (major %u, minor %u) : %s - %s",
       major, minor, msg, sysmsg);
  }
  else
  {
    log_client_error(mysql, "Client GSSAPI error : %s", msg);
  }
}

int auth_client(char *principal_name, char *mech, MYSQL *mysql, MYSQL_PLUGIN_VIO *vio)
{

  int ret= CR_ERROR;
  OM_uint32 major= 0, minor= 0;
  gss_ctx_id_t ctxt= GSS_C_NO_CONTEXT;
  gss_name_t service_name= GSS_C_NO_NAME;

  if (principal_name && principal_name[0])
  {
    /* import principal from plain text */
    gss_buffer_desc principal_name_buf;
    principal_name_buf.length= strlen(principal_name);
    principal_name_buf.value= (void *) principal_name;
    major= gss_import_name(&minor, &principal_name_buf, GSS_C_NT_USER_NAME, &service_name);
    if (GSS_ERROR(major))
    {
      log_error(mysql, major, minor, "gss_import_name");
      return CR_ERROR;
    }
  }

  gss_buffer_desc input= {0,0};
  do
  {
    gss_buffer_desc output= {0,0};
    major= gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &ctxt, service_name,
                                GSS_C_NO_OID, 0, 0, GSS_C_NO_CHANNEL_BINDINGS,
                                &input, NULL, &output, NULL, NULL);
    if (output.length)
    {
      /* send credential */
      if(vio->write_packet(vio, (unsigned char *)output.value, output.length))
      {
        /* Server error packet contains detailed message. */
        ret= CR_OK_HANDSHAKE_COMPLETE;
        gss_release_buffer (&minor, &output);
        goto cleanup;
      }
    }
    gss_release_buffer (&minor, &output);

    if (GSS_ERROR(major))
    {
       log_error(mysql, major, minor,"gss_init_sec_context");
       goto cleanup;
    }

    if (major & GSS_S_CONTINUE_NEEDED)
    {
      int len= vio->read_packet(vio, (unsigned char **) &input.value);
      if (len <= 0)
      {
        /* Server error packet contains detailed message. */
        ret= CR_OK_HANDSHAKE_COMPLETE;
        goto cleanup;
      }
      input.length= len;
    }
  } while (major & GSS_S_CONTINUE_NEEDED);

  ret= CR_OK;

cleanup:
  if (service_name != GSS_C_NO_NAME)
    gss_release_name(&minor, &service_name);
  if (ctxt != GSS_C_NO_CONTEXT)
    gss_delete_sec_context(&minor, &ctxt, GSS_C_NO_BUFFER);

  return ret;
}
<commit_msg>Fix spelling of my name<commit_after>/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood,
Vladislav Vaintroub & MariaDB Corporation

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/

#include <gssapi/gssapi.h>
#include <string.h>
#include <stdio.h>
#include <mysql/plugin_auth.h>
#include <mysqld_error.h>
#include <mysql.h>
#include "gssapi_errmsg.h"

extern void log_client_error(MYSQL *mysql,const char *fmt,...);

/* This sends the error to the client */
static void log_error(MYSQL *mysql, OM_uint32 major, OM_uint32 minor, const char *msg)
{
  if (GSS_ERROR(major))
  {
    char sysmsg[1024];
    gssapi_errmsg(major, minor, sysmsg, sizeof(sysmsg));
    log_client_error(mysql,
      "Client GSSAPI error (major %u, minor %u) : %s - %s",
       major, minor, msg, sysmsg);
  }
  else
  {
    log_client_error(mysql, "Client GSSAPI error : %s", msg);
  }
}

int auth_client(char *principal_name, char *mech, MYSQL *mysql, MYSQL_PLUGIN_VIO *vio)
{

  int ret= CR_ERROR;
  OM_uint32 major= 0, minor= 0;
  gss_ctx_id_t ctxt= GSS_C_NO_CONTEXT;
  gss_name_t service_name= GSS_C_NO_NAME;

  if (principal_name && principal_name[0])
  {
    /* import principal from plain text */
    gss_buffer_desc principal_name_buf;
    principal_name_buf.length= strlen(principal_name);
    principal_name_buf.value= (void *) principal_name;
    major= gss_import_name(&minor, &principal_name_buf, GSS_C_NT_USER_NAME, &service_name);
    if (GSS_ERROR(major))
    {
      log_error(mysql, major, minor, "gss_import_name");
      return CR_ERROR;
    }
  }

  gss_buffer_desc input= {0,0};
  do
  {
    gss_buffer_desc output= {0,0};
    major= gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &ctxt, service_name,
                                GSS_C_NO_OID, 0, 0, GSS_C_NO_CHANNEL_BINDINGS,
                                &input, NULL, &output, NULL, NULL);
    if (output.length)
    {
      /* send credential */
      if(vio->write_packet(vio, (unsigned char *)output.value, output.length))
      {
        /* Server error packet contains detailed message. */
        ret= CR_OK_HANDSHAKE_COMPLETE;
        gss_release_buffer (&minor, &output);
        goto cleanup;
      }
    }
    gss_release_buffer (&minor, &output);

    if (GSS_ERROR(major))
    {
       log_error(mysql, major, minor,"gss_init_sec_context");
       goto cleanup;
    }

    if (major & GSS_S_CONTINUE_NEEDED)
    {
      int len= vio->read_packet(vio, (unsigned char **) &input.value);
      if (len <= 0)
      {
        /* Server error packet contains detailed message. */
        ret= CR_OK_HANDSHAKE_COMPLETE;
        goto cleanup;
      }
      input.length= len;
    }
  } while (major & GSS_S_CONTINUE_NEEDED);

  ret= CR_OK;

cleanup:
  if (service_name != GSS_C_NO_NAME)
    gss_release_name(&minor, &service_name);
  if (ctxt != GSS_C_NO_CONTEXT)
    gss_delete_sec_context(&minor, &ctxt, GSS_C_NO_BUFFER);

  return ret;
}
<|endoftext|>


<commit_before>/* -------------------------------------------------------------------------- *
 *                     OpenSim:  SimulationUtilities.cpp                      *
 * -------------------------------------------------------------------------- *
 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2018 Stanford University and the Authors                *
 * Author(s): OpenSim Team                                                    *
 *                                                                            *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
 * not use this file except in compliance with the License. You may obtain a  *
 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
 *                                                                            *
 * Unless required by applicable law or agreed to in writing, software        *
 * distributed under the License is distributed on an "AS IS" BASIS,          *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
 * See the License for the specific language governing permissions and        *
 * limitations under the License.                                             *
 * -------------------------------------------------------------------------- */

#include "SimulationUtilities.h"

#include "Model/Model.h"
#include "Manager/Manager.h"
#include <simbody/internal/Visualizer_InputListener.h>

using namespace OpenSim;

SimTK::State OpenSim::simulate(Model& model,
    const SimTK::State& initialState,
    double finalTime,
    bool saveStatesFile)
{
    // Returned state begins as a copy of the initial state
    SimTK::State state = initialState;
    SimTK::Visualizer::InputSilo* silo;

    bool simulateOnce = true;

    // Ensure the final time is in the future.
    const double initialTime = initialState.getTime();
    if (finalTime <= initialTime) {
        std::cout << "The final time must be in the future (current time is "
                  << initialTime << "); simulation aborted." << std::endl;
        return state;
    }

    // Configure the visualizer.
    if (model.getUseVisualizer()) {
        SimTK::Visualizer& viz = model.updVisualizer().updSimbodyVisualizer();
        // We use the input silo to get key presses.
        silo = &model.updVisualizer().updInputSilo();

        SimTK::DecorativeText help("Press any key to start a new simulation; "
            "ESC to quit.");
        help.setIsScreenText(true);
        viz.addDecoration(SimTK::MobilizedBodyIndex(0), SimTK::Vec3(0), help);

        viz.setShowSimTime(true);
        viz.drawFrameNow(state);
        std::cout << "A visualizer window has opened." << std::endl;

        // if visualizing enable replay
        simulateOnce = false;
    }

    // Simulate until the user presses ESC (or enters 'q' if visualization has
    // been disabled).
    do {
        if (model.getUseVisualizer()) {
            // Get a key press.
            silo->clear(); // Ignore any previous key presses.
            unsigned key, modifiers;
            silo->waitForKeyHit(key, modifiers);
            if (key == SimTK::Visualizer::InputListener::KeyEsc) { break; }
        }

        // reset the state to the initial state
        state = initialState;
        // Set up manager and simulate.
        Manager manager(model);
        state.setTime(initialTime);
        manager.initialize(state);
        state = manager.integrate(finalTime);

        // Save the states to a storage file (if requested).
        if (saveStatesFile) {
            manager.getStateStorage().print(model.getName() + "_states.sto");
        }
    } while (!simulateOnce);

    return state;
}

std::unique_ptr<Storage>
OpenSim::updatePre40KinematicsStorageFor40MotionType(const Model& pre40Model,
        const Storage &kinematics)
{
    // There is no issue if the kinematics are in internal values (i.e. not
    // converted to degrees)
    if(!kinematics.isInDegrees()) return nullptr;

    if (pre40Model.getDocumentFileVersion() >= 30415) {
        throw Exception("updateKinematicsStorageForUpdatedModel has no updates "
            "to make because the model '" + pre40Model.getName() + "'is up-to-date.\n"
            "If input motion files were generated with this model version, "
            "nothing further must be done. Otherwise, provide the original model "
            "file used to generate the motion files and try again.");
    }

    std::vector<const Coordinate*> problemCoords;
    auto coordinates = pre40Model.getComponentList<Coordinate>();
    for (auto& coord : coordinates) {
        const Coordinate::MotionType oldMotionType =
                coord.getUserSpecifiedMotionTypePriorTo40();
        const Coordinate::MotionType motionType = coord.getMotionType();

        if ((oldMotionType != Coordinate::MotionType::Undefined) &&
            (oldMotionType != motionType)) {
            problemCoords.push_back(&coord);
        }
    }

    if (problemCoords.size() == 0)
        return nullptr;

    std::unique_ptr<Storage> updatedKinematics(kinematics.clone());
    // Cycle the inconsistent Coordinates
    for (const auto& coord : problemCoords) {
        // Get the corresponding column of data and if in degrees
        // undo the radians to degrees conversion on that column.
        int ix = updatedKinematics->getStateIndex(coord->getName());

        if (ix < 0) {
            std::cout << "updateKinematicsStorageForUpdatedModel(): motion '"
            << kinematics.getName() << "' does not contain inconsistent "
            << "coordinate '" << coord->getName() << "'." << std::endl;
        }
        else {
            // convert this column back to internal values by undoing the
            // 180/pi conversion to degrees
            updatedKinematics->multiplyColumn(ix, SimTK_DTR);
        }
    }
    return updatedKinematics;
}

void OpenSim::updatePre40KinematicsFilesFor40MotionType(const Model& model,
        const std::vector<std::string>& filePaths,
        std::string suffix)
{
    // Cycle through the data files
    for (const auto& filePath : filePaths) {
        Storage motion(filePath);
        auto updatedMotion =
            updatePre40KinematicsStorageFor40MotionType(model, motion);

        if (updatedMotion == nullptr) {
            continue; // no update was required, move on to next file
        }

        std::string outFilePath = filePath;
        if (suffix.size()) {
            auto back = filePath.rfind(".");
            outFilePath = filePath.substr(0, back) + suffix +
                            filePath.substr(back);
        }
        std::cout << "Writing converted motion '" << filePath << "' to '"
            << outFilePath << "'." << std::endl;

        updatedMotion->print(outFilePath);
    }
}

void OpenSim::updateConnecteesBySearch(Model& model)
{
    for (auto& comp : model.updComponentList()) {
        for (auto& it : comp._socketsTable) {
            auto& socket = it.second.updRef();
            try {
                socket.finalizeConnection(model);
            } catch (const ComponentNotFoundOnSpecifiedPath& e) {
                const ComponentPath path(socket.getConnecteePath());
                if (path.getNumPathLevels() >= 1) {
                    const Component* found =
                        model.findComponent(path.getComponentName());
                    if (found) {
                        socket.connect(*found);
                        socket.finalizeConnection(model);
                    }
                }
            } catch (const std::exception& e) {
                std::cout << "Warning: Caught exception when processing "
                    "Socket " << it.first << " in " <<
                    comp.getConcreteClassName() << " at " <<
                    comp.getAbsolutePathString() << ": " << e.what() <<
                    std::endl;
            }
        }
    }
}
<commit_msg>Fix MSVC warning about unused exception variable.<commit_after>/* -------------------------------------------------------------------------- *
 *                     OpenSim:  SimulationUtilities.cpp                      *
 * -------------------------------------------------------------------------- *
 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2018 Stanford University and the Authors                *
 * Author(s): OpenSim Team                                                    *
 *                                                                            *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
 * not use this file except in compliance with the License. You may obtain a  *
 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
 *                                                                            *
 * Unless required by applicable law or agreed to in writing, software        *
 * distributed under the License is distributed on an "AS IS" BASIS,          *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
 * See the License for the specific language governing permissions and        *
 * limitations under the License.                                             *
 * -------------------------------------------------------------------------- */

#include "SimulationUtilities.h"

#include "Model/Model.h"
#include "Manager/Manager.h"
#include <simbody/internal/Visualizer_InputListener.h>

using namespace OpenSim;

SimTK::State OpenSim::simulate(Model& model,
    const SimTK::State& initialState,
    double finalTime,
    bool saveStatesFile)
{
    // Returned state begins as a copy of the initial state
    SimTK::State state = initialState;
    SimTK::Visualizer::InputSilo* silo;

    bool simulateOnce = true;

    // Ensure the final time is in the future.
    const double initialTime = initialState.getTime();
    if (finalTime <= initialTime) {
        std::cout << "The final time must be in the future (current time is "
                  << initialTime << "); simulation aborted." << std::endl;
        return state;
    }

    // Configure the visualizer.
    if (model.getUseVisualizer()) {
        SimTK::Visualizer& viz = model.updVisualizer().updSimbodyVisualizer();
        // We use the input silo to get key presses.
        silo = &model.updVisualizer().updInputSilo();

        SimTK::DecorativeText help("Press any key to start a new simulation; "
            "ESC to quit.");
        help.setIsScreenText(true);
        viz.addDecoration(SimTK::MobilizedBodyIndex(0), SimTK::Vec3(0), help);

        viz.setShowSimTime(true);
        viz.drawFrameNow(state);
        std::cout << "A visualizer window has opened." << std::endl;

        // if visualizing enable replay
        simulateOnce = false;
    }

    // Simulate until the user presses ESC (or enters 'q' if visualization has
    // been disabled).
    do {
        if (model.getUseVisualizer()) {
            // Get a key press.
            silo->clear(); // Ignore any previous key presses.
            unsigned key, modifiers;
            silo->waitForKeyHit(key, modifiers);
            if (key == SimTK::Visualizer::InputListener::KeyEsc) { break; }
        }

        // reset the state to the initial state
        state = initialState;
        // Set up manager and simulate.
        Manager manager(model);
        state.setTime(initialTime);
        manager.initialize(state);
        state = manager.integrate(finalTime);

        // Save the states to a storage file (if requested).
        if (saveStatesFile) {
            manager.getStateStorage().print(model.getName() + "_states.sto");
        }
    } while (!simulateOnce);

    return state;
}

std::unique_ptr<Storage>
OpenSim::updatePre40KinematicsStorageFor40MotionType(const Model& pre40Model,
        const Storage &kinematics)
{
    // There is no issue if the kinematics are in internal values (i.e. not
    // converted to degrees)
    if(!kinematics.isInDegrees()) return nullptr;

    if (pre40Model.getDocumentFileVersion() >= 30415) {
        throw Exception("updateKinematicsStorageForUpdatedModel has no updates "
            "to make because the model '" + pre40Model.getName() + "'is up-to-date.\n"
            "If input motion files were generated with this model version, "
            "nothing further must be done. Otherwise, provide the original model "
            "file used to generate the motion files and try again.");
    }

    std::vector<const Coordinate*> problemCoords;
    auto coordinates = pre40Model.getComponentList<Coordinate>();
    for (auto& coord : coordinates) {
        const Coordinate::MotionType oldMotionType =
                coord.getUserSpecifiedMotionTypePriorTo40();
        const Coordinate::MotionType motionType = coord.getMotionType();

        if ((oldMotionType != Coordinate::MotionType::Undefined) &&
            (oldMotionType != motionType)) {
            problemCoords.push_back(&coord);
        }
    }

    if (problemCoords.size() == 0)
        return nullptr;

    std::unique_ptr<Storage> updatedKinematics(kinematics.clone());
    // Cycle the inconsistent Coordinates
    for (const auto& coord : problemCoords) {
        // Get the corresponding column of data and if in degrees
        // undo the radians to degrees conversion on that column.
        int ix = updatedKinematics->getStateIndex(coord->getName());

        if (ix < 0) {
            std::cout << "updateKinematicsStorageForUpdatedModel(): motion '"
            << kinematics.getName() << "' does not contain inconsistent "
            << "coordinate '" << coord->getName() << "'." << std::endl;
        }
        else {
            // convert this column back to internal values by undoing the
            // 180/pi conversion to degrees
            updatedKinematics->multiplyColumn(ix, SimTK_DTR);
        }
    }
    return updatedKinematics;
}

void OpenSim::updatePre40KinematicsFilesFor40MotionType(const Model& model,
        const std::vector<std::string>& filePaths,
        std::string suffix)
{
    // Cycle through the data files
    for (const auto& filePath : filePaths) {
        Storage motion(filePath);
        auto updatedMotion =
            updatePre40KinematicsStorageFor40MotionType(model, motion);

        if (updatedMotion == nullptr) {
            continue; // no update was required, move on to next file
        }

        std::string outFilePath = filePath;
        if (suffix.size()) {
            auto back = filePath.rfind(".");
            outFilePath = filePath.substr(0, back) + suffix +
                            filePath.substr(back);
        }
        std::cout << "Writing converted motion '" << filePath << "' to '"
            << outFilePath << "'." << std::endl;

        updatedMotion->print(outFilePath);
    }
}

void OpenSim::updateConnecteesBySearch(Model& model)
{
    for (auto& comp : model.updComponentList()) {
        for (auto& it : comp._socketsTable) {
            auto& socket = it.second.updRef();
            try {
                socket.finalizeConnection(model);
            } catch (const ComponentNotFoundOnSpecifiedPath&) {
                const ComponentPath path(socket.getConnecteePath());
                if (path.getNumPathLevels() >= 1) {
                    const Component* found =
                        model.findComponent(path.getComponentName());
                    if (found) {
                        socket.connect(*found);
                        socket.finalizeConnection(model);
                    }
                }
            } catch (const std::exception& e) {
                std::cout << "Warning: Caught exception when processing "
                    "Socket " << it.first << " in " <<
                    comp.getConcreteClassName() << " at " <<
                    comp.getAbsolutePathString() << ": " << e.what() <<
                    std::endl;
            }
        }
    }
}
<|endoftext|>


<commit_before>#include <algorithm>

#include "LevelDefinition.h"
#include "LevelUtils.h"

LevelDefinition::EntityPlacementDef::EntityPlacementDef(EntityDefID id, std::vector<LevelDouble3> &&positions)
	: positions(std::move(positions))
{
	this->id = id;
}

LevelDefinition::LockPlacementDef::LockPlacementDef(LockDefID id, std::vector<LevelInt3> &&positions)
	: positions(std::move(positions))
{
	this->id = id;
}

LevelDefinition::TriggerPlacementDef::TriggerPlacementDef(TriggerDefID id, std::vector<LevelInt3> &&positions)
	: positions(std::move(positions))
{
	this->id = id;
}

void LevelDefinition::init(SNInt width, int height, WEInt depth)
{
	this->voxels.init(width, height, depth);
}

SNInt LevelDefinition::getWidth() const
{
	return this->voxels.getWidth();
}

int LevelDefinition::getHeight() const
{
	return this->voxels.getHeight();
}

WEInt LevelDefinition::getDepth() const
{
	return this->voxels.getDepth();
}

LevelDefinition::VoxelDefID LevelDefinition::getVoxel(SNInt x, int y, WEInt z) const
{
	return this->voxels.get(x, y, z);
}

void LevelDefinition::setVoxel(SNInt x, int y, WEInt z, VoxelDefID voxel)
{
	this->voxels.set(x, y, z, voxel);
}

int LevelDefinition::getEntityPlacementDefCount() const
{
	return static_cast<int>(this->entityPlacementDefs.size());
}

const LevelDefinition::EntityPlacementDef &LevelDefinition::getEntityPlacementDef(int index) const
{
	DebugAssertIndex(this->entityPlacementDefs, index);
	return this->entityPlacementDefs[index];
}

int LevelDefinition::getLockPlacementDefCount() const
{
	return static_cast<int>(this->lockPlacementDefs.size());
}

const LevelDefinition::LockPlacementDef &LevelDefinition::getLockPlacementDef(int index) const
{
	DebugAssertIndex(this->lockPlacementDefs, index);
	return this->lockPlacementDefs[index];
}

int LevelDefinition::getTriggerPlacementDefCount() const
{
	return static_cast<int>(this->triggerPlacementDefs.size());
}

const LevelDefinition::TriggerPlacementDef &LevelDefinition::getTriggerPlacementDef(int index) const
{
	DebugAssertIndex(this->triggerPlacementDefs, index);
	return this->triggerPlacementDefs[index];
}

void LevelDefinition::addEntity(EntityDefID id, const LevelDouble3 &position)
{
	const auto iter = std::find_if(this->entityPlacementDefs.begin(), this->entityPlacementDefs.end(),
		[id](const EntityPlacementDef &def)
	{
		return def.id == id;
	});

	if (iter != this->entityPlacementDefs.end())
	{
		std::vector<LevelDouble3> &positions = iter->positions;
		positions.push_back(position);
	}
	else
	{
		this->entityPlacementDefs.emplace_back(id, std::vector<LevelDouble3> { position });
	}
}

void LevelDefinition::addLock(LockDefID id, const LevelInt3 &position)
{
	const auto iter = std::find_if(this->lockPlacementDefs.begin(), this->lockPlacementDefs.end(),
		[id](const LockPlacementDef &def)
	{
		return def.id == id;
	});

	if (iter != this->lockPlacementDefs.end())
	{
		std::vector<LevelInt3> &positions = iter->positions;
		positions.push_back(position);
	}
	else
	{
		this->lockPlacementDefs.emplace_back(id, std::vector<LevelInt3> { position });
	}
}

void LevelDefinition::addTrigger(TriggerDefID id, const LevelInt3 &position)
{
	const auto iter = std::find_if(this->triggerPlacementDefs.begin(), this->triggerPlacementDefs.end(),
		[id](const TriggerPlacementDef &def)
	{
		return def.id == id;
	});

	if (iter != this->triggerPlacementDefs.end())
	{
		std::vector<LevelInt3> &positions = iter->positions;
		positions.push_back(position);
	}
	else
	{
		this->triggerPlacementDefs.emplace_back(id, std::vector<LevelInt3> { position });
	}
}
<commit_msg>Fill LevelDefinition voxels with zeroes by default.<commit_after>#include <algorithm>

#include "LevelDefinition.h"
#include "LevelUtils.h"

LevelDefinition::EntityPlacementDef::EntityPlacementDef(EntityDefID id, std::vector<LevelDouble3> &&positions)
	: positions(std::move(positions))
{
	this->id = id;
}

LevelDefinition::LockPlacementDef::LockPlacementDef(LockDefID id, std::vector<LevelInt3> &&positions)
	: positions(std::move(positions))
{
	this->id = id;
}

LevelDefinition::TriggerPlacementDef::TriggerPlacementDef(TriggerDefID id, std::vector<LevelInt3> &&positions)
	: positions(std::move(positions))
{
	this->id = id;
}

void LevelDefinition::init(SNInt width, int height, WEInt depth)
{
	this->voxels.init(width, height, depth);
	this->voxels.fill(0);
}

SNInt LevelDefinition::getWidth() const
{
	return this->voxels.getWidth();
}

int LevelDefinition::getHeight() const
{
	return this->voxels.getHeight();
}

WEInt LevelDefinition::getDepth() const
{
	return this->voxels.getDepth();
}

LevelDefinition::VoxelDefID LevelDefinition::getVoxel(SNInt x, int y, WEInt z) const
{
	return this->voxels.get(x, y, z);
}

void LevelDefinition::setVoxel(SNInt x, int y, WEInt z, VoxelDefID voxel)
{
	this->voxels.set(x, y, z, voxel);
}

int LevelDefinition::getEntityPlacementDefCount() const
{
	return static_cast<int>(this->entityPlacementDefs.size());
}

const LevelDefinition::EntityPlacementDef &LevelDefinition::getEntityPlacementDef(int index) const
{
	DebugAssertIndex(this->entityPlacementDefs, index);
	return this->entityPlacementDefs[index];
}

int LevelDefinition::getLockPlacementDefCount() const
{
	return static_cast<int>(this->lockPlacementDefs.size());
}

const LevelDefinition::LockPlacementDef &LevelDefinition::getLockPlacementDef(int index) const
{
	DebugAssertIndex(this->lockPlacementDefs, index);
	return this->lockPlacementDefs[index];
}

int LevelDefinition::getTriggerPlacementDefCount() const
{
	return static_cast<int>(this->triggerPlacementDefs.size());
}

const LevelDefinition::TriggerPlacementDef &LevelDefinition::getTriggerPlacementDef(int index) const
{
	DebugAssertIndex(this->triggerPlacementDefs, index);
	return this->triggerPlacementDefs[index];
}

void LevelDefinition::addEntity(EntityDefID id, const LevelDouble3 &position)
{
	const auto iter = std::find_if(this->entityPlacementDefs.begin(), this->entityPlacementDefs.end(),
		[id](const EntityPlacementDef &def)
	{
		return def.id == id;
	});

	if (iter != this->entityPlacementDefs.end())
	{
		std::vector<LevelDouble3> &positions = iter->positions;
		positions.push_back(position);
	}
	else
	{
		this->entityPlacementDefs.emplace_back(id, std::vector<LevelDouble3> { position });
	}
}

void LevelDefinition::addLock(LockDefID id, const LevelInt3 &position)
{
	const auto iter = std::find_if(this->lockPlacementDefs.begin(), this->lockPlacementDefs.end(),
		[id](const LockPlacementDef &def)
	{
		return def.id == id;
	});

	if (iter != this->lockPlacementDefs.end())
	{
		std::vector<LevelInt3> &positions = iter->positions;
		positions.push_back(position);
	}
	else
	{
		this->lockPlacementDefs.emplace_back(id, std::vector<LevelInt3> { position });
	}
}

void LevelDefinition::addTrigger(TriggerDefID id, const LevelInt3 &position)
{
	const auto iter = std::find_if(this->triggerPlacementDefs.begin(), this->triggerPlacementDefs.end(),
		[id](const TriggerPlacementDef &def)
	{
		return def.id == id;
	});

	if (iter != this->triggerPlacementDefs.end())
	{
		std::vector<LevelInt3> &positions = iter->positions;
		positions.push_back(position);
	}
	else
	{
		this->triggerPlacementDefs.emplace_back(id, std::vector<LevelInt3> { position });
	}
}
<|endoftext|>


<commit_before>#include <iostream>
#include <vector>
#include <fstream>
#include <algorithm>
#include <utility>
#include <libgen.h>
#include <queue>
#include <iterator>

using namespace std;

typedef pair<int, int> edge_t;

class Network {
	public:
		void read(istream& input);
		int largestComponentSize() const;

	private:
		vector<edge_t> edges;
		int maxNode;

		vector<int> getColors() const;
};

void Network::read(istream& input)
{
	maxNode = 0;
	edges = vector<edge_t>();

	int from, to;
	while ((input >> from >> to))
	{
		edges.emplace_back(from, to);
		maxNode = max(maxNode, max(from, to));
	}

	sort(edges.begin(), edges.end());
}

int Network::largestComponentSize() const
{
	vector<int> colors = getColors();
	sort(colors.begin(), colors.end());

	int maxComponent = 0;
	auto streakStart = upper_bound(colors.begin(), colors.end(), 0);
	while (streakStart != colors.end()) {
		auto streakEnd = upper_bound(streakStart, colors.end(), *streakStart);
		int componentSize = distance(streakStart, streakEnd);
		maxComponent = max(maxComponent, componentSize);

		streakStart = streakEnd;
	}

	return maxComponent;
}

vector<int> Network::getColors() const
{
	if (edges.size() == 0) {
		return {};
	}

	vector<int> colors(maxNode + 1, 0);

	queue<int> todo; // Allocated outside the loop to prevent excess allocations.
	for (int i = 1; i <= maxNode; i++) {
		if (colors[i] != 0) {
			continue; // Already colored this one.
		}

		todo.push(i);
		while (!todo.empty()) {
			int node = todo.front();
			todo.pop();
			if (colors[node] == 0) {
				// Recolor all nodes with his color to the new one.
				// Queue all related nodes to add to this one.
				colors[node] = i;
				auto it = lower_bound(edges.begin(), edges.end(), make_pair(node, 0));
				for (; it != edges.end() && it->first == node; it++) {
					const int color = colors[it->second];
					if (color == 0) {
						todo.push(it->second);
					} else if (color != i) {
						// Do it immediately, so we don't have multiple recolors in the queue.
						replace(colors.begin(), colors.end(), color, i);
					}
				}
			}
		}
	}

	return colors;
}

int main(int argc, char** argv)
{
	for (int i = 1; i < argc; i++)
	{
		ifstream input(argv[i]);
		Network network;
		network.read(input);
		cout << basename(argv[i]) << ": " << network.largestComponentSize() << endl;
	}
}
<commit_msg>Herimplementeer queue als set.<commit_after>#include <iostream>
#include <vector>
#include <fstream>
#include <algorithm>
#include <utility>
#include <libgen.h>
#include <set>
#include <iterator>

using namespace std;

typedef pair<int, int> edge_t;

class Network {
	public:
		void read(istream& input);
		int largestComponentSize() const;

	private:
		vector<edge_t> edges;
		int maxNode;

		vector<int> getColors() const;
};

void Network::read(istream& input)
{
	maxNode = 0;
	edges = vector<edge_t>();

	int from, to;
	while ((input >> from >> to))
	{
		edges.emplace_back(from, to);
		maxNode = max(maxNode, max(from, to));
	}

	sort(edges.begin(), edges.end());
}

int Network::largestComponentSize() const
{
	vector<int> colors = getColors();
	sort(colors.begin(), colors.end());

	int maxComponent = 0;
	auto streakStart = upper_bound(colors.begin(), colors.end(), 0);
	while (streakStart != colors.end()) {
		auto streakEnd = upper_bound(streakStart, colors.end(), *streakStart);
		int componentSize = distance(streakStart, streakEnd);
		maxComponent = max(maxComponent, componentSize);

		streakStart = streakEnd;
	}

	return maxComponent;
}

vector<int> Network::getColors() const
{
	if (edges.size() == 0) {
		return {};
	}

	vector<int> colors(maxNode + 1, 0);

	for (int i = 1; i <= maxNode; i++) {
		if (colors[i] != 0) {
			continue; // Already colored this one.
		}

		set<int> todo = {i}; // Queue implemented as a set to reduce memory footprint.
		while (!todo.empty()) {
			int node = *todo.begin();
			todo.erase(todo.begin());
			if (colors[node] == 0) {
				// Recolor all nodes with his color to the new one.
				// Queue all related nodes to add to this one.
				colors[node] = i;
				auto it = lower_bound(edges.begin(), edges.end(), make_pair(node, 0));
				for (; it != edges.end() && it->first == node; it++) {
					const int color = colors[it->second];
					if (color == 0) {
						todo.insert(it->second);
					} else if (color != i) {
						// Do it immediately, so we don't have multiple recolors in the queue.
						replace(colors.begin(), colors.end(), color, i);
					}
				}
			}
		}
	}

	return colors;
}

int main(int argc, char** argv)
{
	for (int i = 1; i < argc; i++)
	{
		ifstream input(argv[i]);
		Network network;
		network.read(input);
		cout << basename(argv[i]) << ": " << network.largestComponentSize() << endl;
	}
}
<|endoftext|>


<commit_before><commit_msg>call methodEnter on cmdline args<commit_after><|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: otherjre.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: hr $ $Date: 2006-06-20 00:09:04 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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
 *
 ************************************************************************/

#if !defined INCLUDED_JFW_PLUGIN_OTHERJRE_HXX
#define  INCLUDED_JFW_PLUGIN_OTHERJRE_HXX

#include "vendorbase.hxx"
#include "vendorlist.hxx"

namespace jfw_plugin
{
/* Do not forget to put this class in the vendor map in vendorlist.cxx
 */
class OtherInfo: public VendorBase
{
public:
    static char const* const* getJavaExePaths(int * size);

    static rtl::Reference<VendorBase> createInstance();

    using VendorBase::getLibraryPaths;
    virtual char const* const* getRuntimePaths(int * size);
    virtual char const* const* getLibraryPaths(int* size);
    virtual int compareVersions(const rtl::OUString& sSecond) const;

};

}
#endif
<commit_msg>INTEGRATION: CWS changefileheader (1.4.88); FILE MERGED 2008/03/31 07:24:50 rt 1.4.88.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: otherjre.hxx,v $
 * $Revision: 1.5 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#if !defined INCLUDED_JFW_PLUGIN_OTHERJRE_HXX
#define  INCLUDED_JFW_PLUGIN_OTHERJRE_HXX

#include "vendorbase.hxx"
#include "vendorlist.hxx"

namespace jfw_plugin
{
/* Do not forget to put this class in the vendor map in vendorlist.cxx
 */
class OtherInfo: public VendorBase
{
public:
    static char const* const* getJavaExePaths(int * size);

    static rtl::Reference<VendorBase> createInstance();

    using VendorBase::getLibraryPaths;
    virtual char const* const* getRuntimePaths(int * size);
    virtual char const* const* getLibraryPaths(int* size);
    virtual int compareVersions(const rtl::OUString& sSecond) const;

};

}
#endif
<|endoftext|>


<commit_before>#include "../include/JugadorAuto.hpp"

JugadorAuto::JugadorAuto(const Tablero &t){
	arbol.AsignaRaiz(t);
}

/* TODO Pendiente de revision
void JugadorAuto::CalcularPrimeraFila(const Tablero t){

  for(int i=0; i < t.GetColumnas(); i++)
    InsertarTablero(t,i,arbol);
}

ArbolGeneral<Tablero> JugadorAuto::Calcular(const Tablero t){
  Tablero copia = t;
  ArbolGeneral<Tablero> aux, rama;
  aux.AsignaRaiz(t);

  for(int i=0; i<t.GetColumnas(); i++){
    if(InsertarTablero(t,i,aux)){
      copia.colocarFicha(i);
      rama = Calcular(copia);
      aux.insertar_hijomasizquierda(aux.hijomasizquierda(aux.raiz()), rama);
    }
  }

  return aux;

}

bool JugadorAuto::InsertarTablero(const Tablero t,int pos, ArbolGeneral<Tablero> arbol){
  Tablero aux = t;
  ArbolGeneral<Tablero> copia;
  if((aux.colocarFicha(pos)) != 0){
    copia.AsignaRaiz(aux);
    arbol.insertar_hijomasizquierda(arbol.hijomasizquierda(arbol.raiz()),copia );
    return true;
  }
	else
    return false;
}
*/

ArbolGeneral<Tablero> JugadorAuto::GetArbol(){
  return arbol;
}

/*
Precondicion: El nodo n tiene que ser una hoja
 */
void JugadorAuto::rellenarNodo(ArbolGeneral<Tablero>::Nodo n){
	ArbolGeneral<Tablero>::Nodo hermano_mas_der;

	for (int i = 0; i < n->etiqueta.GetColumnas(); i++) {
		ArbolGeneral<Tablero> hijo;
		Tablero tablero(n->etiqueta);

		if (tablero.colocarFicha(i)) {
			hijo.AsignaRaiz(tablero);

			if (!arbol.hijomasizquierda(n)){
				arbol.insertar_hijomasizquierda(n, hijo);
				hermano_mas_der = arbol.hijomasizquierda(n);
			}
			else{
				arbol.insertar_hermanoderecha(hermano_mas_der, hijo);
				hermano_mas_der = hermano_mas_der->drcha;
			}
		}
	}

}

void JugadorAuto::rellenarArbol(){
	ArbolGeneral<Tablero>::Nodo altura_a_rellenar = arbol.raiz();
	ArbolGeneral<Tablero>::Nodo aux = arbol.raiz();
	for (int i = 0; i < max_profundidad; i++) {
		do {
			rellenarNodo(aux);
			aux = aux->drcha;
		} while(aux);

		altura_a_rellenar = arbol.hijomasizquierda(altura_a_rellenar);
		aux = altura_a_rellenar;
	}
}
<commit_msg>cambiarTurno()<commit_after>#include "../include/JugadorAuto.hpp"

JugadorAuto::JugadorAuto(const Tablero &t){
	arbol.AsignaRaiz(t);
}

/* TODO Pendiente de revision
void JugadorAuto::CalcularPrimeraFila(const Tablero t){

  for(int i=0; i < t.GetColumnas(); i++)
    InsertarTablero(t,i,arbol);
}

ArbolGeneral<Tablero> JugadorAuto::Calcular(const Tablero t){
  Tablero copia = t;
  ArbolGeneral<Tablero> aux, rama;
  aux.AsignaRaiz(t);

  for(int i=0; i<t.GetColumnas(); i++){
    if(InsertarTablero(t,i,aux)){
      copia.colocarFicha(i);
      rama = Calcular(copia);
      aux.insertar_hijomasizquierda(aux.hijomasizquierda(aux.raiz()), rama);
    }
  }

  return aux;

}

bool JugadorAuto::InsertarTablero(const Tablero t,int pos, ArbolGeneral<Tablero> arbol){
  Tablero aux = t;
  ArbolGeneral<Tablero> copia;
  if((aux.colocarFicha(pos)) != 0){
    copia.AsignaRaiz(aux);
    arbol.insertar_hijomasizquierda(arbol.hijomasizquierda(arbol.raiz()),copia );
    return true;
  }
	else
    return false;
}
*/

ArbolGeneral<Tablero> JugadorAuto::GetArbol(){
  return arbol;
}

/*
Precondicion: El nodo n tiene que ser una hoja
 */
void JugadorAuto::rellenarNodo(ArbolGeneral<Tablero>::Nodo n){
	ArbolGeneral<Tablero>::Nodo hermano_mas_der;

	for (int i = 0; i < n->etiqueta.GetColumnas(); i++) {
		ArbolGeneral<Tablero> hijo;
		Tablero tablero(n->etiqueta);

		if (tablero.colocarFicha(i)) {
			tablero.cambiarTurno();
			hijo.AsignaRaiz(tablero);

			if (!arbol.hijomasizquierda(n)){
				arbol.insertar_hijomasizquierda(n, hijo);
				hermano_mas_der = arbol.hijomasizquierda(n);
			}
			else{
				arbol.insertar_hermanoderecha(hermano_mas_der, hijo);
				hermano_mas_der = hermano_mas_der->drcha;
			}
		}
	}

}

void JugadorAuto::rellenarArbol(){
	ArbolGeneral<Tablero>::Nodo altura_a_rellenar = arbol.raiz();
	ArbolGeneral<Tablero>::Nodo aux = arbol.raiz();
	for (int i = 0; i < max_profundidad; i++) {
		do {
			rellenarNodo(aux);
			aux = aux->drcha;
		} while(aux);

		altura_a_rellenar = arbol.hijomasizquierda(altura_a_rellenar);
		aux = altura_a_rellenar;
	}
}
<|endoftext|>