<commit_before>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef BENG_PROXY_RUBBER_HXX
#define BENG_PROXY_RUBBER_HXX

#include "system/LargeAllocation.hxx"
#include "util/Macros.hxx"
#include "util/Compiler.h"

#include <boost/intrusive/list.hpp>

#include <array>

#include <stddef.h>

struct AllocatorStats;
struct RubberObject;
struct RubberTable;

/**
 * The "rubber" memory allocator.  It is a buffer for storing many
 * large objects.  Unlike heap memory, unused areas are given back to
 * the operating system.
 */
class Rubber {
    /**
     * The maximum size of the memory map.  This is the value passed
     * to rubber_new() and will never be changed.
     */
    const size_t max_size;

    /**
     * The sum of all allocation sizes.
     */
    size_t netto_size;

    LargeAllocation allocation;

    /**
     * The table managing the allocations in the memory map.  At the
     * same time, this is the pointer to the memory map.
     */
    RubberTable *const table;

    /**
     * The threshold for each hole list.  The goal is to reduce the cost
     * of searching a hole that fits.
     */
    static constexpr size_t HOLE_THRESHOLDS[] = {
        1024 * 1024, 64 * 1024, 32 * 1024, 16 * 1024, 8192, 4096, 2048, 1024, 64, 0
    };

    gcc_const
    static unsigned LookupHoleThreshold(size_t size) noexcept {
        for (unsigned i = 0;; ++i)
            if (size >= HOLE_THRESHOLDS[i])
                return i;
    }

    static constexpr size_t N_HOLE_THRESHOLDS = ARRAY_SIZE(HOLE_THRESHOLDS);

    struct Hole final
        : boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>> {

        /**
         * The size of this hole (including the size of this struct).
         */
        size_t size;

        /**
         * The allocated objects before and after this hole.
         */
        unsigned previous_id, next_id;
    };

    typedef boost::intrusive::list<Hole,
                                   boost::intrusive::constant_time_size<false>> HoleList;

    /**
     * A list of all holes in the buffer.  Each array element hosts
     * its own list with holes at the size of
     * HOLE_THRESHOLDS[i] or bigger.
     */
    std::array<HoleList, N_HOLE_THRESHOLDS> holes;

public:
    /**
     * Throws std::bad_alloc on error.
     */
    explicit Rubber(size_t _max_size) noexcept;

    ~Rubber() noexcept;

    /**
     * Controls whether forked child processes inherit the allocator.
     * This is enabled by default.
     */
    void ForkCow(bool inherit) noexcept;

    /**
     * Returns the maximum total size of all allocations.
     */
    gcc_pure
    size_t GetMaxSize() const noexcept;

    /**
     * Returns the total size of all allocations.
     */
    size_t GetNettoSize() const noexcept {
        return netto_size;
    }

    /**
     * Returns the memory consumed by this object, not including the
     * allocation table.
     */
    gcc_pure
    size_t GetBruttoSize() const noexcept;

    gcc_pure
    AllocatorStats GetStats() const noexcept;

    void Compress() noexcept;

    /**
     * Add a new object with the specified size.  Use Write() to
     * actually copy data to the object.
     *
     * @param size the size, must be positive
     * @return the object id, or 0 on error
     */
    unsigned Add(size_t size) noexcept;
    void Remove(unsigned id) noexcept;

    /**
     * Shrink an object.  The new size must be smaller (or equal) to
     * the current size.  This is done in-place, possibly leaving a
     * gap that can only be used again after Compress() has been
     * called.
     *
     * @param new_size the new size, must be positive
     */
    void Shrink(unsigned id, size_t new_size) noexcept;

    /**
     * Returns the size of an allocation.  Due to padding, the
     * returned value may be a bit bigger than the size that was
     * passed to Add().
     */
    gcc_pure
    size_t GetSizeOf(unsigned id) const noexcept;

    /**
     * Return a writable pointer to the object.
     */
    gcc_pure
    void *Write(unsigned id) noexcept;

    /**
     * Return a read-only pointer to the object.
     */
    gcc_pure
    const void *Read(unsigned id) const noexcept;

private:
    gcc_pure
    void *WriteAt(size_t offset) noexcept {
        assert(offset <= max_size);

        return (uint8_t *)table + offset;
    }

    gcc_pure
    const void *ReadAt(size_t offset) const noexcept {
        assert(offset <= max_size);

        return (const uint8_t *)table + offset;
    }

    size_t OffsetOf(const void *p) const noexcept {
        return (const uint8_t *)p - (const uint8_t *)table;
    }

    size_t OffsetOf(const Hole &hole) const noexcept {
        return OffsetOf(&hole);
    }

    gcc_pure
    static size_t GetTotalHoleSize(const HoleList &holes) noexcept;

#ifndef NDEBUG
    size_t GetTotalHoleSize() const noexcept;
#endif

    gcc_pure
    static Hole *FindHole(HoleList &holes, size_t size) noexcept;

    gcc_pure
    Hole *FindHole(size_t size) noexcept;

    void AddToHoleList(Hole &hole) noexcept;

    void AddHole(size_t offset, size_t size,
                 unsigned previous_id, unsigned next_id) noexcept;
    void AddHoleAfter(unsigned reference_id,
                      size_t offset, size_t size) noexcept;

    /**
     * Replace the hole with the specified object.  If there is unused
     * space after the object, create a new #Hole instance
     * there.
     */
    void UseHole(Hole &hole, unsigned id, size_t size) noexcept;

    unsigned AddInHole(Hole &hole, size_t size) noexcept;

    /**
     * Try to find a hole between two objects, and insert a new object
     * there.
     *
     * @return the object id, or 0 on error
     */
    unsigned AddInHole(size_t size) noexcept;

    /**
     * Attempt to move the last allocation into a hole.  This is some kind
     * of simplified defragmentation.  It attempts to keep the "brutto"
     * size of this allocator small by filling holes.
     *
     * @param max_size move it only if it's not larger than this size
     */
    bool MoveLast(size_t max_object_size) noexcept;

    gcc_pure
    Hole *FindHoleBetween(RubberObject &a, RubberObject &b) noexcept;

    /**
     * If there is a hole between the two objects, discard it.  This
     * is used to remove holes at the end of the mmap when the last
     * object got removed.
     */
    void DiscardHoleBetween(RubberObject &a, RubberObject &b) noexcept;

    /**
     * The given object shall disappear at its current offset.  This
     * method will replace it with a #Hole instance, or will
     * grow/merge existing #Hole instances surrounding it.
     *
     * This method will not remove the #RubberObject from the table /
     * linked list, nor will it update the netto size.  It assumes
     * that the #RubberObject has already been removed from the linked
     * list.  It will corrupt data previously allocated by the
     * #RubberObject.
     */
    void ReplaceWithHole(RubberObject &o,
                         unsigned previous_id, unsigned next_id) noexcept;

    void MoveData(RubberObject &o, size_t new_offset) noexcept;

    HoleList &GetHoleList(size_t size) noexcept {
        return holes[LookupHoleThreshold(size)];
    }

    HoleList &GetHoleList(Hole &hole) noexcept {
        return GetHoleList(hole.size);
    }

    void RemoveHole(Hole &hole) noexcept {
        GetHoleList(hole).erase(HoleList::s_iterator_to(hole));
    }
};

#endif
<commit_msg>rubber: add class RubberAllocation<commit_after>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef BENG_PROXY_RUBBER_HXX
#define BENG_PROXY_RUBBER_HXX

#include "system/LargeAllocation.hxx"
#include "util/Macros.hxx"
#include "util/Compiler.h"

#include <boost/intrusive/list.hpp>

#include <array>
#include <algorithm>

#include <assert.h>
#include <stddef.h>

struct AllocatorStats;
struct RubberObject;
struct RubberTable;

/**
 * The "rubber" memory allocator.  It is a buffer for storing many
 * large objects.  Unlike heap memory, unused areas are given back to
 * the operating system.
 */
class Rubber {
    /**
     * The maximum size of the memory map.  This is the value passed
     * to rubber_new() and will never be changed.
     */
    const size_t max_size;

    /**
     * The sum of all allocation sizes.
     */
    size_t netto_size;

    LargeAllocation allocation;

    /**
     * The table managing the allocations in the memory map.  At the
     * same time, this is the pointer to the memory map.
     */
    RubberTable *const table;

    /**
     * The threshold for each hole list.  The goal is to reduce the cost
     * of searching a hole that fits.
     */
    static constexpr size_t HOLE_THRESHOLDS[] = {
        1024 * 1024, 64 * 1024, 32 * 1024, 16 * 1024, 8192, 4096, 2048, 1024, 64, 0
    };

    gcc_const
    static unsigned LookupHoleThreshold(size_t size) noexcept {
        for (unsigned i = 0;; ++i)
            if (size >= HOLE_THRESHOLDS[i])
                return i;
    }

    static constexpr size_t N_HOLE_THRESHOLDS = ARRAY_SIZE(HOLE_THRESHOLDS);

    struct Hole final
        : boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>> {

        /**
         * The size of this hole (including the size of this struct).
         */
        size_t size;

        /**
         * The allocated objects before and after this hole.
         */
        unsigned previous_id, next_id;
    };

    typedef boost::intrusive::list<Hole,
                                   boost::intrusive::constant_time_size<false>> HoleList;

    /**
     * A list of all holes in the buffer.  Each array element hosts
     * its own list with holes at the size of
     * HOLE_THRESHOLDS[i] or bigger.
     */
    std::array<HoleList, N_HOLE_THRESHOLDS> holes;

public:
    /**
     * Throws std::bad_alloc on error.
     */
    explicit Rubber(size_t _max_size) noexcept;

    ~Rubber() noexcept;

    /**
     * Controls whether forked child processes inherit the allocator.
     * This is enabled by default.
     */
    void ForkCow(bool inherit) noexcept;

    /**
     * Returns the maximum total size of all allocations.
     */
    gcc_pure
    size_t GetMaxSize() const noexcept;

    /**
     * Returns the total size of all allocations.
     */
    size_t GetNettoSize() const noexcept {
        return netto_size;
    }

    /**
     * Returns the memory consumed by this object, not including the
     * allocation table.
     */
    gcc_pure
    size_t GetBruttoSize() const noexcept;

    gcc_pure
    AllocatorStats GetStats() const noexcept;

    void Compress() noexcept;

    /**
     * Add a new object with the specified size.  Use Write() to
     * actually copy data to the object.
     *
     * @param size the size, must be positive
     * @return the object id, or 0 on error
     */
    unsigned Add(size_t size) noexcept;
    void Remove(unsigned id) noexcept;

    /**
     * Shrink an object.  The new size must be smaller (or equal) to
     * the current size.  This is done in-place, possibly leaving a
     * gap that can only be used again after Compress() has been
     * called.
     *
     * @param new_size the new size, must be positive
     */
    void Shrink(unsigned id, size_t new_size) noexcept;

    /**
     * Returns the size of an allocation.  Due to padding, the
     * returned value may be a bit bigger than the size that was
     * passed to Add().
     */
    gcc_pure
    size_t GetSizeOf(unsigned id) const noexcept;

