<commit_before>/*
	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 <http://www.gnu.org/licenses/>.
*/
/**
 * @author Christian <c@ethdev.com>
 * @date 2016
 * Solidity inline assembly parser.
 */

#include <libsolidity/inlineasm/AsmParser.h>
#include <ctype.h>
#include <algorithm>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/interface/Exceptions.h>

using namespace std;
using namespace dev;
using namespace dev::solidity;
using namespace dev::solidity::assembly;

shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scanner)
{
	try
	{
		m_scanner = _scanner;
		return make_shared<Block>(parseBlock());
	}
	catch (FatalError const&)
	{
		if (m_errors.empty())
			throw; // Something is weird here, rather throw again.
	}
	return nullptr;
}

assembly::Block Parser::parseBlock()
{
	assembly::Block block = createWithLocation<Block>();
	expectToken(Token::LBrace);
	while (m_scanner->currentToken() != Token::RBrace)
		block.statements.emplace_back(parseStatement());
	block.location.end = endPosition();
	m_scanner->next();
	return block;
}

assembly::Statement Parser::parseStatement()
{
	switch (m_scanner->currentToken())
	{
	case Token::Let:
		return parseVariableDeclaration();
	case Token::Function:
		return parseFunctionDefinition();
	case Token::LBrace:
		return parseBlock();
	case Token::Assign:
	{
		if (m_julia)
			break;
		assembly::Assignment assignment = createWithLocation<assembly::Assignment>();
		m_scanner->next();
		expectToken(Token::Colon);
		assignment.variableName.location = location();
		assignment.variableName.name = m_scanner->currentLiteral();
		if (!m_julia && instructions().count(assignment.variableName.name))
			fatalParserError("Identifier expected, got instruction name.");
		assignment.location.end = endPosition();
		expectToken(Token::Identifier);
		return assignment;
	}
	case Token::Return: // opcode
	case Token::Byte: // opcode
	case Token::Address: // opcode
	default:
		break;
	}
	// Options left:
	// Simple instruction (might turn into functional),
	// literal,
	// identifier (might turn into label or functional assignment)
	Statement statement(parseElementaryOperation(false));
	switch (m_scanner->currentToken())
	{
	case Token::LParen:
		return parseFunctionalInstruction(std::move(statement));
	case Token::Colon:
	{
		if (statement.type() != typeid(assembly::Identifier))
			fatalParserError("Label name / variable name must precede \":\".");
		assembly::Identifier const& identifier = boost::get<assembly::Identifier>(statement);
		m_scanner->next();
		// identifier:=: should be parsed as identifier: =: (i.e. a label),
		// while identifier:= (being followed by a non-colon) as identifier := (assignment).
		if (m_scanner->currentToken() == Token::Assign && m_scanner->peekNextToken() != Token::Colon)
		{
			// functional assignment
			FunctionalAssignment funAss = createWithLocation<FunctionalAssignment>(identifier.location);
			if (!m_julia && instructions().count(identifier.name))
				fatalParserError("Cannot use instruction names for identifier names.");
			m_scanner->next();
			funAss.variableName = identifier;
			funAss.value.reset(new Statement(parseExpression()));
			funAss.location.end = locationOf(*funAss.value).end;
			return funAss;
		}
		else
		{
			// label
			if (m_julia)
				fatalParserError("Labels are not supported.");
			Label label = createWithLocation<Label>(identifier.location);
			label.name = identifier.name;
			return label;
		}
	}
	default:
		if (m_julia)
			fatalParserError("Call or assignment expected.");
		break;
	}
	return statement;
}

assembly::Statement Parser::parseExpression()
{
	Statement operation = parseElementaryOperation(true);
	if (m_scanner->currentToken() == Token::LParen)
		return parseFunctionalInstruction(std::move(operation));
	else
		return operation;
}

std::map<string, dev::solidity::Instruction> const& Parser::instructions()
{
	// Allowed instructions, lowercase names.
	static map<string, dev::solidity::Instruction> s_instructions;
	if (s_instructions.empty())
	{
		for (auto const& instruction: solidity::c_instructions)
		{
			if (
				instruction.second == solidity::Instruction::JUMPDEST ||
				(solidity::Instruction::PUSH1 <= instruction.second && instruction.second <= solidity::Instruction::PUSH32)
			)
				continue;
			string name = instruction.first;
			transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); });
			s_instructions[name] = instruction.second;
		}

		// add alias for suicide
		s_instructions["suicide"] = solidity::Instruction::SELFDESTRUCT;
	}
	return s_instructions;
}

assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
{
	Statement ret;
	switch (m_scanner->currentToken())
	{
	case Token::Identifier:
	case Token::Return:
	case Token::Byte:
	case Token::Address:
	{
		string literal;
		if (m_scanner->currentToken() == Token::Return)
			literal = "return";
		else if (m_scanner->currentToken() == Token::Byte)
			literal = "byte";
		else if (m_scanner->currentToken() == Token::Address)
			literal = "address";
		else
			literal = m_scanner->currentLiteral();
		// first search the set of instructions.
		if (!m_julia && instructions().count(literal))
		{
			dev::solidity::Instruction const& instr = instructions().at(literal);
			if (_onlySinglePusher)
			{
				InstructionInfo info = dev::solidity::instructionInfo(instr);
				if (info.ret != 1)
					fatalParserError("Instruction " + info.name + " not allowed in this context.");
			}
			ret = Instruction{location(), instr};
		}
		else
			ret = Identifier{location(), literal};
		m_scanner->next();
		break;
	}
	case Token::StringLiteral:
	case Token::Number:
	case Token::TrueLiteral:
	case Token::FalseLiteral:
	{
		LiteralKind kind = LiteralKind::Number;
		switch (m_scanner->currentToken())
		{
		case Token::StringLiteral:
			kind = LiteralKind::String;
			break;
		case Token::Number:
			kind = LiteralKind::Number;
			break;
		case Token::TrueLiteral:
		case Token::FalseLiteral:
			kind = LiteralKind::Boolean;
			break;
		default:
			break;
		}

		Literal literal{
			location(),
			kind,
			m_scanner->currentLiteral(),
			""
		};
		m_scanner->next();
		if (m_julia)
		{
			expectToken(Token::Colon);
			literal.location.end = endPosition();
			literal.type = expectAsmIdentifier();
		}
		else if (kind == LiteralKind::Boolean)
			fatalParserError("True and false are not valid literals.");
		ret = std::move(literal);
		break;
	}
	default:
		fatalParserError(
			m_julia ?
			"Literal or identifier expected." :
			"Expected elementary inline assembly operation."
		);
	}
	return ret;
}

assembly::VariableDeclaration Parser::parseVariableDeclaration()
{
	VariableDeclaration varDecl = createWithLocation<VariableDeclaration>();
	expectToken(Token::Let);
	varDecl.variable = parseTypedName();
	expectToken(Token::Colon);
	expectToken(Token::Assign);
	varDecl.value.reset(new Statement(parseExpression()));
	varDecl.location.end = locationOf(*varDecl.value).end;
	return varDecl;
}

assembly::FunctionDefinition Parser::parseFunctionDefinition()
{
	FunctionDefinition funDef = createWithLocation<FunctionDefinition>();
	expectToken(Token::Function);
	funDef.name = expectAsmIdentifier();
	expectToken(Token::LParen);
	while (m_scanner->currentToken() != Token::RParen)
	{
		funDef.arguments.emplace_back(parseTypedName());
		if (m_scanner->currentToken() == Token::RParen)
			break;
		expectToken(Token::Comma);
	}
	expectToken(Token::RParen);
	if (m_scanner->currentToken() == Token::Sub)
	{
		expectToken(Token::Sub);
		expectToken(Token::GreaterThan);
		while (true)
		{
			funDef.returns.emplace_back(parseTypedName());
			if (m_scanner->currentToken() == Token::LBrace)
				break;
			expectToken(Token::Comma);
		}
	}
	funDef.body = parseBlock();
	funDef.location.end = funDef.body.location.end;
	return funDef;
}

assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _instruction)
{
	if (_instruction.type() == typeid(Instruction))
	{
		solAssert(!m_julia, "Instructions are invalid in JULIA");
		FunctionalInstruction ret;
		ret.instruction = std::move(boost::get<Instruction>(_instruction));
		ret.location = ret.instruction.location;
		solidity::Instruction instr = ret.instruction.instruction;
		InstructionInfo instrInfo = instructionInfo(instr);
		if (solidity::Instruction::DUP1 <= instr && instr <= solidity::Instruction::DUP16)
			fatalParserError("DUPi instructions not allowed for functional notation");
		if (solidity::Instruction::SWAP1 <= instr && instr <= solidity::Instruction::SWAP16)
			fatalParserError("SWAPi instructions not allowed for functional notation");
		expectToken(Token::LParen);
		unsigned args = unsigned(instrInfo.args);
		for (unsigned i = 0; i < args; ++i)
		{
			ret.arguments.emplace_back(parseExpression());
			if (i != args - 1)
			{
				if (m_scanner->currentToken() != Token::Comma)
					fatalParserError(string(
						"Expected comma (" +
						instrInfo.name +
						" expects " +
						boost::lexical_cast<string>(args) +
						" arguments)"
					));
				else
					m_scanner->next();
			}
		}
		ret.location.end = endPosition();
		if (m_scanner->currentToken() == Token::Comma)
			fatalParserError(
				string("Expected ')' (" + instrInfo.name + " expects " + boost::lexical_cast<string>(args) + " arguments)")
			);
		expectToken(Token::RParen);
		return ret;
	}
	else if (_instruction.type() == typeid(Identifier))
	{
		FunctionCall ret;
		ret.functionName = std::move(boost::get<Identifier>(_instruction));
		ret.location = ret.functionName.location;
		expectToken(Token::LParen);
		while (m_scanner->currentToken() != Token::RParen)
		{
			ret.arguments.emplace_back(parseExpression());
			if (m_scanner->currentToken() == Token::RParen)
				break;
			expectToken(Token::Comma);
		}
		ret.location.end = endPosition();
		expectToken(Token::RParen);
		return ret;
	}
	else
		fatalParserError(
			m_julia ?
			"Function name expected." :
			"Assembly instruction or function name required in front of \"(\")"
		);

	return {};
}

