<commit_before>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2017 The Bitcoin Core developers
// Copyright (c) 2009-2017 The DigiByte Core developers
// Copyright (c) 2018 The Globaltoken Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <pow.h>

#include <arith_uint256.h>
#include <chain.h>
#include <chainparams.h>
#include <globaltoken/hardfork.h>
#include <primitives/block.h>
#include <uint256.h>
#include <streams.h>
#include <crypto/algos/equihash/equihash.h>

#include <globaltoken/hardfork.h>

unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params, int algo)
{
    if(IsHardForkActivated(pindexLast->nTime))
		return GetNextWorkRequiredV2(pindexLast, pblock, params, algo);
	else
		return GetNextWorkRequiredV1(pindexLast, pblock, params, algo);
}

unsigned int GetNextWorkRequiredV1(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params, int algo)
{
    assert(pindexLast != nullptr);
    unsigned int nProofOfWorkLimit = GetAlgoPowLimit(ALGO_SHA256D).GetCompact(); // Before the Hardfork starts, there is just SHA256D

    // Only change once per difficulty adjustment interval
    if ((pindexLast->nHeight+1) % params.DifficultyAdjustmentInterval() != 0)
    {
        if (params.fPowAllowMinDifficultyBlocks)
        {
            // Special difficulty rule for testnet:
            // If the new block's timestamp is more than 2* 10 minutes
            // then allow mining of a min-difficulty block.
            if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + params.nPowTargetSpacing*2)
                return nProofOfWorkLimit;
            else
            {
                // Return the last non-special-min-difficulty-rules-block
                const CBlockIndex* pindex = pindexLast;
                while (pindex->pprev && pindex->nHeight % params.DifficultyAdjustmentInterval() != 0 && pindex->nBits == nProofOfWorkLimit)
                    pindex = pindex->pprev;
                return pindex->nBits;
            }
        }
        return pindexLast->nBits;
    }

    // Go back by what we want to be 14 days worth of blocks
    int nHeightFirst = pindexLast->nHeight - (params.DifficultyAdjustmentInterval()-1);
    assert(nHeightFirst >= 0);
    const CBlockIndex* pindexFirst = pindexLast->GetAncestor(nHeightFirst);
    assert(pindexFirst);

    return CalculateNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params);
}

unsigned int GetNextWorkRequiredV2(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params, int algo)
{

	unsigned int npowWorkLimit = GetAlgoPowLimit(algo).GetCompact();

	// Genesis block
	if (pindexLast == nullptr)
		return npowWorkLimit;

	if (Params().NetworkIDString() == CBaseChainParams::TESTNET)
	{
		// Special difficulty rule for testnet:
		// If the new block's timestamp is more than 2* 10 minutes
		// then allow mining of a min-difficulty block.
		if (pblock->nTime > pindexLast->nTime + params.nPowTargetSpacingV2*2)
			return npowWorkLimit;
		else
		{
			// Return the last non-special-min-difficulty-rules-block
			const CBlockIndex* pindex = pindexLast;
			while (pindex->pprev && pindex->nHeight % params.nInterval != 0 && pindex->nBits == npowWorkLimit)
				pindex = pindex->pprev;
			return pindex->nBits;
		}
	}

	// find first block in averaging interval
	// Go back by what we want to be nAveragingInterval blocks per algo
	const CBlockIndex* pindexFirst = pindexLast;
	for (int i = 0; pindexFirst && i < NUM_ALGOS*params.nAveragingInterval; i++)
	{
		pindexFirst = pindexFirst->pprev;
	}
	const CBlockIndex* pindexPrevAlgo = GetLastBlockIndexForAlgo(pindexLast, algo);
	if (pindexPrevAlgo == nullptr || pindexFirst == nullptr)
		return npowWorkLimit; // not enough blocks available

	// Limit adjustment step
	// Use medians to prevent time-warp attacks
	int64_t nActualTimespan = pindexLast->GetMedianTimePast() - pindexFirst->GetMedianTimePast();
	nActualTimespan = params.nAveragingTargetTimespan + (nActualTimespan - params.nAveragingTargetTimespan)/6;
	if (nActualTimespan < params.nMinActualTimespan)
		nActualTimespan = params.nMinActualTimespan;
	if (nActualTimespan > params.nMaxActualTimespan)
		nActualTimespan = params.nMaxActualTimespan;

	// Global retarget
	arith_uint256 bnNew;
	bnNew.SetCompact(pindexPrevAlgo->nBits);
	bnNew *= nActualTimespan;
	bnNew /= params.nAveragingTargetTimespan;

	// Per-algo retarget
	int nAdjustments = pindexPrevAlgo->nHeight - pindexLast->nHeight + NUM_ALGOS - 1;
	if (nAdjustments > 0)
	{
		for (int i = 0; i < nAdjustments; i++)
		{
			bnNew *= 100;
			bnNew /= 100 + params.nLocalDifficultyAdjustment;
		}
	}
	if (nAdjustments < 0)
	{
		for (int i = 0; i < -nAdjustments; i++)
		{
			bnNew *= 100 + params.nLocalDifficultyAdjustment;
			bnNew /= 100;
		}
	}

	if (bnNew > GetAlgoPowLimit(algo))
		bnNew = GetAlgoPowLimit(algo);

	return bnNew.GetCompact();
}

unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params)
{
    if (params.fPowNoRetargeting)
        return pindexLast->nBits;

    // Limit adjustment step
    int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
    if (nActualTimespan < params.nPowTargetTimespan/4)
        nActualTimespan = params.nPowTargetTimespan/4;
    if (nActualTimespan > params.nPowTargetTimespan*4)
        nActualTimespan = params.nPowTargetTimespan*4;

    // Retarget
    const arith_uint256 bnPowLimit = GetAlgoPowLimit(ALGO_SHA256D);
    arith_uint256 bnNew;
    bnNew.SetCompact(pindexLast->nBits);
    bnNew *= nActualTimespan;
    bnNew /= params.nPowTargetTimespan;

    if (bnNew > bnPowLimit)
        bnNew = bnPowLimit;

    return bnNew.GetCompact();
}

bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams& params)
{
    unsigned int n = params.EquihashN();
    unsigned int k = params.EquihashK();

    // Hash state
    crypto_generichash_blake2b_state state;
    EhInitialiseState(n, k, state);

    // I = the block header minus nonce and solution.
    CEquihashInput I{*pblock};
    // I||V
    CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
    ss << I;
    ss << pblock->nNonce;

    // H(I||V||...
    crypto_generichash_blake2b_update(&state, (unsigned char*)&ss[0], ss.size());

    bool isValid;
    EhIsValidSolution(n, k, state, pblock->nSolution, isValid);
    if (!isValid)
        return false;

    return true;
}

bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& params, int algo)
{
    bool fNegative;
    bool fOverflow;
    arith_uint256 bnTarget;

    bnTarget.SetCompact(nBits, &fNegative, &fOverflow);

    // Check range
    if (fNegative || bnTarget == 0 || fOverflow || bnTarget > GetAlgoPowLimit(algo))
        return false;

    // Check proof of work matches claimed amount
    if (UintToArith256(hash) > bnTarget)
        return false;

    return true;
}

const CBlockIndex* GetLastBlockIndexForAlgo(const CBlockIndex* pindex, int algo)
{
	for (;;)
	{
		if (!pindex)
			return nullptr;
		if (pindex->GetAlgo() == algo)
			return pindex;
		pindex = pindex->pprev;
	}
	return nullptr;
}<commit_msg>Double including fixed<commit_after>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2017 The Bitcoin Core developers
// Copyright (c) 2009-2017 The DigiByte Core developers
// Copyright (c) 2018 The Globaltoken Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <pow.h>

#include <arith_uint256.h>
#include <chain.h>
#include <chainparams.h>
#include <globaltoken/hardfork.h>
#include <primitives/block.h>
#include <uint256.h>
#include <streams.h>
#include <crypto/algos/equihash/equihash.h>

unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params, int algo)
{
    if(IsHardForkActivated(pindexLast->nTime))
		return GetNextWorkRequiredV2(pindexLast, pblock, params, algo);
	else
		return GetNextWorkRequiredV1(pindexLast, pblock, params, algo);
}

unsigned int GetNextWorkRequiredV1(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params, int algo)
{
    assert(pindexLast != nullptr);
    unsigned int nProofOfWorkLimit = GetAlgoPowLimit(ALGO_SHA256D).GetCompact(); // Before the Hardfork starts, there is just SHA256D

    // Only change once per difficulty adjustment interval
    if ((pindexLast->nHeight+1) % params.DifficultyAdjustmentInterval() != 0)
    {
        if (params.fPowAllowMinDifficultyBlocks)
        {
            // Special difficulty rule for testnet:
            // If the new block's timestamp is more than 2* 10 minutes
            // then allow mining of a min-difficulty block.
            if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + params.nPowTargetSpacing*2)
                return nProofOfWorkLimit;
            else
            {
                // Return the last non-special-min-difficulty-rules-block
                const CBlockIndex* pindex = pindexLast;
                while (pindex->pprev && pindex->nHeight % params.DifficultyAdjustmentInterval() != 0 && pindex->nBits == nProofOfWorkLimit)
                    pindex = pindex->pprev;
                return pindex->nBits;
            }
        }
        return pindexLast->nBits;
    }

    // Go back by what we want to be 14 days worth of blocks
    int nHeightFirst = pindexLast->nHeight - (params.DifficultyAdjustmentInterval()-1);
    assert(nHeightFirst >= 0);
    const CBlockIndex* pindexFirst = pindexLast->GetAncestor(nHeightFirst);
    assert(pindexFirst);

    return CalculateNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params);
}

unsigned int GetNextWorkRequiredV2(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params, int algo)
{

	unsigned int npowWorkLimit = GetAlgoPowLimit(algo).GetCompact();

	// Genesis block
	if (pindexLast == nullptr)
		return npowWorkLimit;

	if (Params().NetworkIDString() == CBaseChainParams::TESTNET)
	{
		// Special difficulty rule for testnet:
		// If the new block's timestamp is more than 2* 10 minutes
		// then allow mining of a min-difficulty block.
		if (pblock->nTime > pindexLast->nTime + params.nPowTargetSpacingV2*2)
			return npowWorkLimit;
		else
		{
			// Return the last non-special-min-difficulty-rules-block
			const CBlockIndex* pindex = pindexLast;
			while (pindex->pprev && pindex->nHeight % params.nInterval != 0 && pindex->nBits == npowWorkLimit)
				pindex = pindex->pprev;
			return pindex->nBits;
		}
	}

	// find first block in averaging interval
	// Go back by what we want to be nAveragingInterval blocks per algo
	const CBlockIndex* pindexFirst = pindexLast;
	for (int i = 0; pindexFirst && i < NUM_ALGOS*params.nAveragingInterval; i++)
	{
		pindexFirst = pindexFirst->pprev;
	}
	const CBlockIndex* pindexPrevAlgo = GetLastBlockIndexForAlgo(pindexLast, algo);
	if (pindexPrevAlgo == nullptr || pindexFirst == nullptr)
		return npowWorkLimit; // not enough blocks available

	// Limit adjustment step
	// Use medians to prevent time-warp attacks
	int64_t nActualTimespan = pindexLast->GetMedianTimePast() - pindexFirst->GetMedianTimePast();
	nActualTimespan = params.nAveragingTargetTimespan + (nActualTimespan - params.nAveragingTargetTimespan)/6;
	if (nActualTimespan < params.nMinActualTimespan)
		nActualTimespan = params.nMinActualTimespan;
	if (nActualTimespan > params.nMaxActualTimespan)
		nActualTimespan = params.nMaxActualTimespan;

	// Global retarget
	arith_uint256 bnNew;
	bnNew.SetCompact(pindexPrevAlgo->nBits);
	bnNew *= nActualTimespan;
	bnNew /= params.nAveragingTargetTimespan;

	// Per-algo retarget
	int nAdjustments = pindexPrevAlgo->nHeight - pindexLast->nHeight + NUM_ALGOS - 1;
	if (nAdjustments > 0)
	{
		for (int i = 0; i < nAdjustments; i++)
		{
			bnNew *= 100;
			bnNew /= 100 + params.nLocalDifficultyAdjustment;
		}
	}
	if (nAdjustments < 0)
	{
		for (int i = 0; i < -nAdjustments; i++)
		{
			bnNew *= 100 + params.nLocalDifficultyAdjustment;
			bnNew /= 100;
		}
	}

	if (bnNew > GetAlgoPowLimit(algo))
		bnNew = GetAlgoPowLimit(algo);

	return bnNew.GetCompact();
}

unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params)
{
    if (params.fPowNoRetargeting)
        return pindexLast->nBits;

    // Limit adjustment step
    int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
    if (nActualTimespan < params.nPowTargetTimespan/4)
        nActualTimespan = params.nPowTargetTimespan/4;
    if (nActualTimespan > params.nPowTargetTimespan*4)
        nActualTimespan = params.nPowTargetTimespan*4;

    // Retarget
    const arith_uint256 bnPowLimit = GetAlgoPowLimit(ALGO_SHA256D);
    arith_uint256 bnNew;
    bnNew.SetCompact(pindexLast->nBits);
    bnNew *= nActualTimespan;
    bnNew /= params.nPowTargetTimespan;

    if (bnNew > bnPowLimit)
        bnNew = bnPowLimit;

    return bnNew.GetCompact();
}

bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams& params)
{
    unsigned int n = params.EquihashN();
    unsigned int k = params.EquihashK();

    // Hash state
    crypto_generichash_blake2b_state state;
    EhInitialiseState(n, k, state);

    // I = the block header minus nonce and solution.
    CEquihashInput I{*pblock};
    // I||V
    CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
    ss << I;
    ss << pblock->nNonce;

    // H(I||V||...
    crypto_generichash_blake2b_update(&state, (unsigned char*)&ss[0], ss.size());

    bool isValid;
    EhIsValidSolution(n, k, state, pblock->nSolution, isValid);
    if (!isValid)
        return false;

    return true;
}

bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& params, int algo)
{
    bool fNegative;
    bool fOverflow;
    arith_uint256 bnTarget;

    bnTarget.SetCompact(nBits, &fNegative, &fOverflow);

    // Check range
    if (fNegative || bnTarget == 0 || fOverflow || bnTarget > GetAlgoPowLimit(algo))
        return false;

    // Check proof of work matches claimed amount
    if (UintToArith256(hash) > bnTarget)
        return false;

    return true;
}

const CBlockIndex* GetLastBlockIndexForAlgo(const CBlockIndex* pindex, int algo)
{
	for (;;)
	{
		if (!pindex)
			return nullptr;
		if (pindex->GetAlgo() == algo)
			return pindex;
		pindex = pindex->pprev;
	}
	return nullptr;
}<|endoftext|>