    /**
     * Return a writable pointer to the object.
     */
    gcc_pure
    void *Write(unsigned id) noexcept;

    /**
     * Return a read-only pointer to the object.
     */
    gcc_pure
    const void *Read(unsigned id) const noexcept;

private:
    gcc_pure
    void *WriteAt(size_t offset) noexcept {
        assert(offset <= max_size);

        return (uint8_t *)table + offset;
    }

    gcc_pure
    const void *ReadAt(size_t offset) const noexcept {
        assert(offset <= max_size);

        return (const uint8_t *)table + offset;
    }

    size_t OffsetOf(const void *p) const noexcept {
        return (const uint8_t *)p - (const uint8_t *)table;
    }

    size_t OffsetOf(const Hole &hole) const noexcept {
        return OffsetOf(&hole);
    }

    gcc_pure
    static size_t GetTotalHoleSize(const HoleList &holes) noexcept;

#ifndef NDEBUG
    size_t GetTotalHoleSize() const noexcept;
#endif

    gcc_pure
    static Hole *FindHole(HoleList &holes, size_t size) noexcept;

    gcc_pure
    Hole *FindHole(size_t size) noexcept;

    void AddToHoleList(Hole &hole) noexcept;

    void AddHole(size_t offset, size_t size,
                 unsigned previous_id, unsigned next_id) noexcept;
    void AddHoleAfter(unsigned reference_id,
                      size_t offset, size_t size) noexcept;

    /**
     * Replace the hole with the specified object.  If there is unused
     * space after the object, create a new #Hole instance
     * there.
     */
    void UseHole(Hole &hole, unsigned id, size_t size) noexcept;

    unsigned AddInHole(Hole &hole, size_t size) noexcept;

    /**
     * Try to find a hole between two objects, and insert a new object
     * there.
     *
     * @return the object id, or 0 on error
     */
    unsigned AddInHole(size_t size) noexcept;

    /**
     * Attempt to move the last allocation into a hole.  This is some kind
     * of simplified defragmentation.  It attempts to keep the "brutto"
     * size of this allocator small by filling holes.
     *
     * @param max_size move it only if it's not larger than this size
     */
    bool MoveLast(size_t max_object_size) noexcept;

    gcc_pure
    Hole *FindHoleBetween(RubberObject &a, RubberObject &b) noexcept;

    /**
     * If there is a hole between the two objects, discard it.  This
     * is used to remove holes at the end of the mmap when the last
     * object got removed.
     */
    void DiscardHoleBetween(RubberObject &a, RubberObject &b) noexcept;

    /**
     * The given object shall disappear at its current offset.  This
     * method will replace it with a #Hole instance, or will
     * grow/merge existing #Hole instances surrounding it.
     *
     * This method will not remove the #RubberObject from the table /
     * linked list, nor will it update the netto size.  It assumes
     * that the #RubberObject has already been removed from the linked
     * list.  It will corrupt data previously allocated by the
     * #RubberObject.
     */
    void ReplaceWithHole(RubberObject &o,
                         unsigned previous_id, unsigned next_id) noexcept;

    void MoveData(RubberObject &o, size_t new_offset) noexcept;

    HoleList &GetHoleList(size_t size) noexcept {
        return holes[LookupHoleThreshold(size)];
    }

    HoleList &GetHoleList(Hole &hole) noexcept {
        return GetHoleList(hole.size);
    }

    void RemoveHole(Hole &hole) noexcept {
        GetHoleList(hole).erase(HoleList::s_iterator_to(hole));
    }
};

/**
 * An allocation from a #Rubber instance.  This class "owns" the
 * allocation and frees it automatically.
 */
class RubberAllocation {
    Rubber *rubber = nullptr;
    unsigned id = 0;

public:
    RubberAllocation() = default;

    RubberAllocation(Rubber &_rubber, unsigned _id) noexcept
        :rubber(&_rubber), id(_id) {}

    RubberAllocation(RubberAllocation &&src) noexcept
        :rubber(std::exchange(src.rubber, nullptr)),
         id(std::exchange(src.id, 0)) {}

    ~RubberAllocation() {
        if (id != 0)
            rubber->Remove(id);
    }

    RubberAllocation &operator=(RubberAllocation &&src) noexcept {
        using std::swap;
        swap(rubber, src.rubber);
        swap(id, src.id);
        return *this;
    }

    operator bool() const noexcept {
        return id != 0;
    }

    Rubber &GetRubber() const noexcept {
        assert(*this);
        return *rubber;
    }

    unsigned GetId() const noexcept {
        assert(*this);
        return id;
    }
};

#endif
<|endoftext|>


<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/>.
*/
/** @file Executive.cpp
 * @author Gav Wood <i@gavwood.com>
 * @date 2014
 */

#include "Executive.h"

#include <boost/timer.hpp>
#include <libdevcore/CommonIO.h>
#include <libevm/VMFactory.h>
#include <libevm/VM.h>
#include "Interface.h"
#include "State.h"
#include "ExtVM.h"
#include "Precompiled.h"
#include "BlockChain.h"
using namespace std;
using namespace dev;
using namespace dev::eth;

#define ETH_VMTRACE 1

Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level):
	m_s(_s),
	m_lastHashes(_s.getLastHashes(_bc, (unsigned)_s.info().number - 1)),
	m_depth(_level)
{}

u256 Executive::gasUsed() const
{
	return m_t.gas() - m_endGas;
}

void Executive::accrueSubState(SubState& _parentContext)
{
	if (m_ext)
		_parentContext += m_ext->sub;
}

bool Executive::setup(bytesConstRef _rlp)
{
	// Entry point for a user-executed transaction.
	try
	{
		m_t = Transaction(_rlp, CheckSignature::Sender);
	}
	catch (...)
	{
		clog(StateDetail) << "Invalid Signature";
		m_excepted = TransactionException::InvalidSignature;
		throw;
	}
	return setup();
}

bool Executive::setup()
{
	// Entry point for a user-executed transaction.

	// Avoid invalid transactions.
	auto nonceReq = m_s.transactionsFrom(m_t.sender());
	if (m_t.nonce() != nonceReq)
	{
		clog(StateDetail) << "Invalid Nonce: Require" << nonceReq << " Got" << m_t.nonce();
		m_excepted = TransactionException::InvalidNonce;
		BOOST_THROW_EXCEPTION(InvalidNonce() << RequirementError((bigint)nonceReq, (bigint)m_t.nonce()));
	}

	// Check gas cost is enough.
	auto gasRequired = Interface::txGas(m_t.data());

	if (m_t.gas() < gasRequired)
	{
		clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << gasRequired << " Got" << m_t.gas();
		m_excepted = TransactionException::OutOfGas;
		BOOST_THROW_EXCEPTION(OutOfGas() << RequirementError((bigint)gasRequired, (bigint)m_t.gas()));
	}

	bigint gasCost = (bigint)m_t.gas() * m_t.gasPrice();
	bigint totalCost = m_t.value() + gasCost;

	// Avoid unaffordable transactions.
	if (m_s.balance(m_t.sender()) < totalCost)
	{
		clog(StateDetail) << "Not enough cash: Require >" << totalCost << " Got" << m_s.balance(m_t.sender());
		m_excepted = TransactionException::NotEnoughCash;
		BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(totalCost, (bigint)m_s.balance(m_t.sender())));
	}

	u256 startGasUsed = m_s.gasUsed();
	if (startGasUsed + (bigint)m_t.gas() > m_s.m_currentBlock.gasLimit)
	{
		clog(StateDetail) << "Too much gas used in this block: Require <" << (m_s.m_currentBlock.gasLimit - startGasUsed) << " Got" << m_t.gas();
		m_excepted = TransactionException::BlockGasLimitReached;
		BOOST_THROW_EXCEPTION(BlockGasLimitReached() << RequirementError((bigint)(m_s.m_currentBlock.gasLimit - startGasUsed), (bigint)m_t.gas()));
	}

	// Increment associated nonce for sender.
	m_s.noteSending(m_t.sender());

	// Pay...
	clog(StateDetail) << "Paying" << formatBalance(u256(gasCost)) << "from sender for gas (" << m_t.gas() << "gas at" << formatBalance(m_t.gasPrice()) << ")";
	m_s.subBalance(m_t.sender(), gasCost);

	if (m_t.isCreation())
		return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)gasRequired, &m_t.data(), m_t.sender());
	else
		return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)gasRequired, m_t.sender());
}

bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress)
{
	m_isCreation = false;
//	cnote << "Transferring" << formatBalance(_value) << "to receiver.";
	auto it = !(_codeAddress & ~h160(0xffffffff)) ? precompiled().find((unsigned)(u160)_codeAddress) : precompiled().end();
	if (it != precompiled().end())
	{
		bigint g = it->second.gas(_data);
		if (_gas < g)
		{
			m_endGas = 0;
			m_excepted = TransactionException::OutOfGasBase;
			// Bail from exception.
			return true;	// true actually means "all finished - nothing more to be done regarding go().
		}
		else
		{
			m_endGas = (u256)(_gas - g);
			m_precompiledOut = it->second.exec(_data);
			m_out = &m_precompiledOut;
		}
	}
	else if (m_s.addressHasCode(_codeAddress))
	{
		m_vm = VMFactory::create(_gas);
		bytes const& c = m_s.code(_codeAddress);
		m_ext = make_shared<ExtVM>(m_s, m_lastHashes, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_depth);
	}
	else
		m_endGas = _gas;

	cdebug << m_s;
	State prev = m_s;
	m_s.transferBalance(_senderAddress, _receiveAddress, _value);
	cdebug << m_s;
	cdebug << m_s.diff(prev);

	return !m_ext;
}

bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _init, Address _origin)
{
	m_isCreation = true;

	// We can allow for the reverted state (i.e. that with which m_ext is constructed) to contain the m_newAddress, since
	// we delete it explicitly if we decide we need to revert.
	m_newAddress = right160(sha3(rlpList(_sender, m_s.transactionsFrom(_sender) - 1)));

	// Execute _init.
	if (!_init.empty())
	{
		m_vm = VMFactory::create(_gas);
		m_ext = make_shared<ExtVM>(m_s, m_lastHashes, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_depth);
	}

	m_s.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress), Account::ContractConception);
	m_s.transferBalance(_sender, m_newAddress, _endowment);

	if (_init.empty())
	{
		m_s.m_cache[m_newAddress].setCode({});
		m_endGas = _gas;
	}

	return !m_ext;
}

OnOpFunc Executive::simpleTrace()
{
	return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, VM* voidVM, ExtVMFace const* voidExt)
	{
		ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
		VM& vm = *voidVM;

		ostringstream o;
		o << endl << "    STACK" << endl;
		for (auto i: vm.stack())
			o << (h256)i << endl;
		o << "    MEMORY" << endl << ((vm.memory().size() > 1000) ? " mem size greater than 1000 bytes " : memDump(vm.memory()));
		o << "    STORAGE" << endl;
		for (auto const& i: ext.state().storage(ext.myAddress))
			o << showbase << hex << i.first << ": " << i.second << endl;
		dev::LogOutputStream<VMTraceChannel, false>(true) << o.str();
		dev::LogOutputStream<VMTraceChannel, false>(false) << " | " << dec << ext.depth << " | " << ext.myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " | " << dec << vm.gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32" << " ]";
	};
}