TypedName Parser::parseTypedName()
{
	TypedName typedName = createWithLocation<TypedName>();
	typedName.name = expectAsmIdentifier();
	if (m_julia)
	{
		expectToken(Token::Colon);
		typedName.location.end = endPosition();
		typedName.type = expectAsmIdentifier();
	}
	return typedName;
}

string Parser::expectAsmIdentifier()
{
	string name = m_scanner->currentLiteral();
	if (!m_julia && instructions().count(name))
		fatalParserError("Cannot use instruction names for identifier names.");
	expectToken(Token::Identifier);
	return name;
}
<commit_msg>Accept bool as a type in Julia mode<commit_after>/*
	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 <http://www.gnu.org/licenses/>.
*/
/**
 * @author Christian <c@ethdev.com>
 * @date 2016
 * Solidity inline assembly parser.
 */

#include <libsolidity/inlineasm/AsmParser.h>
#include <ctype.h>
#include <algorithm>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/interface/Exceptions.h>

using namespace std;
using namespace dev;
using namespace dev::solidity;
using namespace dev::solidity::assembly;

shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scanner)
{
	try
	{
		m_scanner = _scanner;
		return make_shared<Block>(parseBlock());
	}
	catch (FatalError const&)
	{
		if (m_errors.empty())
			throw; // Something is weird here, rather throw again.
	}
	return nullptr;
}

assembly::Block Parser::parseBlock()
{
	assembly::Block block = createWithLocation<Block>();
	expectToken(Token::LBrace);
	while (m_scanner->currentToken() != Token::RBrace)
		block.statements.emplace_back(parseStatement());
	block.location.end = endPosition();
	m_scanner->next();
	return block;
}

assembly::Statement Parser::parseStatement()
{
	switch (m_scanner->currentToken())
	{
	case Token::Let:
		return parseVariableDeclaration();
	case Token::Function:
		return parseFunctionDefinition();
	case Token::LBrace:
		return parseBlock();
	case Token::Assign:
	{
		if (m_julia)
			break;
		assembly::Assignment assignment = createWithLocation<assembly::Assignment>();
		m_scanner->next();
		expectToken(Token::Colon);
		assignment.variableName.location = location();
		assignment.variableName.name = m_scanner->currentLiteral();
		if (!m_julia && instructions().count(assignment.variableName.name))
			fatalParserError("Identifier expected, got instruction name.");
		assignment.location.end = endPosition();
		expectToken(Token::Identifier);
		return assignment;
	}
	case Token::Return: // opcode
	case Token::Byte: // opcode
	case Token::Address: // opcode
	default:
		break;
	}
	// Options left:
	// Simple instruction (might turn into functional),
	// literal,
	// identifier (might turn into label or functional assignment)
	Statement statement(parseElementaryOperation(false));
	switch (m_scanner->currentToken())
	{
	case Token::LParen:
		return parseFunctionalInstruction(std::move(statement));
	case Token::Colon:
	{
		if (statement.type() != typeid(assembly::Identifier))
			fatalParserError("Label name / variable name must precede \":\".");
		assembly::Identifier const& identifier = boost::get<assembly::Identifier>(statement);
		m_scanner->next();
		// identifier:=: should be parsed as identifier: =: (i.e. a label),
		// while identifier:= (being followed by a non-colon) as identifier := (assignment).
		if (m_scanner->currentToken() == Token::Assign && m_scanner->peekNextToken() != Token::Colon)
		{
			// functional assignment
			FunctionalAssignment funAss = createWithLocation<FunctionalAssignment>(identifier.location);
			if (!m_julia && instructions().count(identifier.name))
				fatalParserError("Cannot use instruction names for identifier names.");
			m_scanner->next();
			funAss.variableName = identifier;
			funAss.value.reset(new Statement(parseExpression()));
			funAss.location.end = locationOf(*funAss.value).end;
			return funAss;
		}
		else
		{
			// label
			if (m_julia)
				fatalParserError("Labels are not supported.");
			Label label = createWithLocation<Label>(identifier.location);
			label.name = identifier.name;
			return label;
		}
	}
	default:
		if (m_julia)
			fatalParserError("Call or assignment expected.");
		break;
	}
	return statement;
}

assembly::Statement Parser::parseExpression()
{
	Statement operation = parseElementaryOperation(true);
	if (m_scanner->currentToken() == Token::LParen)
		return parseFunctionalInstruction(std::move(operation));
	else
		return operation;
}

std::map<string, dev::solidity::Instruction> const& Parser::instructions()
{
	// Allowed instructions, lowercase names.
	static map<string, dev::solidity::Instruction> s_instructions;
	if (s_instructions.empty())
	{
		for (auto const& instruction: solidity::c_instructions)
		{
			if (
				instruction.second == solidity::Instruction::JUMPDEST ||
				(solidity::Instruction::PUSH1 <= instruction.second && instruction.second <= solidity::Instruction::PUSH32)
			)
				continue;
			string name = instruction.first;
			transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); });
			s_instructions[name] = instruction.second;
		}

		// add alias for suicide
		s_instructions["suicide"] = solidity::Instruction::SELFDESTRUCT;
	}
	return s_instructions;
}

assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
{
	Statement ret;
	switch (m_scanner->currentToken())
	{
	case Token::Identifier:
	case Token::Return:
	case Token::Byte:
	case Token::Address:
	{
		string literal;
		if (m_scanner->currentToken() == Token::Return)
			literal = "return";
		else if (m_scanner->currentToken() == Token::Byte)
			literal = "byte";
		else if (m_scanner->currentToken() == Token::Address)
			literal = "address";
		else
			literal = m_scanner->currentLiteral();
		// first search the set of instructions.
		if (!m_julia && instructions().count(literal))
		{
			dev::solidity::Instruction const& instr = instructions().at(literal);
			if (_onlySinglePusher)
			{
				InstructionInfo info = dev::solidity::instructionInfo(instr);
				if (info.ret != 1)
					fatalParserError("Instruction " + info.name + " not allowed in this context.");
			}
			ret = Instruction{location(), instr};
		}
		else
			ret = Identifier{location(), literal};
		m_scanner->next();
		break;
	}
	case Token::StringLiteral:
	case Token::Number:
	case Token::TrueLiteral:
	case Token::FalseLiteral:
	{
		LiteralKind kind = LiteralKind::Number;
		switch (m_scanner->currentToken())
		{
		case Token::StringLiteral:
			kind = LiteralKind::String;
			break;
		case Token::Number:
			kind = LiteralKind::Number;
			break;
		case Token::TrueLiteral:
		case Token::FalseLiteral:
			kind = LiteralKind::Boolean;
			break;
		default:
			break;
		}

		Literal literal{
			location(),
			kind,
			m_scanner->currentLiteral(),
			""
		};
		m_scanner->next();
		if (m_julia)
		{
			expectToken(Token::Colon);
			literal.location.end = endPosition();
			literal.type = expectAsmIdentifier();
		}
		else if (kind == LiteralKind::Boolean)
			fatalParserError("True and false are not valid literals.");
		ret = std::move(literal);
		break;
	}
	default:
		fatalParserError(
			m_julia ?
			"Literal or identifier expected." :
			"Expected elementary inline assembly operation."
		);
	}
	return ret;
}

assembly::VariableDeclaration Parser::parseVariableDeclaration()
{
	VariableDeclaration varDecl = createWithLocation<VariableDeclaration>();
	expectToken(Token::Let);
	varDecl.variable = parseTypedName();
	expectToken(Token::Colon);
	expectToken(Token::Assign);
	varDecl.value.reset(new Statement(parseExpression()));
	varDecl.location.end = locationOf(*varDecl.value).end;
	return varDecl;
}

assembly::FunctionDefinition Parser::parseFunctionDefinition()
{
	FunctionDefinition funDef = createWithLocation<FunctionDefinition>();
	expectToken(Token::Function);
	funDef.name = expectAsmIdentifier();
	expectToken(Token::LParen);
	while (m_scanner->currentToken() != Token::RParen)
	{
		funDef.arguments.emplace_back(parseTypedName());
		if (m_scanner->currentToken() == Token::RParen)
			break;
		expectToken(Token::Comma);
	}
	expectToken(Token::RParen);
	if (m_scanner->currentToken() == Token::Sub)
	{
		expectToken(Token::Sub);
		expectToken(Token::GreaterThan);
		while (true)
		{
			funDef.returns.emplace_back(parseTypedName());
			if (m_scanner->currentToken() == Token::LBrace)
				break;
			expectToken(Token::Comma);
		}
	}
	funDef.body = parseBlock();
	funDef.location.end = funDef.body.location.end;
	return funDef;
}

assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _instruction)
{
	if (_instruction.type() == typeid(Instruction))
	{
		solAssert(!m_julia, "Instructions are invalid in JULIA");
		FunctionalInstruction ret;
		ret.instruction = std::move(boost::get<Instruction>(_instruction));
		ret.location = ret.instruction.location;
		solidity::Instruction instr = ret.instruction.instruction;
		InstructionInfo instrInfo = instructionInfo(instr);
		if (solidity::Instruction::DUP1 <= instr && instr <= solidity::Instruction::DUP16)
			fatalParserError("DUPi instructions not allowed for functional notation");
		if (solidity::Instruction::SWAP1 <= instr && instr <= solidity::Instruction::SWAP16)
			fatalParserError("SWAPi instructions not allowed for functional notation");
		expectToken(Token::LParen);
		unsigned args = unsigned(instrInfo.args);
		for (unsigned i = 0; i < args; ++i)
		{
			ret.arguments.emplace_back(parseExpression());
			if (i != args - 1)
			{
				if (m_scanner->currentToken() != Token::Comma)
					fatalParserError(string(
						"Expected comma (" +
						instrInfo.name +
						" expects " +
						boost::lexical_cast<string>(args) +
						" arguments)"
					));
				else
					m_scanner->next();
			}
		}
		ret.location.end = endPosition();
		if (m_scanner->currentToken() == Token::Comma)
			fatalParserError(
				string("Expected ')' (" + instrInfo.name + " expects " + boost::lexical_cast<string>(args) + " arguments)")
			);
		expectToken(Token::RParen);
		return ret;
	}
	else if (_instruction.type() == typeid(Identifier))
	{
		FunctionCall ret;
		ret.functionName = std::move(boost::get<Identifier>(_instruction));
		ret.location = ret.functionName.location;
		expectToken(Token::LParen);
		while (m_scanner->currentToken() != Token::RParen)
		{
			ret.arguments.emplace_back(parseExpression());
			if (m_scanner->currentToken() == Token::RParen)
				break;
			expectToken(Token::Comma);
		}
		ret.location.end = endPosition();
		expectToken(Token::RParen);
		return ret;
	}
	else
		fatalParserError(
			m_julia ?
			"Function name expected." :
			"Assembly instruction or function name required in front of \"(\")"
		);

	return {};
}

TypedName Parser::parseTypedName()
{
	TypedName typedName = createWithLocation<TypedName>();
	typedName.name = expectAsmIdentifier();
	if (m_julia)
	{
		expectToken(Token::Colon);
		typedName.location.end = endPosition();
		typedName.type = expectAsmIdentifier();
	}
	return typedName;
}

string Parser::expectAsmIdentifier()
{
	string name = m_scanner->currentLiteral();
	if (m_julia)
	{
		if (m_scanner->currentToken() == Token::Bool)
		{
			m_scanner->next();
			return name;
		}
	}
	else if (instructions().count(name))
		fatalParserError("Cannot use instruction names for identifier names.");
	expectToken(Token::Identifier);
	return name;
}
<|endoftext|>


<commit_before>/**
 * @file   UnixService.cpp
 * @brief  UnixService class implementation.
 * @author zer0
 * @date   2017-05-27
 */

#include <libtbag/app/details/UnixService.hpp>
#include <libtbag/log/Log.hpp>
#include <libtbag/filesystem/Path.hpp>

#if defined(TBAG_PLATFORM_UNIX_LIKE)
#include <unistd.h>
#include <fcntl.h>
#endif

#include <cassert>
#include <csignal>
#include <cstdio>
#include <iostream>

// -------------------
NAMESPACE_LIBTBAG_OPEN
// -------------------

namespace app     {
namespace details {

struct UnixService::Impl : private Noncopyable
{
    UnixService * parent;

#if defined(TBAG_PLATFORM_UNIX_LIKE)
    pid_t  sid; ///< Session ID.
    pid_t pgid; ///< Process group ID.
    pid_t ppid; ///< Parent Process ID.
    pid_t  pid; ///< Process ID.

    pid_t pid_file;
#endif

    Impl(UnixService * p) : parent(p)
    {
#if defined(TBAG_PLATFORM_UNIX_LIKE)
        sid  = 0;
        pgid = 0;
        ppid = 0;
        pid  = 0;
        pid_file = 0;
#endif
    }

    ~Impl()
    { /* EMPTY. */ }

    Err eliminateControlTerminals()
    {
#if defined(TBAG_PLATFORM_UNIX_LIKE)
        pid = fork();

        if (pid > 0) {
            // This is parent process.
            tDLogI("UnixService::Impl::eliminateControlTerminals() Exit Parent Process {}", (int)getpid());
            _exit(0);

        } else if (pid < 0) {
            Err const FORK_ERROR_CODE = getGlobalSystemError();
            tDLogE("UnixService::Impl::eliminateControlTerminals() fork() {} error", getErrName(FORK_ERROR_CODE));
            return FORK_ERROR_CODE;
        }

        // This is child process.
        assert(pid == 0);

        sid = setsid(); // Be a session leader.
        if (sid == -1) {
            Err const SETSID_ERROR_CODE = getGlobalSystemError();
            tDLogE("UnixService::Impl::eliminateControlTerminals() setsid() {} error", getErrName(SETSID_ERROR_CODE));
            return SETSID_ERROR_CODE;
        }

        pgid = getpgid(0);
        ppid = getppid();
         pid = getpid();

        assert(sid == getsid(0));
        assert(pid != 0);
        assert(pgid == pid);

        // Don't use this code:
        //  @code
        //   pid_t const INIT_PROCESS = 1;
        //   assert(ppid == INIT_PROCESS);
        //  @endcode
        // Reason:
        //  It works with root privileges.

        tDLogI("UnixService::Impl::eliminateControlTerminals() Fork child process: SID({}) PGID({}) PPID({}) PID({})",
               (int)sid, (int)pgid, (int)ppid, (int)pid);

        return Err::E_SUCCESS;
#else
        return Err::E_ENOSYS;
#endif
    }

    Err changeRootDirectory()
    {
#if defined(TBAG_PLATFORM_UNIX_LIKE)
        if (chdir("/") != 0) {
            Err const CHDIR_ERROR_CODE = getGlobalSystemError();
            tDLogE("UnixService::Impl::changeRootDirectory() chdir() {} error", getErrName(CHDIR_ERROR_CODE));
            return CHDIR_ERROR_CODE;
        }
        return Err::E_SUCCESS;
#else
        return Err::E_ENOSYS;
#endif
    }

    Err ignoreSignals()
    {
#if defined(TBAG_PLATFORM_UNIX_LIKE)
        ::signal(SIGHUP, SIG_IGN);

        // Ignore TTY signals.
        ::signal(SIGTSTP, SIG_IGN);
        ::signal(SIGTTOU, SIG_IGN);
        ::signal(SIGTTIN, SIG_IGN);

        return Err::E_SUCCESS;
#else
        return Err::E_ENOSYS;
#endif
    }

    Err reopenStdio()
    {
#if defined(TBAG_PLATFORM_UNIX_LIKE)
        stdin  = freopen("/dev/null", "r", stdin);
        stdout = freopen("/dev/null", "w", stdout);
        stderr = freopen("/dev/null", "w", stderr);
        return Err::E_SUCCESS;
#else
        return Err::E_ENOSYS;
#endif
    }

    Err zeroUmask()
    {
#if defined(TBAG_PLATFORM_UNIX_LIKE)
        umask(0);
        return Err::E_SUCCESS;
#else
        return Err::E_ENOSYS;
#endif
    }

    Err createPidFile(std::string const & path)
    {
#if defined(TBAG_PLATFORM_UNIX_LIKE)
        pid_file = open(path.c_str(), (O_RDWR | O_CREAT), 0600);
        if (pid_file == -1) {
            Err const OPEN_ERROR_CODE = getGlobalSystemError();
            tDLogE("UnixService::Impl::createPidFile() open() {} error", getErrName(OPEN_ERROR_CODE));
            return OPEN_ERROR_CODE;
        }

        // Try to lock file.
        if (lockf(pid_file, F_TLOCK, 0) == -1) {
            close(pid_file);
            pid_file = 0;

            // Couldn't get lock on lock file.
            Err const LOCKF_ERROR_CODE = getGlobalSystemError();
            tDLogE("UnixService::Impl::createPidFile() lockf() {} error", getErrName(LOCKF_ERROR_CODE));
            return LOCKF_ERROR_CODE;
        }

        std::string const PID_STRING = std::to_string(getpid());
        // Write pid to lock file.
        ssize_t const write_size = write(pid_file, PID_STRING.c_str(), PID_STRING.length());
        if (write_size == -1) {
            tDLogW("UnixService::Impl::createPidFile() Write PID error: {}", getErrName(getGlobalSystemError()));
        }
        return Err::E_SUCCESS;
#else
        return Err::E_ENOSYS;
#endif
    }

    Err removePidFile()
    {
#if defined(TBAG_PLATFORM_UNIX_LIKE)
        if (pid_file > 0) {
            lockf(pid_file, F_ULOCK, 0);
            close(pid_file);
            pid_file = 0;
        }
        return Err::E_SUCCESS;
#else
        return Err::E_ENOSYS;
#endif
    }
};

// ---------------------------
// UnixService implementation.
// ---------------------------

UnixService::UnixService(std::string const & name)
        : ServiceCommon(name), _impl(new Impl(this)), _pid_path(std::string("/var/run/") + name + ".pid")
{
    assert(_impl != nullptr);
}

UnixService::~UnixService()
{
    // EMPTY.
}

Err UnixService::install()
{
#if defined(TBAG_PLATFORM_UNIX_LIKE)
    return Err::E_ENOSYS;
#else
    return Err::E_ENOSYS;
#endif
}

Err UnixService::uninstall()
{
#if defined(TBAG_PLATFORM_UNIX_LIKE)
    return Err::E_ENOSYS;
#else
    return Err::E_ENOSYS;
#endif
}

Err UnixService::start()
{
    Err code = Err::E_UNKNOWN;
    code = _impl->eliminateControlTerminals();
    if (isFailure(code)) {
        return code;
    }

    code = _impl->changeRootDirectory();
    if (isFailure(code)) {
        return code;
    }

    _impl->ignoreSignals();
    _impl->zeroUmask();
    _impl->createPidFile(_pid_path);
    _impl->reopenStdio();

    return Err::E_SUCCESS;
}

Err UnixService::stop()
{
    _impl->removePidFile();
    if (_pid_path.empty() == false) {
        filesystem::Path(_pid_path).remove();
    }
    return Err::E_SUCCESS;
}

} // namespace details
} // namespace app

// --------------------
NAMESPACE_LIBTBAG_CLOSE
// --------------------

<commit_msg>Trivial commit.<commit_after>/**
 * @file   UnixService.cpp
 * @brief  UnixService class implementation.
 * @author zer0
 * @date   2017-05-27
 */

