<commit_before>/*
	This file is part of cpp-ethereum.

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

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

	You should have received a copy of the GNU General Public License
	along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @author Christian <c@ethdev.com>
 * @date 2014
 * Solidity compiler.
 */

#include <algorithm>
#include <libevmcore/Instruction.h>
#include <libevmcore/Assembly.h>
#include <libsolidity/AST.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/ExpressionCompiler.h>

using namespace std;

namespace dev {
namespace solidity {

void Compiler::compileContract(ContractDefinition& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals)
{
	m_context = CompilerContext(); // clear it just in case

	for (MagicVariableDeclaration const* variable: _magicGlobals)
		m_context.addMagicGlobal(*variable);

	for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
		if (function->getName() != _contract.getName()) // don't add the constructor here
			m_context.addFunction(*function);
	registerStateVariables(_contract);

	appendFunctionSelector(_contract);
	for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
		if (function->getName() != _contract.getName()) // don't add the constructor here
			function->accept(*this);

	packIntoContractCreator(_contract);
}

void Compiler::packIntoContractCreator(ContractDefinition const& _contract)
{
	CompilerContext runtimeContext;
	swap(m_context, runtimeContext);

	registerStateVariables(_contract);

	FunctionDefinition* constructor = nullptr;
	for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
		if (function->getName() == _contract.getName())
		{
			constructor = function.get();
			break;
		}
	if (constructor)
	{
		eth::AssemblyItem returnTag = m_context.pushNewTag();
		m_context.addFunction(*constructor); // note that it cannot be called due to syntactic reasons
		//@todo copy constructor arguments from calldata to memory prior to this
		//@todo calling other functions inside the constructor should either trigger a parse error
		//or we should copy them here (register them above and call "accept") - detecting which
		// functions are referenced / called needs to be done in a recursive way.
		appendCalldataUnpacker(*constructor, true);
		m_context.appendJumpTo(m_context.getFunctionEntryLabel(*constructor));
		constructor->accept(*this);
		m_context << returnTag;
	}

	eth::AssemblyItem sub = m_context.addSubroutine(runtimeContext.getAssembly());
	// stack contains sub size
	m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY;
	m_context << u256(0) << eth::Instruction::RETURN;
}

void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
{
	vector<FunctionDefinition const*> interfaceFunctions = _contract.getInterfaceFunctions();
	vector<eth::AssemblyItem> callDataUnpackerEntryPoints;

	if (interfaceFunctions.size() > 255)
		BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("More than 255 public functions for contract."));

	// retrieve the first byte of the call data, which determines the called function
	// @todo This code had a jump table in a previous version which was more efficient but also
	// error prone (due to the optimizer and variable length tag addresses)
	m_context << u256(1) << u256(0) // some constants
			  << eth::dupInstruction(1) << eth::Instruction::CALLDATALOAD
			  << eth::dupInstruction(2) << eth::Instruction::BYTE
			  << eth::dupInstruction(2);

	// stack here: 1 0 <funid> 0, stack top will be counted up until it matches funid
	for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid)
	{
		callDataUnpackerEntryPoints.push_back(m_context.newTag());
		m_context << eth::dupInstruction(2) << eth::dupInstruction(2) << eth::Instruction::EQ;
		m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.back());
		m_context << eth::dupInstruction(4) << eth::Instruction::ADD;
		//@todo avoid the last ADD (or remove it in the optimizer)
	}
	m_context << eth::Instruction::STOP; // function not found

	for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid)
	{
		FunctionDefinition const& function = *interfaceFunctions[funid];
		m_context << callDataUnpackerEntryPoints[funid];
		eth::AssemblyItem returnTag = m_context.pushNewTag();
		appendCalldataUnpacker(function);
		m_context.appendJumpTo(m_context.getFunctionEntryLabel(function));
		m_context << returnTag;
		appendReturnValuePacker(function);
	}
}

unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory)
{
	// We do not check the calldata size, everything is zero-padded.
	unsigned dataOffset = 1;
	eth::Instruction load = _fromMemory ? eth::Instruction::MLOAD : eth::Instruction::CALLDATALOAD;

	//@todo this can be done more efficiently, saving some CALLDATALOAD calls
	for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
	{
		unsigned const numBytes = var->getType()->getCalldataEncodedSize();
		if (numBytes == 0)
			BOOST_THROW_EXCEPTION(CompilerError()
								  << errinfo_sourceLocation(var->getLocation())
								  << errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
		if (numBytes == 32)
			m_context << u256(dataOffset) << load;
		else
			m_context << (u256(1) << ((32 - numBytes) * 8)) << u256(dataOffset)
					  << load << eth::Instruction::DIV;
		dataOffset += numBytes;
	}
	return dataOffset;
}

void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
{
	//@todo this can be also done more efficiently
	unsigned dataOffset = 0;
	vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters();
	for (unsigned i = 0; i < parameters.size(); ++i)
	{
		Type const& paramType = *parameters[i]->getType();
		unsigned numBytes = paramType.getCalldataEncodedSize();
		if (numBytes == 0)
			BOOST_THROW_EXCEPTION(CompilerError()
								  << errinfo_sourceLocation(parameters[i]->getLocation())
								  << errinfo_comment("Type " + paramType.toString() + " not yet supported."));
		m_context << eth::dupInstruction(parameters.size() - i);
		if (numBytes != 32)
			m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
		m_context << u256(dataOffset) << eth::Instruction::MSTORE;
		dataOffset += numBytes;
	}
	// note that the stack is not cleaned up here
	m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN;
}

void Compiler::registerStateVariables(ContractDefinition const& _contract)
{
	//@todo sort them?
	for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
		m_context.addStateVariable(*variable);
}

bool Compiler::visit(FunctionDefinition& _function)
{
	//@todo to simplify this, the calling convention could by changed such that
	// caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn]
	// although note that this reduces the size of the visible stack

	m_context.startNewFunction();
	m_returnTag = m_context.newTag();
	m_breakTags.clear();
	m_continueTags.clear();

	m_context << m_context.getFunctionEntryLabel(_function);

	// stack upon entry: [return address] [arg0] [arg1] ... [argn]
	// reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp]

	unsigned const numArguments = _function.getParameters().size();
	unsigned const numReturnValues = _function.getReturnParameters().size();
	unsigned const numLocalVariables = _function.getLocalVariables().size();

	for (ASTPointer<VariableDeclaration> const& variable: _function.getParameters())
		m_context.addVariable(*variable);
	for (ASTPointer<VariableDeclaration> const& variable: _function.getReturnParameters())
		m_context.addAndInitializeVariable(*variable);
	for (VariableDeclaration const* localVariable: _function.getLocalVariables())
		m_context.addAndInitializeVariable(*localVariable);

	_function.getBody().accept(*this);

	m_context << m_returnTag;

	// Now we need to re-shuffle the stack. For this we keep a record of the stack layout
	// that shows the target positions of the elements, where "-1" denotes that this element needs
	// to be removed from the stack.
	// Note that the fact that the return arguments are of increasing index is vital for this
	// algorithm to work.

	vector<int> stackLayout;
	stackLayout.push_back(numReturnValues); // target of return address
	stackLayout += vector<int>(numArguments, -1); // discard all arguments
	for (unsigned i = 0; i < numReturnValues; ++i)
		stackLayout.push_back(i);
	stackLayout += vector<int>(numLocalVariables, -1);

	while (stackLayout.back() != int(stackLayout.size() - 1))
		if (stackLayout.back() < 0)
		{
			m_context << eth::Instruction::POP;
			stackLayout.pop_back();
		}
		else
		{
			m_context << eth::swapInstruction(stackLayout.size() - stackLayout.back() - 1);
			swap(stackLayout[stackLayout.back()], stackLayout.back());
		}
	//@todo assert that everything is in place now

	m_context << eth::Instruction::JUMP;

	return false;
}

bool Compiler::visit(IfStatement& _ifStatement)
{
	ExpressionCompiler::compileExpression(m_context, _ifStatement.getCondition());
	eth::AssemblyItem trueTag = m_context.appendConditionalJump();
	if (_ifStatement.getFalseStatement())
		_ifStatement.getFalseStatement()->accept(*this);
	eth::AssemblyItem endTag = m_context.appendJumpToNew();
	m_context << trueTag;
	_ifStatement.getTrueStatement().accept(*this);
	m_context << endTag;
	return false;
}

bool Compiler::visit(WhileStatement& _whileStatement)
{
	eth::AssemblyItem loopStart = m_context.newTag();
	eth::AssemblyItem loopEnd = m_context.newTag();
	m_continueTags.push_back(loopStart);
	m_breakTags.push_back(loopEnd);

	m_context << loopStart;
	ExpressionCompiler::compileExpression(m_context, _whileStatement.getCondition());
	m_context << eth::Instruction::ISZERO;
	m_context.appendConditionalJumpTo(loopEnd);

	_whileStatement.getBody().accept(*this);

	m_context.appendJumpTo(loopStart);
	m_context << loopEnd;

	m_continueTags.pop_back();
	m_breakTags.pop_back();
	return false;
}

bool Compiler::visit(Continue&)
{
	if (!m_continueTags.empty())
		m_context.appendJumpTo(m_continueTags.back());
	return false;
}

bool Compiler::visit(Break&)
{
	if (!m_breakTags.empty())
		m_context.appendJumpTo(m_breakTags.back());
	return false;
}

bool Compiler::visit(Return& _return)
{
	//@todo modifications are needed to make this work with functions returning multiple values
	if (Expression* expression = _return.getExpression())
	{
		ExpressionCompiler::compileExpression(m_context, *expression);
		VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front();
		ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType());

		unsigned stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(firstVariable));
		m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP;
	}
	m_context.appendJumpTo(m_returnTag);
	return false;
}

bool Compiler::visit(VariableDefinition& _variableDefinition)
{
	if (Expression* expression = _variableDefinition.getExpression())
	{
		ExpressionCompiler::compileExpression(m_context, *expression);
		ExpressionCompiler::appendTypeConversion(m_context,
												 *expression->getType(),
												 *_variableDefinition.getDeclaration().getType());
		unsigned baseStackOffset = m_context.getBaseStackOffsetOfVariable(_variableDefinition.getDeclaration());
		unsigned stackPosition = m_context.baseToCurrentStackOffset(baseStackOffset);
		m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP;
	}
	return false;
}

bool Compiler::visit(ExpressionStatement& _expressionStatement)
{
	Expression& expression = _expressionStatement.getExpression();
	ExpressionCompiler::compileExpression(m_context, expression);
//	Type::Category category = expression.getType()->getCategory();
	for (unsigned i = 0; i < expression.getType()->getSizeOnStack(); ++i)
		m_context << eth::Instruction::POP;
	return false;
}

}
}
<commit_msg>Cleanup before return.<commit_after>/*
	This file is part of cpp-ethereum.

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

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

	You should have received a copy of the GNU General Public License
	along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @author Christian <c@ethdev.com>
 * @date 2014
 * Solidity compiler.
 */

#include <algorithm>
#include <libevmcore/Instruction.h>
#include <libevmcore/Assembly.h>
#include <libsolidity/AST.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/ExpressionCompiler.h>

using namespace std;

namespace dev {
namespace solidity {

void Compiler::compileContract(ContractDefinition& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals)
{
	m_context = CompilerContext(); // clear it just in case

	for (MagicVariableDeclaration const* variable: _magicGlobals)
		m_context.addMagicGlobal(*variable);

	for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
		if (function->getName() != _contract.getName()) // don't add the constructor here
			m_context.addFunction(*function);
	registerStateVariables(_contract);

	appendFunctionSelector(_contract);
	for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
		if (function->getName() != _contract.getName()) // don't add the constructor here
			function->accept(*this);

	packIntoContractCreator(_contract);
}

void Compiler::packIntoContractCreator(ContractDefinition const& _contract)
{
	CompilerContext runtimeContext;
	swap(m_context, runtimeContext);

	registerStateVariables(_contract);

	FunctionDefinition* constructor = nullptr;
	for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
		if (function->getName() == _contract.getName())
		{
			constructor = function.get();
			break;
		}
	if (constructor)
	{
		eth::AssemblyItem returnTag = m_context.pushNewTag();
		m_context.addFunction(*constructor); // note that it cannot be called due to syntactic reasons
		//@todo copy constructor arguments from calldata to memory prior to this
		//@todo calling other functions inside the constructor should either trigger a parse error
		//or we should copy them here (register them above and call "accept") - detecting which
		// functions are referenced / called needs to be done in a recursive way.
		appendCalldataUnpacker(*constructor, true);
		m_context.appendJumpTo(m_context.getFunctionEntryLabel(*constructor));
		constructor->accept(*this);
		m_context << returnTag;
	}

	eth::AssemblyItem sub = m_context.addSubroutine(runtimeContext.getAssembly());
	// stack contains sub size
	m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY;
	m_context << u256(0) << eth::Instruction::RETURN;
}

void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
{
	vector<FunctionDefinition const*> interfaceFunctions = _contract.getInterfaceFunctions();
	vector<eth::AssemblyItem> callDataUnpackerEntryPoints;

	if (interfaceFunctions.size() > 255)
		BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("More than 255 public functions for contract."));