bool Executive::go(OnOpFunc const& _onOp)
{
	if (m_vm)
	{
#if ETH_TIMED_EXECUTIONS
		boost::timer t;
#endif
		try
		{
			m_out = m_vm->go(*m_ext, _onOp);
			m_endGas = m_vm->gas();

			if (m_isCreation)
			{
				if (m_out.size() * c_createDataGas <= m_endGas)
				{
					m_codeDeposit = CodeDeposit::Success;
					m_endGas -= m_out.size() * c_createDataGas;
				}
				else
				{
					m_codeDeposit = CodeDeposit::Failed;
					m_out.reset();
				}
				m_s.m_cache[m_newAddress].setCode(m_out.toBytes());
			}
		}
		catch (StepsDone const&)
		{
			return false;
		}
		catch (VMException const& _e)
		{
			clog(StateSafeExceptions) << "Safe VM Exception. " << diagnostic_information(_e);
			m_endGas = 0;
			m_excepted = toTransactionException(_e);
			m_ext->revert();
		}
		catch (Exception const& _e)
		{
			// TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
			cwarn << "Unexpected exception in VM. There may be a bug in this implementation. " << diagnostic_information(_e);
		}
		catch (std::exception const& _e)
		{
			// TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
			cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what();
		}
#if ETH_TIMED_EXECUTIONS
		cnote << "VM took:" << t.elapsed() << "; gas used: " << (sgas - m_endGas);
#endif
	}
	return true;
}

void Executive::finalize()
{
	// Accumulate refunds for suicides.
	if (m_ext)
		m_ext->sub.refunds += c_suicideRefundGas * m_ext->sub.suicides.size();

	// SSTORE refunds...
	// must be done before the miner gets the fees.
	if (m_ext)
		m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds);

	//	cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")";
	m_s.addBalance(m_t.sender(), m_endGas * m_t.gasPrice());

	u256 feesEarned = (m_t.gas() - m_endGas) * m_t.gasPrice();
	m_s.addBalance(m_s.m_currentBlock.coinbaseAddress, feesEarned);

	// Suicides...
	if (m_ext)
		for (auto a: m_ext->sub.suicides)
			m_s.m_cache[a].kill();

	// Logs..
	if (m_ext)
		m_logs = m_ext->sub.logs;
}
<commit_msg>Reduce verbosity.<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/>.
*/
/** @file Executive.cpp
 * @author Gav Wood <i@gavwood.com>
 * @date 2014
 */

#include "Executive.h"

#include <boost/timer.hpp>
#include <libdevcore/CommonIO.h>
#include <libevm/VMFactory.h>
#include <libevm/VM.h>
#include "Interface.h"
#include "State.h"
#include "ExtVM.h"
#include "Precompiled.h"
#include "BlockChain.h"
using namespace std;
using namespace dev;
using namespace dev::eth;

#define ETH_VMTRACE 1

Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level):
	m_s(_s),
	m_lastHashes(_s.getLastHashes(_bc, (unsigned)_s.info().number - 1)),
	m_depth(_level)
{}

u256 Executive::gasUsed() const
{
	return m_t.gas() - m_endGas;
}

void Executive::accrueSubState(SubState& _parentContext)
{
	if (m_ext)
		_parentContext += m_ext->sub;
}

bool Executive::setup(bytesConstRef _rlp)
{
	// Entry point for a user-executed transaction.
	try
	{
		m_t = Transaction(_rlp, CheckSignature::Sender);
	}
	catch (...)
	{
		clog(StateDetail) << "Invalid Signature";
		m_excepted = TransactionException::InvalidSignature;
		throw;
	}
	return setup();
}

bool Executive::setup()
{
	// Entry point for a user-executed transaction.

	// Avoid invalid transactions.
	auto nonceReq = m_s.transactionsFrom(m_t.sender());
	if (m_t.nonce() != nonceReq)
	{
		clog(StateDetail) << "Invalid Nonce: Require" << nonceReq << " Got" << m_t.nonce();
		m_excepted = TransactionException::InvalidNonce;
		BOOST_THROW_EXCEPTION(InvalidNonce() << RequirementError((bigint)nonceReq, (bigint)m_t.nonce()));
	}

	// Check gas cost is enough.
	auto gasRequired = Interface::txGas(m_t.data());

	if (m_t.gas() < gasRequired)
	{
		clog(StateDetail) << "Not enough gas to pay for the transaction: Require >" << gasRequired << " Got" << m_t.gas();
		m_excepted = TransactionException::OutOfGas;
		BOOST_THROW_EXCEPTION(OutOfGas() << RequirementError((bigint)gasRequired, (bigint)m_t.gas()));
	}

	bigint gasCost = (bigint)m_t.gas() * m_t.gasPrice();
	bigint totalCost = m_t.value() + gasCost;

	// Avoid unaffordable transactions.
	if (m_s.balance(m_t.sender()) < totalCost)
	{
		clog(StateDetail) << "Not enough cash: Require >" << totalCost << " Got" << m_s.balance(m_t.sender());
		m_excepted = TransactionException::NotEnoughCash;
		BOOST_THROW_EXCEPTION(NotEnoughCash() << RequirementError(totalCost, (bigint)m_s.balance(m_t.sender())));
	}

	u256 startGasUsed = m_s.gasUsed();
	if (startGasUsed + (bigint)m_t.gas() > m_s.m_currentBlock.gasLimit)
	{
		clog(StateDetail) << "Too much gas used in this block: Require <" << (m_s.m_currentBlock.gasLimit - startGasUsed) << " Got" << m_t.gas();
		m_excepted = TransactionException::BlockGasLimitReached;
		BOOST_THROW_EXCEPTION(BlockGasLimitReached() << RequirementError((bigint)(m_s.m_currentBlock.gasLimit - startGasUsed), (bigint)m_t.gas()));
	}

	// Increment associated nonce for sender.
	m_s.noteSending(m_t.sender());

	// Pay...
	clog(StateDetail) << "Paying" << formatBalance(u256(gasCost)) << "from sender for gas (" << m_t.gas() << "gas at" << formatBalance(m_t.gasPrice()) << ")";
	m_s.subBalance(m_t.sender(), gasCost);

	if (m_t.isCreation())
		return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)gasRequired, &m_t.data(), m_t.sender());
	else
		return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)gasRequired, m_t.sender());
}

bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress)
{
	m_isCreation = false;
//	cnote << "Transferring" << formatBalance(_value) << "to receiver.";
	auto it = !(_codeAddress & ~h160(0xffffffff)) ? precompiled().find((unsigned)(u160)_codeAddress) : precompiled().end();
	if (it != precompiled().end())
	{
		bigint g = it->second.gas(_data);
		if (_gas < g)
		{
			m_endGas = 0;
			m_excepted = TransactionException::OutOfGasBase;
			// Bail from exception.
			return true;	// true actually means "all finished - nothing more to be done regarding go().
		}
		else
		{
			m_endGas = (u256)(_gas - g);
			m_precompiledOut = it->second.exec(_data);
			m_out = &m_precompiledOut;
		}
	}
	else if (m_s.addressHasCode(_codeAddress))
	{
		m_vm = VMFactory::create(_gas);
		bytes const& c = m_s.code(_codeAddress);
		m_ext = make_shared<ExtVM>(m_s, m_lastHashes, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_depth);
	}
	else
		m_endGas = _gas;

	m_s.transferBalance(_senderAddress, _receiveAddress, _value);

	return !m_ext;
}

bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _init, Address _origin)
{
	m_isCreation = true;

	// We can allow for the reverted state (i.e. that with which m_ext is constructed) to contain the m_newAddress, since
	// we delete it explicitly if we decide we need to revert.
	m_newAddress = right160(sha3(rlpList(_sender, m_s.transactionsFrom(_sender) - 1)));

	// Execute _init.
	if (!_init.empty())
	{
		m_vm = VMFactory::create(_gas);
		m_ext = make_shared<ExtVM>(m_s, m_lastHashes, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_depth);
	}

	m_s.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress), Account::ContractConception);
	m_s.transferBalance(_sender, m_newAddress, _endowment);

	if (_init.empty())
	{
		m_s.m_cache[m_newAddress].setCode({});
		m_endGas = _gas;
	}

	return !m_ext;
}

OnOpFunc Executive::simpleTrace()
{
	return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, VM* voidVM, ExtVMFace const* voidExt)
	{
		ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
		VM& vm = *voidVM;

		ostringstream o;
		o << endl << "    STACK" << endl;
		for (auto i: vm.stack())
			o << (h256)i << endl;
		o << "    MEMORY" << endl << ((vm.memory().size() > 1000) ? " mem size greater than 1000 bytes " : memDump(vm.memory()));
		o << "    STORAGE" << endl;
		for (auto const& i: ext.state().storage(ext.myAddress))
			o << showbase << hex << i.first << ": " << i.second << endl;
		dev::LogOutputStream<VMTraceChannel, false>(true) << o.str();
		dev::LogOutputStream<VMTraceChannel, false>(false) << " | " << dec << ext.depth << " | " << ext.myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " | " << dec << vm.gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32" << " ]";
	};
}

bool Executive::go(OnOpFunc const& _onOp)
{
	if (m_vm)
	{
#if ETH_TIMED_EXECUTIONS
		boost::timer t;
#endif
		try
		{
			m_out = m_vm->go(*m_ext, _onOp);
			m_endGas = m_vm->gas();

			if (m_isCreation)
			{
				if (m_out.size() * c_createDataGas <= m_endGas)
				{
					m_codeDeposit = CodeDeposit::Success;
					m_endGas -= m_out.size() * c_createDataGas;
				}
				else
				{
					m_codeDeposit = CodeDeposit::Failed;
					m_out.reset();
				}
				m_s.m_cache[m_newAddress].setCode(m_out.toBytes());
			}
		}
		catch (StepsDone const&)
		{
			return false;
		}
		catch (VMException const& _e)
		{
			clog(StateSafeExceptions) << "Safe VM Exception. " << diagnostic_information(_e);
			m_endGas = 0;
			m_excepted = toTransactionException(_e);
			m_ext->revert();
		}
		catch (Exception const& _e)
		{
			// TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
			cwarn << "Unexpected exception in VM. There may be a bug in this implementation. " << diagnostic_information(_e);
		}
		catch (std::exception const& _e)
		{
			// TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
			cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what();
		}
#if ETH_TIMED_EXECUTIONS
		cnote << "VM took:" << t.elapsed() << "; gas used: " << (sgas - m_endGas);
#endif
	}
	return true;
}