#include <libtbag/app/details/UnixService.hpp>
#include <libtbag/log/Log.hpp>
#include <libtbag/filesystem/Path.hpp>

#if defined(TBAG_PLATFORM_UNIX_LIKE)
#include <unistd.h>
#include <fcntl.h>
#endif

#include <cassert>
#include <csignal>
#include <cstdio>
#include <iostream>

// -------------------
NAMESPACE_LIBTBAG_OPEN
// -------------------

namespace app     {
namespace details {

struct UnixService::Impl : private Noncopyable
{
    UnixService * parent;

#if defined(TBAG_PLATFORM_UNIX_LIKE)
    pid_t  sid; ///< Session ID.
    pid_t pgid; ///< Process group ID.
    pid_t ppid; ///< Parent Process ID.
    pid_t  pid; ///< Process ID.

    pid_t pid_file;
#endif

    Impl(UnixService * p) : parent(p)
    {
#if defined(TBAG_PLATFORM_UNIX_LIKE)
        sid  = 0;
        pgid = 0;
        ppid = 0;
        pid  = 0;
        pid_file = 0;
#endif
    }

    ~Impl()
    { /* EMPTY. */ }

    Err eliminateControlTerminals()
    {
#if defined(TBAG_PLATFORM_UNIX_LIKE)
        pid = fork();

        if (pid > 0) {
            // This is parent process.
            tDLogI("UnixService::Impl::eliminateControlTerminals() Exit Parent Process {}", (int)getpid());
            _exit(0);

        } else if (pid < 0) {
            Err const FORK_ERROR_CODE = getGlobalSystemError();
            tDLogE("UnixService::Impl::eliminateControlTerminals() fork() {} error", FORK_ERROR_CODE);
            return FORK_ERROR_CODE;
        }

        // This is child process.
        assert(pid == 0);

        sid = setsid(); // Be a session leader.
        if (sid == -1) {
            Err const SETSID_ERROR_CODE = getGlobalSystemError();
            tDLogE("UnixService::Impl::eliminateControlTerminals() setsid() {} error", SETSID_ERROR_CODE);
            return SETSID_ERROR_CODE;
        }

        pgid = getpgid(0);
        ppid = getppid();
         pid = getpid();

        assert(sid == getsid(0));
        assert(pid != 0);
        assert(pgid == pid);

        // Don't use this code:
        //  @code
        //   pid_t const INIT_PROCESS = 1;
        //   assert(ppid == INIT_PROCESS);
        //  @endcode
        // Reason:
        //  It works with root privileges.

        tDLogI("UnixService::Impl::eliminateControlTerminals() "
               "Fork child process: SID({}) PGID({}) PPID({}) PID({})",
               (int)sid, (int)pgid, (int)ppid, (int)pid);

        return Err::E_SUCCESS;
#else
        return Err::E_ENOSYS;
#endif
    }

    Err changeRootDirectory()
    {
#if defined(TBAG_PLATFORM_UNIX_LIKE)
        if (chdir("/") != 0) {
            Err const CHDIR_ERROR_CODE = getGlobalSystemError();
            tDLogE("UnixService::Impl::changeRootDirectory() chdir() {} error", CHDIR_ERROR_CODE);
            return CHDIR_ERROR_CODE;
        }
        return Err::E_SUCCESS;
#else
        return Err::E_ENOSYS;
#endif
    }

    Err ignoreSignals()
    {
#if defined(TBAG_PLATFORM_UNIX_LIKE)
        ::signal(SIGHUP, SIG_IGN);

        // Ignore TTY signals.
        ::signal(SIGTSTP, SIG_IGN);
        ::signal(SIGTTOU, SIG_IGN);
        ::signal(SIGTTIN, SIG_IGN);

        return Err::E_SUCCESS;
#else
        return Err::E_ENOSYS;
#endif
    }

    Err reopenStdio()
    {
#if defined(TBAG_PLATFORM_UNIX_LIKE)
        stdin  = freopen("/dev/null", "r", stdin);
        stdout = freopen("/dev/null", "w", stdout);
        stderr = freopen("/dev/null", "w", stderr);
        return Err::E_SUCCESS;
#else
        return Err::E_ENOSYS;
#endif
    }

    Err zeroUmask()
    {
#if defined(TBAG_PLATFORM_UNIX_LIKE)
        umask(0);
        return Err::E_SUCCESS;
#else
        return Err::E_ENOSYS;
#endif
    }

    Err createPidFile(std::string const & path)
    {
#if defined(TBAG_PLATFORM_UNIX_LIKE)
        pid_file = open(path.c_str(), (O_RDWR | O_CREAT), 0600);
        if (pid_file == -1) {
            Err const OPEN_ERROR_CODE = getGlobalSystemError();
            tDLogE("UnixService::Impl::createPidFile() open() {} error", OPEN_ERROR_CODE);
            return OPEN_ERROR_CODE;
        }

        // Try to lock file.
        if (lockf(pid_file, F_TLOCK, 0) == -1) {
            close(pid_file);
            pid_file = 0;

            // Couldn't get lock on lock file.
            Err const LOCKF_ERROR_CODE = getGlobalSystemError();
            tDLogE("UnixService::Impl::createPidFile() lockf() {} error", LOCKF_ERROR_CODE);
            return LOCKF_ERROR_CODE;
        }

        std::string const PID_STRING = std::to_string(getpid());
        // Write pid to lock file.
        ssize_t const write_size = write(pid_file, PID_STRING.c_str(), PID_STRING.length());
        if (write_size == -1) {
            tDLogW("UnixService::Impl::createPidFile() Write PID error: {}", getGlobalSystemError());
        }
        return Err::E_SUCCESS;
#else
        return Err::E_ENOSYS;
#endif
    }

    Err removePidFile()
    {
#if defined(TBAG_PLATFORM_UNIX_LIKE)
        if (pid_file > 0) {
            lockf(pid_file, F_ULOCK, 0);
            close(pid_file);
            pid_file = 0;
        }
        return Err::E_SUCCESS;
#else
        return Err::E_ENOSYS;
#endif
    }
};

// ---------------------------
// UnixService implementation.
// ---------------------------

UnixService::UnixService(std::string const & name)
        : ServiceCommon(name), _impl(new Impl(this)), _pid_path(std::string("/var/run/") + name + ".pid")
{
    assert(_impl != nullptr);
}

UnixService::~UnixService()
{
    // EMPTY.
}

Err UnixService::install()
{
#if defined(TBAG_PLATFORM_UNIX_LIKE)
    return Err::E_ENOSYS;
#else
    return Err::E_ENOSYS;
#endif
}

Err UnixService::uninstall()
{
#if defined(TBAG_PLATFORM_UNIX_LIKE)
    return Err::E_ENOSYS;
#else
    return Err::E_ENOSYS;
#endif
}

Err UnixService::start()
{
    Err code = Err::E_UNKNOWN;
    code = _impl->eliminateControlTerminals();
    if (isFailure(code)) {
        return code;
    }

    code = _impl->changeRootDirectory();
    if (isFailure(code)) {
        return code;
    }

    _impl->ignoreSignals();
    _impl->zeroUmask();
    _impl->createPidFile(_pid_path);
    _impl->reopenStdio();

    return Err::E_SUCCESS;
}

Err UnixService::stop()
{
    _impl->removePidFile();
    if (_pid_path.empty() == false) {
        filesystem::Path(_pid_path).remove();
    }
    return Err::E_SUCCESS;
}

} // namespace details
} // namespace app

// --------------------
NAMESPACE_LIBTBAG_CLOSE
// --------------------

<|endoftext|>