<commit_before>#pragma once
//=====================================================================//
/*! @file
	@brief  NES Emulator クラス
	@author 平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include "main.hpp"
#include "utils/i_scene.hpp"
#include "utils/director.hpp"
#include "widgets/widget.hpp"
#include "widgets/widget_frame.hpp"
#include "widgets/widget_terminal.hpp"
#include "gl_fw/gltexfb.hpp"
#include "snd_io/pcm.hpp"

extern "C" {
	#include "nes.h"
	#include "nesinput.h"
	extern const rgb_t* get_palette();
};

namespace app {

	class nesemu : public utils::i_scene {

		utils::director<core>&	director_;

		gui::widget_frame*		terminal_frame_;
		gui::widget_terminal*	terminal_core_;

		gl::texfb			texfb_;

		static const int nes_width_  = 256;
		static const int nes_height_ = 224;

		nes_t*	nes_;

		uint8_t		sound_[1024];
		al::audio	audio_;

		uint8_t	fb_[nes_width_ * nes_height_ * 4];

		nesinput_t	inp_[2];

		void pad_()
		{
	   		gl::core& core = gl::core::get_instance();

			const gl::device& dev = core.get_device();

			inp_[0].data = 0;
			inp_[1].data = 0;

			// A
			if(dev.get_level(gl::device::key::Z)) {
				inp_[0].data |= INP_PAD_A;
			}
			if(dev.get_level(gl::device::key::GAME_0)) {
				inp_[0].data |= INP_PAD_A;
			}

			// B
			if(dev.get_level(gl::device::key::X)) {
				inp_[0].data |= INP_PAD_B;
			}
			if(dev.get_level(gl::device::key::GAME_1)) {
				inp_[0].data |= INP_PAD_B;
			}

			// SELECT
			if(dev.get_level(gl::device::key::_1)) {
				inp_[0].data |= INP_PAD_SELECT;
			}
			if(dev.get_level(gl::device::key::GAME_2)) {
				inp_[0].data |= INP_PAD_SELECT;
			}
			// START
			if(dev.get_level(gl::device::key::_2)) {
				inp_[0].data |= INP_PAD_START;
			}
			if(dev.get_level(gl::device::key::GAME_3)) {
				inp_[0].data |= INP_PAD_START;
			}

			if(dev.get_level(gl::device::key::LEFT)) {
   		 		inp_[0].data |= INP_PAD_LEFT;
			}
			if(dev.get_level(gl::device::key::GAME_LEFT)) {
   		 		inp_[0].data |= INP_PAD_LEFT;
			}
			if(dev.get_level(gl::device::key::RIGHT)) {
   		 		inp_[0].data |= INP_PAD_RIGHT;
			}
			if(dev.get_level(gl::device::key::GAME_RIGHT)) {
   		 		inp_[0].data |= INP_PAD_RIGHT;
			}
			if(dev.get_level(gl::device::key::UP)) {
   		 		inp_[0].data |= INP_PAD_UP;
			}
			if(dev.get_level(gl::device::key::GAME_UP)) {
   		 		inp_[0].data |= INP_PAD_RIGHT;
			}
			if(dev.get_level(gl::device::key::DOWN)) {
   		 		inp_[0].data |= INP_PAD_DOWN;
			}
			if(dev.get_level(gl::device::key::GAME_DOWN)) {
   		 		inp_[0].data |= INP_PAD_DOWN;
			}
		}

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief  コンストラクター
		*/
		//-----------------------------------------------------------------//
		nesemu(utils::director<core>& d) : director_(d),
			terminal_frame_(nullptr), terminal_core_(nullptr),
			nes_(nullptr)
		{ }

		//-----------------------------------------------------------------//
		/*!
			@brief  デストラクター
		*/
		//-----------------------------------------------------------------//
		virtual ~nesemu() { }

		//-----------------------------------------------------------------//
		/*!
			@brief  初期化
		*/
		//-----------------------------------------------------------------//
		void initialize()
		{
	   		gl::core& core = gl::core::get_instance();

			texfb_.initialize(nes_width_, nes_height_, 32);

			nes_ = nes_create();

			// regist input
			inp_[0].type = INP_JOYPAD0;
			inp_[0].data = 0;
			input_register(&inp_[0]);
			inp_[1].type = INP_JOYPAD1;
			inp_[1].data = 0;
			input_register(&inp_[1]);

			audio_ = al::create_audio(al::audio_format::PCM8_MONO);
			audio_->create(44100, 44100 / 60);

			auto path = core.get_current_path();

//			path += "/GALAXIAN.NES";
			path += "/Zombie.nes";
//			path += "/DRAGONQ1.NES";

			nes_insertcart(path.c_str(), nes_);
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  アップデート
		*/
		//-----------------------------------------------------------------//
		void update()
		{
//	   		gl::core& core = gl::core::get_instance();
//			const gl::device& dev = core.get_device();
        	gui::widget_director& wd = director_.at().widget_director_;

			if(nes_ != nullptr) {
				pad_();

				nes_emulate(1);

				// copy sound
				{
//					apu_t* a = nes_->apu;
//					int n = a->sample_rate / a->refresh_rate;
					int len = 44100 / 60;
					apu_process(sound_, len);
					al::sound& sound = director_.at().sound_;
					for(int i = 0; i < len; ++i) {
						al::pcm8_m w(sound_[i]);
						audio_->put(i, w);
					}
					sound.queue_audio(audio_);
				}

				// copy video
				bitmap_t* v = nes_->vidbuf;
				const rgb_t* lut = get_palette();
				for(int h = 0; h < nes_height_; ++h) {
					const uint8_t* src = &v->data[h * v->pitch];
					uint8_t* dst = &fb_[h * nes_width_ * 4];
					for(int w = 0; w < nes_width_; ++w) {
						auto idx = *src++;
						idx &= 63;
						*dst++ = lut[idx].r;  // R
						*dst++ = lut[idx].g;  // G
						*dst++ = lut[idx].b;  // B
						*dst++ = 255;  // alpha
					}
				}

				texfb_.rendering(gl::texfb::image::RGBA, (const char*)&fb_[0]);
			}
			texfb_.flip();

			wd.update();
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  レンダリング
		*/
		//-----------------------------------------------------------------//
		void render()
		{
	   		gl::core& core = gl::core::get_instance();

			const vtx::spos& siz = core.get_rect().size;

			texfb_.setup_matrix(0, 0, siz.x, siz.y);

			float scale = 1.0f;
			float ofsx = 0.0f;
			float ofsy = 0.0f;
			if(siz.x < siz.y) {
				scale = static_cast<float>(siz.x) / static_cast<float>(nes_width_);
				float h = static_cast<float>(nes_height_);
				ofsy = (static_cast<float>(siz.y) - h * scale) * 0.5f;
			} else {
				scale = static_cast<float>(siz.y) / static_cast<float>(nes_height_);
				float w = static_cast<float>(nes_width_);
				ofsx = (static_cast<float>(siz.x) - w * scale) * 0.5f;
			}
			gl::glTranslate(ofsx, ofsy);
			gl::glScale(scale);
			texfb_.draw();

			director_.at().widget_director_.service();
			director_.at().widget_director_.render();
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  廃棄
		*/
		//-----------------------------------------------------------------//
		void destroy()
		{
			nes_destroy(nes_);
		}

	};

}
<commit_msg>update filer<commit_after>#pragma once
//=====================================================================//
/*! @file
	@brief  NES Emulator クラス
	@author 平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include "main.hpp"
#include "utils/i_scene.hpp"
#include "utils/director.hpp"
#include "widgets/widget.hpp"
#include "widgets/widget_frame.hpp"
#include "widgets/widget_terminal.hpp"
#include "widgets/widget_filer.hpp"
#include "gl_fw/gltexfb.hpp"
#include "snd_io/pcm.hpp"

extern "C" {
	#include "nes.h"
	#include "nesinput.h"
	extern const rgb_t* get_palette();
};

namespace app {

	class nesemu : public utils::i_scene {

		utils::director<core>&	director_;

		gui::widget_frame*		terminal_frame_;
		gui::widget_terminal*	terminal_core_;

		gui::widget_filer*		filer_;

		gl::texfb			texfb_;

		static const int nes_width_  = 256;
		static const int nes_height_ = 224;

		nes_t*	nes_;

		uint8_t		sound_[1024];
		al::audio	audio_;

		uint8_t	fb_[nes_width_ * nes_height_ * 4];

		nesinput_t	inp_[2];

		bool	rom_active_;

		void pad_()
		{
	   		gl::core& core = gl::core::get_instance();

			const gl::device& dev = core.get_device();

			inp_[0].data = 0;
			inp_[1].data = 0;

			// A
			if(dev.get_level(gl::device::key::Z)) {
				inp_[0].data |= INP_PAD_A;
			}
			if(dev.get_level(gl::device::key::GAME_0)) {
				inp_[0].data |= INP_PAD_A;
			}

			// B
			if(dev.get_level(gl::device::key::X)) {
				inp_[0].data |= INP_PAD_B;
			}
			if(dev.get_level(gl::device::key::GAME_1)) {
				inp_[0].data |= INP_PAD_B;
			}

			// SELECT
			if(dev.get_level(gl::device::key::_1)) {
				inp_[0].data |= INP_PAD_SELECT;
			}
			if(dev.get_level(gl::device::key::GAME_2)) {
				inp_[0].data |= INP_PAD_SELECT;
			}
			// START
			if(dev.get_level(gl::device::key::_2)) {
				inp_[0].data |= INP_PAD_START;
			}
			if(dev.get_level(gl::device::key::GAME_3)) {
				inp_[0].data |= INP_PAD_START;
			}

			if(dev.get_level(gl::device::key::LEFT)) {
   		 		inp_[0].data |= INP_PAD_LEFT;
			}
			if(dev.get_level(gl::device::key::GAME_LEFT)) {
   		 		inp_[0].data |= INP_PAD_LEFT;
			}
			if(dev.get_level(gl::device::key::RIGHT)) {
   		 		inp_[0].data |= INP_PAD_RIGHT;
			}
			if(dev.get_level(gl::device::key::GAME_RIGHT)) {
   		 		inp_[0].data |= INP_PAD_RIGHT;
			}
			if(dev.get_level(gl::device::key::UP)) {
   		 		inp_[0].data |= INP_PAD_UP;
			}
			if(dev.get_level(gl::device::key::GAME_UP)) {
   		 		inp_[0].data |= INP_PAD_RIGHT;
			}
			if(dev.get_level(gl::device::key::DOWN)) {
   		 		inp_[0].data |= INP_PAD_DOWN;
			}
			if(dev.get_level(gl::device::key::GAME_DOWN)) {
   		 		inp_[0].data |= INP_PAD_DOWN;
			}
		}

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief  コンストラクター
		*/
		//-----------------------------------------------------------------//
		nesemu(utils::director<core>& d) : director_(d),
			terminal_frame_(nullptr), terminal_core_(nullptr),
			nes_(nullptr), rom_active_(false)
		{ }

		//-----------------------------------------------------------------//
		/*!
			@brief  デストラクター
		*/
		//-----------------------------------------------------------------//
		virtual ~nesemu() { }

		//-----------------------------------------------------------------//
		/*!
			@brief  初期化
		*/
		//-----------------------------------------------------------------//
		void initialize()
		{
	   		gl::core& core = gl::core::get_instance();

			using namespace gui;

			widget_director& wd = director_.at().widget_director_;
			widget::param wp(vtx::irect(10, 10, 200, 200));
			widget_filer::param wp_(core.get_current_path(), "", true);
			wp_.select_file_func_ = [this](const std::string& fn) {
				nes_insertcart(fn.c_str(), nes_);
				rom_active_ = true;
			};
			filer_ = wd.add_widget<widget_filer>(wp, wp_);
			filer_->enable(false);

			texfb_.initialize(nes_width_, nes_height_, 32);

			nes_ = nes_create();

			// regist input
			inp_[0].type = INP_JOYPAD0;
			inp_[0].data = 0;
			input_register(&inp_[0]);
			inp_[1].type = INP_JOYPAD1;
			inp_[1].data = 0;
			input_register(&inp_[1]);

			audio_ = al::create_audio(al::audio_format::PCM8_MONO);
			audio_->create(44100, 44100 / 60);

//			auto path = core.get_current_path();

//			path += "/GALAXIAN.NES";
//			path += "/Zombie.nes";
//			path += "/DRAGONQ1.NES";
//			path += "/DRAGONW2.NES";
//			path += "/SOLSTICE.NES";
//			path += "/GRADIUS.NES";

//			nes_insertcart(path.c_str(), nes_);

			// プリファレンスの取得
			sys::preference& pre = director_.at().preference_;
			if(filer_) {
				filer_->load(pre);
			}
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  アップデート
		*/
		//-----------------------------------------------------------------//
		void update()
		{
	   		gl::core& core = gl::core::get_instance();
			const gl::device& dev = core.get_device();

			if(dev.get_positive(gl::device::key::ENTER)) {
				filer_->enable(!filer_->get_enable());
			}

        	gui::widget_director& wd = director_.at().widget_director_;

			if(nes_ != nullptr && rom_active_) {
				pad_();

				nes_emulate(1);

				// copy sound
				{
					int len = 44100 / 60;
					apu_process(sound_, len);
					al::sound& sound = director_.at().sound_;
					for(int i = 0; i < len; ++i) {
						al::pcm8_m w(sound_[i]);
						audio_->put(i, w);
					}
					sound.queue_audio(audio_);
				}

				// copy video
				bitmap_t* v = nes_->vidbuf;
				const rgb_t* lut = get_palette();
				if(v != nullptr && lut != nullptr) {
					for(int h = 0; h < nes_height_; ++h) {
						const uint8_t* src = &v->data[h * v->pitch];
						uint8_t* dst = &fb_[h * nes_width_ * 4];
						for(int w = 0; w < nes_width_; ++w) {
							auto idx = *src++;
							idx &= 63;
							*dst++ = lut[idx].r;  // R
							*dst++ = lut[idx].g;  // G
							*dst++ = lut[idx].b;  // B
							*dst++ = 255;  // alpha
						}
					}
					texfb_.rendering(gl::texfb::image::RGBA, (const char*)&fb_[0]);
				}
			}

			texfb_.flip();

			wd.update();
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  レンダリング
		*/
		//-----------------------------------------------------------------//
		void render()
		{
	   		gl::core& core = gl::core::get_instance();

			const vtx::spos& siz = core.get_rect().size;

			texfb_.setup_matrix(0, 0, siz.x, siz.y);

			float scale = 1.0f;
			float ofsx = 0.0f;
			float ofsy = 0.0f;
			if(siz.x < siz.y) {
				scale = static_cast<float>(siz.x) / static_cast<float>(nes_width_);
				float h = static_cast<float>(nes_height_);
				ofsy = (static_cast<float>(siz.y) - h * scale) * 0.5f;
			} else {
				scale = static_cast<float>(siz.y) / static_cast<float>(nes_height_);
				float w = static_cast<float>(nes_width_);
				ofsx = (static_cast<float>(siz.x) - w * scale) * 0.5f;
			}
			gl::glTranslate(ofsx, ofsy);
			gl::glScale(scale);
			texfb_.draw();

			director_.at().widget_director_.service();
			director_.at().widget_director_.render();
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  廃棄
		*/
		//-----------------------------------------------------------------//
		void destroy()
		{
			nes_destroy(nes_);

			sys::preference& pre = director_.at().preference_;
			if(filer_) {
				filer_->save(pre);
			}
		}

	};

}
<|endoftext|>


<commit_before>#pragma once

#include <string>
#include <functional>
#include <cmath>
#include <random>
#include <iostream>
#include <vector>
#include <unordered_map>
#include <utility>
#include "tbb/parallel_sort.h"
#include "tbb/mutex.h"
#include "tbb/pipeline.h"
#include "util.hpp"
#include "point.hpp"

#define VALUE(x) std::cout << #x "=" << x << std::endl

namespace psh
{
	template<uint d, class T>
	class map
	{
		static_assert(d > 0, "d must be larger than 0.");
	public:
		struct data_t
		{
			point<d> location;
			T contents;
		};

		struct bucket : public std::vector<data_t>
		{
			uint phi_index;

			bucket(uint phi_index) : phi_index(phi_index) { }
			friend bool operator<(const bucket& lhs, const bucket& rhs) {
				return lhs.size() > rhs.size();
			}
		};

		struct entry
		{
			uint k;
			uint hk;
			T contents;

			entry() : k(1), hk(1), contents(T()) { };
			entry(const data_t& data, uint M2) : k(1), contents(data.contents)
			{
				rehash(data, M2);
			}

			static constexpr uint h(const point<d>& p, uint M2, uint k)
			{
				return p * point<d>::increasing_pow(k) * M2;
			}

			void rehash(const point<d>& location, uint M2, uint new_k = 1)
			{
				k = new_k;
				hk = h(location, M2, k);
			}

			void rehash(const data_t& data, uint M2, uint new_k = 1)
			{
				rehash(data.location, M2, new_k);
			}

			constexpr bool equals(const point<d>& p, uint M2) const
			{
				return hk == h(p, M2, k);
			}
		};

		struct entry_large : public entry
		{
			point<d> location;
			entry_large() : entry(), location(point<d>()) { };
			entry_large(const data_t& data, uint M2)
				: entry(data, M2), location(data.location) { };
			void rehash(uint M2) { entry::rehash(location, M2, entry::k + 1); };
		};

		uint M0;
		uint M1;
		uint M2;
		uint n;
		uint m_bar;
		uint m;
		uint r_bar;
		uint r;
		std::vector<point<d>> phi;
		std::vector<entry> H;
		std::default_random_engine generator;

		map(const std::vector<data_t>& data, const point<d>& domain_size)
			: n(data.size()), m_bar(std::ceil(std::pow(n, 1.0f / d))), m(std::pow(m_bar, d)),
			  r_bar(std::ceil(std::pow(n / d, 1.0f / d)) - 1), generator(time(0))
		{
			M0 = prime();
			while ((M1 = prime()) == M0);
			M2 = prime();

			VALUE(m);
			VALUE(m_bar);

			VALUE(M0);
			VALUE(M1);
			VALUE(M2);

			bool create_succeeded = false;

			std::uniform_int_distribution<uint> m_dist(0, m - 1);

			do
			{
				r_bar += d;
				r = std::pow(r_bar, d);
				VALUE(r);
				VALUE(r_bar);

				create_succeeded = create(data, domain_size, m_dist);

			} while (!create_succeeded);
		}

		uint prime()
		{
			static const std::vector<uint> primes{ 53, 97, 193, 389, 769, 1543, 3079,
				6151, 12289, 24593, 49157, 98317, 196613, 393241, 786433, 1572869,
				3145739, 6291469 };
			static std::uniform_int_distribution<uint> prime_dist(0, primes.size() - 1);

			return primes[prime_dist(generator)];
		}

		bool bad_m_r()
		{
			auto m_mod_r = m_bar % r_bar;
			return m_mod_r == 1 || m_mod_r == r - 1;
		}

		void insert(const bucket& b, std::vector<entry_large>& H_hat, std::vector<bool>& H_b_hat,
			const decltype(phi)& phi_hat)
		{
			for (auto& element : b)
			{
				auto hashed = h(element.location, phi_hat);
				auto i = point_to_index<d>(hashed, m_bar, m);
				H_hat[i] = entry_large(element, M2);
				H_b_hat[i] = true;
			}
		}

		bool jiggle_offsets(std::vector<entry_large>& H_hat, std::vector<bool>& H_b_hat,
			decltype(phi)& phi_hat, const bucket& b,
			std::uniform_int_distribution<uint>& m_dist)
		{
			uint start_offset = m_dist(generator);

			bool found = false;
			point<d> found_offset;
			tbb::mutex mutex;

			uint chunk_index = 0;
			const uint num_cores = 8;
			const uint group_size = r / num_cores + 1;

			tbb::parallel_pipeline(num_cores,
				tbb::make_filter<void, uint>(tbb::filter::serial,
					[=, &chunk_index, &found, &group_size](tbb::flow_control& fc) {
						if (found || chunk_index >= r)
						{
							fc.stop();
						}
						chunk_index += group_size;
						return chunk_index;
					}) &
				tbb::make_filter<uint, void>(tbb::filter::parallel,
					[=, &mutex, &found, &found_offset, &b, &phi_hat, &H_hat, &H_b_hat](uint i0)
					{
						for (uint i = i0; i < i0 + group_size && !found; i++)
						{
							auto phi_offset = index_to_point<d>((start_offset + i) % m, m_bar, m);

							bool collision = false;
							for (auto& element : b)
							{
								auto h0 = M0 * element.location;
								auto h1 = M1 * element.location;
								auto index = point_to_index<d>(h1, r_bar, r);
								auto offset = index == b.phi_index ? phi_offset : phi_hat[index];
								auto hash = h0 + offset;

								collision = H_b_hat[point_to_index<d>(hash, m_bar, m)];
								if (collision)
									break;
							}

							if (!collision)
							{
								tbb::mutex::scoped_lock lock(mutex);
								if (!found)
								{
									found = true;
									found_offset = phi_offset;
								}
							}
						}
					})
				);
			if (found)
			{
				phi_hat[b.phi_index] = found_offset;
				insert(b, H_hat, H_b_hat, phi_hat);
				return true;
			}
			return false;
		}

		std::vector<bucket> create_buckets(const std::vector<data_t>& data)
		{
			std::vector<bucket> buckets;
			buckets.reserve(r);
			{
				uint i = 0;
				std::generate_n(std::back_inserter(buckets), r, [&] {
						return bucket(i++);
					});
			}

			for (auto& element : data)
			{
				auto h1 = M1 * element.location;
				buckets[point_to_index<d>(h1, r_bar, r)].push_back(element);
			}

			std::cout << "buckets created" << std::endl;

			tbb::parallel_sort(buckets.begin(), buckets.end());
			std::cout << "buckets sorted" << std::endl;

			return buckets;
		}

		bool create(const std::vector<data_t>& data, const point<d>& domain_size,
			std::uniform_int_distribution<uint>& m_dist)
		{
			decltype(phi) phi_hat(r);
			std::vector<entry_large> H_hat(m);
			std::vector<bool> H_b_hat(m, false);
			std::cout << "creating " << r << " buckets" << std::endl;

			if (bad_m_r())
				return false;

			auto buckets = create_buckets(data);
			std::cout << "jiggling offsets" << std::endl;

			for (uint i = 0; i < buckets.size(); i++)
			{
				if (buckets[i].size() == 0)
					break;
				if (i % (buckets.size() / 10) == 0)
					std::cout << (100 * i) / buckets.size() << "% done" << std::endl;

				if (!jiggle_offsets(H_hat, H_b_hat, phi_hat, buckets[i], m_dist))
				{
					return false;
				}
			}

			std::cout << "done!" << std::endl;
			phi = std::move(phi_hat);
			hash_positions(data, domain_size, H_hat);
			std::copy(H_hat.begin(), H_hat.end(), std::back_inserter(H));

			return true;
		}

		void hash_positions(const std::vector<data_t>& data, const point<d>& domain_size,
			std::vector<entry_large>& H_hat)
		{
			// domain_size - 1 to get the highest indices in each direction
			// width is assumed to be equal in all directions
			// we don't expect it to roll over so we mod by the maximum uint, -1
			// and then add 1 to get the size, not the index
			uint domain_i_max = point_to_index<d>(domain_size - 1, domain_size[0],
				uint(-1)) + 1;
			std::vector<bool> indices(m);
			{
				uint j = 0;
				uint k = point_to_index<d>(data[j].location, domain_size[0], uint(-1));

				// first sweep
				for (uint i = 0; i < domain_i_max; i++)
				{
					if (k == i)
					{
						j++;
						k = point_to_index<d>(data[j].location, domain_size[0], uint(-1));
						continue;
					}
					// for each point p in original image which is empty

					auto p = index_to_point<d>(i, domain_size[0], uint(-1));
					uint l = point_to_index<d>(h(p), m_bar, m);
					if (H_hat[l].hk == entry::h(p, M2, 1))
					{
						//     if (get(p).hk == p.hk)
						//         indices.add(p)
						indices[l] = true;
					}
				}
				std::cout << "data size: " << data.size() << std::endl;
				std::cout << "indices size: " << indices.size() << std::endl;
			}

			std::unordered_map<uint, std::vector<uint>> collisions;
			{
				// second sweep
				for (uint i = 0; i < domain_i_max; i++)
				{
					// for each point p in original image

					auto p = index_to_point<d>(i, domain_size[0], uint(-1));
					uint l = point_to_index<d>(h(p), m_bar, m);
					// it's like collect everyone that maps to the same thing
					if (indices[l])
					{
						collisions[l].push_back(i);
					}
				}
			}

			// third sweep
			for (auto& kvp : collisions)
			{
			rehash:
				// kvp.first == l == location in H
				H_hat[kvp.first].rehash(M2);

				for (uint i : kvp.second)
				{
					// i == index in U, does not exist in input data
					// if one of these have the same hk as som H_hat[kvp.first]
					// break, success = false, call the po-po
					auto p = index_to_point<d>(i, domain_size[0], uint(-1));
					uint hk = entry::h(p, M2, H_hat[kvp.first].k);
					if (H_hat[kvp.first].location != p && H_hat[kvp.first].hk == hk)
					{
						goto rehash;
					}
				}
			}
		}

		point<d> h(const point<d>& p, const decltype(phi)& phi_hat) const
		{
			auto h0 = M0 * p;
			auto h1 = M1 * p;
			auto i = point_to_index<d>(h1, r_bar, r);
			auto offset = phi_hat[i];
			return h0 + offset;
		}

		point<d> h(const point<d>& p) const
		{
			return h(p, phi);
		}

		T get(const point<d>& p) const
		{
			auto i = point_to_index<d>(h(p), m_bar, m);
			if (H[i].equals(p, M2))
				return H[i].contents;
			else
				throw std::out_of_range("Element not found in map");
		}

		uint memory_size() const
		{
			return sizeof(*this)
				+ sizeof(typename decltype(phi)::value_type) * phi.capacity()
				+ sizeof(typename decltype(H)::value_type) * H.capacity();
		}
	};
}<commit_msg>simplified lambda capture<commit_after>#pragma once

#include <string>
#include <functional>
#include <cmath>
#include <random>
#include <iostream>
#include <vector>
#include <unordered_map>
#include <utility>
#include "tbb/parallel_sort.h"
#include "tbb/mutex.h"
#include "tbb/pipeline.h"
#include "util.hpp"
#include "point.hpp"

#define VALUE(x) std::cout << #x "=" << x << std::endl

namespace psh
{
	template<uint d, class T>
	class map
	{
		static_assert(d > 0, "d must be larger than 0.");
	public:
		struct data_t
		{
			point<d> location;
			T contents;
		};

		struct bucket : public std::vector<data_t>
		{
			uint phi_index;

			bucket(uint phi_index) : phi_index(phi_index) { }
			friend bool operator<(const bucket& lhs, const bucket& rhs) {
				return lhs.size() > rhs.size();
			}
		};

		struct entry
		{
			uint k;
			uint hk;
			T contents;

			entry() : k(1), hk(1), contents(T()) { };
			entry(const data_t& data, uint M2) : k(1), contents(data.contents)
			{
				rehash(data, M2);
			}

			static constexpr uint h(const point<d>& p, uint M2, uint k)
			{
				return p * point<d>::increasing_pow(k) * M2;
			}

			void rehash(const point<d>& location, uint M2, uint new_k = 1)
			{
				k = new_k;
				hk = h(location, M2, k);
			}

			void rehash(const data_t& data, uint M2, uint new_k = 1)
			{
				rehash(data.location, M2, new_k);
			}

			constexpr bool equals(const point<d>& p, uint M2) const
			{
				return hk == h(p, M2, k);
			}
		};

		struct entry_large : public entry
		{
			point<d> location;
			entry_large() : entry(), location(point<d>()) { };
			entry_large(const data_t& data, uint M2)
				: entry(data, M2), location(data.location) { };
			void rehash(uint M2) { entry::rehash(location, M2, entry::k + 1); };
		};

		uint M0;
		uint M1;
		uint M2;
		uint n;
		uint m_bar;
		uint m;
		uint r_bar;
		uint r;
		std::vector<point<d>> phi;
		std::vector<entry> H;
		std::default_random_engine generator;

		map(const std::vector<data_t>& data, const point<d>& domain_size)
			: n(data.size()), m_bar(std::ceil(std::pow(n, 1.0f / d))), m(std::pow(m_bar, d)),
			  r_bar(std::ceil(std::pow(n / d, 1.0f / d)) - 1), generator(time(0))
		{
			M0 = prime();
			while ((M1 = prime()) == M0);
			M2 = prime();

			VALUE(m);
			VALUE(m_bar);

			VALUE(M0);
			VALUE(M1);
			VALUE(M2);

			bool create_succeeded = false;

			std::uniform_int_distribution<uint> m_dist(0, m - 1);

			do
			{
				r_bar += d;
				r = std::pow(r_bar, d);
				VALUE(r);
				VALUE(r_bar);

				create_succeeded = create(data, domain_size, m_dist);

			} while (!create_succeeded);
		}

		uint prime()
		{
			static const std::vector<uint> primes{ 53, 97, 193, 389, 769, 1543, 3079,
				6151, 12289, 24593, 49157, 98317, 196613, 393241, 786433, 1572869,
				3145739, 6291469 };
			static std::uniform_int_distribution<uint> prime_dist(0, primes.size() - 1);

			return primes[prime_dist(generator)];
		}

		bool bad_m_r()
		{
			auto m_mod_r = m_bar % r_bar;
			return m_mod_r == 1 || m_mod_r == r - 1;
		}

		void insert(const bucket& b, std::vector<entry_large>& H_hat, std::vector<bool>& H_b_hat,
			const decltype(phi)& phi_hat)
		{
			for (auto& element : b)
			{
				auto hashed = h(element.location, phi_hat);
				auto i = point_to_index<d>(hashed, m_bar, m);
				H_hat[i] = entry_large(element, M2);
				H_b_hat[i] = true;
			}
		}

		bool jiggle_offsets(std::vector<entry_large>& H_hat, std::vector<bool>& H_b_hat,
			decltype(phi)& phi_hat, const bucket& b,
			std::uniform_int_distribution<uint>& m_dist)
		{
			uint start_offset = m_dist(generator);

			bool found = false;
			point<d> found_offset;
			tbb::mutex mutex;

			uint chunk_index = 0;
			const uint num_cores = 8;
			const uint group_size = r / num_cores + 1;

			tbb::parallel_pipeline(num_cores,
				tbb::make_filter<void, uint>(tbb::filter::serial,
					[&, group_size](tbb::flow_control& fc) {
						if (found || chunk_index >= r)
						{
							fc.stop();
						}
						chunk_index += group_size;
						return chunk_index;
					}) &
				tbb::make_filter<uint, void>(tbb::filter::parallel,
					[&, group_size](uint i0)
					{
						for (uint i = i0; i < i0 + group_size && !found; i++)
						{
							auto phi_offset = index_to_point<d>((start_offset + i) % m, m_bar, m);

							bool collision = false;
							for (auto& element : b)
							{
								auto h0 = M0 * element.location;
								auto h1 = M1 * element.location;
								auto index = point_to_index<d>(h1, r_bar, r);
								auto offset = index == b.phi_index ? phi_offset : phi_hat[index];
								auto hash = h0 + offset;

								collision = H_b_hat[point_to_index<d>(hash, m_bar, m)];
								if (collision)
									break;
							}

							if (!collision)
							{
								tbb::mutex::scoped_lock lock(mutex);
								if (!found)
								{
									found = true;
									found_offset = phi_offset;
								}
							}
						}
					})
				);
			if (found)
			{
				phi_hat[b.phi_index] = found_offset;
				insert(b, H_hat, H_b_hat, phi_hat);
				return true;
			}
			return false;
		}

		std::vector<bucket> create_buckets(const std::vector<data_t>& data)
		{
			std::vector<bucket> buckets;
			buckets.reserve(r);
			{
				uint i = 0;
				std::generate_n(std::back_inserter(buckets), r, [&] {
						return bucket(i++);
					});
			}

			for (auto& element : data)
			{
				auto h1 = M1 * element.location;
				buckets[point_to_index<d>(h1, r_bar, r)].push_back(element);
			}

			std::cout << "buckets created" << std::endl;

			tbb::parallel_sort(buckets.begin(), buckets.end());
			std::cout << "buckets sorted" << std::endl;

			return buckets;
		}

		bool create(const std::vector<data_t>& data, const point<d>& domain_size,
			std::uniform_int_distribution<uint>& m_dist)
		{
			decltype(phi) phi_hat(r);
			std::vector<entry_large> H_hat(m);
			std::vector<bool> H_b_hat(m, false);
			std::cout << "creating " << r << " buckets" << std::endl;

			if (bad_m_r())
				return false;

			auto buckets = create_buckets(data);
			std::cout << "jiggling offsets" << std::endl;

			for (uint i = 0; i < buckets.size(); i++)
			{
				if (buckets[i].size() == 0)
					break;
				if (i % (buckets.size() / 10) == 0)
					std::cout << (100 * i) / buckets.size() << "% done" << std::endl;

				if (!jiggle_offsets(H_hat, H_b_hat, phi_hat, buckets[i], m_dist))
				{
					return false;
				}
			}

			std::cout << "done!" << std::endl;
			phi = std::move(phi_hat);
			hash_positions(data, domain_size, H_hat);
			std::copy(H_hat.begin(), H_hat.end(), std::back_inserter(H));

			return true;
		}

		void hash_positions(const std::vector<data_t>& data, const point<d>& domain_size,
			std::vector<entry_large>& H_hat)
		{
			// domain_size - 1 to get the highest indices in each direction
			// width is assumed to be equal in all directions
			// we don't expect it to roll over so we mod by the maximum uint, -1
			// and then add 1 to get the size, not the index
			uint domain_i_max = point_to_index<d>(domain_size - 1, domain_size[0],
				uint(-1)) + 1;
			std::vector<bool> indices(m);
			{
				uint j = 0;
				uint k = point_to_index<d>(data[j].location, domain_size[0], uint(-1));

				// first sweep
				for (uint i = 0; i < domain_i_max; i++)
				{
					if (k == i)
					{
						j++;
						k = point_to_index<d>(data[j].location, domain_size[0], uint(-1));
						continue;
					}
					// for each point p in original image which is empty

					auto p = index_to_point<d>(i, domain_size[0], uint(-1));
					uint l = point_to_index<d>(h(p), m_bar, m);
					if (H_hat[l].hk == entry::h(p, M2, 1))
					{
						//     if (get(p).hk == p.hk)
						//         indices.add(p)
						indices[l] = true;
					}
				}
				std::cout << "data size: " << data.size() << std::endl;
				std::cout << "indices size: " << indices.size() << std::endl;
			}

			std::unordered_map<uint, std::vector<uint>> collisions;
			{
				// second sweep
				for (uint i = 0; i < domain_i_max; i++)
				{
					// for each point p in original image

					auto p = index_to_point<d>(i, domain_size[0], uint(-1));
					uint l = point_to_index<d>(h(p), m_bar, m);
					// it's like collect everyone that maps to the same thing
					if (indices[l])
					{
						collisions[l].push_back(i);
					}
				}
			}

			// third sweep
			for (auto& kvp : collisions)
			{
			rehash:
				// kvp.first == l == location in H
				H_hat[kvp.first].rehash(M2);

				for (uint i : kvp.second)
				{
					// i == index in U, does not exist in input data
					// if one of these have the same hk as som H_hat[kvp.first]
					// break, success = false, call the po-po
					auto p = index_to_point<d>(i, domain_size[0], uint(-1));
					uint hk = entry::h(p, M2, H_hat[kvp.first].k);
					if (H_hat[kvp.first].location != p && H_hat[kvp.first].hk == hk)
					{
						goto rehash;
					}
				}
			}
		}

		point<d> h(const point<d>& p, const decltype(phi)& phi_hat) const
		{
			auto h0 = M0 * p;
			auto h1 = M1 * p;
			auto i = point_to_index<d>(h1, r_bar, r);
			auto offset = phi_hat[i];
			return h0 + offset;
		}

		point<d> h(const point<d>& p) const
		{
			return h(p, phi);
		}

		T get(const point<d>& p) const
		{
			auto i = point_to_index<d>(h(p), m_bar, m);
			if (H[i].equals(p, M2))
				return H[i].contents;
			else
				throw std::out_of_range("Element not found in map");
		}

		uint memory_size() const
		{
			return sizeof(*this)
				+ sizeof(typename decltype(phi)::value_type) * phi.capacity()
				+ sizeof(typename decltype(H)::value_type) * H.capacity();
		}
	};
}<|endoftext|>


<commit_before>
#ifndef __OBJ_CACHE_H__
#define __OBJ_CACHE_H__

// node
#include <node.h>
#include <node_object_wrap.h>

// nan
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <nan.h>
#pragma GCC diagnostic pop

#include <map>

#include "../gdal_common.hpp"

//TODO: This could use some serious cleaning

struct ObjectCacheWeakCallbackData {
	void *key;
	void *cache;
};

template <typename K>
struct ObjectCacheItem {
	_NanWeakCallbackInfo<v8::Object, ObjectCacheWeakCallbackData> *cbinfo;
	K *key;
	K *alias;
};

// a class for maintaining a map of native pointers and persistent JS handles
// objects are removed from cache and their destructor is called when they go out of scope

template <typename K, typename W>
class ObjectCache {
public:
	//map a native pointer to a handle to the V8 obj that wraps it
	void add(K *key, v8::Handle<v8::Object> obj);
	void add(K *key, K *alias, v8::Handle<v8::Object> obj);

	//fetch the V8 obj that wraps the native pointer (or alias)
	//call has() before calling to get() to ensure handle exists
	v8::Handle<v8::Object> get(K *key);

	//check if native pointer has been wrapped, or if an alias wraps it
	bool has(K *key);
	void erase(K *key);

	ObjectCache();
	~ObjectCache();

private:
	NAN_WEAK_CALLBACK(cacheWeakCallback);

	ObjectCacheItem<K> getItem(K *key);
	void erase(ObjectCacheItem<K> key);
	std::map<K*, ObjectCacheItem<K> > cache;
	std::map<K*, K*> aliases;
};

template <typename K, typename W>
ObjectCache<K, W>::ObjectCache()
	: cache(), aliases()
{
}

template <typename K, typename W>
ObjectCache<K, W>::~ObjectCache()
{
}

template <typename K, typename W>
template<typename T, typename P>
void ObjectCache<K, W>::cacheWeakCallback(const _NanWeakCallbackData<T, P> &data)
{
	//called when only reference to object is weak - after object destructor is called (... or before, who knows)
	ObjectCacheWeakCallbackData *info = (ObjectCacheWeakCallbackData*) data.GetParameter();
	ObjectCache<K, W> *cache = (ObjectCache<K, W>*) info->cache;
	K *key = (K*) info->key;

	LOG("ObjectCache Weak Callback [%p]", key);

	// dispose of the wrapped object before the handle in the cache gets deleted by NAN
	// but do not dispose if destructor has already been called
	if(cache->has(key)) {
		W* wrapped = node::ObjectWrap::Unwrap<W>(data.GetValue());
		wrapped->dispose();
	}

	delete info;
}

template <typename K, typename W>
void ObjectCache<K, W>::add(K *key, K *alias, v8::Handle<v8::Object> obj)
{
	ObjectCacheWeakCallbackData *cbdata = new ObjectCacheWeakCallbackData();
	cbdata->cache = this;
	cbdata->key = key;

	ObjectCacheItem<K> item;
	item.key    = key;
	item.alias  = alias;
	item.cbinfo = NanMakeWeakPersistent(obj, cbdata, cacheWeakCallback);

	//add it to the map
	cache[key] = item;
	if(alias) {
		aliases[alias] = key;
	}
}

template <typename K, typename W>
void ObjectCache<K, W>::add(K *key, v8::Handle<v8::Object> obj)
{
	add(key, NULL, obj);
}

template <typename K, typename W>
bool ObjectCache<K, W>::has(K *key)
{
	return cache.count(key) > 0 || aliases.count(key) > 0;
}

template <typename K, typename W>
v8::Handle<v8::Object> ObjectCache<K, W>::get(K *key)
{
	return NanNew(getItem(key).cbinfo->persistent);
}

template <typename K, typename W>
ObjectCacheItem<K> ObjectCache<K, W>::getItem(K *key)
{
	//return handle to existing object if already wrapped
	//check by calling has() first
	if (aliases.count(key) > 0) {
		key = aliases[key];
	}
	return cache[key];
}

template <typename K, typename W>
void ObjectCache<K, W>::erase(K *key)
{
	if(has(key)){
		erase(getItem(key));
	}
}

template <typename K, typename W>
void ObjectCache<K, W>::erase(ObjectCacheItem<K> item)
{

	LOG("ObjectCache erasing [%p]", item.key);
	cache.erase(item.key);
	if(item.alias){
		LOG("ObjectCache erasing alias [%p]", item.alias);
		aliases.erase(item.alias);
	}
}

#endif<commit_msg>Made ObjectCache deal with different objects being the same address more gracefully.<commit_after>
#ifndef __OBJ_CACHE_H__
#define __OBJ_CACHE_H__

// node
#include <node.h>
#include <node_object_wrap.h>

// nan
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <nan.h>
#pragma GCC diagnostic pop

#include <map>
#include <cstdlib>

#include "../gdal_common.hpp"

//TODO: This could use some serious cleaning

struct ObjectCacheWeakCallbackData {
	void *key;
	void *cache;
	int id;
};

template <typename K>
struct ObjectCacheItem {
	_NanWeakCallbackInfo<v8::Object, ObjectCacheWeakCallbackData> *cbinfo;
	K *key;
	K *alias;
	int id; // a uniq id so that the weak callback can distinguish if the object in the cache with the given key is referring to the same object as when the persistent pointer was made
};

// a class for maintaining a map of native pointers and persistent JS handles
// objects are removed from cache and their destructor is called when they go out of scope

template <typename K, typename W>
class ObjectCache {
public:
	//map a native pointer to a handle to the V8 obj that wraps it
	void add(K *key, v8::Handle<v8::Object> obj);
	void add(K *key, K *alias, v8::Handle<v8::Object> obj);

	//fetch the V8 obj that wraps the native pointer (or alias)
	//call has() before calling to get() to ensure handle exists
	v8::Handle<v8::Object> get(K *key);

	//check if native pointer has been wrapped, or if an alias wraps it
	bool has(K *key);
	void erase(K *key);

	ObjectCache();
	~ObjectCache();

private:
	NAN_WEAK_CALLBACK(cacheWeakCallback);

	ObjectCacheItem<K> getItem(K *key);
	void erase(ObjectCacheItem<K> key);
	std::map<K*, ObjectCacheItem<K> > cache;
	std::map<K*, K*> aliases;
};

template <typename K, typename W>
ObjectCache<K, W>::ObjectCache()
	: cache(), aliases()
{
}

template <typename K, typename W>
ObjectCache<K, W>::~ObjectCache()
{
}

template <typename K, typename W>
template<typename T, typename P>
void ObjectCache<K, W>::cacheWeakCallback(const _NanWeakCallbackData<T, P> &data)
{
	//called when only reference to object is weak - after object destructor is called (... or before, who knows)
	ObjectCacheWeakCallbackData *info = (ObjectCacheWeakCallbackData*) data.GetParameter();
	ObjectCache<K, W> *cache = (ObjectCache<K, W>*) info->cache;
	K *key = (K*) info->key;

	LOG("ObjectCache Weak Callback [%p]", key);

	// dispose of the wrapped object before the handle in the cache gets deleted by NAN
	// but do not dispose if destructor has already been called
	if(cache->has(key)) {
		//double check that the item in the cache isnt something new (meaning object was already disposed)
		if(cache->getItem(key).id == info->id) {
			W* wrapped = node::ObjectWrap::Unwrap<W>(data.GetValue());
			wrapped->dispose();
		}
	}

	delete info;
}

template <typename K, typename W>
void ObjectCache<K, W>::add(K *key, K *alias, v8::Handle<v8::Object> obj)
{
	ObjectCacheWeakCallbackData *cbdata = new ObjectCacheWeakCallbackData();
	cbdata->cache = this;
	cbdata->key = key;
	cbdata->id  = rand();

	ObjectCacheItem<K> item;
	item.key    = key;
	item.alias  = alias;
	item.id     = cbdata->id;
	item.cbinfo = NanMakeWeakPersistent(obj, cbdata, cacheWeakCallback);

	//add it to the map
	cache[key] = item;
	if(alias) {
		aliases[alias] = key;
	}
}

template <typename K, typename W>
void ObjectCache<K, W>::add(K *key, v8::Handle<v8::Object> obj)
{
	add(key, NULL, obj);
}

template <typename K, typename W>
bool ObjectCache<K, W>::has(K *key)
{
	return cache.count(key) > 0 || aliases.count(key) > 0;
}

template <typename K, typename W>
v8::Handle<v8::Object> ObjectCache<K, W>::get(K *key)
{
	return NanNew(getItem(key).cbinfo->persistent);
}

template <typename K, typename W>
ObjectCacheItem<K> ObjectCache<K, W>::getItem(K *key)
{
	//return handle to existing object if already wrapped
	//check by calling has() first
	if (aliases.count(key) > 0) {
		key = aliases[key];
	}
	return cache[key];
}

template <typename K, typename W>
void ObjectCache<K, W>::erase(K *key)
{
	if(has(key)){
		erase(getItem(key));
	}
}

template <typename K, typename W>
void ObjectCache<K, W>::erase(ObjectCacheItem<K> item)
{

	LOG("ObjectCache erasing [%p]", item.key);
	cache.erase(item.key);
	if(item.alias){
		LOG("ObjectCache erasing alias [%p]", item.alias);
		aliases.erase(item.alias);
	}
}

#endif<|endoftext|>


<commit_before>/*
 * ModSecurity, http://www.modsecurity.org/
 * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
 *
 * You may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * If any of the files related to licensing are missing or if you have any
 * other questions related to licensing please contact Trustwave Holdings, Inc.
 * directly using the email address security@modsecurity.org.
 *
 */

#include "src/rule.h"

#include <stdio.h>

#include <algorithm>
#include <iostream>
#include <string>
#include <cstring>
#include <list>
#include <utility>

#include "operators/operator.h"
#include "actions/action.h"
#include "modsecurity/modsecurity.h"
#include "actions/transformations/none.h"
#include "variables/variations/exclusion.h"
#include "src/utils.h"
#include "modsecurity/rules.h"
#include "src/macro_expansion.h"

using modsecurity::Variables::Variations::Exclusion;

namespace modsecurity {

using operators::Operator;
using actions::Action;
using Variables::Variable;
using actions::transformations::None;

Rule::~Rule() {
    if (op != NULL) {
        delete op;
    }
    while (actions_conf.empty() == false) {
        auto *a = actions_conf.back();
        actions_conf.pop_back();
        delete a;
    }
    while (actions_runtime_pre.empty() == false) {
        auto *a = actions_runtime_pre.back();
        actions_runtime_pre.pop_back();
        delete a;
    }
    while (actions_runtime_pos.empty() == false) {
        auto *a = actions_runtime_pos.back();
        actions_runtime_pos.pop_back();
        delete a;
    }
    while (variables != NULL && variables->empty() == false) {
        auto *a = variables->back();
        variables->pop_back();
        delete a;
    }

    if (variables != NULL) {
        delete variables;
    }
}

Rule::Rule(std::string marker)
    : chained(false),
    chainedRule(NULL),
    variables(NULL),
    op(NULL),
    rule_id(0),
    phase(-1),
    m_unconditional(false),
    m_secmarker(true),
    m_marker(marker),
    m_referenceCount(0),
    m_fileName(""),
    m_lineNumber(0) { }

Rule::Rule(Operator *_op,
        std::vector<Variable *> *_variables,
        std::vector<Action *> *actions,
        std::string fileName,
        int lineNumber): chained(false),
    chainedRule(NULL),
    variables(_variables),
    op(_op),
    rule_id(0),
    phase(-1),
    m_unconditional(false),
    m_secmarker(false),
    m_marker(""),
    m_referenceCount(0),
    m_fileName(fileName),
    m_lineNumber(lineNumber) {
    if (actions != NULL) {
        for (Action *a : *actions) {
            if (a->action_kind == Action::ConfigurationKind) {
                actions_conf.push_back(a);
                a->evaluate(this, NULL);
            } else if (a->action_kind
                == Action::RunTimeBeforeMatchAttemptKind) {
                actions_runtime_pre.push_back(a);
            } else if (a->action_kind == Action::RunTimeOnlyIfMatchKind) {
                actions_runtime_pos.push_back(a);
            } else {
                std::cout << "General failure, action: " << a->name;
                std::cout << " has an unknown type." << std::endl;
                delete a;
            }
        }
    }
    /**
     * If phase is not entered, we assume phase 2. For historical reasons.
     *
     */
    if (phase == -1) {
        phase = ModSecurity::Phases::RequestHeadersPhase;
    }

    if (op == NULL) {
        m_unconditional = true;
    }

    delete actions;
}

bool Rule::evaluateActions(Assay *assay) {
    int none = 0;
    bool containsDisruptive = false;
    // int transformations = 0;
    for (Action *a : this->actions_runtime_pre) {
        if (a->m_isNone) {
            none++;
        }
    }

#ifndef NO_LOGS
    assay->debug(4, "Running unconditional rule.");
#endif

    if (none == 0) {
        /*
        for (Action *a : assay->m_rules->defaultActions[this->phase]) {
            if (a->action_kind == actions::Action::RunTimeBeforeMatchAttemptKind) {
                value = a->evaluate(value, assay);
                assay->debug(9, "(SecDefaultAction) T (" + \
                    std::to_string(transformations) + ") " + \
                    a->name + ": \"" + value +"\"");
                    transformations++;
            }
        }
        */
    }

    for (Action *a : this->actions_runtime_pre) {
        /*
        if (none == 0) {
            value = a->evaluate(value, assay);
            assay->debug(9, " T (" + \
                std::to_string(transformations) + ") " + \
                a->name + ": \"" + value +"\"");
                transformations++;
        }
        */
        if (a->m_isNone) {
            none--;
        }
    }

    for (Action *a : this->actions_runtime_pos) {
        if (a->isDisruptive() == false) {
#ifndef NO_LOGS
            assay->debug(4, "Running (_non_ disruptive) action: " + a->action);
#endif
            a->evaluate(this, assay);
        } else {
            containsDisruptive = true;
        }
    }

    for (Action *a : assay->m_rules->defaultActions[this->phase]) {
        if (a->action_kind == actions::Action::RunTimeOnlyIfMatchKind) {
            if (a->isDisruptive()) {
                if (containsDisruptive) {
#ifndef NO_LOGS
                    assay->debug(4, "(SecDefaultAction) " \
                        "_ignoring_ action: " + a->action + \
                        " (rule contains a disruptive action)");
#endif
                } else {
                    if (assay->m_rules->secRuleEngine
                        == Rules::EnabledRuleEngine) {
#ifndef NO_LOGS
                        assay->debug(4, "(SecDefaultAction) " \
                            "Running action: " + a->action + \
                            " (rule _does not_ contains a " \
                            "disruptive action)");
#endif
                            a->evaluate(this, assay);
                    } else {
#ifndef NO_LOGS
                        assay->debug(4, "(SecDefaultAction) " \
                            "_Not_ running action: " + a->action + \
                            ". Rule _does not_ contains a " \
                            "disruptive action, but SecRuleEngine is not On.");
#endif
                    }
                }
            } else {
#ifndef NO_LOGS
                assay->debug(4, "(SecDefaultAction) Running action: " + \
                    a->action);
                    a->evaluate(this, assay);
#endif
            }
        }
    }

    for (Action *a : this->actions_runtime_pos) {
        if (a->isDisruptive()
            && assay->m_rules->secRuleEngine == Rules::EnabledRuleEngine) {
#ifndef NO_LOGS
            assay->debug(4, "Running (disruptive) action: " + a->action);
#endif
            a->evaluate(this, assay);
        } else if (a->isDisruptive()) {
#ifndef NO_LOGS
            assay->debug(4, "Not running disruptive action: " + \
                a->action + ". SecRuleEngine is not On");
#endif
        }
    }

    return true;
}

bool Rule::evaluate(Assay *assay) {
    bool ret = false;
    std::vector<Variable *> *variables = this->variables;

    if (m_secmarker == true) {
        return true;
    }
    if (m_unconditional == true) {
        return evaluateActions(assay);
    }

#ifndef NO_LOGS
    std::string eparam = MacroExpansion::expand(this->op->param, assay);

    if (this->op->param != eparam) {
        eparam = "\"" + eparam + "\" Was: \"" + this->op->param + "\"";
    } else {
        eparam = "\"" + eparam + "\"";
    }

    assay->debug(4, "(Rule: " + std::to_string(rule_id) \
        + ") Executing operator \"" + this->op->op \
        + "\" with param " \
        + eparam \
        + " against " \
        + Variable::to_s(variables) + ".");

    clock_t begin = clock();
#endif

    std::list<std::string> exclusions;
    for (int i = 0; i < variables->size(); i++) {
        Variable *variable = variables->at(i);
        if (variable->m_isExclusion) {
            std::vector<const transaction::Variable *> z;
            variable->evaluateInternal(assay, &z);
            for (auto &y : z) {
                exclusions.push_back(y->m_key);
            }
            exclusions.push_back(variable->m_name);
        }
    }

    for (int i = 0; i < variables->size(); i++) {
        int transformations = 0;
        Variable *variable = variables->at(i);
        if (variable->m_isExclusion) {
            continue;
        }

        std::vector<const transaction::Variable *> e;
        variable->evaluateInternal(assay, &e);

        for (auto &v : e) {
            if (std::find(exclusions.begin(), exclusions.end(),
                v->m_key) != exclusions.end()) {
#ifndef NO_LOGS
                assay->debug(9, "Variable: " + v->m_key + " is part of the" +
                    " exclusion list, skipping...");
#endif
                continue;
            }
            std::string value = v->m_value;
            int none = 0;
            for (Action *a : this->actions_runtime_pre) {
                if (a->m_isNone) {
                    none++;
                }
            }

            // Check for transformations on the SecDefaultAction
            // Notice that first we make sure that won't be a t:none
            // on the target rule.
            if (none == 0) {
                for (Action *a : assay->m_rules->defaultActions[this->phase]) {
                    if (a->action_kind ==
                        actions::Action::RunTimeBeforeMatchAttemptKind) {
                        value = a->evaluate(value, assay);
#ifndef NO_LOGS
                        assay->debug(9, "(SecDefaultAction) T (" + \
                            std::to_string(transformations) + ") " + \
                            a->name + ": \"" + value +"\"");
#endif
                        transformations++;
                    }
                }
            }

            for (Action *a : this->actions_runtime_pre) {
                if (none == 0) {
                    value = a->evaluate(value, assay);
#ifndef NO_LOGS
                    assay->debug(9, " T (" + \
                            std::to_string(transformations) + ") " + \
                            a->name + ": \"" + value +"\"");
#endif
                    transformations++;
                }
                if (a->m_isNone) {
                    none--;
                }
            }

#ifndef NO_LOGS
            assay->debug(9, "Target value: \"" + limitTo(80,
                toHexIfNeeded(value)) + "\" (Variable: " + v->m_key + ")");
#endif

            ret = this->op->evaluate(assay, value);

#ifndef NO_LOGS
            clock_t end = clock();
            double elapsed_secs = static_cast<double>(end - begin) \
                / CLOCKS_PER_SEC;

            assay->debug(4, "Operator completed in " + \
                std::to_string(elapsed_secs) + " seconds");
#endif

            if (ret) {
                bool containsDisruptive = false;
                bool chainResult = false;
#ifndef NO_LOGS
                assay->debug(4, "Rule returned 1.");
#endif

                for (Action *a :
                    this->actions_runtime_pos) {
                    if (a->isDisruptive() == false) {
#ifndef NO_LOGS
                        assay->debug(4, "Running (_non_ disruptive) action: " \
                            + a->action);
#endif
                        a->evaluate(this, assay);
                    } else {
                        containsDisruptive = true;
                    }
                }

                if (this->chained && this->chainedRule == NULL) {
#ifndef NO_LOGS
                    assay->debug(4, "Rule is marked as chained but there " \
                        "isn't a subsequent rule.");
#endif
                }
                if (this->chained && this->chainedRule != NULL) {
#ifndef NO_LOGS
                    assay->debug(4, "Executing chained rule.");
#endif
                    if (assay->m_collections.storeOrUpdateFirst("MATCHED_VAR",
                        value) == false) {
                        assay->m_collections.store("MATCHED_VAR", value);
                    }
                    if (assay->m_collections.storeOrUpdateFirst(
                        "MATCHED_VAR_NAME", v->m_key) == false) {
                        assay->m_collections.store("MATCHED_VAR_NAME",
                            v->m_key);
                    }
                    assay->m_collections.store("MATCHED_VARS:"
                        + v->m_key, value);
                    assay->m_collections.store("MATCHED_VARS_NAMES:"
                        + v->m_key, v->m_key);
                    chainResult = this->chainedRule->evaluate(assay);
                    assay->m_collections.storeOrUpdateFirst("MATCHED_VAR", "");
                    assay->m_collections.del("MATCHED_VARS:" + v->m_key);
                    assay->m_collections.del("MATCHED_VARS_NAMES:" + v->m_key);
                    assay->m_collections.del("MATCHED_VARS_NAME");
                }
                if (this->chained && chainResult == true || !this->chained) {
                    for (Action *a :
                        assay->m_rules->defaultActions[this->phase]) {
                        if (a->action_kind
                            == actions::Action::RunTimeOnlyIfMatchKind) {
                            if (a->isDisruptive()) {
                                if (containsDisruptive) {
#ifndef NO_LOGS
                                    assay->debug(4, "(SecDefaultAction) " \
                                        "_ignoring_ action: " + a->action \
                                        + " (rule contains " \
                                        + "a disruptive action)");
#endif
                                } else {
                                    if (assay->m_rules->secRuleEngine
                                        == Rules::EnabledRuleEngine) {
#ifndef NO_LOGS
                                        assay->debug(4, "(SecDefaultAction) " \
                                            "Running action: " + a->action + \
                                            " (rule _does not_ contains a " \
                                            "disruptive action)");
#endif
                                        a->evaluate(this, assay);
                                    } else {
#ifndef NO_LOGS
                                        assay->debug(4, "(SecDefaultAction) " \
                                            "_Not_ running action: " \
                                            + a->action + ". Rule _does not_" \
                                            + " contains a disruptive action,"\
                                            + " but SecRuleEngine is not On.");
#endif
                                    }
                                }
                            } else {
#ifndef NO_LOGS
                                assay->debug(4, "(SecDefaultAction) Running " \
                                    "action: " + a->action + "!!" \
                                    + std::to_string(a->isDisruptive()));
#endif
                                a->evaluate(this, assay);
                            }
                        }
                    }
                    for (Action *a :
                        this->actions_runtime_pos) {
                        if (a->isDisruptive()
                            && assay->m_rules->secRuleEngine
                                == Rules::EnabledRuleEngine) {
#ifndef NO_LOGS
                            assay->debug(4, "Running (disruptive) action: " + \
                                a->action);
#endif
                            a->evaluate(this, assay);
                        } else if (a->isDisruptive()) {
#ifndef NO_LOGS
                            assay->debug(4,
                                "Not running disruptive action: " + \
                                a->action + ". SecRuleEngine is not On");
#endif
                        }
                    }
                }

            } else {
#ifndef NO_LOGS
                assay->debug(4, "Rule returned 0.");
#endif
            }
        }

        while (e.empty() == false) {
            delete e.back();
            e.pop_back();
        }
    }
    return ret;
}

}  // namespace ModSecurity
<commit_msg>Clarifies conditional by placing its parts into parentheses<commit_after>/*
 * ModSecurity, http://www.modsecurity.org/
 * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
 *
 * You may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * If any of the files related to licensing are missing or if you have any
 * other questions related to licensing please contact Trustwave Holdings, Inc.
 * directly using the email address security@modsecurity.org.
 *
 */

#include "src/rule.h"

#include <stdio.h>

#include <algorithm>
#include <iostream>
#include <string>
#include <cstring>
#include <list>
#include <utility>

#include "operators/operator.h"
#include "actions/action.h"
#include "modsecurity/modsecurity.h"
#include "actions/transformations/none.h"
#include "variables/variations/exclusion.h"
#include "src/utils.h"
#include "modsecurity/rules.h"
#include "src/macro_expansion.h"

using modsecurity::Variables::Variations::Exclusion;

namespace modsecurity {

using operators::Operator;
using actions::Action;
using Variables::Variable;
using actions::transformations::None;

Rule::~Rule() {
    if (op != NULL) {
        delete op;
    }
    while (actions_conf.empty() == false) {
        auto *a = actions_conf.back();
        actions_conf.pop_back();
        delete a;
    }
    while (actions_runtime_pre.empty() == false) {
        auto *a = actions_runtime_pre.back();
        actions_runtime_pre.pop_back();
        delete a;
    }
    while (actions_runtime_pos.empty() == false) {
        auto *a = actions_runtime_pos.back();
        actions_runtime_pos.pop_back();
        delete a;
    }
    while (variables != NULL && variables->empty() == false) {
        auto *a = variables->back();
        variables->pop_back();
        delete a;
    }

    if (variables != NULL) {
        delete variables;
    }
}

Rule::Rule(std::string marker)
    : chained(false),
    chainedRule(NULL),
    variables(NULL),
    op(NULL),
    rule_id(0),
    phase(-1),
    m_unconditional(false),
    m_secmarker(true),
    m_marker(marker),
    m_referenceCount(0),
    m_fileName(""),
    m_lineNumber(0) { }

Rule::Rule(Operator *_op,
        std::vector<Variable *> *_variables,
        std::vector<Action *> *actions,
        std::string fileName,
        int lineNumber): chained(false),
    chainedRule(NULL),
    variables(_variables),
    op(_op),
    rule_id(0),
    phase(-1),
    m_unconditional(false),
    m_secmarker(false),
    m_marker(""),
    m_referenceCount(0),
    m_fileName(fileName),
    m_lineNumber(lineNumber) {
    if (actions != NULL) {
        for (Action *a : *actions) {
            if (a->action_kind == Action::ConfigurationKind) {
                actions_conf.push_back(a);
                a->evaluate(this, NULL);
            } else if (a->action_kind
                == Action::RunTimeBeforeMatchAttemptKind) {
                actions_runtime_pre.push_back(a);
            } else if (a->action_kind == Action::RunTimeOnlyIfMatchKind) {
                actions_runtime_pos.push_back(a);
            } else {
                std::cout << "General failure, action: " << a->name;
                std::cout << " has an unknown type." << std::endl;
                delete a;
            }
        }
    }
    /**
     * If phase is not entered, we assume phase 2. For historical reasons.
     *
     */
    if (phase == -1) {
        phase = ModSecurity::Phases::RequestHeadersPhase;
    }

    if (op == NULL) {
        m_unconditional = true;
    }

    delete actions;
}

bool Rule::evaluateActions(Assay *assay) {
    int none = 0;
    bool containsDisruptive = false;
    // int transformations = 0;
    for (Action *a : this->actions_runtime_pre) {
        if (a->m_isNone) {
            none++;
        }
    }

#ifndef NO_LOGS
    assay->debug(4, "Running unconditional rule.");
#endif

    if (none == 0) {
        /*
        for (Action *a : assay->m_rules->defaultActions[this->phase]) {
            if (a->action_kind == actions::Action::RunTimeBeforeMatchAttemptKind) {
                value = a->evaluate(value, assay);
                assay->debug(9, "(SecDefaultAction) T (" + \
                    std::to_string(transformations) + ") " + \
                    a->name + ": \"" + value +"\"");
                    transformations++;
            }
        }
        */
    }

    for (Action *a : this->actions_runtime_pre) {
        /*
        if (none == 0) {
            value = a->evaluate(value, assay);
            assay->debug(9, " T (" + \
                std::to_string(transformations) + ") " + \
                a->name + ": \"" + value +"\"");
                transformations++;
        }
        */
        if (a->m_isNone) {
            none--;
        }
    }

    for (Action *a : this->actions_runtime_pos) {
        if (a->isDisruptive() == false) {
#ifndef NO_LOGS
            assay->debug(4, "Running (_non_ disruptive) action: " + a->action);
#endif
            a->evaluate(this, assay);
        } else {
            containsDisruptive = true;
        }
    }

    for (Action *a : assay->m_rules->defaultActions[this->phase]) {
        if (a->action_kind == actions::Action::RunTimeOnlyIfMatchKind) {
            if (a->isDisruptive()) {
                if (containsDisruptive) {
#ifndef NO_LOGS
                    assay->debug(4, "(SecDefaultAction) " \
                        "_ignoring_ action: " + a->action + \
                        " (rule contains a disruptive action)");
#endif
                } else {
                    if (assay->m_rules->secRuleEngine
                        == Rules::EnabledRuleEngine) {
#ifndef NO_LOGS
                        assay->debug(4, "(SecDefaultAction) " \
                            "Running action: " + a->action + \
                            " (rule _does not_ contains a " \
                            "disruptive action)");
#endif
                            a->evaluate(this, assay);
                    } else {
#ifndef NO_LOGS
                        assay->debug(4, "(SecDefaultAction) " \
                            "_Not_ running action: " + a->action + \
                            ". Rule _does not_ contains a " \
                            "disruptive action, but SecRuleEngine is not On.");
#endif
                    }
                }
            } else {
#ifndef NO_LOGS
                assay->debug(4, "(SecDefaultAction) Running action: " + \
                    a->action);
                    a->evaluate(this, assay);
#endif
            }
        }
    }

    for (Action *a : this->actions_runtime_pos) {
        if (a->isDisruptive()
            && assay->m_rules->secRuleEngine == Rules::EnabledRuleEngine) {
#ifndef NO_LOGS
            assay->debug(4, "Running (disruptive) action: " + a->action);
#endif
            a->evaluate(this, assay);
        } else if (a->isDisruptive()) {
#ifndef NO_LOGS
            assay->debug(4, "Not running disruptive action: " + \
                a->action + ". SecRuleEngine is not On");
#endif
        }
    }

    return true;
}

bool Rule::evaluate(Assay *assay) {
    bool ret = false;
    std::vector<Variable *> *variables = this->variables;

    if (m_secmarker == true) {
        return true;
    }
    if (m_unconditional == true) {
        return evaluateActions(assay);
    }

#ifndef NO_LOGS
    std::string eparam = MacroExpansion::expand(this->op->param, assay);

    if (this->op->param != eparam) {
        eparam = "\"" + eparam + "\" Was: \"" + this->op->param + "\"";
    } else {
        eparam = "\"" + eparam + "\"";
    }

    assay->debug(4, "(Rule: " + std::to_string(rule_id) \
        + ") Executing operator \"" + this->op->op \
        + "\" with param " \
        + eparam \
        + " against " \
        + Variable::to_s(variables) + ".");

    clock_t begin = clock();
#endif

    std::list<std::string> exclusions;
    for (int i = 0; i < variables->size(); i++) {
        Variable *variable = variables->at(i);
        if (variable->m_isExclusion) {
            std::vector<const transaction::Variable *> z;
            variable->evaluateInternal(assay, &z);
            for (auto &y : z) {
                exclusions.push_back(y->m_key);
            }
            exclusions.push_back(variable->m_name);
        }
    }

    for (int i = 0; i < variables->size(); i++) {
        int transformations = 0;
        Variable *variable = variables->at(i);
        if (variable->m_isExclusion) {
            continue;
        }

        std::vector<const transaction::Variable *> e;
        variable->evaluateInternal(assay, &e);

        for (auto &v : e) {
            if (std::find(exclusions.begin(), exclusions.end(),
                v->m_key) != exclusions.end()) {
#ifndef NO_LOGS
                assay->debug(9, "Variable: " + v->m_key + " is part of the" +
                    " exclusion list, skipping...");
#endif
                continue;
            }
            std::string value = v->m_value;
            int none = 0;
            for (Action *a : this->actions_runtime_pre) {
                if (a->m_isNone) {
                    none++;
                }
            }

            // Check for transformations on the SecDefaultAction
            // Notice that first we make sure that won't be a t:none
            // on the target rule.
            if (none == 0) {
                for (Action *a : assay->m_rules->defaultActions[this->phase]) {
                    if (a->action_kind ==
                        actions::Action::RunTimeBeforeMatchAttemptKind) {
                        value = a->evaluate(value, assay);
#ifndef NO_LOGS
                        assay->debug(9, "(SecDefaultAction) T (" + \
                            std::to_string(transformations) + ") " + \
                            a->name + ": \"" + value +"\"");
#endif
                        transformations++;
                    }
                }
            }

            for (Action *a : this->actions_runtime_pre) {
                if (none == 0) {
                    value = a->evaluate(value, assay);
#ifndef NO_LOGS
                    assay->debug(9, " T (" + \
                            std::to_string(transformations) + ") " + \
                            a->name + ": \"" + value +"\"");
#endif
                    transformations++;
                }
                if (a->m_isNone) {
                    none--;
                }
            }

#ifndef NO_LOGS
            assay->debug(9, "Target value: \"" + limitTo(80,
                toHexIfNeeded(value)) + "\" (Variable: " + v->m_key + ")");
#endif

            ret = this->op->evaluate(assay, value);

#ifndef NO_LOGS
            clock_t end = clock();
            double elapsed_secs = static_cast<double>(end - begin) \
                / CLOCKS_PER_SEC;

            assay->debug(4, "Operator completed in " + \
                std::to_string(elapsed_secs) + " seconds");
#endif

            if (ret) {
                bool containsDisruptive = false;
                bool chainResult = false;
#ifndef NO_LOGS
                assay->debug(4, "Rule returned 1.");
#endif

                for (Action *a :
                    this->actions_runtime_pos) {
                    if (a->isDisruptive() == false) {
#ifndef NO_LOGS
                        assay->debug(4, "Running (_non_ disruptive) action: " \
                            + a->action);
#endif
                        a->evaluate(this, assay);
                    } else {
                        containsDisruptive = true;
                    }
                }

                if (this->chained && this->chainedRule == NULL) {
#ifndef NO_LOGS
                    assay->debug(4, "Rule is marked as chained but there " \
                        "isn't a subsequent rule.");
#endif
                }
                if (this->chained && this->chainedRule != NULL) {
#ifndef NO_LOGS
                    assay->debug(4, "Executing chained rule.");
#endif
                    if (assay->m_collections.storeOrUpdateFirst("MATCHED_VAR",
                        value) == false) {
                        assay->m_collections.store("MATCHED_VAR", value);
                    }
                    if (assay->m_collections.storeOrUpdateFirst(
                        "MATCHED_VAR_NAME", v->m_key) == false) {
                        assay->m_collections.store("MATCHED_VAR_NAME",
                            v->m_key);
                    }
                    assay->m_collections.store("MATCHED_VARS:"
                        + v->m_key, value);
                    assay->m_collections.store("MATCHED_VARS_NAMES:"
                        + v->m_key, v->m_key);
                    chainResult = this->chainedRule->evaluate(assay);
                    assay->m_collections.storeOrUpdateFirst("MATCHED_VAR", "");
                    assay->m_collections.del("MATCHED_VARS:" + v->m_key);
                    assay->m_collections.del("MATCHED_VARS_NAMES:" + v->m_key);
                    assay->m_collections.del("MATCHED_VARS_NAME");
                }
                if ((this->chained && chainResult == true) || !this->chained) {
                    for (Action *a :
                        assay->m_rules->defaultActions[this->phase]) {
                        if (a->action_kind
                            == actions::Action::RunTimeOnlyIfMatchKind) {
                            if (a->isDisruptive()) {
                                if (containsDisruptive) {
#ifndef NO_LOGS
                                    assay->debug(4, "(SecDefaultAction) " \
                                        "_ignoring_ action: " + a->action \
                                        + " (rule contains " \
                                        + "a disruptive action)");
#endif
                                } else {
                                    if (assay->m_rules->secRuleEngine
                                        == Rules::EnabledRuleEngine) {
#ifndef NO_LOGS
                                        assay->debug(4, "(SecDefaultAction) " \
                                            "Running action: " + a->action + \
                                            " (rule _does not_ contains a " \
                                            "disruptive action)");
#endif
                                        a->evaluate(this, assay);
                                    } else {
#ifndef NO_LOGS
                                        assay->debug(4, "(SecDefaultAction) " \
                                            "_Not_ running action: " \
                                            + a->action + ". Rule _does not_" \
                                            + " contains a disruptive action,"\
                                            + " but SecRuleEngine is not On.");
#endif
                                    }
                                }
                            } else {
#ifndef NO_LOGS
                                assay->debug(4, "(SecDefaultAction) Running " \
                                    "action: " + a->action + "!!" \
                                    + std::to_string(a->isDisruptive()));
#endif
                                a->evaluate(this, assay);
                            }
                        }
                    }
                    for (Action *a :
                        this->actions_runtime_pos) {
                        if (a->isDisruptive()
                            && assay->m_rules->secRuleEngine
                                == Rules::EnabledRuleEngine) {
#ifndef NO_LOGS
                            assay->debug(4, "Running (disruptive) action: " + \
                                a->action);
#endif
                            a->evaluate(this, assay);
                        } else if (a->isDisruptive()) {
#ifndef NO_LOGS
                            assay->debug(4,
                                "Not running disruptive action: " + \
                                a->action + ". SecRuleEngine is not On");
#endif
                        }
                    }
                }

            } else {
#ifndef NO_LOGS
                assay->debug(4, "Rule returned 0.");
#endif
            }
        }

        while (e.empty() == false) {
            delete e.back();
            e.pop_back();
        }
    }
    return ret;
}

}  // namespace ModSecurity
<|endoftext|>