void Executive::finalize()
{
	// Accumulate refunds for suicides.
	if (m_ext)
		m_ext->sub.refunds += c_suicideRefundGas * m_ext->sub.suicides.size();

	// SSTORE refunds...
	// must be done before the miner gets the fees.
	if (m_ext)
		m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds);

	//	cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")";
	m_s.addBalance(m_t.sender(), m_endGas * m_t.gasPrice());

	u256 feesEarned = (m_t.gas() - m_endGas) * m_t.gasPrice();
	m_s.addBalance(m_s.m_currentBlock.coinbaseAddress, feesEarned);

	// Suicides...
	if (m_ext)
		for (auto a: m_ext->sub.suicides)
			m_s.m_cache[a].kill();

	// Logs..
	if (m_ext)
		m_logs = m_ext->sub.logs;
}
<|endoftext|>


<commit_before>#include "webinforetriever.h"

#include <curl/curl.h>
#include <sstream>
#include <string>

#include "logger.h"
#include "stringutils.h"

namespace
{

static size_t curlWriteCallBack(void *contents, size_t size, size_t nmemb, void *userp)
{
    ((std::stringstream*)userp)->write((char*)contents, size * nmemb);
    return size * nmemb;
}

}

namespace geecxx
{

WebInfoRetriever::~WebInfoRetriever()
{
    curl_global_cleanup();
}

WebInfoRetriever& WebInfoRetriever::getInstance()
{
    // Static variable initialization ensures threadsafety in C++11 standard
    static WebInfoRetriever webInfoRetriever;

    return webInfoRetriever;
}

bool WebInfoRetriever::retrievePageTitle(const std::string& url, std::string &pageTitle)
{
    if (!_isInitiliazed) {
        return false;
    }

    long statusCode;
    std::string response;
    // Retrieve headers information first to check Content-Type
    if (!sendHttpRequest(url, RequestType::HEADER, response, statusCode)) {
        return false;
    }

    std::string contentType = getHttpHeaderField(response, "Content-Type");
    if (std::string::npos == contentType.find("text/html")) {
        // Not a HTML document, that's an error
        return false;
    }

    if (statusCode != 200l) {
        // HTML document with wrong HTTP code, we inform the user
        pageTitle = strHttpError(statusCode);
        return true;
    }

    // Retrieve body now that we are safe
    if (!sendHttpRequest(url, RequestType::BODY, response, statusCode)) {
        return false;
    }

    pageTitle = extractTitleFromContent(response);

    return true;
}

WebInfoRetriever::WebInfoRetriever()
{
    if (CURLE_OK == curl_global_init(CURL_GLOBAL_DEFAULT))
    {
        _isInitiliazed = true;
    } else {
        LOG_ERROR("Failed to initialize curl.");
    }
}

std::string WebInfoRetriever::extractTitleFromContent(const std::string &pageContent)
{
    size_t titleBegin = stringutils::findNoCase(pageContent, "<title>") + 7;
    size_t titleEnd = stringutils::findNoCase(pageContent, "</title>");
    if (std::string::npos == titleBegin || std::string::npos == titleEnd) {
        return "";
    }

    std::string title = pageContent.substr(titleBegin, titleEnd - titleBegin);

    title = _htmlEntitiesHelper.decode(title);
    // Inline formatting should happen after HTML entities decoding that may
    // bring extra white spaces
    stringutils::formatInline(title);

    return title;
}

std::string WebInfoRetriever::getHttpHeaderField(const std::string &headers, const std::string& key)
{
    std::string value;
    std::string line;

    std::string pattern = std::string(key + ": ");

    std::stringstream headersStream(headers);
    while (std::getline(headersStream, line)) {
        size_t o = stringutils::findNoCase(line, pattern);
        if (o != std::string::npos) {
            value = line.substr(o + pattern.size());
            break;
        }
    }

    return value;
}

bool WebInfoRetriever::sendHttpRequest(const std::string &url, RequestType type, std::string &response, long& statusCode)
{
    CURL *curl;
    CURLcode res;
    std::stringstream readBuffer;

    curl = curl_easy_init();
    if (nullptr == curl) {
        LOG_ERROR("Failed to allocate curl object.");
        return false;
    }

    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ::curlWriteCallBack);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    if (RequestType::HEADER == type) {
        curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
        curl_easy_setopt(curl, CURLOPT_HEADER, 1L);
    }

    res = curl_easy_perform(curl);
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode);
    curl_easy_cleanup(curl);
    if (res != CURLE_OK) {
        LOG_ERROR("CURL request failed: " + std::string(curl_easy_strerror(res)));
        return false;
    }

    response = readBuffer.str();

    return true;
}

std::string WebInfoRetriever::strHttpError(const long& errorCode)
{
    std::stringstream message;
    message << "HTTP " << errorCode << " - ";

    switch (errorCode) {
    case 400l:
        message << "Bad Request";
        break;
    case 401l:
        message << "Unauthorized";
        break;
    case 403l:
        message << "Forbidden";
        break;
    case 404l:
        message << "Not Found";
        break;
    case 429l:
        message << "Too many requests";
        break;
    case 500l:
        message << "Internal Server Error";
        break;
    case 502l:
        message << "Bad Gateway";
        break;
    case 503l:
        message << "Service Unavailable";
        break;
    case 504l:
        message << "Bad Gateway";
        break;
    default:
        message << "No description available";
    }

    return message.str();
}

}
<commit_msg>Improved logging of CURL errors<commit_after>#include "webinforetriever.h"

#include <curl/curl.h>
#include <sstream>
#include <string>

#include "logger.h"
#include "stringutils.h"

namespace
{

static size_t curlWriteCallBack(void *contents, size_t size, size_t nmemb, void *userp)
{
    ((std::stringstream*)userp)->write((char*)contents, size * nmemb);
    return size * nmemb;
}

}

namespace geecxx
{

WebInfoRetriever::~WebInfoRetriever()
{
    curl_global_cleanup();
}

WebInfoRetriever& WebInfoRetriever::getInstance()
{
    // Static variable initialization ensures threadsafety in C++11 standard
    static WebInfoRetriever webInfoRetriever;

    return webInfoRetriever;
}

bool WebInfoRetriever::retrievePageTitle(const std::string& url, std::string &pageTitle)
{
    if (!_isInitiliazed) {
        return false;
    }

    long statusCode;
    std::string response;
    // Retrieve headers information first to check Content-Type
    if (!sendHttpRequest(url, RequestType::HEADER, response, statusCode)) {
        return false;
    }

    std::string contentType = getHttpHeaderField(response, "Content-Type");
    if (std::string::npos == contentType.find("text/html")) {
        // Not a HTML document, that's an error
        return false;
    }

    if (statusCode != 200l) {
        // HTML document with wrong HTTP code, we inform the user
        pageTitle = strHttpError(statusCode);
        return true;
    }

    // Retrieve body now that we are safe
    if (!sendHttpRequest(url, RequestType::BODY, response, statusCode)) {
        return false;
    }

    pageTitle = extractTitleFromContent(response);

    return true;
}

WebInfoRetriever::WebInfoRetriever()
{
    if (CURLE_OK == curl_global_init(CURL_GLOBAL_DEFAULT))
    {
        _isInitiliazed = true;
    } else {
        LOG_ERROR("Failed to initialize curl.");
    }
}

std::string WebInfoRetriever::extractTitleFromContent(const std::string &pageContent)
{
    size_t titleBegin = stringutils::findNoCase(pageContent, "<title>") + 7;
    size_t titleEnd = stringutils::findNoCase(pageContent, "</title>");
    if (std::string::npos == titleBegin || std::string::npos == titleEnd) {
        return "";
    }

    std::string title = pageContent.substr(titleBegin, titleEnd - titleBegin);

    title = _htmlEntitiesHelper.decode(title);
    // Inline formatting should happen after HTML entities decoding that may
    // bring extra white spaces
    stringutils::formatInline(title);

    return title;
}

std::string WebInfoRetriever::getHttpHeaderField(const std::string &headers, const std::string& key)
{
    std::string value;
    std::string line;

    std::string pattern = std::string(key + ": ");

    std::stringstream headersStream(headers);
    while (std::getline(headersStream, line)) {
        size_t o = stringutils::findNoCase(line, pattern);
        if (o != std::string::npos) {
            value = line.substr(o + pattern.size());
            break;
        }
    }

    return value;
}

bool WebInfoRetriever::sendHttpRequest(const std::string &url, RequestType type, std::string &response, long& statusCode)
{
    CURL *curl;
    CURLcode res;
    std::stringstream readBuffer;

    curl = curl_easy_init();
    if (nullptr == curl) {
        LOG_ERROR("Failed to allocate curl object.");
        return false;
    }

    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ::curlWriteCallBack);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    if (RequestType::HEADER == type) {
        curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
        curl_easy_setopt(curl, CURLOPT_HEADER, 1L);
    }

    res = curl_easy_perform(curl);
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode);
    curl_easy_cleanup(curl);
    if (res != CURLE_OK) {
        LOG_ERROR("CURL request failed for URL: " + url);
        LOG_ERROR("Reason: " + std::string(curl_easy_strerror(res)));
        return false;
    }

    response = readBuffer.str();

    return true;
}

std::string WebInfoRetriever::strHttpError(const long& errorCode)
{
    std::stringstream message;
    message << "HTTP " << errorCode << " - ";

    switch (errorCode) {
    case 400l:
        message << "Bad Request";
        break;
    case 401l:
        message << "Unauthorized";
        break;
    case 403l:
        message << "Forbidden";
        break;
    case 404l:
        message << "Not Found";
        break;
    case 429l:
        message << "Too many requests";
        break;
    case 500l:
        message << "Internal Server Error";
        break;
    case 502l:
        message << "Bad Gateway";
        break;
    case 503l:
        message << "Service Unavailable";
        break;
    case 504l:
        message << "Bad Gateway";
        break;
    default:
        message << "No description available";
    }

    return message.str();
}

}
<|endoftext|>


