/*
	This file is part of solidity.
	solidity 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.
	solidity 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 solidity.  If not, see .
*/
// SPDX-License-Identifier: GPL-3.0
/**
 * Component that can generate various useful Yul functions.
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
using namespace solidity;
using namespace solidity::util;
using namespace solidity::frontend;
string YulUtilFunctions::identityFunction()
{
	string functionName = "identity";
	return m_functionCollector.createFunction("identity", [&](vector& _args, vector& _rets) {
		_args.push_back("value");
		_rets.push_back("ret");
		return "ret := value";
	});
}
string YulUtilFunctions::combineExternalFunctionIdFunction()
{
	string functionName = "combine_external_function_id";
	return m_functionCollector.createFunction(functionName, [&]() {
		return Whiskers(R"(
			function (addr, selector) -> combined {
				combined := (or((addr), and(selector, 0xffffffff)))
			}
		)")
		("functionName", functionName)
		("shl32", shiftLeftFunction(32))
		("shl64", shiftLeftFunction(64))
		.render();
	});
}
string YulUtilFunctions::splitExternalFunctionIdFunction()
{
	string functionName = "split_external_function_id";
	return m_functionCollector.createFunction(functionName, [&]() {
		return Whiskers(R"(
			function (combined) -> addr, selector {
				combined := (combined)
				selector := and(combined, 0xffffffff)
				addr := (combined)
			}
		)")
		("functionName", functionName)
		("shr32", shiftRightFunction(32))
		("shr64", shiftRightFunction(64))
		.render();
	});
}
string YulUtilFunctions::copyToMemoryFunction(bool _fromCalldata)
{
	string functionName = "copy_" + string(_fromCalldata ? "calldata" : "memory") + "_to_memory";
	return m_functionCollector.createFunction(functionName, [&]() {
		if (_fromCalldata)
		{
			return Whiskers(R"(
				function (src, dst, length) {
					calldatacopy(dst, src, length)
					// clear end
					mstore(add(dst, length), 0)
				}
			)")
			("functionName", functionName)
			.render();
		}
		else
		{
			return Whiskers(R"(
				function (src, dst, length) {
					let i := 0
					for { } lt(i, length) { i := add(i, 32) }
					{
						mstore(add(dst, i), mload(add(src, i)))
					}
					if gt(i, length)
					{
						// clear end
						mstore(add(dst, length), 0)
					}
				}
			)")
			("functionName", functionName)
			.render();
		}
	});
}
string YulUtilFunctions::copyLiteralToMemoryFunction(string const& _literal)
{
	string functionName = "copy_literal_to_memory_" + util::toHex(util::keccak256(_literal).asBytes());
	return m_functionCollector.createFunction(functionName, [&]() {
		return Whiskers(R"(
			function () -> memPtr {
				memPtr := ()
				(add(memPtr, 32))
			}
			)")
			("functionName", functionName)
			("arrayAllocationFunction", allocateMemoryArrayFunction(*TypeProvider::array(DataLocation::Memory, true)))
			("size", to_string(_literal.size()))
			("storeLiteralInMem", storeLiteralInMemoryFunction(_literal))
			.render();
	});
}
string YulUtilFunctions::storeLiteralInMemoryFunction(string const& _literal)
{
	string functionName = "store_literal_in_memory_" + util::toHex(util::keccak256(_literal).asBytes());
	return m_functionCollector.createFunction(functionName, [&]() {
		size_t words = (_literal.length() + 31) / 32;
		vector