<commit_before>
/*
 * Web Upload Engine -- MeeGo social networking uploads
 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
 * Contact: Jukka Tiihonen <jukka.tiihonen@nokia.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU Lesser General Public License,
 * version 2.1, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "WebUpload/PostSimpleHttp"
#include <QDebug>
#include <QNetworkConfiguration>
#include <QFile>

#define DBG_PREFIX "PostSHttp:"
#define DBG_STREAM qDebug() << DBG_PREFIX
#define WARN_STREAM qWarning() << DBG_PREFIX
#define CRIT_STREAM qCritical() << DBG_PREFIX

using namespace WebUpload;

PostSimpleHttp::PostSimpleHttp (QObject * parent) : PostBase (parent),
    netAM (new QNetworkAccessManager(this)), currentReply (0) {

    // Connect signals
    connect (netAM, SIGNAL(finished(QNetworkReply*)), this,
        SLOT(namFinished(QNetworkReply*)));
    connect (netAM, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this,
        SLOT(namSslErrors(QNetworkReply*,QList<QSslError>)));
}

PostSimpleHttp::~PostSimpleHttp() {
    netAM->disconnect (this);
    delete netAM;
}

void PostSimpleHttp::stopMediaUpload () {
    if (currentReply != 0 && currentReply->isRunning()) {
        currentReply->abort();
    } else {
        WARN_STREAM << "stopMediaUpload ignored, currentReply undefined";
    }
}

void PostSimpleHttp::uploadMedia (Media * media) {

    DBG_STREAM << "Calling generateRequest";
    currentReply = 0;

    if (media->type() == Media::TYPE_FILE) {
        QString originalFilePath = media->srcFilePath ();
        QString copyFilePath = media->copyFilePath ();
        if (!QFile::exists (originalFilePath) ||
            !QFile::exists (copyFilePath)) {

            Q_EMIT (mediaError(WebUpload::Error::missingFiles()));
            return;
        }
    }

    currentReply = generateRequest (media);

    if (currentReply == 0) {
        WARN_STREAM << "Generate request returned null";
        // Letting this stay as custom error, since this should not normally
        // happen
        Q_EMIT (mediaError(WebUpload::Error::custom ("System Failure",
            "Failed to create request")));
    } else {
        // Connect progress signal
        QObject::connect (currentReply, SIGNAL(uploadProgress(qint64,qint64)),
            this, SLOT(nrUpProgress(qint64,qint64)));
    }
}

void PostSimpleHttp::namFinished (QNetworkReply * reply) {

    if (currentReply != reply) {
        CRIT_STREAM << "Reply mismatch" << currentReply << reply;
        reply->deleteLater();
        return;
    }

    currentReply = 0;

    QNetworkReply::NetworkError replyError = reply->error();
    reply->disconnect (this);

    switch (replyError) {
        case QNetworkReply::OperationCanceledError:
            DBG_STREAM << "NAM finished, operation cancelled";
            Q_EMIT (stopped ());
            break;

        case QNetworkReply::ConnectionRefusedError:
        case QNetworkReply::RemoteHostClosedError:
        case QNetworkReply::HostNotFoundError:
        case QNetworkReply::TimeoutError:
        case QNetworkReply::SslHandshakeFailedError:
        case QNetworkReply::ProxyConnectionRefusedError:
        case QNetworkReply::ProxyConnectionClosedError:
        case QNetworkReply::ProxyNotFoundError:
        case QNetworkReply::ProxyTimeoutError:
        case QNetworkReply::ProxyAuthenticationRequiredError:
        case QNetworkReply::UnknownNetworkError:
        case QNetworkReply::UnknownProxyError:
        {
            int httpCode = reply->attribute (
                QNetworkRequest::HttpStatusCodeAttribute).toInt();
            WARN_STREAM << "NAM finished with an connection error" <<
                replyError << reply->errorString() << " and http code " <<
                httpCode;
            Q_EMIT (mediaError(WebUpload::Error::connectFailure()));
            break;
        }

        default:
            DBG_STREAM << "Calling handleResponse" << replyError;
            handleResponse (reply);
            break;
    }

    reply->deleteLater ();
}

void PostSimpleHttp::namSslErrors (QNetworkReply * reply,
    const QList<QSslError> & errors) {

    if (currentReply != reply) {
        CRIT_STREAM << "Reply mismatch" << currentReply << reply;
        reply->deleteLater();
        return;
    }

    const QSslError::SslError errorEnum = QSslError::NoError;
    int i = 0;
    for (i = 0; errorEnum == QSslError::NoError && i < errors.size (); ++i) {
        errorEnum = errors.at (i).error ();
    }

    if (firstError == QSslError::UnableToGetLocalIssuerCertificate) {
        QSslCertificate cert = errors.at (i).certificate ();
        if (!cert.isNull ()) {
            QByteArray certDer = cert.toDer ();
            QDBusInterface iface ("com.nokia.certman", "/com/nokia/certman");
            if (iface.isValid ()) {
                QDBusReply<bool> result =
                    iface.call ("CheckCertificate", certDer);
                if (result) {
                    reply->ignoreSslErrors ();
                    return;
                }
            }
        }
    }

    currentReply = 0;
    reply->deleteLater ();

    if (errorEnum == QSslError::NoError) {
        qWarning() << "QNetworkAccessManager::sslErrors signal emitted with "
            "no ssl errors - just marking transfer as failed";
        Q_EMIT (mediaError(WebUpload::Error::transferFailed()));
        return;
    }

    WebUpload::Error error = WebUpload::Error::connectFailure();
    switch (errorEnum) {
        case QSslError::CertificateNotYetValid:
            //% "Check device time and date."
            error->setDescription (qtTrId ("qtn_tui_invalid_device_time"));
            break;

        default:
            //% "Secure connection failed"
            error->setDescription (qtTrId ("qtn_tui_ssl_connection_failed"));
            break;
    }

    Q_EMIT (mediaError(error));
}

void PostSimpleHttp::nrUpProgress (qint64 bytesSent, qint64 bytesTotal) {

    if (bytesTotal > 0) {
        float progressAmt = ((float)bytesSent)/((float)bytesTotal);
        DBG_STREAM << "progress:" << progressAmt;
        Q_EMIT (mediaProgress(progressAmt));
    } else {
        DBG_STREAM << "undefined progress";
    }
}
<commit_msg>Added missing header files so compilation can go through<commit_after>
/*
 * Web Upload Engine -- MeeGo social networking uploads
 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
 * Contact: Jukka Tiihonen <jukka.tiihonen@nokia.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU Lesser General Public License,
 * version 2.1, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "WebUpload/PostSimpleHttp"
#include <QDebug>
#include <QNetworkConfiguration>
#include <QFile>

#include <QSslError>
#include <QSslCertificate>
#include <QDBusInterface>
#include <QDBusReply>

#define DBG_PREFIX "PostSHttp:"
#define DBG_STREAM qDebug() << DBG_PREFIX
#define WARN_STREAM qWarning() << DBG_PREFIX
#define CRIT_STREAM qCritical() << DBG_PREFIX

using namespace WebUpload;

PostSimpleHttp::PostSimpleHttp (QObject * parent) : PostBase (parent),
    netAM (new QNetworkAccessManager(this)), currentReply (0) {

    // Connect signals
    connect (netAM, SIGNAL(finished(QNetworkReply*)), this,
        SLOT(namFinished(QNetworkReply*)));
    connect (netAM, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this,
        SLOT(namSslErrors(QNetworkReply*,QList<QSslError>)));
}

PostSimpleHttp::~PostSimpleHttp() {
    netAM->disconnect (this);
    delete netAM;
}

void PostSimpleHttp::stopMediaUpload () {
    if (currentReply != 0 && currentReply->isRunning()) {
        currentReply->abort();
    } else {
        WARN_STREAM << "stopMediaUpload ignored, currentReply undefined";
    }
}

void PostSimpleHttp::uploadMedia (Media * media) {

    DBG_STREAM << "Calling generateRequest";
    currentReply = 0;

    if (media->type() == Media::TYPE_FILE) {
        QString originalFilePath = media->srcFilePath ();
        QString copyFilePath = media->copyFilePath ();
        if (!QFile::exists (originalFilePath) ||
            !QFile::exists (copyFilePath)) {

            Q_EMIT (mediaError(WebUpload::Error::missingFiles()));
            return;
        }
    }

    currentReply = generateRequest (media);

    if (currentReply == 0) {
        WARN_STREAM << "Generate request returned null";
        // Letting this stay as custom error, since this should not normally
        // happen
        Q_EMIT (mediaError(WebUpload::Error::custom ("System Failure",
            "Failed to create request")));
    } else {
        // Connect progress signal
        QObject::connect (currentReply, SIGNAL(uploadProgress(qint64,qint64)),
            this, SLOT(nrUpProgress(qint64,qint64)));
    }
}

void PostSimpleHttp::namFinished (QNetworkReply * reply) {

    if (currentReply != reply) {
        CRIT_STREAM << "Reply mismatch" << currentReply << reply;
        reply->deleteLater();
        return;
    }

    currentReply = 0;

    QNetworkReply::NetworkError replyError = reply->error();
    reply->disconnect (this);

    switch (replyError) {
        case QNetworkReply::OperationCanceledError:
            DBG_STREAM << "NAM finished, operation cancelled";
            Q_EMIT (stopped ());
            break;

        case QNetworkReply::ConnectionRefusedError:
        case QNetworkReply::RemoteHostClosedError:
        case QNetworkReply::HostNotFoundError:
        case QNetworkReply::TimeoutError:
        case QNetworkReply::SslHandshakeFailedError:
        case QNetworkReply::ProxyConnectionRefusedError:
        case QNetworkReply::ProxyConnectionClosedError:
        case QNetworkReply::ProxyNotFoundError:
        case QNetworkReply::ProxyTimeoutError:
        case QNetworkReply::ProxyAuthenticationRequiredError:
        case QNetworkReply::UnknownNetworkError:
        case QNetworkReply::UnknownProxyError:
        {
            int httpCode = reply->attribute (
                QNetworkRequest::HttpStatusCodeAttribute).toInt();
            WARN_STREAM << "NAM finished with an connection error" <<
                replyError << reply->errorString() << " and http code " <<
                httpCode;
            Q_EMIT (mediaError(WebUpload::Error::connectFailure()));
            break;
        }

        default:
            DBG_STREAM << "Calling handleResponse" << replyError;
            handleResponse (reply);
            break;
    }

    reply->deleteLater ();
}

void PostSimpleHttp::namSslErrors (QNetworkReply * reply,
    const QList<QSslError> & errors) {

    if (currentReply != reply) {
        CRIT_STREAM << "Reply mismatch" << currentReply << reply;
        reply->deleteLater();
        return;
    }

    QSslError::SslError errorEnum = QSslError::NoError;
    int i = 0;
    for (i = 0; errorEnum == QSslError::NoError && i < errors.size (); ++i) {
        errorEnum = errors.at (i).error ();
    }

    if (errorEnum == QSslError::UnableToGetLocalIssuerCertificate) {
        QSslCertificate cert = errors.at (i).certificate ();
        if (!cert.isNull ()) {
            QByteArray certDer = cert.toDer ();
            QDBusInterface iface ("com.nokia.certman", "/com/nokia/certman");
            if (iface.isValid ()) {
                QDBusReply<bool> result =
                    iface.call ("CheckCertificate", certDer);
                if (result) {
                    reply->ignoreSslErrors ();
                    return;
                }
            }
        }
    }

    currentReply = 0;
    reply->deleteLater ();

    if (errorEnum == QSslError::NoError) {
        qWarning() << "QNetworkAccessManager::sslErrors signal emitted with "
            "no ssl errors - just marking transfer as failed";
        Q_EMIT (mediaError(WebUpload::Error::transferFailed()));
        return;
    }

    WebUpload::Error error = WebUpload::Error::connectFailure();
    switch (errorEnum) {
        case QSslError::CertificateNotYetValid:
            //% "Check device time and date."
            error.setDescription (qtTrId ("qtn_tui_invalid_device_time"));
            break;

        default:
            //% "Secure connection failed"
            error.setDescription (qtTrId ("qtn_tui_ssl_connection_failed"));
            break;
    }

    Q_EMIT (mediaError(error));
}

void PostSimpleHttp::nrUpProgress (qint64 bytesSent, qint64 bytesTotal) {

    if (bytesTotal > 0) {
        float progressAmt = ((float)bytesSent)/((float)bytesTotal);
        DBG_STREAM << "progress:" << progressAmt;
        Q_EMIT (mediaProgress(progressAmt));
    } else {
        DBG_STREAM << "undefined progress";
    }
}
<|endoftext|>


<commit_before>//
// libtgvoip is free and unencumbered public domain software.
// For more information, see http://unlicense.org or the UNLICENSE file
// you should have received with this source code distribution.
//

#ifndef TGVOIP_NO_DSP
#include "webrtc_dsp/modules/audio_processing/include/audio_processing.h"
#include "webrtc_dsp/api/audio/audio_frame.h"
#endif

#include "EchoCanceller.h"
#include "audio/AudioOutput.h"
#include "audio/AudioInput.h"
#include "logging.h"
#include "VoIPServerConfig.h"
#include <string.h>
#include <stdio.h>

using namespace tgvoip;

EchoCanceller::EchoCanceller(bool enableAEC, bool enableNS, bool enableAGC){
#ifndef TGVOIP_NO_DSP
	this->enableAEC=enableAEC;
	this->enableAGC=enableAGC;
	this->enableNS=enableNS;
	isOn=true;

	webrtc::Config extraConfig;
#ifdef TGVOIP_USE_DESKTOP_DSP
	extraConfig.Set(new webrtc::DelayAgnostic(true));
#endif

	apm=webrtc::AudioProcessingBuilder().Create(extraConfig);

	webrtc::AudioProcessing::Config config;
	config.echo_canceller.enabled = enableAEC;
#ifndef TGVOIP_USE_DESKTOP_DSP
	config.echo_canceller.mobile_mode = true;
#else
	config.echo_canceller.mobile_mode = false;
#endif
	config.high_pass_filter.enabled = enableAEC;
	config.gain_controller2.enabled = enableAGC;
	apm->ApplyConfig(config);

	webrtc::NoiseSuppression::Level nsLevel;
#ifdef __APPLE__
	switch(ServerConfig::GetSharedInstance()->GetInt("webrtc_ns_level_vpio", 0)){
#else
	switch(ServerConfig::GetSharedInstance()->GetInt("webrtc_ns_level", 2)){
#endif
		case 0:
			nsLevel=webrtc::NoiseSuppression::Level::kLow;
			break;
		case 1:
			nsLevel=webrtc::NoiseSuppression::Level::kModerate;
			break;
		case 3:
			nsLevel=webrtc::NoiseSuppression::Level::kVeryHigh;
			break;
		case 2:
		default:
			nsLevel=webrtc::NoiseSuppression::Level::kHigh;
			break;
	}
	apm->noise_suppression()->set_level(nsLevel);
	apm->noise_suppression()->Enable(enableNS);
	if(enableAGC){
		apm->gain_control()->set_mode(webrtc::GainControl::Mode::kAdaptiveDigital);
		apm->gain_control()->set_target_level_dbfs(ServerConfig::GetSharedInstance()->GetInt("webrtc_agc_target_level", 9));
		apm->gain_control()->enable_limiter(ServerConfig::GetSharedInstance()->GetBoolean("webrtc_agc_enable_limiter", true));
		apm->gain_control()->set_compression_gain_db(ServerConfig::GetSharedInstance()->GetInt("webrtc_agc_compression_gain", 20));
	}
	apm->voice_detection()->set_likelihood(webrtc::VoiceDetection::Likelihood::kVeryLowLikelihood);

	audioFrame=new webrtc::AudioFrame();
	audioFrame->samples_per_channel_=480;
	audioFrame->sample_rate_hz_=48000;
	audioFrame->num_channels_=1;

	farendQueue=new BlockingQueue<int16_t*>(11);
	farendBufferPool=new BufferPool(960*2, 10);
	running=true;
	bufferFarendThread=new Thread(std::bind(&EchoCanceller::RunBufferFarendThread, this));
	bufferFarendThread->Start();

#else
	this->enableAEC=this->enableAGC=enableAGC=this->enableNS=enableNS=false;
	isOn=true;
#endif
}

EchoCanceller::~EchoCanceller(){
#ifndef TGVOIP_NO_DSP
	delete apm;
	delete audioFrame;
#endif
}

void EchoCanceller::Start(){

}

void EchoCanceller::Stop(){

}

void EchoCanceller::SpeakerOutCallback(unsigned char* data, size_t len){
    if(len!=960*2 || !enableAEC || !isOn)
		return;
#ifndef TGVOIP_NO_DSP
	int16_t* buf=(int16_t*)farendBufferPool->Get();
	if(buf){
		memcpy(buf, data, 960*2);
		farendQueue->Put(buf);
	}
#endif
}

#ifndef TGVOIP_NO_DSP
void EchoCanceller::RunBufferFarendThread(){
	webrtc::AudioFrame frame;
	frame.num_channels_=1;
	frame.sample_rate_hz_=48000;
	frame.samples_per_channel_=480;
	while(running){
		int16_t* samplesIn=farendQueue->GetBlocking();
		if(samplesIn){
			memcpy(frame.mutable_data(), samplesIn, 480*2);
			apm->ProcessReverseStream(&frame);
			memcpy(frame.mutable_data(), samplesIn+480, 480*2);
			apm->ProcessReverseStream(&frame);
			didBufferFarend=true;
			farendBufferPool->Reuse(reinterpret_cast<unsigned char*>(samplesIn));
		}
	}
}
#endif

void EchoCanceller::Enable(bool enabled){
	isOn=enabled;
}

void EchoCanceller::ProcessInput(int16_t* inOut, size_t numSamples, bool& hasVoice){
	if(!isOn || (!enableAEC && !enableAGC && !enableNS)){
		return;
	}
	int delay=audio::AudioInput::GetEstimatedDelay()+audio::AudioOutput::GetEstimatedDelay();
	assert(numSamples==960);

	memcpy(audioFrame->mutable_data(), inOut, 480*2);
	if(enableAEC)
    	apm->set_stream_delay_ms(delay);
	apm->ProcessStream(audioFrame);
	if(enableVAD)
    	hasVoice=apm->voice_detection()->stream_has_voice();
	memcpy(inOut, audioFrame->data(), 480*2);
	memcpy(audioFrame->mutable_data(), inOut+480, 480*2);
	if(enableAEC)
    	apm->set_stream_delay_ms(delay);
	apm->ProcessStream(audioFrame);
	if(enableVAD){
    	hasVoice=hasVoice || apm->voice_detection()->stream_has_voice();
	}
	memcpy(inOut+480, audioFrame->data(), 480*2);
}

void EchoCanceller::SetAECStrength(int strength){
#ifndef TGVOIP_NO_DSP
	/*if(aec){
#ifndef TGVOIP_USE_DESKTOP_DSP
		AecmConfig cfg;
		cfg.cngMode=AecmFalse;
		cfg.echoMode=(int16_t) strength;
		WebRtcAecm_set_config(aec, cfg);
#endif
	}*/