<commit_before>/**************************************************************************

 The MIT License (MIT)

 Copyright (c) 2015 Dmitry Sovetov

 https://github.com/dmsovetov

 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 "WindowsWindow.h"

DC_BEGIN_DREEMCHEST

namespace Platform {

// ** createWindow
IWindow* createWindow( u32 width, u32 height )
{
    WindowsWindow* window = DC_NEW WindowsWindow;

    if( window->create( width, height ) ) {
        return window;
    }

    delete window;
    return NULL;
}

WindowsWindow::Windows WindowsWindow::s_windows;

u32 WindowsWindow::s_windowCount = 0;

// ** WindowsWindow::WindowsWindow
WindowsWindow::WindowsWindow( void ) : m_owner( NULL ), m_window( NULL )
{

}

// ** WindowsWindow::~WindowsWindow
WindowsWindow::~WindowsWindow( void )
{

}

// ** WindowsWindow::close
void WindowsWindow::close( void )
{
	NIMBLE_BREAK_IF( m_window == NULL, "invalid handle" );

	// ** Destroy window
	if( !DestroyWindow( m_window ) ) {
		return;
	}

	// ** Free the window class
	if( !UnregisterClass( m_className.c_str(), m_applicationInstance ) ) {
		return;
	}

	// ** Remove window from registry
	s32 removed = s_windows.erase( m_window );
	NIMBLE_BREAK_IF( removed != 1, "failed to remove window from a shared list" );

	m_window				= NULL;
	m_applicationInstance	= NULL;
}

// ** WindowsWindow::setOwner
void WindowsWindow::setOwner( Window* value )
{
    m_owner = value;
}

// ** WindowsWindow::owner
Window* WindowsWindow::owner( void ) const
{
    return m_owner;
}

// ** WindowsWindow::width
u32 WindowsWindow::width( void ) const
{
    RECT rect;
	GetClientRect( m_window, &rect );
	return rect.right - rect.left;
}

// ** WindowsWindow::height
u32 WindowsWindow::height( void ) const
{
	RECT rect;
	GetClientRect( m_window, &rect );
	return rect.bottom - rect.top;
}

// ** WindowsWindow::mapCursorToWindow
void WindowsWindow::mapCursorToWindow( s32& x, s32& y ) const
{
	POINT point = { x, y };
	ScreenToClient( m_window, &point );
	x = point.x;
	y = point.y;
}

// ** WindowsWindow::setCaption
void WindowsWindow::setCaption( const String& value )
{
	SetWindowText( m_window, value.c_str() );
}

// ** WindowsWindow::caption
String WindowsWindow::caption( void ) const
{
	s8 text[128];
    GetWindowText( m_window, text, 128 );
	return text;
}

// ** WindowsWindow::handle
void* WindowsWindow::handle( void ) const
{
    return reinterpret_cast<void*>( m_window );
}

// ** WindowsWindow::create
bool WindowsWindow::create( u32 width, u32 height )
{
	m_applicationInstance = GetModuleHandle( 0 );

	memset( &m_windowClass, 0, sizeof( m_windowClass ) );

	m_className = "dreemchest" + toString( s_windowCount++ );

	m_windowClass.hCursor		= LoadCursor( NULL, IDC_ARROW );
	m_windowClass.hInstance		= m_applicationInstance;
	m_windowClass.lpfnWndProc	= ( WNDPROC )windowProc;
	m_windowClass.lpszClassName	= m_className.c_str();
	m_windowClass.style			= CS_HREDRAW | CS_VREDRAW;
	m_windowClass.hbrBackground	= ( HBRUSH )COLOR_GRAYTEXT;

	if( !RegisterClass( &m_windowClass ) ) {
		LogError( "window", "failed to register window class" );
		return false;
	}

	// ** Initialize the window style
	u32 style = WS_OVERLAPPEDWINDOW | WS_SYSMENU | WS_MINIMIZEBOX;

	// ** Adjust the window rect to make sure the client are size is right.
	adjustWindowSize( style, width, height );

	// ** Caclulate the window position
	u32 x = ( GetSystemMetrics( SM_CXSCREEN ) / 2 ) - ( width  / 2 );
	u32 y = ( GetSystemMetrics( SM_CYSCREEN ) / 2 ) - ( height / 2 );

	m_window = CreateWindowEx( 0,
		m_windowClass.lpszClassName, "",
		style,
		x, y,
		width, height,
		NULL, NULL, m_applicationInstance, NULL );

	if( !m_window ) {
		LogError( "window", "failed to create window" );
		return false;
	}

	s_windows[m_window] = this;

	ShowWindow( m_window, SW_SHOW );
	SetFocus( m_window );

    return true;
}

// ** WindowsWindow::adjustWindowSize
void WindowsWindow::adjustWindowSize( u32 style, u32& width, u32& height )
{
	RECT rect = { 0, 0, width, height };
	AdjustWindowRect( &rect, style, FALSE );

	width  = rect.right  - rect.left;
	height = rect.bottom - rect.top;
}

// ** WindowsWindow::windowProc
LRESULT WindowsWindow::windowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
	WindowsWindow* window = s_windows[hWnd];

	switch( message ) {
	case WM_CLOSE:			window->owner()->release();
							if( s_windows.size() == 0 ) {
								PostQuitMessage( 0 );
							}
							break;

	case WM_KEYDOWN:		window->owner()->notifyKeyDown( translateKey( ( u32 )wParam ) );
							break;

	case WM_KEYUP:			window->owner()->notifyKeyUp( translateKey( ( u32 )wParam ) );
							break;

	case WM_LBUTTONDOWN:	window->owner()->notifyMouseDown( LOWORD( lParam ), HIWORD( lParam ) );
							break;

	case WM_LBUTTONUP:		window->owner()->notifyMouseUp( LOWORD( lParam ), HIWORD( lParam ) );
							break;

	case WM_MOUSEMOVE:		window->owner()->notifyMouseMove( LOWORD( lParam ), HIWORD( lParam ), LOWORD( lParam ), HIWORD( lParam ) );
							break;

	case WM_RBUTTONDOWN:	break;

	case WM_RBUTTONUP:		break;

	case WM_CHAR:			break;

	}

	return DefWindowProc( hWnd, message, wParam, lParam );
}

// ** WindowsWindow::translateKey
Key WindowsWindow::translateKey( u32 vkey )
{
	switch( vkey ) {
		// ** Numbers
	case 0x30: return Key::_0; break;	// 0
	case 0x31: return Key::_1; break;	// 1
	case 0x32: return Key::_2; break;	// 2
	case 0x33: return Key::_3; break;	// 3
	case 0x34: return Key::_4; break;	// 4
	case 0x35: return Key::_5; break;	// 5
	case 0x36: return Key::_6; break;	// 6
	case 0x37: return Key::_7; break;	// 7
	case 0x38: return Key::_8; break;	// 8
	case 0x39: return Key::_9; break;	// 9

	case VK_NUMPAD0: return Key::Num0; break;
	case VK_NUMPAD1: return Key::Num1; break;
	case VK_NUMPAD2: return Key::Num2; break;
	case VK_NUMPAD3: return Key::Num3; break;
	case VK_NUMPAD4: return Key::Num4; break;
	case VK_NUMPAD5: return Key::Num5; break;
	case VK_NUMPAD6: return Key::Num6; break;
	case VK_NUMPAD7: return Key::Num7; break;
	case VK_NUMPAD8: return Key::Num8; break;
	case VK_NUMPAD9: return Key::Num9; break;

		// ** Characters
	case 0x41: return Key::A; break;	// A
	case 0x42: return Key::B; break;	// B
	case 0x43: return Key::C; break;	// C
	case 0x44: return Key::D; break;	// D
	case 0x45: return Key::E; break;	// E
	case 0x46: return Key::F; break;	// F
	case 0x47: return Key::G; break;	// G
	case 0x48: return Key::H; break;	// H
	case 0x49: return Key::I; break;	// I
	case 0x4A: return Key::J; break;	// J
	case 0x4B: return Key::K; break;	// K
	case 0x4C: return Key::L; break;	// L
	case 0x4D: return Key::M; break;	// M
	case 0x4E: return Key::N; break;	// N
	case 0x4F: return Key::O; break;	// O
	case 0x50: return Key::P; break;	// P
	case 0x51: return Key::Q; break;	// Q
	case 0x52: return Key::R; break;	// R
	case 0x53: return Key::S; break;	// S
	case 0x54: return Key::T; break;	// T
	case 0x55: return Key::U; break;	// U
	case 0x56: return Key::V; break;	// V
	case 0x57: return Key::W; break;	// W
	case 0x58: return Key::X; break;	// X
	case 0x59: return Key::Y; break;	// Y
	case 0x5A: return Key::Z; break;	// Z

		// ** Functional
	case VK_F1: return Key::F1; break;
	case VK_F2: return Key::F2; break;
	case VK_F3: return Key::F3; break;
	case VK_F4: return Key::F4; break;
	case VK_F5: return Key::F5; break;
	case VK_F6: return Key::F6; break;
	case VK_F7: return Key::F7; break;
	case VK_F8: return Key::F8; break;
	case VK_F9: return Key::F9; break;
	case VK_F10: return Key::F10; break;
	case VK_F11: return Key::F11; break;
	case VK_F12: return Key::F12; break;

	case VK_ESCAPE: return Key::Escape; break;
	case VK_TAB: return Key::Tab; break;
	case VK_CAPITAL: return Key::Capital; break;
	case VK_SHIFT: return Key::Shift; break;
	case VK_CONTROL: return Key::Control; break;
	case VK_LWIN: return Key::Win; break;
	case VK_MENU: return Key::Alt; break;
	case VK_SPACE: return Key::Space; break;

	case VK_RETURN: return Key::Return; break;
	case VK_BACK: return Key::Back; break;

	case VK_PAUSE: return Key::Pause; break;
	case VK_SNAPSHOT: return Key::Snapshot; break;
	case VK_SCROLL: return Key::Scroll; break;

	case VK_NEXT: return Key::PgDown; break;
	case VK_PRIOR: return Key::PgUp; break;
	case VK_HOME: return Key::Home; break;
	case VK_END: return Key::End; break;
	case VK_INSERT: return Key::Insert; break;
	case VK_DELETE: return Key::Delete; break;

	case VK_UP: return Key::Up; break;
	case VK_DOWN: return Key::Down; break;
	case VK_LEFT: return Key::Left; break;
	case VK_RIGHT: return Key::Right; break;

		// ** Numpad
	case VK_NUMLOCK: return Key::Numlock; break;
	case VK_DIVIDE: return Key::Divide; break;
	case VK_MULTIPLY: return Key::Multiply; break;
	case VK_SUBTRACT: return Key::Subtract; break;
	case VK_ADD: return Key::Add; break;
	case VK_DECIMAL: return Key::Decimal; break;

		// ** Mouse
	case VK_RBUTTON: return Key::RButton; break;
	case VK_LBUTTON: return Key::LButton; break;
	case VK_MBUTTON: return Key::MButton; break;
	};

	return Key::Total;
}

} // namespace Platform

DC_END_DREEMCHEST<commit_msg>Fixed: Windows compilation error<commit_after>/**************************************************************************

 The MIT License (MIT)

 Copyright (c) 2015 Dmitry Sovetov

 https://github.com/dmsovetov

 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 "WindowsWindow.h"

DC_BEGIN_DREEMCHEST

namespace Platform {

// ** createWindow
IWindow* createWindow( u32 width, u32 height )
{
    WindowsWindow* window = DC_NEW WindowsWindow;

    if( window->create( width, height ) ) {
        return window;
    }

    delete window;
    return NULL;
}

WindowsWindow::Windows WindowsWindow::s_windows;

u32 WindowsWindow::s_windowCount = 0;

// ** WindowsWindow::WindowsWindow
WindowsWindow::WindowsWindow( void ) : m_owner( NULL ), m_window( NULL )
{

}

// ** WindowsWindow::~WindowsWindow
WindowsWindow::~WindowsWindow( void )
{

}

// ** WindowsWindow::close
void WindowsWindow::close( void )
{
	NIMBLE_BREAK_IF( m_window == NULL, "invalid handle" );

	// ** Destroy window
	if( !DestroyWindow( m_window ) ) {
		return;
	}

	// ** Free the window class
	if( !UnregisterClass( m_className.c_str(), m_applicationInstance ) ) {
		return;
	}

	// ** Remove window from registry
	s32 removed = s_windows.erase( m_window );
	NIMBLE_BREAK_IF( removed != 1, "failed to remove window from a shared list" );

	m_window				= NULL;
	m_applicationInstance	= NULL;
}

// ** WindowsWindow::setOwner
void WindowsWindow::setOwner( Window* value )
{
    m_owner = value;
}

// ** WindowsWindow::owner
Window* WindowsWindow::owner( void ) const
{
    return m_owner;
}

// ** WindowsWindow::width
u32 WindowsWindow::width( void ) const
{
    RECT rect;
	GetClientRect( m_window, &rect );
	return rect.right - rect.left;
}

// ** WindowsWindow::height
u32 WindowsWindow::height( void ) const
{
	RECT rect;
	GetClientRect( m_window, &rect );
	return rect.bottom - rect.top;
}

// ** WindowsWindow::mapCursorToWindow
void WindowsWindow::mapCursorToWindow( s32& x, s32& y ) const
{
	POINT point = { x, y };
	ScreenToClient( m_window, &point );
	x = point.x;
	y = point.y;
}

// ** WindowsWindow::setCaption
void WindowsWindow::setCaption( const String& value )
{
	SetWindowText( m_window, value.c_str() );
}

// ** WindowsWindow::caption
String WindowsWindow::caption( void ) const
{
	s8 text[128];
    GetWindowText( m_window, text, 128 );
	return text;
}

// ** WindowsWindow::handle
void* WindowsWindow::handle( void ) const
{
    return reinterpret_cast<void*>( m_window );
}

// ** WindowsWindow::create
bool WindowsWindow::create( u32 width, u32 height )
{
	m_applicationInstance = GetModuleHandle( 0 );

	memset( &m_windowClass, 0, sizeof( m_windowClass ) );

	m_className = "dreemchest" + toString( s_windowCount++ );

	m_windowClass.hCursor		= LoadCursor( NULL, IDC_ARROW );
	m_windowClass.hInstance		= m_applicationInstance;
	m_windowClass.lpfnWndProc	= ( WNDPROC )windowProc;
	m_windowClass.lpszClassName	= m_className.c_str();
	m_windowClass.style			= CS_HREDRAW | CS_VREDRAW;
	m_windowClass.hbrBackground	= ( HBRUSH )COLOR_GRAYTEXT;

	if( !RegisterClass( &m_windowClass ) ) {
		LogError( "window", "failed to register window class" );
		return false;
	}

	// ** Initialize the window style
	u32 style = WS_OVERLAPPEDWINDOW | WS_SYSMENU | WS_MINIMIZEBOX;

	// ** Adjust the window rect to make sure the client are size is right.
	adjustWindowSize( style, width, height );

	// ** Caclulate the window position
	u32 x = ( GetSystemMetrics( SM_CXSCREEN ) / 2 ) - ( width  / 2 );
	u32 y = ( GetSystemMetrics( SM_CYSCREEN ) / 2 ) - ( height / 2 );

	m_window = CreateWindowEx( 0,
		m_windowClass.lpszClassName, "",
		style,
		x, y,
		width, height,
		NULL, NULL, m_applicationInstance, NULL );

	if( !m_window ) {
		LogError( "window", "failed to create window" );
		return false;
	}

	s_windows[m_window] = this;

	ShowWindow( m_window, SW_SHOW );
	SetFocus( m_window );

    return true;
}

// ** WindowsWindow::adjustWindowSize
void WindowsWindow::adjustWindowSize( u32 style, u32& width, u32& height )
{
	RECT rect = { 0, 0, width, height };
	AdjustWindowRect( &rect, style, FALSE );

	width  = rect.right  - rect.left;
	height = rect.bottom - rect.top;
}

// ** WindowsWindow::windowProc
LRESULT WindowsWindow::windowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
	WindowsWindow* window = s_windows[hWnd];

	switch( message ) {
	case WM_CLOSE:			window->owner()->release();
							if( s_windows.size() == 0 ) {
								PostQuitMessage( 0 );
							}
							break;

	case WM_KEYDOWN:		window->owner()->notifyKeyDown( translateKey( ( u32 )wParam ) );
							break;

	case WM_KEYUP:			window->owner()->notifyKeyUp( translateKey( ( u32 )wParam ) );
							break;

    case WM_LBUTTONDOWN:	window->owner()->notifyMouseDown( Window::LeftMouseButton, LOWORD( lParam ), HIWORD( lParam ) );
							break;

	case WM_LBUTTONUP:		window->owner()->notifyMouseUp(Window::LeftMouseButton,  LOWORD( lParam ), HIWORD( lParam ) );
							break;

	case WM_MOUSEMOVE:		window->owner()->notifyMouseMove( Window::LeftMouseButton, LOWORD( lParam ), HIWORD( lParam ), LOWORD( lParam ), HIWORD( lParam ) );
							break;

	case WM_RBUTTONDOWN:	window->owner()->notifyMouseDown( Window::RightMouseButton, LOWORD( lParam ), HIWORD( lParam ) );
							break;

	case WM_RBUTTONUP:		window->owner()->notifyMouseDown( Window::RightMouseButton, LOWORD( lParam ), HIWORD( lParam ) );
							break;

	case WM_CHAR:			break;

	}

	return DefWindowProc( hWnd, message, wParam, lParam );
}

// ** WindowsWindow::translateKey
Key WindowsWindow::translateKey( u32 vkey )
{
	switch( vkey ) {
		// ** Numbers
	case 0x30: return Key::_0; break;	// 0
	case 0x31: return Key::_1; break;	// 1
	case 0x32: return Key::_2; break;	// 2
	case 0x33: return Key::_3; break;	// 3
	case 0x34: return Key::_4; break;	// 4
	case 0x35: return Key::_5; break;	// 5
	case 0x36: return Key::_6; break;	// 6
	case 0x37: return Key::_7; break;	// 7
	case 0x38: return Key::_8; break;	// 8
	case 0x39: return Key::_9; break;	// 9

	case VK_NUMPAD0: return Key::Num0; break;
	case VK_NUMPAD1: return Key::Num1; break;
	case VK_NUMPAD2: return Key::Num2; break;
	case VK_NUMPAD3: return Key::Num3; break;
	case VK_NUMPAD4: return Key::Num4; break;
	case VK_NUMPAD5: return Key::Num5; break;
	case VK_NUMPAD6: return Key::Num6; break;
	case VK_NUMPAD7: return Key::Num7; break;
	case VK_NUMPAD8: return Key::Num8; break;
	case VK_NUMPAD9: return Key::Num9; break;

		// ** Characters
	case 0x41: return Key::A; break;	// A
	case 0x42: return Key::B; break;	// B
	case 0x43: return Key::C; break;	// C
	case 0x44: return Key::D; break;	// D
	case 0x45: return Key::E; break;	// E
	case 0x46: return Key::F; break;	// F
	case 0x47: return Key::G; break;	// G
	case 0x48: return Key::H; break;	// H
	case 0x49: return Key::I; break;	// I
	case 0x4A: return Key::J; break;	// J
	case 0x4B: return Key::K; break;	// K
	case 0x4C: return Key::L; break;	// L
	case 0x4D: return Key::M; break;	// M
	case 0x4E: return Key::N; break;	// N
	case 0x4F: return Key::O; break;	// O
	case 0x50: return Key::P; break;	// P
	case 0x51: return Key::Q; break;	// Q
	case 0x52: return Key::R; break;	// R
	case 0x53: return Key::S; break;	// S
	case 0x54: return Key::T; break;	// T
	case 0x55: return Key::U; break;	// U
	case 0x56: return Key::V; break;	// V
	case 0x57: return Key::W; break;	// W
	case 0x58: return Key::X; break;	// X
	case 0x59: return Key::Y; break;	// Y
	case 0x5A: return Key::Z; break;	// Z

		// ** Functional
	case VK_F1: return Key::F1; break;
	case VK_F2: return Key::F2; break;
	case VK_F3: return Key::F3; break;
	case VK_F4: return Key::F4; break;
	case VK_F5: return Key::F5; break;
	case VK_F6: return Key::F6; break;
	case VK_F7: return Key::F7; break;
	case VK_F8: return Key::F8; break;
	case VK_F9: return Key::F9; break;
	case VK_F10: return Key::F10; break;
	case VK_F11: return Key::F11; break;
	case VK_F12: return Key::F12; break;

	case VK_ESCAPE: return Key::Escape; break;
	case VK_TAB: return Key::Tab; break;
	case VK_CAPITAL: return Key::Capital; break;
	case VK_SHIFT: return Key::Shift; break;
	case VK_CONTROL: return Key::Control; break;
	case VK_LWIN: return Key::Win; break;
	case VK_MENU: return Key::Alt; break;
	case VK_SPACE: return Key::Space; break;

	case VK_RETURN: return Key::Return; break;
	case VK_BACK: return Key::Back; break;

	case VK_PAUSE: return Key::Pause; break;
	case VK_SNAPSHOT: return Key::Snapshot; break;
	case VK_SCROLL: return Key::Scroll; break;

	case VK_NEXT: return Key::PgDown; break;
	case VK_PRIOR: return Key::PgUp; break;
	case VK_HOME: return Key::Home; break;
	case VK_END: return Key::End; break;
	case VK_INSERT: return Key::Insert; break;
	case VK_DELETE: return Key::Delete; break;

	case VK_UP: return Key::Up; break;
	case VK_DOWN: return Key::Down; break;
	case VK_LEFT: return Key::Left; break;
	case VK_RIGHT: return Key::Right; break;

		// ** Numpad
	case VK_NUMLOCK: return Key::Numlock; break;
	case VK_DIVIDE: return Key::Divide; break;
	case VK_MULTIPLY: return Key::Multiply; break;
	case VK_SUBTRACT: return Key::Subtract; break;
	case VK_ADD: return Key::Add; break;
	case VK_DECIMAL: return Key::Decimal; break;

		// ** Mouse
	case VK_RBUTTON: return Key::RButton; break;
	case VK_LBUTTON: return Key::LButton; break;
	case VK_MBUTTON: return Key::MButton; break;
	};

	return Key::Total;
}

} // namespace Platform

DC_END_DREEMCHEST<|endoftext|>


<commit_before>/*
 * Widget declarations.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "widget_class.hxx"

#include <string.h>

static bool
widget_check_untrusted_host(const WidgetClass &cls, const char *host)
{
    if (cls.untrusted_host == nullptr)
        /* trusted widget is only allowed on a trusted host name
           (host==nullptr) */
        return host == nullptr;

    if (host == nullptr)
        /* untrusted widget not allowed on trusted host name */
        return false;

    /* untrusted widget only allowed on matching untrusted host
       name */
    return strcmp(host, cls.untrusted_host) == 0;
}