	// retrieve the first byte of the call data, which determines the called function
	// @todo This code had a jump table in a previous version which was more efficient but also
	// error prone (due to the optimizer and variable length tag addresses)
	m_context << u256(1) << u256(0) // some constants
			  << eth::dupInstruction(1) << eth::Instruction::CALLDATALOAD
			  << eth::dupInstruction(2) << eth::Instruction::BYTE
			  << eth::dupInstruction(2);

	// stack here: 1 0 <funid> 0, stack top will be counted up until it matches funid
	for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid)
	{
		callDataUnpackerEntryPoints.push_back(m_context.newTag());
		m_context << eth::dupInstruction(2) << eth::dupInstruction(2) << eth::Instruction::EQ;
		m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.back());
		m_context << eth::dupInstruction(4) << eth::Instruction::ADD;
		//@todo avoid the last ADD (or remove it in the optimizer)
	}
	m_context << eth::Instruction::STOP; // function not found

	for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid)
	{
		FunctionDefinition const& function = *interfaceFunctions[funid];
		m_context << callDataUnpackerEntryPoints[funid];
		eth::AssemblyItem returnTag = m_context.pushNewTag();
		appendCalldataUnpacker(function);
		m_context.appendJumpTo(m_context.getFunctionEntryLabel(function));
		m_context << returnTag;
		appendReturnValuePacker(function);
	}
}

unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory)
{
	// We do not check the calldata size, everything is zero-padded.
	unsigned dataOffset = 1;
	eth::Instruction load = _fromMemory ? eth::Instruction::MLOAD : eth::Instruction::CALLDATALOAD;

	//@todo this can be done more efficiently, saving some CALLDATALOAD calls
	for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
	{
		unsigned const numBytes = var->getType()->getCalldataEncodedSize();
		if (numBytes == 0)
			BOOST_THROW_EXCEPTION(CompilerError()
								  << errinfo_sourceLocation(var->getLocation())
								  << errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
		if (numBytes == 32)
			m_context << u256(dataOffset) << load;
		else
			m_context << (u256(1) << ((32 - numBytes) * 8)) << u256(dataOffset)
					  << load << eth::Instruction::DIV;
		dataOffset += numBytes;
	}
	return dataOffset;
}

void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
{
	//@todo this can be also done more efficiently
	unsigned dataOffset = 0;
	vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters();
	for (unsigned i = 0; i < parameters.size(); ++i)
	{
		Type const& paramType = *parameters[i]->getType();
		unsigned numBytes = paramType.getCalldataEncodedSize();
		if (numBytes == 0)
			BOOST_THROW_EXCEPTION(CompilerError()
								  << errinfo_sourceLocation(parameters[i]->getLocation())
								  << errinfo_comment("Type " + paramType.toString() + " not yet supported."));
		m_context << eth::dupInstruction(parameters.size() - i);
		if (numBytes != 32)
			m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
		m_context << u256(dataOffset) << eth::Instruction::MSTORE;
		dataOffset += numBytes;
	}
	// note that the stack is not cleaned up here
	m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN;
}

void Compiler::registerStateVariables(ContractDefinition const& _contract)
{
	//@todo sort them?
	for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
		m_context.addStateVariable(*variable);
}

bool Compiler::visit(FunctionDefinition& _function)
{
	//@todo to simplify this, the calling convention could by changed such that
	// caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn]
	// although note that this reduces the size of the visible stack

	m_context.startNewFunction();
	m_returnTag = m_context.newTag();
	m_breakTags.clear();
	m_continueTags.clear();

	m_context << m_context.getFunctionEntryLabel(_function);

	// stack upon entry: [return address] [arg0] [arg1] ... [argn]
	// reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp]

	for (ASTPointer<VariableDeclaration const> const& variable: _function.getParameters())
		m_context.addVariable(*variable);
	for (ASTPointer<VariableDeclaration const> const& variable: _function.getReturnParameters())
		m_context.addAndInitializeVariable(*variable);
	for (VariableDeclaration const* localVariable: _function.getLocalVariables())
		m_context.addAndInitializeVariable(*localVariable);

	_function.getBody().accept(*this);

	m_context << m_returnTag;

	// Now we need to re-shuffle the stack. For this we keep a record of the stack layout
	// that shows the target positions of the elements, where "-1" denotes that this element needs
	// to be removed from the stack.
	// Note that the fact that the return arguments are of increasing index is vital for this
	// algorithm to work.

	unsigned argumentsSize = 0;
	for (ASTPointer<VariableDeclaration const> const& variable: _function.getParameters())
		argumentsSize += variable->getType()->getSizeOnStack();
	unsigned returnValuesSize = 0;
	for (ASTPointer<VariableDeclaration const> const& variable: _function.getReturnParameters())
		returnValuesSize += variable->getType()->getSizeOnStack();
	unsigned localVariablesSize = 0;
	for (VariableDeclaration const* localVariable: _function.getLocalVariables())
		localVariablesSize += localVariable->getType()->getSizeOnStack();

	vector<int> stackLayout;
	stackLayout.push_back(returnValuesSize); // target of return address
	stackLayout += vector<int>(argumentsSize, -1); // discard all arguments
	for (unsigned i = 0; i < returnValuesSize; ++i)
		stackLayout.push_back(i);
	stackLayout += vector<int>(localVariablesSize, -1);

	while (stackLayout.back() != int(stackLayout.size() - 1))
		if (stackLayout.back() < 0)
		{
			m_context << eth::Instruction::POP;
			stackLayout.pop_back();
		}
		else
		{
			m_context << eth::swapInstruction(stackLayout.size() - stackLayout.back() - 1);
			swap(stackLayout[stackLayout.back()], stackLayout.back());
		}
	//@todo assert that everything is in place now

	m_context << eth::Instruction::JUMP;

	return false;
}

bool Compiler::visit(IfStatement& _ifStatement)
{
	ExpressionCompiler::compileExpression(m_context, _ifStatement.getCondition());
	eth::AssemblyItem trueTag = m_context.appendConditionalJump();
	if (_ifStatement.getFalseStatement())
		_ifStatement.getFalseStatement()->accept(*this);
	eth::AssemblyItem endTag = m_context.appendJumpToNew();
	m_context << trueTag;
	_ifStatement.getTrueStatement().accept(*this);
	m_context << endTag;
	return false;
}

bool Compiler::visit(WhileStatement& _whileStatement)
{
	eth::AssemblyItem loopStart = m_context.newTag();
	eth::AssemblyItem loopEnd = m_context.newTag();
	m_continueTags.push_back(loopStart);
	m_breakTags.push_back(loopEnd);

	m_context << loopStart;
	ExpressionCompiler::compileExpression(m_context, _whileStatement.getCondition());
	m_context << eth::Instruction::ISZERO;
	m_context.appendConditionalJumpTo(loopEnd);

	_whileStatement.getBody().accept(*this);

	m_context.appendJumpTo(loopStart);
	m_context << loopEnd;

	m_continueTags.pop_back();
	m_breakTags.pop_back();
	return false;
}

bool Compiler::visit(Continue&)
{
	if (!m_continueTags.empty())
		m_context.appendJumpTo(m_continueTags.back());
	return false;
}

bool Compiler::visit(Break&)
{
	if (!m_breakTags.empty())
		m_context.appendJumpTo(m_breakTags.back());
	return false;
}

bool Compiler::visit(Return& _return)
{
	//@todo modifications are needed to make this work with functions returning multiple values
	if (Expression* expression = _return.getExpression())
	{
		ExpressionCompiler::compileExpression(m_context, *expression);
		VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front();
		ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType());

		unsigned stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(firstVariable));
		m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP;
	}
	m_context.appendJumpTo(m_returnTag);
	return false;
}

bool Compiler::visit(VariableDefinition& _variableDefinition)
{
	if (Expression* expression = _variableDefinition.getExpression())
	{
		ExpressionCompiler::compileExpression(m_context, *expression);
		ExpressionCompiler::appendTypeConversion(m_context,
												 *expression->getType(),
												 *_variableDefinition.getDeclaration().getType());
		unsigned baseStackOffset = m_context.getBaseStackOffsetOfVariable(_variableDefinition.getDeclaration());
		unsigned stackPosition = m_context.baseToCurrentStackOffset(baseStackOffset);
		m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP;
	}
	return false;
}

bool Compiler::visit(ExpressionStatement& _expressionStatement)
{
	Expression& expression = _expressionStatement.getExpression();
	ExpressionCompiler::compileExpression(m_context, expression);
//	Type::Category category = expression.getType()->getCategory();
	for (unsigned i = 0; i < expression.getType()->getSizeOnStack(); ++i)
		m_context << eth::Instruction::POP;
	return false;
}

}
}
<|endoftext|>


<commit_before>/*
 * @File:			Controls.cpp
 * @Description:	Constrols class handles output to motors and motor controllers.
 * @Author: 		Rikin Katyal
 */

#include "Controls.h"

Controls::Controls()
{
	library = &getLibrary();

 	// Get variables from Data class
 	this -> update();

 	// Initialize Talons for drive system
 	t1 = CANTalon(DN1);
 	t2 = CANTalon(DN2);
 	t3 = CANTalon(DN3);
 	t4 = CANTalon(DN4);

 	// Set starting variables
 	trianglesLowered = false;

}

Controls::~Controls()
{
	// Controls destructor
}

void Controls::driveBase()
{

}

void Controls::pivotLeft()
{

}

void Controls::pivotRight()
{

}

void Controls::grabBall()
{

}

void Controls::reverseBall()
{

}

void Controls::launchBall()
{

}

void Controls::liftTriangles()
{

}

void Controls::lowerTriangles()
{

}

void Controls::toggleTriangles()
{
	if (trianglesLowered)
	{
		this -> liftTriangles();
	}
	else
	{
		this -> lowerTriangles();
	}
}