#endif
}

void EchoCanceller::SetVoiceDetectionEnabled(bool enabled){
	enableVAD=enabled;
	apm->voice_detection()->Enable(enabled);
}

using namespace tgvoip::effects;

AudioEffect::~AudioEffect(){

}

void AudioEffect::SetPassThrough(bool passThrough){
	this->passThrough=passThrough;
}

Volume::Volume(){

}

Volume::~Volume(){

}

void Volume::Process(int16_t* inOut, size_t numSamples){
	if(level==1.0f || passThrough){
		return;
	}
	for(size_t i=0;i<numSamples;i++){
		float sample=(float)inOut[i]*multiplier;
		if(sample>32767.0f)
			inOut[i]=INT16_MAX;
		else if(sample<-32768.0f)
			inOut[i]=INT16_MIN;
		else
			inOut[i]=(int16_t)sample;
	}
}

void Volume::SetLevel(float level){
	this->level=level;
	float db;
	if(level<1.0f)
		db=-50.0f*(1.0f-level);
	else if(level>1.0f && level<=2.0f)
		db=10.0f*(level-1.0f);
	else
		db=0.0f;
	multiplier=expf(db/20.0f * logf(10.0f));
}

float Volume::GetLevel(){
	return level;
}
<commit_msg>Fixed build with TGVOIP_NO_DSP<commit_after>//
// libtgvoip is free and unencumbered public domain software.
// For more information, see http://unlicense.org or the UNLICENSE file
// you should have received with this source code distribution.
//

#ifndef TGVOIP_NO_DSP
#include "webrtc_dsp/modules/audio_processing/include/audio_processing.h"
#include "webrtc_dsp/api/audio/audio_frame.h"
#endif

#include "EchoCanceller.h"
#include "audio/AudioOutput.h"
#include "audio/AudioInput.h"
#include "logging.h"
#include "VoIPServerConfig.h"
#include <string.h>
#include <stdio.h>
#include <math.h>

using namespace tgvoip;