static bool
widget_check_untrusted_prefix(const WidgetClass &cls, const char *host)
{
    if (cls.untrusted_prefix == nullptr)
        /* trusted widget is only allowed on a trusted host name
           (host==nullptr) */
        return host == nullptr;

    if (host == nullptr)
        /* untrusted widget not allowed on trusted host name */
        return false;

    /* untrusted widget only allowed on matching untrusted host
       name */
    size_t length = strlen(cls.untrusted_prefix);
    return memcmp(host, cls.untrusted_prefix, length) == 0 &&
        host[length] == '.';
}

static bool
widget_check_untrusted_site_suffix(const WidgetClass &cls,
                                   const char *host, const char *site_name)
{
    if (cls.untrusted_site_suffix == nullptr)
        /* trusted widget is only allowed on a trusted host name
           (host==nullptr) */
        return host == nullptr;

    if (host == nullptr || site_name == nullptr)
        /* untrusted widget not allowed on trusted host name */
        return false;

    size_t site_name_length = strlen(site_name);
    return memcmp(host, site_name, site_name_length) == 0 &&
        host[site_name_length] == '.' &&
        strcmp(host + site_name_length + 1,
               cls.untrusted_site_suffix) == 0;
}

static bool
widget_check_untrusted_raw_site_suffix(const WidgetClass &cls,
                                       const char *host, const char *site_name)
{
    if (cls.untrusted_raw_site_suffix == nullptr)
        /* trusted widget is only allowed on a trusted host name
           (host==nullptr) */
        return host == nullptr;

    if (host == nullptr || site_name == nullptr)
        /* untrusted widget not allowed on trusted host name */
        return false;

    size_t site_name_length = strlen(site_name);
    return memcmp(host, site_name, site_name_length) == 0 &&
        strcmp(host + site_name_length,
               cls.untrusted_raw_site_suffix) == 0;
}