void Controls::update()
{

}<commit_msg>fixed {<commit_after>/*
 * @File:			Controls.cpp
 * @Description:	Constrols class handles output to motors and motor controllers.
 * @Author: 		Rikin Katyal
 */

#include "Controls.h"

Controls::Controls() {
	library = &getLibrary();

 	// Get variables from Data class
 	this -> update();

 	// Initialize Talons for drive system
 	t1 = CANTalon(DN1);
 	t2 = CANTalon(DN2);
 	t3 = CANTalon(DN3);
 	t4 = CANTalon(DN4);

 	// Set starting variables
 	trianglesLowered = false;

}

Controls::~Controls() {
	// Controls destructor
}

void Controls::driveBase() {

}

void Controls::pivotLeft() {

}

void Controls::pivotRight() {

}

void Controls::grabBall() {

}

void Controls::reverseBall() {

}

void Controls::launchBall() {

}

void Controls::liftTriangles() {

}

void Controls::lowerTriangles() {

}

void Controls::toggleTriangles() {
	if (trianglesLowered) {
		this -> liftTriangles();
	}
	else {
		this -> lowerTriangles();
	}
}

void Controls::update() {

}<|endoftext|>


<commit_before>#include <stdexcept>
#include <sstream>
#include <fstream>
#include "db-redis.h"
#include "types.h"
#include "util.h"

#define DB_REDIS_HMGET_NUMFIELDS 30

static inline int64_t stoi64(const std::string &s)
{
	std::stringstream tmp(s);
	int64_t t;
	tmp >> t;
	return t;
}

static inline std::string i64tos(int64_t i)
{
	std::ostringstream os;
	os << i;
	return os.str();
}

std::string get_setting_default(std::string name, std::istream &is, const std::string def)
{
	try {
		return get_setting(name, is);
	} catch(std::runtime_error e) {
		return def;
	}
}

DBRedis::DBRedis(const std::string &mapdir)
{
	std::ifstream ifs((mapdir + "/world.mt").c_str());
	if(!ifs.good())
		throw std::runtime_error("Failed to read world.mt");
	std::string tmp;
	try {
		tmp = get_setting("redis_address", ifs);
		ifs.seekg(0);
		hash = get_setting("redis_hash", ifs);
		ifs.seekg(0);
	} catch(std::runtime_error e) {
		throw std::runtime_error("Set redis_address and redis_hash in world.mt to use the redis backend");
	}
	const char *addr = tmp.c_str();
	int port = stoi64(get_setting_default("redis_port", ifs, "6379"));
	ctx = redisConnect(addr, port);
	if(!ctx)
		throw std::runtime_error("Cannot allocate redis context");
	else if(ctx->err) {
		std::string err = std::string("Connection error: ") + ctx->errstr;
		redisFree(ctx);
		throw std::runtime_error(err);
	}

	loadPosCache();
}

DBRedis::~DBRedis()
{
	redisFree(ctx);
}

std::vector<BlockPos> DBRedis::getBlockPos()
{
	return posCache;
}

std::string DBRedis::replyTypeStr(int type) {
	switch(type) {
		case REDIS_REPLY_STATUS:
			return "REDIS_REPLY_STATUS";
		case REDIS_REPLY_ERROR:
			return "REDIS_REPLY_ERROR";
		case REDIS_REPLY_INTEGER:
			return "REDIS_REPLY_INTEGER";
		case REDIS_REPLY_NIL:
			return "REDIS_REPLY_NIL";
		case REDIS_REPLY_STRING:
			return "REDIS_REPLY_STRING";
		case REDIS_REPLY_ARRAY:
			return "REDIS_REPLY_ARRAY";
		default:
			return "UNKNOWN_REPLY_TYPE";
	}
}

void DBRedis::loadPosCache()
{
	redisReply *reply;
	reply = (redisReply*) redisCommand(ctx, "HKEYS %s", hash.c_str());
	if(!reply)
		throw std::runtime_error(std::string("redis command 'HKEYS %s' failed: ") + ctx->errstr);
	if(reply->type != REDIS_REPLY_ARRAY)
		throw std::runtime_error("Failed to get keys from database");
	for(size_t i = 0; i < reply->elements; i++) {
		if(reply->element[i]->type != REDIS_REPLY_STRING)
			throw std::runtime_error("Got wrong response to 'HKEYS %s' command");
		posCache.push_back(decodeBlockPos(stoi64(reply->element[i]->str)));
	}

	freeReplyObject(reply);
}

void DBRedis::HMGET(const std::vector<BlockPos> &positions, std::vector<ustring> *result)
{
	const char *argv[DB_REDIS_HMGET_NUMFIELDS + 2];
	argv[0] = "HMGET";
	argv[1] = hash.c_str();

	std::vector<BlockPos>::const_iterator position = positions.begin();
	std::size_t remaining = positions.size();
	while (remaining > 0) {
		const std::size_t batch_size =
			(remaining > DB_REDIS_HMGET_NUMFIELDS) ? DB_REDIS_HMGET_NUMFIELDS : remaining;
		redisReply *reply;
		{
			// storage to preserve std::string::c_str() validity
			std::vector<std::string> keys;
			keys.reserve(batch_size);
			for (std::size_t i = 0; i < batch_size; ++i) {
				keys.push_back(i64tos(encodeBlockPos(*position++)));
				argv[i+2] = keys.back().c_str();
			}
			reply = (redisReply*) redisCommandArgv(ctx, batch_size + 2, argv, NULL);
		}
		if(!reply) {
			throw std::runtime_error("HMGET failed");
		}
		if (reply->type != REDIS_REPLY_ARRAY) {
			freeReplyObject(reply);
			throw std::runtime_error(std::string("HMGET unexpected reply type ")
					+ replyTypeStr(reply->type));
		}
		if (reply->elements != batch_size) {
			freeReplyObject(reply);
			throw std::runtime_error("HMGET wrong number of elements");
		}
		for (std::size_t i = 0; i < batch_size; ++i) {
			redisReply *subreply = reply->element[i];
			if(!subreply) {
				throw std::runtime_error("HMGET failed");
			}
			if (subreply->type != REDIS_REPLY_STRING) {
				freeReplyObject(reply);
				throw std::runtime_error(std::string("HMGET wrong subreply type ")
						+ replyTypeStr(subreply->type));
			}
			if (subreply->len == 0) {
				freeReplyObject(reply);
				throw std::runtime_error("HMGET empty string");
			}
			result->push_back(ustring((const unsigned char *) subreply->str, subreply->len));
		}
		freeReplyObject(reply);
		remaining -= batch_size;
	}
}

void DBRedis::getBlocksOnZ(std::map<int16_t, BlockList> &blocks, int16_t zPos)
{
	redisReply *reply;
	std::string tmp;

	for (std::vector<BlockPos>::iterator it = posCache.begin(); it != posCache.end(); ++it) {
		if (it->z != zPos) {
			continue;
		}
		tmp = i64tos(encodeBlockPos(*it));
		reply = (redisReply*) redisCommand(ctx, "HGET %s %s", hash.c_str(), tmp.c_str());
		if(!reply)
			throw std::runtime_error(std::string("redis command 'HGET %s %s' failed: ") + ctx->errstr);
		if (reply->type == REDIS_REPLY_STRING && reply->len != 0) {
			Block b(*it, ustring((const unsigned char *) reply->str, reply->len));
			blocks[b.first.x].push_back(b);
		} else
			throw std::runtime_error("Got wrong response to 'HGET %s %s' command");
		freeReplyObject(reply);
	}
}
<commit_msg>DBRedis: use HMGET in getBlocksOnZ<commit_after>#include <stdexcept>
#include <sstream>
#include <fstream>
#include "db-redis.h"
#include "types.h"
#include "util.h"

#define DB_REDIS_HMGET_NUMFIELDS 30

static inline int64_t stoi64(const std::string &s)
{
	std::stringstream tmp(s);
	int64_t t;
	tmp >> t;
	return t;
}

static inline std::string i64tos(int64_t i)
{
	std::ostringstream os;
	os << i;
	return os.str();
}

std::string get_setting_default(std::string name, std::istream &is, const std::string def)
{
	try {
		return get_setting(name, is);
	} catch(std::runtime_error e) {
		return def;
	}
}

DBRedis::DBRedis(const std::string &mapdir)
{
	std::ifstream ifs((mapdir + "/world.mt").c_str());
	if(!ifs.good())
		throw std::runtime_error("Failed to read world.mt");
	std::string tmp;
	try {
		tmp = get_setting("redis_address", ifs);
		ifs.seekg(0);
		hash = get_setting("redis_hash", ifs);
		ifs.seekg(0);
	} catch(std::runtime_error e) {
		throw std::runtime_error("Set redis_address and redis_hash in world.mt to use the redis backend");
	}
	const char *addr = tmp.c_str();
	int port = stoi64(get_setting_default("redis_port", ifs, "6379"));
	ctx = redisConnect(addr, port);
	if(!ctx)
		throw std::runtime_error("Cannot allocate redis context");
	else if(ctx->err) {
		std::string err = std::string("Connection error: ") + ctx->errstr;
		redisFree(ctx);
		throw std::runtime_error(err);
	}

	loadPosCache();
}

DBRedis::~DBRedis()
{
	redisFree(ctx);
}

std::vector<BlockPos> DBRedis::getBlockPos()
{
	return posCache;
}

std::string DBRedis::replyTypeStr(int type) {
	switch(type) {
		case REDIS_REPLY_STATUS:
			return "REDIS_REPLY_STATUS";
		case REDIS_REPLY_ERROR:
			return "REDIS_REPLY_ERROR";
		case REDIS_REPLY_INTEGER:
			return "REDIS_REPLY_INTEGER";
		case REDIS_REPLY_NIL:
			return "REDIS_REPLY_NIL";
		case REDIS_REPLY_STRING:
			return "REDIS_REPLY_STRING";
		case REDIS_REPLY_ARRAY:
			return "REDIS_REPLY_ARRAY";
		default:
			return "UNKNOWN_REPLY_TYPE";
	}
}

void DBRedis::loadPosCache()
{
	redisReply *reply;
	reply = (redisReply*) redisCommand(ctx, "HKEYS %s", hash.c_str());
	if(!reply)
		throw std::runtime_error(std::string("redis command 'HKEYS %s' failed: ") + ctx->errstr);
	if(reply->type != REDIS_REPLY_ARRAY)
		throw std::runtime_error("Failed to get keys from database");
	for(size_t i = 0; i < reply->elements; i++) {
		if(reply->element[i]->type != REDIS_REPLY_STRING)
			throw std::runtime_error("Got wrong response to 'HKEYS %s' command");
		posCache.push_back(decodeBlockPos(stoi64(reply->element[i]->str)));
	}

	freeReplyObject(reply);
}

void DBRedis::HMGET(const std::vector<BlockPos> &positions, std::vector<ustring> *result)
{
	const char *argv[DB_REDIS_HMGET_NUMFIELDS + 2];
	argv[0] = "HMGET";
	argv[1] = hash.c_str();

	std::vector<BlockPos>::const_iterator position = positions.begin();
	std::size_t remaining = positions.size();
	while (remaining > 0) {
		const std::size_t batch_size =
			(remaining > DB_REDIS_HMGET_NUMFIELDS) ? DB_REDIS_HMGET_NUMFIELDS : remaining;
		redisReply *reply;
		{
			// storage to preserve std::string::c_str() validity
			std::vector<std::string> keys;
			keys.reserve(batch_size);
			for (std::size_t i = 0; i < batch_size; ++i) {
				keys.push_back(i64tos(encodeBlockPos(*position++)));
				argv[i+2] = keys.back().c_str();
			}
			reply = (redisReply*) redisCommandArgv(ctx, batch_size + 2, argv, NULL);
		}
		if(!reply) {
			throw std::runtime_error("HMGET failed");
		}
		if (reply->type != REDIS_REPLY_ARRAY) {
			freeReplyObject(reply);
			throw std::runtime_error(std::string("HMGET unexpected reply type ")
					+ replyTypeStr(reply->type));
		}
		if (reply->elements != batch_size) {
			freeReplyObject(reply);
			throw std::runtime_error("HMGET wrong number of elements");
		}
		for (std::size_t i = 0; i < batch_size; ++i) {
			redisReply *subreply = reply->element[i];
			if(!subreply) {
				throw std::runtime_error("HMGET failed");
			}
			if (subreply->type != REDIS_REPLY_STRING) {
				freeReplyObject(reply);
				throw std::runtime_error(std::string("HMGET wrong subreply type ")
						+ replyTypeStr(subreply->type));
			}
			if (subreply->len == 0) {
				freeReplyObject(reply);
				throw std::runtime_error("HMGET empty string");
			}
			result->push_back(ustring((const unsigned char *) subreply->str, subreply->len));
		}
		freeReplyObject(reply);
		remaining -= batch_size;
	}
}

void DBRedis::getBlocksOnZ(std::map<int16_t, BlockList> &blocks, int16_t zPos)
{
	std::vector<BlockPos> z_positions;
	for (std::vector<BlockPos>::const_iterator it = posCache.begin(); it != posCache.end(); ++it) {
		if (it->z != zPos) {
			continue;
		}
		z_positions.push_back(*it);
	}
	std::vector<ustring> z_blocks;
	HMGET(z_positions, &z_blocks);

	std::vector<ustring>::const_iterator z_block = z_blocks.begin();
	for (std::vector<BlockPos>::const_iterator pos = z_positions.begin();
			pos != z_positions.end();
			++pos, ++z_block) {
		blocks[pos->x].push_back(Block(*pos, *z_block));
	}
}
<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
//
// CoinQ_script.cpp
//
// Copyright (c) 2013 Eric Lombrozo
//
// All Rights Reserved.

#include "CoinQ_script.h"

// TODO: Move all base58check stuff to another module.
#include <Base58Check.h>

namespace CoinQ {
namespace Script {

uchar_vector opPushData(uint32_t nBytes)
{
    uchar_vector rval;
    if (nBytes <= 0x4b) {
        rval.push_back((unsigned char)nBytes);
    }
    else if (nBytes <= 0xff) {
        rval.push_back(0x4c);
        rval.push_back((unsigned char)nBytes);
    }
    else if (nBytes <= 0xffff) {
        rval.push_back(0x4d);
        rval.push_back((unsigned char)(nBytes >> 8));
        rval.push_back((unsigned char)(nBytes & 0xff));
    }
    else {
        rval.push_back(0x4e);
        rval.push_back((unsigned char)(nBytes >> 24));
        rval.push_back((unsigned char)((nBytes >> 16) & 0xff));
        rval.push_back((unsigned char)((nBytes >> 8) & 0xff));
        rval.push_back((unsigned char)(nBytes & 0xff));
    }

    return rval;
}

uint32_t getDataLength(const uchar_vector& script, uint& pos)
{
    if (pos >= script.size()) {
        throw std::runtime_error("Script pos past end.");
    }

    unsigned char op = script[pos++];
    if (op <= 0x4b) {
        return (uint32_t)op;
    }
    else if (op == 0x4c) {
        if (pos >= script.size()) {
            throw std::runtime_error("Script pos past end.");
        }
        return (uint32_t)script[pos++];
    }
    else if (op == 0x4d) {
        if (pos + 1 >= script.size()) {
            throw std::runtime_error("Script pos past end.");
        }
        uint32_t rval = (uint32_t)script[pos++] << 8;
        rval         += (uint32_t)script[pos++];
        return rval;
    }
    else if (op == 0x4e) {
        if (pos + 3 >= script.size()) {
            throw std::runtime_error("Script pos past end.");
        }
        uint32_t rval = (uint32_t)script[pos++] << 24;
        rval         += (uint32_t)script[pos++] << 16;
        rval         += (uint32_t)script[pos++] << 8;
        rval         += (uint32_t)script[pos++];
        return rval;
    }
    else {
        throw std::runtime_error("Operation is not push data.");
    }
}

payee_t getScriptPubKeyPayee(const uchar_vector& scriptPubKey)
{
    if (scriptPubKey.size()   == 25 &&
        scriptPubKey[0]       == 0x76 &&
        scriptPubKey[1]       == 0xa9 &&
        scriptPubKey[2]       == 0x14 &&
        scriptPubKey[23]      == 0x88 &&
        scriptPubKey[24]      == 0xac)
    {
        return std::make_pair(SCRIPT_PUBKEY_PAY_TO_PUBKEY_HASH, uchar_vector(scriptPubKey.begin() + 3, scriptPubKey.begin() + 23));
    }

    if (scriptPubKey.size()   == 23 &&
        scriptPubKey[0]       == 0xa9 &&
        scriptPubKey[1]       == 0x14 &&
        scriptPubKey[22]      == 0x87)
    {
        return std::make_pair(SCRIPT_PUBKEY_PAY_TO_SCRIPT_HASH, uchar_vector(scriptPubKey.begin() + 2, scriptPubKey.begin() + 22));
    }

    uint pos = 0;
    uint32_t dataLength = getDataLength(scriptPubKey, pos);
    if (pos + dataLength + 1== scriptPubKey.size() &&
        scriptPubKey[pos + dataLength + 1] == 0xac)
    {
        return std::make_pair(SCRIPT_PUBKEY_PAY_TO_PUBKEY, uchar_vector(scriptPubKey.begin() + pos, scriptPubKey.begin() + pos + dataLength));
    }

    if (scriptPubKey.size() == 0)
    {
        return std::make_pair(SCRIPT_PUBKEY_EMPTY_SCRIPT, uchar_vector());
    }

    return std::make_pair(SCRIPT_PUBKEY_UNKNOWN_TYPE, uchar_vector());
}

uchar_vector getTxOutScriptForAddress(const std::string& address, const unsigned char addressVersions[])
{
    uchar_vector hash;
    unsigned int version;
    if (!fromBase58Check(address, hash, version)) {
        throw std::runtime_error("Invalid address checksum.");
    }

    uchar_vector script;
    if (version == addressVersions[0]) {
        if (hash.size() != 20) {
            throw std::runtime_error("Invalid pubkey hash length.");
        }
        script.push_back(0x76); // OP_PUSHDATA1
        script.push_back(0xa9); // OP_HASH160
        script.push_back(0x14); // push a 20 byte hash
        script += hash;
        script.push_back(0x88); // OP_EQUALVERIFY
        script.push_back(0xac); // OP_CHECKSIG
    }
    else if (version == addressVersions[1]) {
        if (hash.size() != 20) {
            throw std::runtime_error("Invalid script hash length.");
        }
        script.push_back(0xa9); // OP_HASH160
        script.push_back(0x14); // push a 20 byte hash
        script += hash;
        script.push_back(0x87); // OP_EQUAL
    }
    else {
        throw std::runtime_error("Invalid address version.");
    }

    return script;
}

std::string getAddressForTxOutScript(const bytes_t& txoutscript, const unsigned char addressVersions[])
{
    payee_t payee = getScriptPubKeyPayee(txoutscript);
    switch (payee.first) {
    case SCRIPT_PUBKEY_PAY_TO_PUBKEY_HASH:
        return toBase58Check(payee.second, addressVersions[0]);

    case SCRIPT_PUBKEY_PAY_TO_SCRIPT_HASH:
        return toBase58Check(payee.second, addressVersions[1]);

    default:
        return "";
    }
}

/*
 * class Script
*/
Script::Script(type_t type, unsigned int minsigs, const std::vector<bytes_t>& pubkeys, const std::vector<bytes_t>& sigs)
    : type_(type), minsigs_(minsigs), pubkeys_(pubkeys), sigs_(sigs)
{
    if (type == PAY_TO_PUBKEY_HASH || type == PAY_TO_PUBKEY) {
        if (minsigs != 1 || pubkeys.size() != 1 || sigs.size() > 1) {
            throw std::runtime_error("Invalid parameters.");
        }
        if (sigs_.empty()) { sigs_.push_back(bytes_t()); }
        hash_ = ripemd160(sha256(pubkeys[0]));
    }

    if (type == PAY_TO_MULTISIG_SCRIPT_HASH) {
        if (minsigs == 0 || pubkeys.size() > 16 || minsigs > pubkeys.size() || sigs.size() > pubkeys.size()) {
            throw std::runtime_error("Invalid parameters.");
        }
        if (sigs_.empty()) { sigs_.assign(pubkeys.size(), bytes_t()); }
        uchar_vector redeemscript;
        redeemscript.push_back(minsigs + 0x50);
        for (auto& pubkey: pubkeys_) {
           redeemscript += opPushData(pubkey.size());
           redeemscript += pubkey;
        }
        redeemscript.push_back(pubkeys_.size() + 0x50);
        redeemscript.push_back(0xae); // OP_CHECKMULTISIG
        redeemscript_ = redeemscript;
        hash_ = ripemd160(sha256(redeemscript));
    }

}

Script::Script(const bytes_t& txinscript)
{
    std::vector<bytes_t> objects;
    unsigned int pos = 0;
    while (pos < txinscript.size()) {
        std::size_t size = getDataLength(txinscript, pos);
        if (pos + size > txinscript.size()) {
            throw std::runtime_error("Push operation exceeds data size.");
        }
        objects.push_back(bytes_t(txinscript.begin() + pos, txinscript.begin() + pos + size));
        pos += size;
    }

    if (objects.size() == 2) {
        type_ = PAY_TO_PUBKEY_HASH;
        pubkeys_.push_back(objects[1]);
        minsigs_ = 1;
        sigs_.push_back(objects[0]);
        hash_ = ripemd160(sha256(pubkeys_[0]));
    }
    else if (objects.size() >= 3 && objects[0].empty()) {
        // Parse redeemscript
        bytes_t redeemscript = objects.back();

        if (redeemscript.size() < 3) {
            throw std::runtime_error("Redeem script is too short.");
        }

        // size opcode is offset by 0x50
        unsigned char minsigs = redeemscript[0];
        if (minsigs < 0x51 || minsigs > 0x60) {
            throw std::runtime_error("Invalid signature minimum.");
        }

        unsigned char numkeys = 0x50;
        unsigned int pos = 1;
        while (true) {
            unsigned char byte = redeemscript[pos++];
            if (pos >= redeemscript.size()) {
                throw std::runtime_error("Script terminates prematurely.");
            }
            if (byte >= 0x51 && byte <= 0x60) {
                // Interpret byte as signature counter.
                if (byte != numkeys) {
                    throw std::runtime_error("Invalid signature count.");
                }
                if (numkeys < minsigs) {
                    throw std::runtime_error("The required signature minimum exceeds the number of keys.");
                }
                // Redeemscript must terminate with OP_CHECKMULTISIG
                if (redeemscript[pos++] != 0xae || pos > redeemscript.size()) {
                    throw std::runtime_error("Invalid script termination.");
                }
                break;
            }
            // Interpret byte as pubkey size
            if (byte > 0x4b || pos + byte > redeemscript.size()) {
                std::stringstream err;
                err << "Invalid OP at byte " << pos - 1 << ".";
                throw std::runtime_error(err.str());
            }
            numkeys++;
            if (numkeys > 0x60) {
                throw std::runtime_error("Public key maximum of 16 exceeded.");
            }
            pubkeys_.push_back(bytes_t(redeemscript.begin() + pos, redeemscript.begin() + pos + byte));
            pos += byte;
        }

        type_ = PAY_TO_MULTISIG_SCRIPT_HASH;
        minsigs_ = minsigs - 0x50;
        sigs_.assign(objects.begin() + 1, objects.end() - 1);
        redeemscript_ = redeemscript;
        hash_ = ripemd160(sha256(redeemscript));
    }
    else {
        type_ = UNKNOWN;
    }
}

bytes_t Script::txinscript(sigtype_t sigtype) const
{
    uchar_vector script;

    if (sigtype != SIGN) {
        if (type_ == PAY_TO_MULTISIG_SCRIPT_HASH) {
            script.push_back(0x00);
        }

        for (auto& sig: sigs_) {
            if (!sig.empty() || sigtype == EDIT) {
                script += opPushData(sig.size());
                script += sig;
            }
        }
    }

    if (type_ == PAY_TO_PUBKEY_HASH) {
        if (sigtype != SIGN) {
            script += opPushData(pubkeys_[0].size());
            script += pubkeys_[0];
        }
        else {
            script += txoutscript();
        }
    }
    else if (type_ == PAY_TO_MULTISIG_SCRIPT_HASH) {
        if (sigtype != SIGN) {
            script += opPushData(redeemscript_.size());
        }
        script += redeemscript_;
    }

    return script;
}

bytes_t Script::txoutscript() const
{
    uchar_vector script;

    if (type_ == PAY_TO_PUBKEY_HASH) {
        script.push_back(0x76); // OP_PUSHDATA1
        script.push_back(0xa9); // OP_HASH160
        script.push_back(0x14); // push a 20 byte hash
        script += hash_;
        script.push_back(0x88); // OP_EQUALVERIFY
        script.push_back(0xac); // OP_CHECKSIG
    }
    else if (type_ == PAY_TO_MULTISIG_SCRIPT_HASH) {
        script.push_back(0xa9); // OP_HASH160
        script.push_back(0x14); // push a 20 byte hash
        script += hash_;
        script.push_back(0x87); // OP_EQUAL
    }
    else if (type_ == PAY_TO_PUBKEY) {
        script += opPushData(pubkeys_[0].size());
        script += pubkeys_[0];
        script.push_back(0xac); // OP_CHECKSIG
    }

    return script;
}

unsigned int Script::sigsneeded() const
{
    unsigned int sigsneeded = minsigs_;
    for (auto& sig: sigs_) {
        if (!sig.empty()) {
            sigsneeded--;
        }
        if (sigsneeded == 0) break;
    }

    return sigsneeded;
}

std::vector<bytes_t> Script::missingsigs() const
{
    std::vector<bytes_t> missingsigs;

    unsigned int i = 0;
    unsigned int nsigs = 0;
    for (auto& sig: sigs_) {
        if (!sig.empty()) {
            nsigs++;
            if (nsigs > minsigs_) break;
        }
        else {
            missingsigs.push_back(pubkeys_[i]);
        }
        i++;
    }

    return missingsigs;
}

bool Script::addSig(const bytes_t& pubkey, const bytes_t& sig)
{
    unsigned int i = 0;
    unsigned int nsigs = 0;
    for (auto& sig_: sigs_) {
        if (!sig_.empty()) {
            nsigs++;
            if (nsigs > minsigs_) return false;
        }
        else if (pubkeys_[i] == pubkey) {
            sig_ = sig;
            return true;
        }
        i++;
    }

    return false;
}

void Script::clearSigs()
{
    sigs_.clear();
    for (unsigned int i = 0; i < pubkeys_.size(); i++) { sigs_.push_back(bytes_t()); }
}

}
}
<commit_msg>OP_PUSHDATA2 and OP_PUSHDATA4 endianness issues<commit_after>///////////////////////////////////////////////////////////////////////////////
//
// CoinQ_script.cpp
//
// Copyright (c) 2013 Eric Lombrozo
//
// All Rights Reserved.

#include "CoinQ_script.h"

// TODO: Move all base58check stuff to another module.
#include <Base58Check.h>

namespace CoinQ {
namespace Script {

uchar_vector opPushData(uint32_t nBytes)
{
    uchar_vector rval;
    if (nBytes <= 0x4b) {
        rval.push_back((unsigned char)nBytes);
    }
    else if (nBytes <= 0xff) {
        rval.push_back(0x4c);
        rval.push_back((unsigned char)nBytes);
    }
    else if (nBytes <= 0xffff) {
        rval.push_back(0x4d);
        rval.push_back((unsigned char)(nBytes & 0xff));
        rval.push_back((unsigned char)(nBytes >> 8));
    }
    else {
        rval.push_back(0x4e);
        rval.push_back((unsigned char)(nBytes & 0xff));
        rval.push_back((unsigned char)((nBytes >> 8) & 0xff));
        rval.push_back((unsigned char)((nBytes >> 16) & 0xff));
        rval.push_back((unsigned char)(nBytes >> 24));
    }

    return rval;
}

uint32_t getDataLength(const uchar_vector& script, uint& pos)
{
    if (pos >= script.size()) {
        throw std::runtime_error("Script pos past end.");
    }

    unsigned char op = script[pos++];
    if (op <= 0x4b) {
        return (uint32_t)op;
    }
    else if (op == 0x4c) {
        if (pos >= script.size()) {
            throw std::runtime_error("Script pos past end.");
        }
        return (uint32_t)script[pos++];
    }
    else if (op == 0x4d) {
        if (pos + 1 >= script.size()) {
            throw std::runtime_error("Script pos past end.");
        }
        uint32_t rval = (uint32_t)script[pos++];
        rval         += (uint32_t)script[pos++] << 8;;
        return rval;
    }
    else if (op == 0x4e) {
        if (pos + 3 >= script.size()) {
            throw std::runtime_error("Script pos past end.");
        }
        uint32_t rval = (uint32_t)script[pos++];
        rval         += (uint32_t)script[pos++] << 8;
        rval         += (uint32_t)script[pos++] << 16;
        rval         += (uint32_t)script[pos++] << 24;
        return rval;
    }
    else {
        throw std::runtime_error("Operation is not push data.");
    }
}

payee_t getScriptPubKeyPayee(const uchar_vector& scriptPubKey)
{
    if (scriptPubKey.size()   == 25 &&
        scriptPubKey[0]       == 0x76 &&
        scriptPubKey[1]       == 0xa9 &&
        scriptPubKey[2]       == 0x14 &&
        scriptPubKey[23]      == 0x88 &&
        scriptPubKey[24]      == 0xac)
    {
        return std::make_pair(SCRIPT_PUBKEY_PAY_TO_PUBKEY_HASH, uchar_vector(scriptPubKey.begin() + 3, scriptPubKey.begin() + 23));
    }

    if (scriptPubKey.size()   == 23 &&
        scriptPubKey[0]       == 0xa9 &&
        scriptPubKey[1]       == 0x14 &&
        scriptPubKey[22]      == 0x87)
    {
        return std::make_pair(SCRIPT_PUBKEY_PAY_TO_SCRIPT_HASH, uchar_vector(scriptPubKey.begin() + 2, scriptPubKey.begin() + 22));
    }

    uint pos = 0;
    uint32_t dataLength = getDataLength(scriptPubKey, pos);
    if (pos + dataLength + 1== scriptPubKey.size() &&
        scriptPubKey[pos + dataLength + 1] == 0xac)
    {
        return std::make_pair(SCRIPT_PUBKEY_PAY_TO_PUBKEY, uchar_vector(scriptPubKey.begin() + pos, scriptPubKey.begin() + pos + dataLength));
    }

    if (scriptPubKey.size() == 0)
    {
        return std::make_pair(SCRIPT_PUBKEY_EMPTY_SCRIPT, uchar_vector());
    }

    return std::make_pair(SCRIPT_PUBKEY_UNKNOWN_TYPE, uchar_vector());
}

uchar_vector getTxOutScriptForAddress(const std::string& address, const unsigned char addressVersions[])
{
    uchar_vector hash;
    unsigned int version;
    if (!fromBase58Check(address, hash, version)) {
        throw std::runtime_error("Invalid address checksum.");
    }

    uchar_vector script;
    if (version == addressVersions[0]) {
        if (hash.size() != 20) {
            throw std::runtime_error("Invalid pubkey hash length.");
        }
        script.push_back(0x76); // OP_PUSHDATA1
        script.push_back(0xa9); // OP_HASH160
        script.push_back(0x14); // push a 20 byte hash
        script += hash;
        script.push_back(0x88); // OP_EQUALVERIFY
        script.push_back(0xac); // OP_CHECKSIG
    }
    else if (version == addressVersions[1]) {
        if (hash.size() != 20) {
            throw std::runtime_error("Invalid script hash length.");
        }
        script.push_back(0xa9); // OP_HASH160
        script.push_back(0x14); // push a 20 byte hash
        script += hash;
        script.push_back(0x87); // OP_EQUAL
    }
    else {
        throw std::runtime_error("Invalid address version.");
    }

    return script;
}

std::string getAddressForTxOutScript(const bytes_t& txoutscript, const unsigned char addressVersions[])
{
    payee_t payee = getScriptPubKeyPayee(txoutscript);
    switch (payee.first) {
    case SCRIPT_PUBKEY_PAY_TO_PUBKEY_HASH:
        return toBase58Check(payee.second, addressVersions[0]);

    case SCRIPT_PUBKEY_PAY_TO_SCRIPT_HASH:
        return toBase58Check(payee.second, addressVersions[1]);

    default:
        return "";
    }
}

/*
 * class Script
*/
Script::Script(type_t type, unsigned int minsigs, const std::vector<bytes_t>& pubkeys, const std::vector<bytes_t>& sigs)
    : type_(type), minsigs_(minsigs), pubkeys_(pubkeys), sigs_(sigs)
{
    if (type == PAY_TO_PUBKEY_HASH || type == PAY_TO_PUBKEY) {
        if (minsigs != 1 || pubkeys.size() != 1 || sigs.size() > 1) {
            throw std::runtime_error("Invalid parameters.");
        }
        if (sigs_.empty()) { sigs_.push_back(bytes_t()); }
        hash_ = ripemd160(sha256(pubkeys[0]));
    }

    if (type == PAY_TO_MULTISIG_SCRIPT_HASH) {
        if (minsigs == 0 || pubkeys.size() > 16 || minsigs > pubkeys.size() || sigs.size() > pubkeys.size()) {
            throw std::runtime_error("Invalid parameters.");
        }
        if (sigs_.empty()) { sigs_.assign(pubkeys.size(), bytes_t()); }
        uchar_vector redeemscript;
        redeemscript.push_back(minsigs + 0x50);
        for (auto& pubkey: pubkeys_) {
           redeemscript += opPushData(pubkey.size());
           redeemscript += pubkey;
        }
        redeemscript.push_back(pubkeys_.size() + 0x50);
        redeemscript.push_back(0xae); // OP_CHECKMULTISIG
        redeemscript_ = redeemscript;
        hash_ = ripemd160(sha256(redeemscript));
    }

}

Script::Script(const bytes_t& txinscript)
{
    std::vector<bytes_t> objects;
    unsigned int pos = 0;
    while (pos < txinscript.size()) {
        std::size_t size = getDataLength(txinscript, pos);
        if (pos + size > txinscript.size()) {
            throw std::runtime_error("Push operation exceeds data size.");
        }
        objects.push_back(bytes_t(txinscript.begin() + pos, txinscript.begin() + pos + size));
        pos += size;
    }

    if (objects.size() == 2) {
        type_ = PAY_TO_PUBKEY_HASH;
        pubkeys_.push_back(objects[1]);
        minsigs_ = 1;
        sigs_.push_back(objects[0]);
        hash_ = ripemd160(sha256(pubkeys_[0]));
    }
    else if (objects.size() >= 3 && objects[0].empty()) {
        // Parse redeemscript
        bytes_t redeemscript = objects.back();

        if (redeemscript.size() < 3) {
            throw std::runtime_error("Redeem script is too short.");
        }

        // size opcode is offset by 0x50
        unsigned char minsigs = redeemscript[0];
        if (minsigs < 0x51 || minsigs > 0x60) {
            throw std::runtime_error("Invalid signature minimum.");
        }

        unsigned char numkeys = 0x50;
        unsigned int pos = 1;
        while (true) {
            unsigned char byte = redeemscript[pos++];
            if (pos >= redeemscript.size()) {
                throw std::runtime_error("Script terminates prematurely.");
            }
            if (byte >= 0x51 && byte <= 0x60) {
                // Interpret byte as signature counter.
                if (byte != numkeys) {
                    throw std::runtime_error("Invalid signature count.");
                }
                if (numkeys < minsigs) {
                    throw std::runtime_error("The required signature minimum exceeds the number of keys.");
                }
                // Redeemscript must terminate with OP_CHECKMULTISIG
                if (redeemscript[pos++] != 0xae || pos > redeemscript.size()) {
                    throw std::runtime_error("Invalid script termination.");
                }
                break;
            }
            // Interpret byte as pubkey size
            if (byte > 0x4b || pos + byte > redeemscript.size()) {
                std::stringstream err;
                err << "Invalid OP at byte " << pos - 1 << ".";
                throw std::runtime_error(err.str());
            }
            numkeys++;
            if (numkeys > 0x60) {
                throw std::runtime_error("Public key maximum of 16 exceeded.");
            }
            pubkeys_.push_back(bytes_t(redeemscript.begin() + pos, redeemscript.begin() + pos + byte));
            pos += byte;
        }

        type_ = PAY_TO_MULTISIG_SCRIPT_HASH;
        minsigs_ = minsigs - 0x50;
        sigs_.assign(objects.begin() + 1, objects.end() - 1);
        redeemscript_ = redeemscript;
        hash_ = ripemd160(sha256(redeemscript));
    }
    else {
        type_ = UNKNOWN;
    }
}

bytes_t Script::txinscript(sigtype_t sigtype) const
{
    uchar_vector script;

    if (sigtype != SIGN) {
        if (type_ == PAY_TO_MULTISIG_SCRIPT_HASH) {
            script.push_back(0x00);
        }

        for (auto& sig: sigs_) {
            if (!sig.empty() || sigtype == EDIT) {
                script += opPushData(sig.size());
                script += sig;
            }
        }
    }

    if (type_ == PAY_TO_PUBKEY_HASH) {
        if (sigtype != SIGN) {
            script += opPushData(pubkeys_[0].size());
            script += pubkeys_[0];
        }
        else {
            script += txoutscript();
        }
    }
    else if (type_ == PAY_TO_MULTISIG_SCRIPT_HASH) {
        if (sigtype != SIGN) {
            script += opPushData(redeemscript_.size());
        }
        script += redeemscript_;
    }

    return script;
}

bytes_t Script::txoutscript() const
{
    uchar_vector script;

    if (type_ == PAY_TO_PUBKEY_HASH) {
        script.push_back(0x76); // OP_PUSHDATA1
        script.push_back(0xa9); // OP_HASH160
        script.push_back(0x14); // push a 20 byte hash
        script += hash_;
        script.push_back(0x88); // OP_EQUALVERIFY
        script.push_back(0xac); // OP_CHECKSIG
    }
    else if (type_ == PAY_TO_MULTISIG_SCRIPT_HASH) {
        script.push_back(0xa9); // OP_HASH160
        script.push_back(0x14); // push a 20 byte hash
        script += hash_;
        script.push_back(0x87); // OP_EQUAL
    }
    else if (type_ == PAY_TO_PUBKEY) {
        script += opPushData(pubkeys_[0].size());
        script += pubkeys_[0];
        script.push_back(0xac); // OP_CHECKSIG
    }

    return script;
}

unsigned int Script::sigsneeded() const
{
    unsigned int sigsneeded = minsigs_;
    for (auto& sig: sigs_) {
        if (!sig.empty()) {
            sigsneeded--;
        }
        if (sigsneeded == 0) break;
    }

    return sigsneeded;
}

std::vector<bytes_t> Script::missingsigs() const
{
    std::vector<bytes_t> missingsigs;

    unsigned int i = 0;
    unsigned int nsigs = 0;
    for (auto& sig: sigs_) {
        if (!sig.empty()) {
            nsigs++;
            if (nsigs > minsigs_) break;
        }
        else {
            missingsigs.push_back(pubkeys_[i]);
        }
        i++;
    }

    return missingsigs;
}

bool Script::addSig(const bytes_t& pubkey, const bytes_t& sig)
{
    unsigned int i = 0;
    unsigned int nsigs = 0;
    for (auto& sig_: sigs_) {
        if (!sig_.empty()) {
            nsigs++;
            if (nsigs > minsigs_) return false;
        }
        else if (pubkeys_[i] == pubkey) {
            sig_ = sig;
            return true;
        }
        i++;
    }

    return false;
}

void Script::clearSigs()
{
    sigs_.clear();
    for (unsigned int i = 0; i < pubkeys_.size(); i++) { sigs_.push_back(bytes_t()); }
}

}
}
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2021.  All rights reserved
 */
#include "Stroika/Frameworks/StroikaPreComp.h"

#include <iostream>

#include "Stroika/Foundation/Characters/String2Int.h"
#include "Stroika/Foundation/Characters/ToString.h"
#include "Stroika/Foundation/DataExchange/InternetMediaTypeRegistry.h"
#include "Stroika/Foundation/Execution/CommandLine.h"
#include "Stroika/Foundation/Execution/Module.h"
#include "Stroika/Foundation/Execution/SignalHandlers.h"
#include "Stroika/Foundation/Execution/TimeOutException.h"
#include "Stroika/Foundation/Execution/WaitableEvent.h"
#include "Stroika/Foundation/IO/Network/HTTP/Exception.h"
#include "Stroika/Foundation/IO/Network/HTTP/Headers.h"
#include "Stroika/Foundation/IO/Network/HTTP/Methods.h"
#include "Stroika/Foundation/Streams/TextReader.h"

#include "Stroika/Frameworks/WebServer/ConnectionManager.h"
#include "Stroika/Frameworks/WebServer/FileSystemRouter.h"
#include "Stroika/Frameworks/WebServer/Router.h"

#include "AppVersion.h"

using namespace std;

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::IO::Network;
using namespace Stroika::Foundation::IO::Network::HTTP;
using namespace Stroika::Frameworks::WebServer;

using Characters::String;
using Memory::BLOB;

namespace {
    /*
     *  It's often helpful to structure together, routes, special interceptors, with your connection manager, to package up
     *  all the logic /options for HTTP interface.
     *
     *  This particular organization also makes it easy to save instance variables with the webserver (like a pointer to a handler)
     *  and accesss them from the Route handler functions.
     */
    struct MyWebServer_ {
        const Router      kRouter_;
        ConnectionManager fConnectionMgr_;
        MyWebServer_ (uint16_t portNumber)
            : kRouter_
        {
            Sequence<Route>
            {
                Route{MethodsRegEx::kGet, L""_RegEx, DefaultPage_},
                    Route{MethodsRegEx::kPost, L"SetAppState"_RegEx, SetAppState_},
                    Route{MethodsRegEx::kGet, L"FRED"_RegEx, [] (Request*, Response* response) {
                              response->write (L"FRED");
                              response->SetContentType (DataExchange::InternetMediaTypes::kText_PLAIN);
                          }},
                    Route
                {
                    MethodsRegEx::kGet,
                        L"Files/.*"_RegEx,
                        FileSystemRouter{Execution::GetEXEDir () / L"html", L"Files"_k, Sequence<String>{L"index.html"_k}},
                }
            }
        }
#if __cpp_designated_initializers
        , fConnectionMgr_
        {
            SocketAddresses (InternetAddresses_Any (), portNumber), kRouter_, ConnectionManager::Options { .fBindFlags = Socket::BindFlags{}, .fServerHeader = L"Stroika-Sample-WebServer/"_k + AppVersion::kVersion.AsMajorMinorString () }
        }
#else
        , fConnectionMgr_
        {
            SocketAddresses (InternetAddresses_Any (), portNumber), kRouter_, ConnectionManager::Options { {}, {}, Socket::BindFlags{}, L"Stroika-Sample-WebServer/"_k + AppVersion::kVersion.AsMajorMinorString () }
        }
#endif
        {
        }
        // Can declare arguments as Request*,Response*
        static void DefaultPage_ (Request*, Response* response)
        {
            response->writeln (L"");
            response->writeln (L"
Hi Mom
");
            response->writeln (L"
");
            response->writeln (L"Run the service (under the debugger if you wish)");
            response->writeln (L"
curl http://localhost:8080/ OR
");
            response->writeln (L"
curl http://localhost:8080/FRED OR      (to see error handling)
");
            response->writeln (L"
curl -H \"Content-Type: application/json\" -X POST -d '{\"AppState\":\"Start\"}' http://localhost:8080/SetAppState
");
            response->writeln (L"
curl http://localhost:8080/Files/index.html -v
");
            response->writeln (L"
");
            response->writeln (L"");

            response->SetContentType (DataExchange::InternetMediaTypes::kHTML);
        }
        // Can declare arguments as Message* message
        static void SetAppState_ (Message* message)
        {
            String argsAsString = Streams::TextReader::New (message->PeekRequest ()->GetBody ()).ReadAll ();
            message->PeekResponse ()->writeln (L"
Hi SetAppState (" + argsAsString.As<wstring> () + L")
");
            message->PeekResponse ()->SetContentType (DataExchange::InternetMediaTypes::kHTML);
        }
    };
}

int main (int argc, const char* argv[])
{
    Debug::TraceContextBumper                            ctx{Stroika_Foundation_Debug_OptionalizeTraceArgs (L"main", L"argv=%s", Characters::ToString (vector<const char*>{argv, argv + argc}).c_str ())};
    Execution::SignalHandlerRegistry::SafeSignalsManager safeSignals;
#if qPlatform_POSIX
    Execution::SignalHandlerRegistry::Get ().SetSignalHandlers (SIGPIPE, Execution::SignalHandlerRegistry::kIGNORED);
#endif
    uint16_t                  portNumber = 8080;
    Time::DurationSecondsType quitAfter  = numeric_limits<Time::DurationSecondsType>::max ();

    Sequence<String> args = Execution::ParseCommandLine (argc, argv);
    for (auto argi = args.begin (); argi != args.end (); ++argi) {
        if (Execution::MatchesCommandLineArgument (*argi, L"port")) {
            ++argi;
            if (argi != args.end ()) {
                portNumber = Characters::String2Int<uint16_t> (*argi);
            }
            else {
                cerr << "Expected arg to -port" << endl;
                return EXIT_FAILURE;
            }
        }
        else if (Execution::MatchesCommandLineArgument (*argi, L"quit-after")) {
            ++argi;
            if (argi != args.end ()) {
                quitAfter = Characters::String2Float<Time::DurationSecondsType> (*argi);
            }
            else {
                cerr << "Expected arg to -quit-after" << endl;
                return EXIT_FAILURE;
            }
        }
    }

    try {
        MyWebServer_ myWebServer{portNumber};        // listen and dispatch while this object exists
        Execution::WaitableEvent{}.Wait (quitAfter); // wait forever - til user hits ctrl-c
    }
    catch (const Execution::TimeOutException&) {
        cerr << "Timed out - so - exiting..." << endl;
        return EXIT_SUCCESS;
    }
    catch (...) {
        cerr << "Error encountered: " << Characters::ToString (current_exception ()).AsNarrowSDKString () << " - terminating..." << endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}
<commit_msg>handle !__cpp_designated_initializers case<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2021.  All rights reserved
 */
#include "Stroika/Frameworks/StroikaPreComp.h"

#include <iostream>

#include "Stroika/Foundation/Characters/String2Int.h"
#include "Stroika/Foundation/Characters/ToString.h"
#include "Stroika/Foundation/DataExchange/InternetMediaTypeRegistry.h"
#include "Stroika/Foundation/Execution/CommandLine.h"
#include "Stroika/Foundation/Execution/Module.h"
#include "Stroika/Foundation/Execution/SignalHandlers.h"
#include "Stroika/Foundation/Execution/TimeOutException.h"
#include "Stroika/Foundation/Execution/WaitableEvent.h"
#include "Stroika/Foundation/IO/Network/HTTP/Exception.h"
#include "Stroika/Foundation/IO/Network/HTTP/Headers.h"
#include "Stroika/Foundation/IO/Network/HTTP/Methods.h"
#include "Stroika/Foundation/Streams/TextReader.h"

#include "Stroika/Frameworks/WebServer/ConnectionManager.h"
#include "Stroika/Frameworks/WebServer/FileSystemRouter.h"
#include "Stroika/Frameworks/WebServer/Router.h"

#include "AppVersion.h"

using namespace std;

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::IO::Network;
using namespace Stroika::Foundation::IO::Network::HTTP;
using namespace Stroika::Frameworks::WebServer;

using Characters::String;
using Memory::BLOB;

namespace {
    /*
     *  It's often helpful to structure together, routes, special interceptors, with your connection manager, to package up
     *  all the logic /options for HTTP interface.
     *
     *  This particular organization also makes it easy to save instance variables with the webserver (like a pointer to a handler)
     *  and accesss them from the Route handler functions.
     */
    struct MyWebServer_ {
        const Router      kRouter_;
        ConnectionManager fConnectionMgr_;
        MyWebServer_ (uint16_t portNumber)
            : kRouter_
        {
            Sequence<Route>
            {
                Route{MethodsRegEx::kGet, L""_RegEx, DefaultPage_},
                    Route{MethodsRegEx::kPost, L"SetAppState"_RegEx, SetAppState_},
                    Route{MethodsRegEx::kGet, L"FRED"_RegEx, [] (Request*, Response* response) {
                              response->write (L"FRED");
                              response->SetContentType (DataExchange::InternetMediaTypes::kText_PLAIN);
                          }},
                    Route
                {
                    MethodsRegEx::kGet,
                        L"Files/.*"_RegEx,
                        FileSystemRouter{Execution::GetEXEDir () / L"html", L"Files"_k, Sequence<String>{L"index.html"_k}},
                }
            }
        }
#if __cpp_designated_initializers
        , fConnectionMgr_
        {
            SocketAddresses (InternetAddresses_Any (), portNumber), kRouter_, ConnectionManager::Options { .fBindFlags = Socket::BindFlags{}, .fServerHeader = L"Stroika-Sample-WebServer/"_k + AppVersion::kVersion.AsMajorMinorString () }
        }
#else
        , fConnectionMgr_
        {
            SocketAddresses (InternetAddresses_Any (), portNumber), kRouter_, ConnectionManager::Options { nullopt, nullopt, nullopt, Socket::BindFlags{}, L"Stroika-Sample-WebServer/"_k + AppVersion::kVersion.AsMajorMinorString () }
        }
#endif
        {
        }
        // Can declare arguments as Request*,Response*
        static void DefaultPage_ (Request*, Response* response)
        {
            response->writeln (L"");
            response->writeln (L"
Hi Mom
");
            response->writeln (L"
");
            response->writeln (L"Run the service (under the debugger if you wish)");
            response->writeln (L"
curl http://localhost:8080/ OR
");
            response->writeln (L"
curl http://localhost:8080/FRED OR      (to see error handling)
");
            response->writeln (L"
curl -H \"Content-Type: application/json\" -X POST -d '{\"AppState\":\"Start\"}' http://localhost:8080/SetAppState
");
            response->writeln (L"
curl http://localhost:8080/Files/index.html -v
");
            response->writeln (L"
");
            response->writeln (L"");

            response->SetContentType (DataExchange::InternetMediaTypes::kHTML);
        }
        // Can declare arguments as Message* message
        static void SetAppState_ (Message* message)
        {
            String argsAsString = Streams::TextReader::New (message->PeekRequest ()->GetBody ()).ReadAll ();
            message->PeekResponse ()->writeln (L"
Hi SetAppState (" + argsAsString.As<wstring> () + L")
");
            message->PeekResponse ()->SetContentType (DataExchange::InternetMediaTypes::kHTML);
        }
    };
}

int main (int argc, const char* argv[])
{
    Debug::TraceContextBumper                            ctx{Stroika_Foundation_Debug_OptionalizeTraceArgs (L"main", L"argv=%s", Characters::ToString (vector<const char*>{argv, argv + argc}).c_str ())};
    Execution::SignalHandlerRegistry::SafeSignalsManager safeSignals;
#if qPlatform_POSIX
    Execution::SignalHandlerRegistry::Get ().SetSignalHandlers (SIGPIPE, Execution::SignalHandlerRegistry::kIGNORED);
#endif
    uint16_t                  portNumber = 8080;
    Time::DurationSecondsType quitAfter  = numeric_limits<Time::DurationSecondsType>::max ();

    Sequence<String> args = Execution::ParseCommandLine (argc, argv);
    for (auto argi = args.begin (); argi != args.end (); ++argi) {
        if (Execution::MatchesCommandLineArgument (*argi, L"port")) {
            ++argi;
            if (argi != args.end ()) {
                portNumber = Characters::String2Int<uint16_t> (*argi);
            }
            else {
                cerr << "Expected arg to -port" << endl;
                return EXIT_FAILURE;
            }
        }
        else if (Execution::MatchesCommandLineArgument (*argi, L"quit-after")) {
            ++argi;
            if (argi != args.end ()) {
                quitAfter = Characters::String2Float<Time::DurationSecondsType> (*argi);
            }
            else {
                cerr << "Expected arg to -quit-after" << endl;
                return EXIT_FAILURE;
            }
        }
    }

    try {
        MyWebServer_ myWebServer{portNumber};        // listen and dispatch while this object exists
        Execution::WaitableEvent{}.Wait (quitAfter); // wait forever - til user hits ctrl-c
    }
    catch (const Execution::TimeOutException&) {
        cerr << "Timed out - so - exiting..." << endl;
        return EXIT_SUCCESS;
    }
    catch (...) {
        cerr << "Error encountered: " << Characters::ToString (current_exception ()).AsNarrowSDKString () << " - terminating..." << endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}
<|endoftext|>


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

#include "Tests.h"
#include <UnitTest++.h>
#include <MTScheduler.h>

SUITE(StackSizeTests)
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct StandartStackSizeTask
{
	MT_DECLARE_TASK(StandartStackSizeTask, MT::StackRequirements::STANDARD, MT::Color::Blue);

	void Do(MT::FiberContext&)
	{
		byte stackData[28000];
		for (uint32 i = 0; i < MT_ARRAY_SIZE(stackData); i++)
		{
			stackData[i] = 0x0D;
		}
	}
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct ExtendedStackSizeTask
{
	MT_DECLARE_TASK(ExtendedStackSizeTask, MT::StackRequirements::EXTENDED, MT::Color::Red);

	void Do(MT::FiberContext&)
	{
		byte stackData[262144];
		for (uint32 i = 0; i < MT_ARRAY_SIZE(stackData); i++)
		{
			stackData[i] = 0x0D;
		}
	}

};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST(RunStandartTasks)
{
	MT::TaskScheduler scheduler;

	StandartStackSizeTask tasks[100];
	scheduler.RunAsync(MT::TaskGroup::Default(), &tasks[0], MT_ARRAY_SIZE(tasks));
	CHECK(scheduler.WaitAll(1000));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST(RunExtendedTasks)
{
	MT::TaskScheduler scheduler;

	ExtendedStackSizeTask tasks[100];
	scheduler.RunAsync(MT::TaskGroup::Default(), &tasks[0], MT_ARRAY_SIZE(tasks));
	CHECK(scheduler.WaitAll(1000));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST(RunMixedTasks)
{
	MT::TaskScheduler scheduler;

	MT::TaskPool<ExtendedStackSizeTask, 64> extendedTaskPool;
	MT::TaskPool<StandartStackSizeTask, 64> standardTaskPool;

	MT::TaskHandle taskHandles[100];
	for (size_t i = 0; i < MT_ARRAY_SIZE(taskHandles); ++i)
	{
		MT::TaskHandle handle;

		if (i & 1)
		{
			handle = extendedTaskPool.Alloc(ExtendedStackSizeTask());
		} else
		{
			handle = standardTaskPool.Alloc(StandartStackSizeTask());
		}
		taskHandles[i] = handle;
	}

	scheduler.RunAsync(MT::TaskGroup::Default(), &taskHandles[0], MT_ARRAY_SIZE(taskHandles));
	CHECK(scheduler.WaitAll(1000));
}

}
<commit_msg>Fixed OSX tests<commit_after>// The MIT License (MIT)
//
// 	Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev
//
// 	Permission is hereby granted, free of charge, to any person obtaining a copy
// 	of this software and associated documentation files (the "Software"), to deal
// 	in the Software without restriction, including without limitation the rights
// 	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// 	copies of the Software, and to permit persons to whom the Software is
// 	furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
// 	all copies or substantial portions of the Software.
//
// 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// 	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// 	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// 	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// 	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// 	THE SOFTWARE.

#include "Tests.h"
#include <UnitTest++.h>
#include <MTScheduler.h>

SUITE(StackSizeTests)
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct StandartStackSizeTask
{
	MT_DECLARE_TASK(StandartStackSizeTask, MT::StackRequirements::STANDARD, MT::Color::Blue);

	void Do(MT::FiberContext&)
	{
		//byte stackData[28000];

		// Looks like OSX ASAN took too many stack space
		byte stackData[20000];
		for (uint32 i = 0; i < MT_ARRAY_SIZE(stackData); i++)
		{
			stackData[i] = 0x0D;
		}
	}
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct ExtendedStackSizeTask
{
	MT_DECLARE_TASK(ExtendedStackSizeTask, MT::StackRequirements::EXTENDED, MT::Color::Red);

	void Do(MT::FiberContext&)
	{
		byte stackData[262144];
		for (uint32 i = 0; i < MT_ARRAY_SIZE(stackData); i++)
		{
			stackData[i] = 0x0D;
		}
	}

};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST(RunStandartTasks)
{
	MT::TaskScheduler scheduler;

	StandartStackSizeTask tasks[100];
	scheduler.RunAsync(MT::TaskGroup::Default(), &tasks[0], MT_ARRAY_SIZE(tasks));
	CHECK(scheduler.WaitAll(1000));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST(RunExtendedTasks)
{
	MT::TaskScheduler scheduler;

	ExtendedStackSizeTask tasks[100];
	scheduler.RunAsync(MT::TaskGroup::Default(), &tasks[0], MT_ARRAY_SIZE(tasks));
	CHECK(scheduler.WaitAll(1000));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TEST(RunMixedTasks)
{
	MT::TaskScheduler scheduler;

	MT::TaskPool<ExtendedStackSizeTask, 64> extendedTaskPool;
	MT::TaskPool<StandartStackSizeTask, 64> standardTaskPool;

	MT::TaskHandle taskHandles[100];
	for (size_t i = 0; i < MT_ARRAY_SIZE(taskHandles); ++i)
	{
		MT::TaskHandle handle;

		if (i & 1)
		{
			handle = extendedTaskPool.Alloc(ExtendedStackSizeTask());
		} else
		{
			handle = standardTaskPool.Alloc(StandartStackSizeTask());
		}
		taskHandles[i] = handle;
	}

	scheduler.RunAsync(MT::TaskGroup::Default(), &taskHandles[0], MT_ARRAY_SIZE(taskHandles));
	CHECK(scheduler.WaitAll(1000));
}

}
<|endoftext|>


<commit_before>#include "PlayerSession.h"
#include "netstream.h"
#include "gamedefine.h"
#include "GameServer.h"

#ifdef WIN32
#include <Windows.h>
#include <ShellAPI.h>
#else
#include<unistd.h>
#endif // WIN32

bool StartProccess(unsigned long long qwPlayerPoint)
{
#ifdef WIN32
	SHELLEXECUTEINFO shell = { sizeof(shell) };
	shell.fMask = SEE_MASK_NOCLOSEPROCESS;
	shell.lpVerb = "open";
	shell.lpFile = "WebGame.exe";
	char szBuffer[512] = { 0 };
	GetExePath();
	sprintf(szBuffer, "--game_manager_ip %s --game_manager_port %d --player_point %llu",
		GameServer::Instance()->GetServerListenIp().c_str(), GameServer::Instance()->GetServerListenPort(), qwPlayerPoint);
	shell.lpParameters = szBuffer;
	shell.lpDirectory = GetExePath();
	shell.nShow = SW_SHOWNORMAL;
	BOOL ret = ShellExecuteEx(&shell);
	return ret == TRUE;
#else
	char szExePath[512] = { 0 };
	sprintf(szExePath, "%s/WebGame", GetExePath());
	char szServerIp[32] = { 0 };
	sprintf(szServerIp, "%s", GameServer::Instance()->GetServerListenIp().c_str());
	char szServerPort[8] = { 0 };
	sprintf(szServerPort, "%d", GameServer::Instance()->GetServerListenPort());
	char szPlayerPoint[16] = { 0 };
	sprintf(szPlayerPoint, "%llu", qwPlayerPoint);
	char *arg[] = { szExePath, "--game_manager_ip", szServerIp,
		"--game_manager_port", szServerPort, "--player_point", szPlayerPoint, 0 };
	int pid = vfork();
	if (pid < 0)
	{
		LogExe(LogLv_Error, "%s %s %s %s %s %s %s",
			arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6]);
	}

	if (pid == 0)
	{
		execv(arg[0], arg);
		_exit(0);
	}
	else
	{
		return true;
	}
	return false;
#endif // WIN32
}

const static unsigned int g_dwPlayerSessionBuffLen = 64 * 1024;
static char g_pPlayerSessionBuf[g_dwPlayerSessionBuffLen];

CPlayerSession::CPlayerSession()
{
}

CPlayerSession::~CPlayerSession()
{
}

void CPlayerSession::OnConnect(void)
{

}

void CPlayerSession::OnClose(void)
{

}

void CPlayerSession::OnError(UINT32 dwErrorNo)
{
	LogExe(LogLv_Debug, "ip : %s, port : %d, connect addr : %p, error no : %d", GetRemoteIPStr(), GetRemotePort(), (GetConnection()), dwErrorNo);
}

void CPlayerSession::OnRecv(const char* pBuf, UINT32 dwLen)
{
	CNetStream oStream(pBuf, dwLen);
	Protocol::EGameProtocol eProrocol;
	oStream.ReadInt((int&)eProrocol);
	const char* pData = pBuf + sizeof(UINT32);
	dwLen -= sizeof(UINT32);

	switch (eProrocol)
	{
	case Protocol::PLAYER_REQUEST_GAME_MANAGER_INFO:			OnRequestGameManagerInfo(pData, dwLen);	break;
	default:	Assert(0);	break;
	}
}

void CPlayerSession::Release(void)
{
	LogExe(LogLv_Debug, "ip : %s, port : %d, connect addr : %p", GetRemoteIPStr(), GetRemotePort(), GetConnection());
	OnDestroy();

	Init(NULL);
}

void CPlayerSession::OnRequestGameManagerInfo(const char* pBuf, UINT32 dwLen)
{
	CNetStream oStream(pBuf, dwLen);
	stPLAYER_REQUEST_GAME_MANAGER_INFO oPLAYER_REQUEST_GAME_MANAGER_INFO;
	oPLAYER_REQUEST_GAME_MANAGER_INFO.Read(oStream);

	if (StartProccess((unsigned long long)this))
	{
		GameServer::Instance()->AddRequestPlayer(this);
	}
}

void CPlayerSession::OnGameInfo(stGAME_NOTIFY_GAME_MANAGER_INFO & refInfo)
{
	CNetStream oStream(ENetStreamType_Write, g_pPlayerSessionBuf, g_dwPlayerSessionBuffLen);
	oStream.WriteInt(Protocol::GAME_MANAGER_ACK_PLAYER_INFO_RESULT);
	stGAME_MANAGER_ACK_PLAYER_INFO_RESULT oGAME_MANAGER_ACK_PLAYER_INFO_RESULT;
	oGAME_MANAGER_ACK_PLAYER_INFO_RESULT.wPlayerPort = refInfo.wPlayerPort;
	oGAME_MANAGER_ACK_PLAYER_INFO_RESULT.wServerPort = refInfo.wServerPort;
	oGAME_MANAGER_ACK_PLAYER_INFO_RESULT.wSlaveServerPort = refInfo.wSlaveServerPort;

	oGAME_MANAGER_ACK_PLAYER_INFO_RESULT.Write(oStream);
	Send(g_pPlayerSessionBuf, g_dwPlayerSessionBuffLen - oStream.GetDataLength());
}

//////////////////////////////////////////////////////////////////////////
void CWebSocketPlayerSession::Release(void)
{
	LogExe(LogLv_Debug, "ip : %s, port : %d, connect addr : %p", GetRemoteIPStr(), GetRemotePort(), GetConnection());
	OnDestroy();

	Init(NULL);
}

//////////////////////////////////////////////////////////////////////////
FxSession * WebSocketPlayerSessionManager::CreateSession()
{
	FxSession* pSession = m_poolSessions.FetchObj();
	return pSession;
}

bool WebSocketPlayerSessionManager::Init()
{
	return m_poolSessions.Init(64, 64);
}

void WebSocketPlayerSessionManager::Release(FxSession * pSession)
{
	Assert(0);
}

void WebSocketPlayerSessionManager::Release(CWebSocketPlayerSession* pSession)
{
	m_poolSessions.ReleaseObj(pSession);
}

<commit_msg>fix<commit_after>#include "PlayerSession.h"
#include "netstream.h"
#include "gamedefine.h"
#include "GameServer.h"

#ifdef WIN32
#include <Windows.h>
#include <ShellAPI.h>
#else
#include<unistd.h>
#endif // WIN32

bool StartProccess(unsigned long long qwPlayerPoint)
{
#ifdef WIN32
	SHELLEXECUTEINFO shell = { sizeof(shell) };
	shell.fMask = SEE_MASK_NOCLOSEPROCESS;
	shell.lpVerb = "open";
	shell.lpFile = "WebGame.exe";
	char szBuffer[512] = { 0 };
	GetExePath();
	sprintf(szBuffer, "--game_manager_ip %s --game_manager_port %d --player_point %llu",
		GameServer::Instance()->GetServerListenIp().c_str(), GameServer::Instance()->GetServerListenPort(), qwPlayerPoint);
	shell.lpParameters = szBuffer;
	shell.lpDirectory = GetExePath();
	shell.nShow = SW_SHOWNORMAL;
	BOOL ret = ShellExecuteEx(&shell);
	return ret == TRUE;
#else
	char szExePath[512] = { 0 };
	sprintf(szExePath, "%s/WebGame", GetExePath());
	char szServerIp[32] = { 0 };
	sprintf(szServerIp, "%s", GameServer::Instance()->GetServerListenIp().c_str());
	char szServerPort[8] = { 0 };
	sprintf(szServerPort, "%d", GameServer::Instance()->GetServerListenPort());
	char szPlayerPoint[16] = { 0 };
	sprintf(szPlayerPoint, "%llu", qwPlayerPoint);
	char *arg[] = { szExePath, "--game_manager_ip", szServerIp,
		"--game_manager_port", szServerPort, "--player_point", szPlayerPoint, 0 };
	int pid = vfork();
	if (pid < 0)
	{
		LogExe(LogLv_Error, "%s %s %s %s %s %s %s",
			arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6]);
		return false;
	}

	if (pid == 0)
	{
		execv(arg[0], arg);
		_exit(0);
	}
	else
	{
		return true;
	}
	return false;
#endif // WIN32
}

const static unsigned int g_dwPlayerSessionBuffLen = 64 * 1024;
static char g_pPlayerSessionBuf[g_dwPlayerSessionBuffLen];

CPlayerSession::CPlayerSession()
{
}

CPlayerSession::~CPlayerSession()
{
}

void CPlayerSession::OnConnect(void)
{

}

void CPlayerSession::OnClose(void)
{

}

void CPlayerSession::OnError(UINT32 dwErrorNo)
{
	LogExe(LogLv_Debug, "ip : %s, port : %d, connect addr : %p, error no : %d", GetRemoteIPStr(), GetRemotePort(), (GetConnection()), dwErrorNo);
}

void CPlayerSession::OnRecv(const char* pBuf, UINT32 dwLen)
{
	CNetStream oStream(pBuf, dwLen);
	Protocol::EGameProtocol eProrocol;
	oStream.ReadInt((int&)eProrocol);
	const char* pData = pBuf + sizeof(UINT32);
	dwLen -= sizeof(UINT32);

	switch (eProrocol)
	{
	case Protocol::PLAYER_REQUEST_GAME_MANAGER_INFO:			OnRequestGameManagerInfo(pData, dwLen);	break;
	default:	Assert(0);	break;
	}
}

void CPlayerSession::Release(void)
{
	LogExe(LogLv_Debug, "ip : %s, port : %d, connect addr : %p", GetRemoteIPStr(), GetRemotePort(), GetConnection());
	OnDestroy();

	Init(NULL);
}

void CPlayerSession::OnRequestGameManagerInfo(const char* pBuf, UINT32 dwLen)
{
	CNetStream oStream(pBuf, dwLen);
	stPLAYER_REQUEST_GAME_MANAGER_INFO oPLAYER_REQUEST_GAME_MANAGER_INFO;
	oPLAYER_REQUEST_GAME_MANAGER_INFO.Read(oStream);

	if (StartProccess((unsigned long long)this))
	{
		GameServer::Instance()->AddRequestPlayer(this);
	}
}

void CPlayerSession::OnGameInfo(stGAME_NOTIFY_GAME_MANAGER_INFO & refInfo)
{
	CNetStream oStream(ENetStreamType_Write, g_pPlayerSessionBuf, g_dwPlayerSessionBuffLen);
	oStream.WriteInt(Protocol::GAME_MANAGER_ACK_PLAYER_INFO_RESULT);
	stGAME_MANAGER_ACK_PLAYER_INFO_RESULT oGAME_MANAGER_ACK_PLAYER_INFO_RESULT;
	oGAME_MANAGER_ACK_PLAYER_INFO_RESULT.wPlayerPort = refInfo.wPlayerPort;
	oGAME_MANAGER_ACK_PLAYER_INFO_RESULT.wServerPort = refInfo.wServerPort;
	oGAME_MANAGER_ACK_PLAYER_INFO_RESULT.wSlaveServerPort = refInfo.wSlaveServerPort;

	oGAME_MANAGER_ACK_PLAYER_INFO_RESULT.Write(oStream);
	Send(g_pPlayerSessionBuf, g_dwPlayerSessionBuffLen - oStream.GetDataLength());
}

//////////////////////////////////////////////////////////////////////////
void CWebSocketPlayerSession::Release(void)
{
	LogExe(LogLv_Debug, "ip : %s, port : %d, connect addr : %p", GetRemoteIPStr(), GetRemotePort(), GetConnection());
	OnDestroy();

	Init(NULL);
}

//////////////////////////////////////////////////////////////////////////
FxSession * WebSocketPlayerSessionManager::CreateSession()
{
	FxSession* pSession = m_poolSessions.FetchObj();
	return pSession;
}

bool WebSocketPlayerSessionManager::Init()
{
	return m_poolSessions.Init(64, 64);
}

void WebSocketPlayerSessionManager::Release(FxSession * pSession)
{
	Assert(0);
}

void WebSocketPlayerSessionManager::Release(CWebSocketPlayerSession* pSession)
{
	m_poolSessions.ReleaseObj(pSession);
}

<|endoftext|>


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

#include <fstream>
#include <setjmp.h>
#include "compiler/cstring.h"
#include "interpreter/driver.h"
#include "interpreter/parser.hh"
#include "interpreter/position.hh"
#include "vm/vm.h"
#include "scanner.h"

namespace clever {

jmp_buf fatal_error;

/**
 * Interpreter constructor
 */
Interpreter::Interpreter(int* argc, char*** argv)
{
}

/**
 * Executes the script
 */
void Interpreter::execute(bool interactive)
{
	m_compiler.getIR();

	VM().run(m_compiler.getIR());

	m_compiler.shutdown();
}

/**
 * Read the file defined in file property
 */
void Driver::readFile(std::string& source) const
{
	std::string line;
	std::fstream filep(m_file->c_str());

	if (!filep) {
		std::cerr << "Couldn't open file " << m_file->str() << std::endl;
		exit(1);
	}
	source = "";

	while (!filep.eof()) {
		getline(filep, line);
		source += line;
		source += '\n';
	}

	filep.clear();
	filep.close();
}

/**
 * Starts the parsing of the supplied file
 */
int Driver::loadFile(const std::string& filename)
{
	ScannerState* new_scanner = new ScannerState;
	Parser parser(*this, *new_scanner, m_compiler);
	std::string& source = new_scanner->getSource();

	m_is_file = true;
	m_file = CSTRING(filename);

	readFile(source);

	m_scanners.push(new_scanner);

	const unsigned char* s = reinterpret_cast<const unsigned char*>(source.c_str());

	m_scanners.top()->set_cursor(s);
	m_scanners.top()->set_limit(s + source.length());

	// Bison debug option
	parser.set_debug_level(m_trace_parsing);

	int result = parser.parse();

	delete new_scanner;
	m_scanners.pop();

	return result;
}

/**
 * Starts the parsing of the supplied string
 */
int Driver::loadStr(const std::string& code, bool importStd)
{
	ScannerState *new_scanner = new ScannerState;
	Parser parser(*this, *new_scanner, m_compiler);
	std::string& source = new_scanner->getSource();

	if (importStd) {
		source += std::string("import std.*;");
	}
	source += code;

	/* Set the source code to scanner read it */
	m_scanners.push(new_scanner);

	const unsigned char* s = reinterpret_cast<const unsigned char*>(source.c_str());
	m_scanners.top()->set_cursor(s);
	m_scanners.top()->set_limit(s + source.length());

	/* Bison debug option */
	parser.set_debug_level(m_trace_parsing);

	int result = parser.parse();

	delete new_scanner;
	m_scanners.pop();

	return result;
}

/**
 * Prints an error message and exit
 */
void Driver::error(const location& location, const std::string& msg) const
{
	position last = location.end - 1;

	if (last.filename) {
		std::cerr << msg << " in " << *last.filename <<
			" on line " << last.line << std::endl;
	} else {
		std::cerr << msg << " on line " << last.line << std::endl;
	}
	exit(1);
}

/**
 * Prints an error message
 */
void Driver::error(const std::string& message) const
{
	std::cerr << message << std::endl;
}

} // clever
<commit_msg>- Removed unneeded call<commit_after>/**
 * Clever programming language
 * Copyright (c) 2011-2012 Clever Team
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include <fstream>
#include <setjmp.h>
#include "compiler/cstring.h"
#include "interpreter/driver.h"
#include "interpreter/parser.hh"
#include "interpreter/position.hh"
#include "vm/vm.h"
#include "scanner.h"

namespace clever {

jmp_buf fatal_error;

/**
 * Interpreter constructor
 */
Interpreter::Interpreter(int* argc, char*** argv)
{
}

/**
 * Executes the script
 */
void Interpreter::execute(bool interactive)
{
	VM().run(m_compiler.getIR());

	m_compiler.shutdown();
}

/**
 * Read the file defined in file property
 */
void Driver::readFile(std::string& source) const
{
	std::string line;
	std::fstream filep(m_file->c_str());

	if (!filep) {
		std::cerr << "Couldn't open file " << m_file->str() << std::endl;
		exit(1);
	}
	source = "";

	while (!filep.eof()) {
		getline(filep, line);
		source += line;
		source += '\n';
	}

	filep.clear();
	filep.close();
}

/**
 * Starts the parsing of the supplied file
 */
int Driver::loadFile(const std::string& filename)
{
	ScannerState* new_scanner = new ScannerState;
	Parser parser(*this, *new_scanner, m_compiler);
	std::string& source = new_scanner->getSource();

	m_is_file = true;
	m_file = CSTRING(filename);

	readFile(source);

	m_scanners.push(new_scanner);

	const unsigned char* s = reinterpret_cast<const unsigned char*>(source.c_str());

	m_scanners.top()->set_cursor(s);
	m_scanners.top()->set_limit(s + source.length());

	// Bison debug option
	parser.set_debug_level(m_trace_parsing);

	int result = parser.parse();

	delete new_scanner;
	m_scanners.pop();

	return result;
}

/**
 * Starts the parsing of the supplied string
 */
int Driver::loadStr(const std::string& code, bool importStd)
{
	ScannerState *new_scanner = new ScannerState;
	Parser parser(*this, *new_scanner, m_compiler);
	std::string& source = new_scanner->getSource();

	if (importStd) {
		source += std::string("import std.*;");
	}
	source += code;

	/* Set the source code to scanner read it */
	m_scanners.push(new_scanner);

	const unsigned char* s = reinterpret_cast<const unsigned char*>(source.c_str());
	m_scanners.top()->set_cursor(s);
	m_scanners.top()->set_limit(s + source.length());

	/* Bison debug option */
	parser.set_debug_level(m_trace_parsing);

	int result = parser.parse();

	delete new_scanner;
	m_scanners.pop();

	return result;
}

/**
 * Prints an error message and exit
 */
void Driver::error(const location& location, const std::string& msg) const
{
	position last = location.end - 1;

	if (last.filename) {
		std::cerr << msg << " in " << *last.filename <<
			" on line " << last.line << std::endl;
	} else {
		std::cerr << msg << " on line " << last.line << std::endl;
	}
	exit(1);
}

/**
 * Prints an error message
 */
void Driver::error(const std::string& message) const
{
	std::cerr << message << std::endl;
}

} // clever
<|endoftext|>