EchoCanceller::EchoCanceller(bool enableAEC, bool enableNS, bool enableAGC){
#ifndef TGVOIP_NO_DSP
	this->enableAEC=enableAEC;
	this->enableAGC=enableAGC;
	this->enableNS=enableNS;
	isOn=true;

	webrtc::Config extraConfig;
#ifdef TGVOIP_USE_DESKTOP_DSP
	extraConfig.Set(new webrtc::DelayAgnostic(true));
#endif

	apm=webrtc::AudioProcessingBuilder().Create(extraConfig);

	webrtc::AudioProcessing::Config config;
	config.echo_canceller.enabled = enableAEC;
#ifndef TGVOIP_USE_DESKTOP_DSP
	config.echo_canceller.mobile_mode = true;
#else
	config.echo_canceller.mobile_mode = false;
#endif
	config.high_pass_filter.enabled = enableAEC;
	config.gain_controller2.enabled = enableAGC;
	apm->ApplyConfig(config);

	webrtc::NoiseSuppression::Level nsLevel;
#ifdef __APPLE__
	switch(ServerConfig::GetSharedInstance()->GetInt("webrtc_ns_level_vpio", 0)){
#else
	switch(ServerConfig::GetSharedInstance()->GetInt("webrtc_ns_level", 2)){
#endif
		case 0:
			nsLevel=webrtc::NoiseSuppression::Level::kLow;
			break;
		case 1:
			nsLevel=webrtc::NoiseSuppression::Level::kModerate;
			break;
		case 3:
			nsLevel=webrtc::NoiseSuppression::Level::kVeryHigh;
			break;
		case 2:
		default:
			nsLevel=webrtc::NoiseSuppression::Level::kHigh;
			break;
	}
	apm->noise_suppression()->set_level(nsLevel);
	apm->noise_suppression()->Enable(enableNS);
	if(enableAGC){
		apm->gain_control()->set_mode(webrtc::GainControl::Mode::kAdaptiveDigital);
		apm->gain_control()->set_target_level_dbfs(ServerConfig::GetSharedInstance()->GetInt("webrtc_agc_target_level", 9));
		apm->gain_control()->enable_limiter(ServerConfig::GetSharedInstance()->GetBoolean("webrtc_agc_enable_limiter", true));
		apm->gain_control()->set_compression_gain_db(ServerConfig::GetSharedInstance()->GetInt("webrtc_agc_compression_gain", 20));
	}
	apm->voice_detection()->set_likelihood(webrtc::VoiceDetection::Likelihood::kVeryLowLikelihood);

	audioFrame=new webrtc::AudioFrame();
	audioFrame->samples_per_channel_=480;
	audioFrame->sample_rate_hz_=48000;
	audioFrame->num_channels_=1;

	farendQueue=new BlockingQueue<int16_t*>(11);
	farendBufferPool=new BufferPool(960*2, 10);
	running=true;
	bufferFarendThread=new Thread(std::bind(&EchoCanceller::RunBufferFarendThread, this));
	bufferFarendThread->Start();

#else
	this->enableAEC=this->enableAGC=enableAGC=this->enableNS=enableNS=false;
	isOn=true;
#endif
}

EchoCanceller::~EchoCanceller(){
#ifndef TGVOIP_NO_DSP
	delete apm;
	delete audioFrame;
#endif
}

void EchoCanceller::Start(){

}

void EchoCanceller::Stop(){

}

void EchoCanceller::SpeakerOutCallback(unsigned char* data, size_t len){
    if(len!=960*2 || !enableAEC || !isOn)
		return;
#ifndef TGVOIP_NO_DSP
	int16_t* buf=(int16_t*)farendBufferPool->Get();
	if(buf){
		memcpy(buf, data, 960*2);
		farendQueue->Put(buf);
	}
#endif
}

#ifndef TGVOIP_NO_DSP
void EchoCanceller::RunBufferFarendThread(){
	webrtc::AudioFrame frame;
	frame.num_channels_=1;
	frame.sample_rate_hz_=48000;
	frame.samples_per_channel_=480;
	while(running){
		int16_t* samplesIn=farendQueue->GetBlocking();
		if(samplesIn){
			memcpy(frame.mutable_data(), samplesIn, 480*2);
			apm->ProcessReverseStream(&frame);
			memcpy(frame.mutable_data(), samplesIn+480, 480*2);
			apm->ProcessReverseStream(&frame);
			didBufferFarend=true;
			farendBufferPool->Reuse(reinterpret_cast<unsigned char*>(samplesIn));
		}
	}
}
#endif

void EchoCanceller::Enable(bool enabled){
	isOn=enabled;
}

void EchoCanceller::ProcessInput(int16_t* inOut, size_t numSamples, bool& hasVoice){
#ifndef TGVOIP_NO_DSP
	if(!isOn || (!enableAEC && !enableAGC && !enableNS)){
		return;
	}
	int delay=audio::AudioInput::GetEstimatedDelay()+audio::AudioOutput::GetEstimatedDelay();
	assert(numSamples==960);

	memcpy(audioFrame->mutable_data(), inOut, 480*2);
	if(enableAEC)
    	apm->set_stream_delay_ms(delay);
	apm->ProcessStream(audioFrame);
	if(enableVAD)
    	hasVoice=apm->voice_detection()->stream_has_voice();
	memcpy(inOut, audioFrame->data(), 480*2);
	memcpy(audioFrame->mutable_data(), inOut+480, 480*2);
	if(enableAEC)
    	apm->set_stream_delay_ms(delay);
	apm->ProcessStream(audioFrame);
	if(enableVAD){
    	hasVoice=hasVoice || apm->voice_detection()->stream_has_voice();
	}
	memcpy(inOut+480, audioFrame->data(), 480*2);
#endif
}

void EchoCanceller::SetAECStrength(int strength){
#ifndef TGVOIP_NO_DSP
	/*if(aec){
#ifndef TGVOIP_USE_DESKTOP_DSP
		AecmConfig cfg;
		cfg.cngMode=AecmFalse;
		cfg.echoMode=(int16_t) strength;
		WebRtcAecm_set_config(aec, cfg);
#endif
	}*/
#endif
}

void EchoCanceller::SetVoiceDetectionEnabled(bool enabled){
	enableVAD=enabled;
#ifndef TGVOIP_NO_DSP
	apm->voice_detection()->Enable(enabled);
#endif
}

using namespace tgvoip::effects;

AudioEffect::~AudioEffect(){

}

void AudioEffect::SetPassThrough(bool passThrough){
	this->passThrough=passThrough;
}

Volume::Volume(){

}

Volume::~Volume(){

}

void Volume::Process(int16_t* inOut, size_t numSamples){
	if(level==1.0f || passThrough){
		return;
	}
	for(size_t i=0;i<numSamples;i++){
		float sample=(float)inOut[i]*multiplier;
		if(sample>32767.0f)
			inOut[i]=INT16_MAX;
		else if(sample<-32768.0f)
			inOut[i]=INT16_MIN;
		else
			inOut[i]=(int16_t)sample;
	}
}

void Volume::SetLevel(float level){
	this->level=level;
	float db;
	if(level<1.0f)
		db=-50.0f*(1.0f-level);
	else if(level>1.0f && level<=2.0f)
		db=10.0f*(level-1.0f);
	else
		db=0.0f;
	multiplier=expf(db/20.0f * logf(10.0f));
}

float Volume::GetLevel(){
	return level;
}
<|endoftext|>


<commit_before>/*******************************
Copyright (c) 2016-2017 Grégoire Angerand

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 "String.h"
#include "Vector.h"
#include <y/test/test.h>
#include <memory>
#include <cstring>

namespace y {
namespace core {
// --------------------------------------------------- LONG ---------------------------------------------------

String::LongData::LongData() : data(nullptr), capacity(0), length(0) {
}

String::LongData::LongData(const LongData& l) : LongData(l.data, l.length) {
}

String::LongData::LongData(LongData&& l) : data(l.data), capacity(l.capacity), length(l.length) {
	l.data = nullptr;
}

String::LongData::LongData(const char* str, usize len) : LongData(str, compute_capacity(len), len) {
}

String::LongData::LongData(const char* str, usize cap, usize len) : data(alloc_long(cap)), capacity(cap), length(len) {
	if(str) {
		memcpy(data, str, len);
	}
	*(data + len) = 0;
}

// --------------------------------------------------- SHORT ---------------------------------------------------

String::ShortData::ShortData() : _data{0}, _length(0) {
}

String::ShortData::ShortData(const ShortData& s) {
	memcpy(this, &s, sizeof(ShortData));
}

String::ShortData::ShortData(const char* str, usize len) : _length(len) {
	if(str) {
		memcpy(_data, str, len);
	}
	*(_data + len) = 0;
}

// --------------------------------------------------- ALLOC ---------------------------------------------------

char* String::alloc_long(usize capacity) {
	return new char[capacity + 1];
}

usize String::compute_capacity(usize len) {
	return DefaultVectorResizePolicy().ideal_capacity(len);
}

void String::free_long(LongData& d) {
	delete[] d.data;
}

// --------------------------------------------------- STRING ---------------------------------------------------

String::String() : _s(ShortData()) {
}

String::String(const String& str) {
	if(str.is_long()) {
		new(&_l) LongData(str._l);
	} else {
		new(&_s) ShortData(str._s);
	}
}

String::String(String&& str) {
	if(str.is_long()) {
		new(&_l) LongData(std::move(str._l));
	} else {
		new(&_s) ShortData(str._s);
	}
}

String::String(const char* str) : String(str, strlen(str)) {
}

String::String(const char* str, usize len) {
	if(len > max_short_size) {
		new(&_l) LongData(str, len);
	} else {
		new(&_s) ShortData(str, len);
	}
}

String::String(const char* beg, const char* end) : String(beg, usize(end - beg)) {
}

String::~String() {
	if(is_long()) {
		free_long(_l);
	}
}

String String::from_owned(Owner<char*> owned) {
	usize len = strlen(owned);
	String str;
	str._l.length = len;
	str._l.capacity = len;
	str._l.data = owned;
	return str;
}

usize String::size() const {
	return is_long() ? _l.length : _s._length;
}

usize String::capacity() const {
	return is_long() ? _l.capacity : max_short_size;
}

bool String::is_empty() const {
	return !size();
}

bool String::is_long() const {
	return _l.length._is_long;
}

void String::clear() {
	if(is_long()) {
		free_long(_l);
	}
	new(&_s) ShortData();
}

char* String::data() {
	return is_long() ? _l.data : _s._data;
}

const char* String::data() const {
	return is_long() ? _l.data : _s._data;
}

String::iterator String::find(const String& str) {
	return const_cast<iterator>(const_this()->find(str));
}

String::const_iterator String::find(const String& str) const {
	const_iterator found = strstr(data(), str);
	return found ? found : end();
}

String String::sub_str(usize beg) const {
	return beg < size() ? String(begin() + beg) : String();
}

String String::sub_str(usize beg, usize len) const {
	usize si = size();
	beg = std::min(beg, si);
	return String(begin() + beg, std::min(len, si - beg));
}

String::operator const char*() const {
	return data();
}

String::operator char*() {
	return data();
}

void String::swap(String& str) {
	u8 str_buffer[sizeof(ShortData)];
	memcpy(str_buffer, &str._s, sizeof(ShortData));
	memcpy(&str._s, &_s, sizeof(ShortData));
	memcpy(&_s, str_buffer, sizeof(ShortData));
}

String& String::operator=(const String& str) {
	if(&str != this) {
		if(is_long()) {
			free_long(_l);
		}
		if(str.is_long()) {
			new(&_l) LongData(str._l);
		} else {
			new(&_s) ShortData(str._s);
		}
	}
	return *this;
}

String& String::operator=(String&& str) {
	swap(str);
	return *this;
}

String& String::operator+=(const String& str) {
	usize self_size = size();
	usize other_size = str.size();
	usize total_size = self_size + other_size;
	char* self_data = data();
	const char* other_data = str.data();

	if(capacity() >= total_size) {
		// in place
		memcpy(self_data + self_size, other_data, other_size);
		if(is_long()) {
			self_data[_l.length = total_size] = 0;
		} else {
			self_data[_s._length = total_size] = 0;
		}
	} else {
		LongData new_dat(nullptr, total_size);
		memcpy(new_dat.data, self_data, self_size);
		memcpy(new_dat.data + self_size, other_data, other_size);
		new_dat.data[total_size] = 0;

		if(is_long()) {
			free_long(_l);
		}
		memcpy(&_l, &new_dat, sizeof(LongData));
	}
	return *this;
}

char& String::operator[](usize i) {
	return data()[i];
}

char String::operator[](usize i) const {
	return data()[i];
}

bool String::operator==(const String& str) const {
	return size() == str.size() ? std::equal(begin(), end(), str.begin(), str.end()) : false;
}

bool String::operator!=(const String& str) const {
	return !operator==(str);
}

bool String::operator<(const String& str) const {
	//return strcmp(data(), str.data()) < 0;
	return std::lexicographical_compare(begin(), end(), str.begin(), str.end());
}

/*static usize utf8_len(char c) {
	if(c & 0x80) {
		usize len = 0;
		for(; c & 0x80; c <<= 1) {
			++len;
		}
		return len;
	}
	return 1;
}

Vector<u32> String::to_unicode() const {
	usize si = size();
	const char* dat = data();
	auto utf8 = vector_with_capacity<u32>((si * 2) / 3);
	while(dat < end()) {
		usize len = utf8_len(*dat);
		//u32 buffer = c & (0xFF >> len);
		if(len == 1) {
			utf8 << u32(*dat++);
		} else {
			u32 buffer = *dat++ & (0xFF >> len);
			for(usize l = 1; l < len; ++l) {
				buffer = (buffer << 6) | (*dat++ & 0x3F);
			}
			utf8 << buffer;
		}
	}
	return utf8;
}*/

}
}