bool
WidgetClass::CheckHost(const char *host, const char *site_name) const
{
    if (untrusted_host != nullptr)
        return widget_check_untrusted_host(*this, host);
    else if (untrusted_prefix != nullptr)
        return widget_check_untrusted_prefix(*this, host);
    else if (untrusted_site_suffix != nullptr)
        return widget_check_untrusted_site_suffix(*this, host, site_name);
    else if (untrusted_raw_site_suffix != nullptr)
        return widget_check_untrusted_raw_site_suffix(*this, host, site_name);
    else
        /* trusted widget is only allowed on a trusted host name
           (host==nullptr) */
        return host == nullptr;
}
<commit_msg>widget_untrusted: remove redundant checks<commit_after>/*
 * Widget declarations.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "widget_class.hxx"

#include <string.h>

static bool
widget_check_untrusted_host(const WidgetClass &cls, const char *host)
{
    assert(cls.untrusted_host != nullptr);

    if (host == nullptr)
        /* untrusted widget not allowed on trusted host name */
        return false;

    /* untrusted widget only allowed on matching untrusted host
       name */
    return strcmp(host, cls.untrusted_host) == 0;
}

static bool
widget_check_untrusted_prefix(const WidgetClass &cls, const char *host)
{
    assert(cls.untrusted_prefix != nullptr);

    if (host == nullptr)
        /* untrusted widget not allowed on trusted host name */
        return false;

    /* untrusted widget only allowed on matching untrusted host
       name */
    size_t length = strlen(cls.untrusted_prefix);
    return memcmp(host, cls.untrusted_prefix, length) == 0 &&
        host[length] == '.';
}

static bool
widget_check_untrusted_site_suffix(const WidgetClass &cls,
                                   const char *host, const char *site_name)
{
    assert(cls.untrusted_site_suffix != nullptr);

    if (host == nullptr || site_name == nullptr)
        /* untrusted widget not allowed on trusted host name */
        return false;

    size_t site_name_length = strlen(site_name);
    return memcmp(host, site_name, site_name_length) == 0 &&
        host[site_name_length] == '.' &&
        strcmp(host + site_name_length + 1,
               cls.untrusted_site_suffix) == 0;
}

static bool
widget_check_untrusted_raw_site_suffix(const WidgetClass &cls,
                                       const char *host, const char *site_name)
{
    assert(cls.untrusted_raw_site_suffix != nullptr);

    if (host == nullptr || site_name == nullptr)
        /* untrusted widget not allowed on trusted host name */
        return false;

    size_t site_name_length = strlen(site_name);
    return memcmp(host, site_name, site_name_length) == 0 &&
        strcmp(host + site_name_length,
               cls.untrusted_raw_site_suffix) == 0;
}

bool
WidgetClass::CheckHost(const char *host, const char *site_name) const
{
    if (untrusted_host != nullptr)
        return widget_check_untrusted_host(*this, host);
    else if (untrusted_prefix != nullptr)
        return widget_check_untrusted_prefix(*this, host);
    else if (untrusted_site_suffix != nullptr)
        return widget_check_untrusted_site_suffix(*this, host, site_name);
    else if (untrusted_raw_site_suffix != nullptr)
        return widget_check_untrusted_raw_site_suffix(*this, host, site_name);
    else
        /* trusted widget is only allowed on a trusted host name
           (host==nullptr) */
        return host == nullptr;
}
<|endoftext|>


<commit_before>#include <stdio.h>
#include <unistd.h>
#include <libgen.h>
#include <sys/time.h>
#include <omp.h>

#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <deque>
#include <unordered_map>
#include <algorithm>

// Typedef for our primary data types
typedef std::vector<std::string> pages_t;
typedef std::unordered_map<std::string, int> page_ids_t;
typedef std::unordered_map<int,std::vector<int>> page_links_t;

/* Load in the page titles and page ids from a file.
 *
 * Each line of the file will represent the page ID, starting from line 1 until
 * the end of the file. The page titles will be stored into the pages vector.
 * The page titles => ids will be stored in the page_ids map
 *
 * Returns: The total number of pages loaded
 */
int load_page_titles(const std::string &file_path, pages_t &pages, page_ids_t &page_ids)
{
	std::ifstream page_titles_file(file_path);
	std::string page_name;

	// We won't be using the first index of the vector for anything since
	// the page_id indexes start at 1 instead of 0. This should help to
	// prevent any errors during development
	pages.push_back("");

	// Read the page name into the vector and into the hash-map
	int index = 1;

	while (std::getline(page_titles_file, page_name))
	{
		pages.push_back(page_name);
		page_ids[page_name] = index++;
	}

	return index - 1;
}

/* Load in the page links from a file.
 *
 * Each line of the file should start with a page_id that represents a page, it
 * should then follow with a colon (':'). All numbers after the colon represent
 * page_ids that that page links to.
 *
 * Read all page links into the page_links hash map, where the key is the
 * page_id and the value is a vector of page_ids the page links to.
 *
 * Returns: The total number of links loaded
 */
int load_page_links(const std::string &file_path, page_links_t &page_links)
{
	std::ifstream page_links_file(file_path);
	std::string link_id;

	int current_link = 0;
	int total_links  = 0;

	while (page_links_file >> link_id)
	{
		if (link_id.back() == ':')
		{
			current_link = atoi(link_id.c_str());
		}
		else
		{
			page_links[current_link].push_back(atoi(link_id.c_str()));
			total_links++;
		}
	}

	return total_links;
}

/* Given a hash map containing page_ids that represent a path back to a given
 * root node extract those Ids and order them so that we move from the root node
 * back to the tail_id.
 *
 * The root node is the node whos parent value is -1.
 *
 * Returns: A vector containing the page ids of the path in order.
 */
std::vector<int> path_from_parents(std::unordered_map<int, int> &parent_links,
	int target_id)
{
	int current = target_id;
	std::vector<int> target_path;

	// Only fill the target_path vector if we have a parent link chain
	if (parent_links[target_id])
	{
		// Traverse thrugh the parent_links till we reach the root node
		do
		{
			target_path.push_back(current);
		}
		while ((current = parent_links[current]) != -1);

		// Reverse the target path list to be in proper order
		std::reverse(target_path.begin(), target_path.end());
	}

	return target_path;
}

/* Calculate the shortest path between two page_ids in the page links graph.
 * This uses the Depth First Search algorithm to travers through each of the
 * child nodes looking to see if they are the target id. If none are, then we
 * will traverse through all their children, up until we can find the target id.
 *
 * Returns: See the return value of `path_from_parents`.
 */
std::vector<int> path_between_pages(page_links_t &page_links,
	int start_id, int target_id)
{
	// Keep track of parent nodes so we can traverse back through the path
	std::unordered_map<int, int> parent_links;

	// The starting node will always be considered 'visited'
	parent_links[start_id] = -1;

	// Queue of the current nodes to look at
	std::deque<int> current_nodes;
	current_nodes.push_back(start_id);

	bool found_target = false;

	// Search until we find our target node
	while ( ! found_target && ! current_nodes.empty())
	{
		int available_nodes = current_nodes.size();

		#pragma omp parallel for
		for (int i = 0; i < available_nodes; ++i)
		{
			// Since we can't break out of parallelize loops we just need to
			// finish off what evers left by continuing through them all
			#pragma omp flush (found_target)
			if (found_target) continue;

			int current;

			#pragma omp critical
			{
				current = current_nodes.front();
				current_nodes.pop_front();
			}

			// Iterate through all linked pages
			for (int page_id : page_links[current])
			{
				// Keep track of how we traveled to this node
				#pragma omp critical
				if (parent_links[page_id] == 0)
				{
					parent_links[page_id] = current;

					if (target_id == page_id)
					{
						found_target = true;
						#pragma omp flush (found_target)
					}
					else
					{
						current_nodes.push_back(page_id);
					}
				}
			}
		}
	}

	return path_from_parents(parent_links, target_id);
}

/* Use the external python searcher script to find a given page by a search term
 * and output the resulting page name.
 *
 * Returns: Valid Wikipedia page name, or an empty string if one cannot be found
 */
std::string search_for_page(const std::string &py_searcher_bin, const std::string &search)
{
	FILE *pipe = popen((py_searcher_bin + " \"" + search + "\"").c_str(), "r");

	char buffer[255];
	std::string page_name = "";

	while ( ! feof(pipe))
	{
		if (fgets(buffer, 255, pipe) != NULL)
		{
			page_name += buffer;
		}
	}

	return page_name;
}

/* Kick off the main process:
 *
 *  1. Load in all of the wikipedia pages and page links into memory as a vector
 *     and hash map. Do this in parallel if possible
 *  2. Begin reading user input for what pages they would like to path find
 *  3. Use an external python script to get the page names from wikis search
 *  4. Path find using the BFS algorithm (again, in parallel)
 *  5. Repeat 2
 */