<commit_msg>added a bunch of missing std::<commit_after>/*******************************
Copyright (c) 2016-2017 Grégoire Angerand

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 "String.h"
#include "Vector.h"
#include <y/test/test.h>
#include <memory>
#include <cstring>

namespace y {
namespace core {
// --------------------------------------------------- LONG ---------------------------------------------------

String::LongData::LongData() : data(nullptr), capacity(0), length(0) {
}

String::LongData::LongData(const LongData& l) : LongData(l.data, l.length) {
}

String::LongData::LongData(LongData&& l) : data(l.data), capacity(l.capacity), length(l.length) {
	l.data = nullptr;
}

String::LongData::LongData(const char* str, usize len) : LongData(str, compute_capacity(len), len) {
}

String::LongData::LongData(const char* str, usize cap, usize len) : data(alloc_long(cap)), capacity(cap), length(len) {
	if(str) {
		std::memcpy(data, str, len);
	}
	*(data + len) = 0;
}

// --------------------------------------------------- SHORT ---------------------------------------------------

String::ShortData::ShortData() : _data{0}, _length(0) {
}

String::ShortData::ShortData(const ShortData& s) {
	std::memcpy(this, &s, sizeof(ShortData));
}

String::ShortData::ShortData(const char* str, usize len) : _length(len) {
	if(str) {
		std::memcpy(_data, str, len);
	}
	*(_data + len) = 0;
}

// --------------------------------------------------- ALLOC ---------------------------------------------------

char* String::alloc_long(usize capacity) {
	return new char[capacity + 1];
}

usize String::compute_capacity(usize len) {
	return DefaultVectorResizePolicy().ideal_capacity(len);
}

void String::free_long(LongData& d) {
	delete[] d.data;
}

// --------------------------------------------------- STRING ---------------------------------------------------

String::String() : _s(ShortData()) {
}

String::String(const String& str) {
	if(str.is_long()) {
		new(&_l) LongData(str._l);
	} else {
		new(&_s) ShortData(str._s);
	}
}

String::String(String&& str) {
	if(str.is_long()) {
		new(&_l) LongData(std::move(str._l));
	} else {
		new(&_s) ShortData(str._s);
	}
}

String::String(const char* str) : String(str, strlen(str)) {
}

String::String(const char* str, usize len) {
	if(len > max_short_size) {
		new(&_l) LongData(str, len);
	} else {
		new(&_s) ShortData(str, len);
	}
}

String::String(const char* beg, const char* end) : String(beg, usize(end - beg)) {
}

String::~String() {
	if(is_long()) {
		free_long(_l);
	}
}

String String::from_owned(Owner<char*> owned) {
	usize len = strlen(owned);
	String str;
	str._l.length = len;
	str._l.capacity = len;
	str._l.data = owned;
	return str;
}

usize String::size() const {
	return is_long() ? _l.length : _s._length;
}

usize String::capacity() const {
	return is_long() ? _l.capacity : max_short_size;
}

bool String::is_empty() const {
	return !size();
}

bool String::is_long() const {
	return _l.length._is_long;
}

void String::clear() {
	if(is_long()) {
		free_long(_l);
	}
	new(&_s) ShortData();
}

char* String::data() {
	return is_long() ? _l.data : _s._data;
}

const char* String::data() const {
	return is_long() ? _l.data : _s._data;
}

String::iterator String::find(const String& str) {
	return const_cast<iterator>(const_this()->find(str));
}

String::const_iterator String::find(const String& str) const {
	const_iterator found = std::strstr(data(), str);
	return found ? found : end();
}

String String::sub_str(usize beg) const {
	return beg < size() ? String(begin() + beg) : String();
}

String String::sub_str(usize beg, usize len) const {
	usize si = size();
	beg = std::min(beg, si);
	return String(begin() + beg, std::min(len, si - beg));
}

String::operator const char*() const {
	return data();
}

String::operator char*() {
	return data();
}

void String::swap(String& str) {
	u8 str_buffer[sizeof(ShortData)];
	std::memcpy(str_buffer, &str._s, sizeof(ShortData));
	std::memcpy(&str._s, &_s, sizeof(ShortData));
	std::memcpy(&_s, str_buffer, sizeof(ShortData));
}

String& String::operator=(const String& str) {
	if(&str != this) {
		if(is_long()) {
			free_long(_l);
		}
		if(str.is_long()) {
			new(&_l) LongData(str._l);
		} else {
			new(&_s) ShortData(str._s);
		}
	}
	return *this;
}

String& String::operator=(String&& str) {
	swap(str);
	return *this;
}

String& String::operator+=(const String& str) {
	usize self_size = size();
	usize other_size = str.size();
	usize total_size = self_size + other_size;
	char* self_data = data();
	const char* other_data = str.data();

	if(capacity() >= total_size) {
		// in place
		std::memcpy(self_data + self_size, other_data, other_size);
		if(is_long()) {
			self_data[_l.length = total_size] = 0;
		} else {
			self_data[_s._length = total_size] = 0;
		}
	} else {
		LongData new_dat(nullptr, total_size);
		std::memcpy(new_dat.data, self_data, self_size);
		std::memcpy(new_dat.data + self_size, other_data, other_size);
		new_dat.data[total_size] = 0;

		if(is_long()) {
			free_long(_l);
		}
		std::memcpy(&_l, &new_dat, sizeof(LongData));
	}
	return *this;
}

char& String::operator[](usize i) {
	return data()[i];
}

char String::operator[](usize i) const {
	return data()[i];
}

bool String::operator==(const String& str) const {
	return size() == str.size() ? std::equal(begin(), end(), str.begin(), str.end()) : false;
}

bool String::operator!=(const String& str) const {
	return !operator==(str);
}

bool String::operator<(const String& str) const {
	//return strcmp(data(), str.data()) < 0;
	return std::lexicographical_compare(begin(), end(), str.begin(), str.end());
}

/*static usize utf8_len(char c) {
	if(c & 0x80) {
		usize len = 0;
		for(; c & 0x80; c <<= 1) {
			++len;
		}
		return len;
	}
	return 1;
}

Vector<u32> String::to_unicode() const {
	usize si = size();
	const char* dat = data();
	auto utf8 = vector_with_capacity<u32>((si * 2) / 3);
	while(dat < end()) {
		usize len = utf8_len(*dat);
		//u32 buffer = c & (0xFF >> len);
		if(len == 1) {
			utf8 << u32(*dat++);
		} else {
			u32 buffer = *dat++ & (0xFF >> len);
			for(usize l = 1; l < len; ++l) {
				buffer = (buffer << 6) | (*dat++ & 0x3F);
			}
			utf8 << buffer;
		}
	}
	return utf8;
}*/

}
}

<|endoftext|>