int main(int argc, char* argv[])
{
	// Used to store a list of page titles. Each index represents a page title
	pages_t pages;

	// Used to reverse lookup a page ID by it's title
	page_ids_t page_ids;

	// Used to store a list of page_ids that a given page_id links to
	page_links_t page_links;

	std::cout << "Welcome to Six-Degrees-of-Wikipedia path finder\n"
	          << "Please while we load Wikipedia into memory.\n\n";

	// Determine the base directory path
	char path_buffer[1024];
	readlink("/proc/self/exe", path_buffer, sizeof(path_buffer) - 1);
	std::string base_path = std::string(dirname(path_buffer)) + "/../";

	// Load in all page titles into a vector
	std::cout << "\033[32m==>\033[0m Reading in page titles as a vector & map\n";

	{
		struct timeval start, end;

		gettimeofday(&start, 0);
		int total = load_page_titles(base_path + "data/titles-sorted", pages, page_ids);
		gettimeofday(&end, 0);

		double duration = ((end.tv_sec - start.tv_sec) * 1000000u + end.tv_usec - start.tv_usec) / 1.e6;
		std::cout << "\033[34m  -> \033[0mRead in " << total    << " page titles\n";
		std::cout << "\033[34m  -> \033[0mTook "    << duration << " seconds\n";
	}

	// Load in all page links into a hash map
	std::cout << "\033[32m==>\033[0m Reading in page links as a map\n";

	{
		struct timeval start, end;

		gettimeofday(&start, 0);
		int total = load_page_links(base_path + "data/links-simple-sorted", page_links);
		gettimeofday(&end, 0);

		double duration = ((end.tv_sec - start.tv_sec) * 1000000u + end.tv_usec - start.tv_usec) / 1.e6;
		std::cout << "\033[94m  -> \033[0mRead in " << total    << " page links\n";
		std::cout << "\033[94m  -> \033[0mTook "    << duration << " seconds\n";
	}

	std::cout << "\033[92m==>\033[0m Beginning BFS on Wikipedia graph\n";

	// Get the ID's of the start and target nodes
	int start_id  = page_ids[argv[3]],
	    target_id = page_ids[argv[4]];

	if (start_id == target_id)
	{
		std::cout << "\n\033[92mNo clicks required. The page is the same!\n";
	}

	std::vector<int> target_path;

	struct timeval start, end;

	gettimeofday(&start, 0);
	target_path = path_between_pages(page_links, start_id, target_id);
	gettimeofday(&end, 0);

	double duration = ((end.tv_sec - start.tv_sec) * 1000000u + end.tv_usec - start.tv_usec) / 1.e6;
	std::cout << "\033[94m  -> \033[0mTook "    << duration << " seconds\n\n";

	// Print out the path between the pages
	for (int page_id : target_path)
	{
		std::cout << " * " << pages[page_id] << '\n';
	}

	return 0;
}
<commit_msg>Allow multiple path finding searches<commit_after>#include <stdio.h>
#include <unistd.h>
#include <libgen.h>
#include <sys/time.h>
#include <omp.h>

#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <deque>
#include <unordered_map>
#include <algorithm>

// Typedef for our primary data types
typedef std::vector<std::string> pages_t;
typedef std::unordered_map<std::string, int> page_ids_t;
typedef std::unordered_map<int,std::vector<int>> page_links_t;

/* Load in the page titles and page ids from a file.
 *
 * Each line of the file will represent the page ID, starting from line 1 until
 * the end of the file. The page titles will be stored into the pages vector.
 * The page titles => ids will be stored in the page_ids map
 *
 * Returns: The total number of pages loaded
 */
int load_page_titles(const std::string &file_path, pages_t &pages, page_ids_t &page_ids)
{
	std::ifstream page_titles_file(file_path);
	std::string page_name;

	// We won't be using the first index of the vector for anything since
	// the page_id indexes start at 1 instead of 0. This should help to
	// prevent any errors during development
	pages.push_back("");

	// Read the page name into the vector and into the hash-map
	int index = 1;

	while (std::getline(page_titles_file, page_name))
	{
		pages.push_back(page_name);
		page_ids[page_name] = index++;
	}

	return index - 1;
}

/* Load in the page links from a file.
 *
 * Each line of the file should start with a page_id that represents a page, it
 * should then follow with a colon (':'). All numbers after the colon represent
 * page_ids that that page links to.
 *
 * Read all page links into the page_links hash map, where the key is the
 * page_id and the value is a vector of page_ids the page links to.
 *
 * Returns: The total number of links loaded
 */
int load_page_links(const std::string &file_path, page_links_t &page_links)
{
	std::ifstream page_links_file(file_path);
	std::string link_id;

	int current_link = 0;
	int total_links  = 0;

	while (page_links_file >> link_id)
	{
		if (link_id.back() == ':')
		{
			current_link = atoi(link_id.c_str());
		}
		else
		{
			page_links[current_link].push_back(atoi(link_id.c_str()));
			total_links++;
		}
	}

	return total_links;
}

/* Given a hash map containing page_ids that represent a path back to a given
 * root node extract those Ids and order them so that we move from the root node
 * back to the tail_id.
 *
 * The root node is the node whos parent value is -1.
 *
 * Returns: A vector containing the page ids of the path in order.
 */
std::vector<int> path_from_parents(std::unordered_map<int, int> &parent_links,
	int target_id)
{
	int current = target_id;
	std::vector<int> target_path;

	// Only fill the target_path vector if we have a parent link chain
	if (parent_links[target_id])
	{
		// Traverse thrugh the parent_links till we reach the root node
		do
		{
			target_path.push_back(current);
		}
		while ((current = parent_links[current]) != -1);

		// Reverse the target path list to be in proper order
		std::reverse(target_path.begin(), target_path.end());
	}

	return target_path;
}

/* Calculate the shortest path between two page_ids in the page links graph.
 * This uses the Depth First Search algorithm to travers through each of the
 * child nodes looking to see if they are the target id. If none are, then we
 * will traverse through all their children, up until we can find the target id.
 *
 * Returns: See the return value of `path_from_parents`.
 */
std::vector<int> path_between_pages(page_links_t &page_links,
	int start_id, int target_id)
{
	// Keep track of parent nodes so we can traverse back through the path
	std::unordered_map<int, int> parent_links;

	// The starting node will always be considered 'visited'
	parent_links[start_id] = -1;

	// Queue of the current nodes to look at
	std::deque<int> current_nodes;
	current_nodes.push_back(start_id);

	bool found_target = false;

	// Search until we find our target node
	while ( ! found_target && ! current_nodes.empty())
	{
		int available_nodes = current_nodes.size();

		#pragma omp parallel for
		for (int i = 0; i < available_nodes; ++i)
		{
			// Since we can't break out of parallelize loops we just need to
			// finish off what evers left by continuing through them all
			#pragma omp flush (found_target)
			if (found_target) continue;

			int current;

			#pragma omp critical
			{
				current = current_nodes.front();
				current_nodes.pop_front();
			}

			// Iterate through all linked pages
			for (int page_id : page_links[current])
			{
				// Keep track of how we traveled to this node
				#pragma omp critical
				if (parent_links[page_id] == 0)
				{
					parent_links[page_id] = current;

					if (target_id == page_id)
					{
						found_target = true;
						#pragma omp flush (found_target)
					}
					else
					{
						current_nodes.push_back(page_id);
					}
				}
			}
		}
	}

	return path_from_parents(parent_links, target_id);
}

/* Use the external python searcher script to find a given page by a search term
 * and output the resulting page name.
 *
 * Returns: Valid Wikipedia page name, or an empty string if one cannot be found
 */
std::string search_for_page(const std::string &py_searcher_bin, const std::string &search)
{
	FILE *pipe = popen((py_searcher_bin + " \"" + search + "\"").c_str(), "r");

	char buffer[255];
	std::string page_name = "";

	while ( ! feof(pipe))
	{
		if (fgets(buffer, 255, pipe) != NULL)
		{
			page_name += buffer;
		}
	}

	return page_name;
}

/* Kick off the main process:
 *
 *  1. Load in all of the wikipedia pages and page links into memory as a vector
 *     and hash map. Do this in parallel if possible
 *  2. Begin reading user input for what pages they would like to path find
 *  3. Use an external python script to get the page names from wikis search
 *  4. Path find using the BFS algorithm (again, in parallel)
 *  5. Repeat 2
 */
int main(int argc, char* argv[])
{
	// Used to store a list of page titles. Each index represents a page title
	pages_t pages;

	// Used to reverse lookup a page ID by it's title
	page_ids_t page_ids;

	// Used to store a list of page_ids that a given page_id links to
	page_links_t page_links;

	std::cout << "Welcome to Six-Degrees-of-Wikipedia path finder\n"
	          << "Please while we load Wikipedia into memory.\n\n";

	// Determine the base directory path
	char path_buffer[1024];
	readlink("/proc/self/exe", path_buffer, sizeof(path_buffer) - 1);
	std::string base_path = std::string(dirname(path_buffer)) + "/../";

	// Load in all page titles into a vector
	std::cout << "\033[32m==>\033[0m Reading in page titles as a vector & map\n";

	{
		struct timeval start, end;

		gettimeofday(&start, 0);
		int total = load_page_titles(base_path + "data/titles-sorted", pages, page_ids);
		gettimeofday(&end, 0);

		double duration = ((end.tv_sec - start.tv_sec) * 1000000u + end.tv_usec - start.tv_usec) / 1.e6;
		std::cout << "\033[34m  -> \033[0mRead in " << total    << " page titles\n";
		std::cout << "\033[34m  -> \033[0mTook "    << duration << " seconds\n";
	}

	// Load in all page links into a hash map
	std::cout << "\033[32m==>\033[0m Reading in page links as a map\n";

	{
		struct timeval start, end;

		gettimeofday(&start, 0);
		int total = load_page_links(base_path + "data/links-simple-sorted", page_links);
		gettimeofday(&end, 0);

		double duration = ((end.tv_sec - start.tv_sec) * 1000000u + end.tv_usec - start.tv_usec) / 1.e6;
		std::cout << "\033[34m  -> \033[0mRead in " << total    << " page links\n";
		std::cout << "\033[34m  -> \033[0mTook "    << duration << " seconds\n";
	}

	std::cout << "\033[32m==>\033[0m Wikipedia graph loaded! Ready to path find!\n\n";

	while (true)
	{
		std::string start_page, target_page;

		std::cout << "Start Wikipedia Page: ";
		std::getline(std::cin, start_page);

		std::cout << "Target Wikipedia Page: ";
		std::getline(std::cin, target_page);

		// Use the the wiki_search.py script to lookup the wikipedia article name
		start_page  = search_for_page(base_path + "bin/wiki_search.py", start_page);
		target_page = search_for_page(base_path + "bin/wiki_search.py", target_page);

		// Remove newline from page names
		start_page.pop_back();
		target_page.pop_back();

		// Locate page Ids
		int start_id  = page_ids[start_page],
			target_id = page_ids[target_page];

		// Ensure both pages exist
		if ( ! start_id)
		{
			std::cout << "Start page is not a valid Wikipedia page";
			continue;
		}

		if ( ! target_id)
		{
			std::cout << "Start page is not a valid Wikipedia page";
			continue;
		}

		// Begin search
		std::cout << "\n\033[32m==>\033[0m Finding path from \033[35m"
		          << start_page  << "\033[0m → \033[34m"
		          << target_page << "\033[0m\n";

		std::vector<int> target_path;
		struct timeval start, end;

		gettimeofday(&start, 0);
		target_path = path_between_pages(page_links, start_id, target_id);
		gettimeofday(&end, 0);

		double duration = ((end.tv_sec - start.tv_sec) * 1000000u + end.tv_usec - start.tv_usec) / 1.e6;
		std::cout << "\033[34m  -> \033[0mTook "    << duration << " seconds\n\n";

		// Print out the path between the pages
		for (int page_id : target_path)
		{
			std::cout << " * " << pages[page_id] << '\n';
		}
	}

	return 0;
}
<|endoftext|>