<commit_before>// Copyright (c) 2011-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <test/util/setup_common.h>

#include <banman.h>
#include <chainparams.h>
#include <consensus/consensus.h>
#include <consensus/params.h>
#include <consensus/validation.h>
#include <crypto/sha256.h>
#include <init.h>
#include <miner.h>
#include <net.h>
#include <noui.h>
#include <pow.h>
#include <rpc/blockchain.h>
#include <rpc/register.h>
#include <rpc/server.h>
#include <script/sigcache.h>
#include <streams.h>
#include <txdb.h>
#include <util/memory.h>
#include <util/strencodings.h>
#include <util/time.h>
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>

#include <functional>

const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;

FastRandomContext g_insecure_rand_ctx;

std::ostream& operator<<(std::ostream& os, const uint256& num)
{
    os << num.ToString();
    return os;
}

BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
    : m_path_root(fs::temp_directory_path() / "test_common_" PACKAGE_NAME / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
{
    fs::create_directories(m_path_root);
    gArgs.ForceSetArg("-datadir", m_path_root.string());
    ClearDatadirCache();
    SelectParams(chainName);
    gArgs.ForceSetArg("-printtoconsole", "0");
    InitLogging();
    LogInstance().StartLogging();
    SHA256AutoDetect();
    ECC_Start();
    SetupEnvironment();
    SetupNetworking();
    InitSignatureCache();
    InitScriptExecutionCache();
    fCheckBlockIndex = true;
    static bool noui_connected = false;
    if (!noui_connected) {
        noui_connect();
        noui_connected = true;
    }
}

BasicTestingSetup::~BasicTestingSetup()
{
    LogInstance().DisconnectTestLogger();
    fs::remove_all(m_path_root);
    ECC_Stop();
}

TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
{
    const CChainParams& chainparams = Params();
    // Ideally we'd move all the RPC tests to the functional testing framework
    // instead of unit tests, but for now we need these here.
    g_rpc_node = &m_node;
    RegisterAllCoreRPCCommands(tableRPC);

    // We have to run a scheduler thread to prevent ActivateBestChain
    // from blocking due to queue overrun.
    threadGroup.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
    GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);

    mempool.setSanityCheck(1.0);
    pblocktree.reset(new CBlockTreeDB(1 << 20, true));
    g_chainstate = MakeUnique<CChainState>();
    ::ChainstateActive().InitCoinsDB(
        /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
    assert(!::ChainstateActive().CanFlushToDisk());
    ::ChainstateActive().InitCoinsCache();
    assert(::ChainstateActive().CanFlushToDisk());
    if (!LoadGenesisBlock(chainparams)) {
        throw std::runtime_error("LoadGenesisBlock failed.");
    }

    BlockValidationState state;
    if (!ActivateBestChain(state, chainparams)) {
        throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
    }

    // Start script-checking threads. Set g_parallel_script_checks to true so they are used.
    constexpr int script_check_threads = 2;
    for (int i = 0; i < script_check_threads; ++i) {
        threadGroup.create_thread([i]() { return ThreadScriptCheck(i); });
    }
    g_parallel_script_checks = true;

    m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
    m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
}

TestingSetup::~TestingSetup()
{
    threadGroup.interrupt_all();
    threadGroup.join_all();
    GetMainSignals().FlushBackgroundCallbacks();
    GetMainSignals().UnregisterBackgroundSignalScheduler();
    g_rpc_node = nullptr;
    m_node.connman.reset();
    m_node.banman.reset();
    UnloadBlockIndex();
    g_chainstate.reset();
    pblocktree.reset();
}

TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
{
    // CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests.
    // TODO: fix the code to support SegWit blocks.
    gArgs.ForceSetArg("-segwitheight", "432");
    TurnOffSegwitForUnitTests();
    // Need to recreate chainparams
    //SelectParams(CBaseChainParams::REGTEST);

    // Generate a 100-block chain:
    coinbaseKey.MakeNewKey(true);
    CScript scriptPubKey = CScript() <<  ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
    for (int i = 0; i < COINBASE_MATURITY; i++)
    {
        std::vector<CMutableTransaction> noTxns;
        CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey);
        m_coinbase_txns.push_back(b.vtx[0]);
    }
}

// Create a new block with just given transactions, coinbase paying to
// scriptPubKey, and try to add it to the current chain.
CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey)
{
    const CChainParams& chainparams = Params();
    std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
    CBlock& block = pblocktemplate->block;

    // Replace mempool-selected txns with just coinbase plus passed-in txns:
    block.vtx.resize(1);
    for (const CMutableTransaction& tx : txns)
        block.vtx.push_back(MakeTransactionRef(tx));
    // IncrementExtraNonce creates a valid coinbase and merkleRoot
    {
        LOCK(cs_main);
        unsigned int extraNonce = 0;
        IncrementExtraNonce(&block, ::ChainActive().Tip(), extraNonce);
    }

    while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;

    std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
    ProcessNewBlock(chainparams, shared_pblock, true, nullptr);

    CBlock result = block;
    return result;
}

TestChain100Setup::~TestChain100Setup()
{
    gArgs.ForceSetArg("-segwitheight", "0");
}

CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx) {
    return FromTx(MakeTransactionRef(tx));
}

CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransactionRef& tx)
{
    return CTxMemPoolEntry(tx, nFee, nTime, nHeight,
                           spendsCoinbase, sigOpCost, lp);
}

/**
 * @returns a real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af)
 *      with 9 txs.
 */
CBlock getBlock13b8a()
{
    CBlock block;
    CDataStream stream(ParseHex("0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930901000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0146ffffffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf254bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada902780da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100dab24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd022100fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb401ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369ed2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98ec706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21adc6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e8625a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d898235e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff0280969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd388ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c52292d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2fafd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae740605658022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e01003614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b48304502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddcce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad56529371864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd90111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d001410443bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffffffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a12322d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70ae67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b95600db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b2179010000008b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3ca2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b491d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144af553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1af03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d98a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a038fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f63512850811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e91349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e8860c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb6042034aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000"), SER_NETWORK, PROTOCOL_VERSION);
    stream >> block;
    return block;
}
<commit_msg>remove file<commit_after><|endoftext|>


<commit_before>// Copyright (C) 2012 Andrei Alexandrescu.

#ifndef NONSTD_EXPECTED_ALEXANDRESCU_H_INCLUDED
#define NONSTD_EXPECTED_ALEXANDRESCU_H_INCLUDED

#include <algorithm>            // std::move()
#include <exception>            // std::exception_ptr

template<class T>
class Expected
{
    //union // todo(azerg): uncomment when going to vc2015
    //{
        T ham;
        std::exception_ptr spam;
    //};

    bool gotHam;

    Expected() {} //used internally

public:
    ~Expected()
    {
        if ( gotHam )
            ham.~T();
        else
            spam.~exception_ptr();
    }

    Expected( const T & rhs ) : ham( rhs ), gotHam( true ) {}

    Expected( T && rhs ) : ham( std::move( rhs ) ), gotHam( true ) {}

    Expected( const Expected & rhs )
        : gotHam( rhs.gotHam )
    {
        if( gotHam )
            new( &ham ) T ( rhs.ham );
        else
            new( &spam ) std::exception_ptr( rhs.spam );
    }

    Expected( Expected && rhs )
        : gotHam( rhs.gotHam )
    {
        if( gotHam )
            new( &ham ) T ( std::move( rhs.ham ) );
        else
            new( &spam ) std::exception_ptr( std::move( rhs.spam ) );

    }

    void swap( Expected & rhs )
    {
        if( gotHam )
        {
            if( rhs.gotHam )
            {
                using std::swap;
                swap( ham, rhs.ham );
            }
            else
            {
                auto t = std::move( rhs.spam );
                new( &rhs.ham ) T( std::move( ham ) );
                new( &spam ) std::exception_ptr( t );
                std::swap( gotHam, rhs.gotHam );
            }
        }
        else
        {
            if( rhs.gotHam )
            {
                rhs.swap( *this );
            }
            else
            {
                spam.swap( rhs.spam );
                std::swap( gotHam, rhs.gotHam );
            }
        }
    }

    template<class E>
    static Expected<T> fromException( const E & exception )
    {
        if( typeid( exception ) != typeid( E ) )
        {
            throw std::invalid_argument( "slicing detected" );
        }
        return fromException( std::make_exception_ptr( exception ) );
    }

    static Expected<T> fromException( std::exception_ptr p )
    {
        Expected<T> result;
        result.gotHam = false;

        new( &result.spam ) std::exception_ptr( std::move( p ) );

        return result;
    }

    static Expected<T> fromException()
    {
        return fromException( std::current_exception() );
    }

    // Access:

    bool valid() const
    {
        return gotHam;
    }

    T & get()
    {
        if( !gotHam )
            std::rethrow_exception( spam );
        return ham;

    }

    const T & get() const
    {
        if( !gotHam )
            std::rethrow_exception( spam );
        return ham;
    }

    // Probing for Type:

    template<class E>
    bool hasException() const
    {
        try
        {
            if( !gotHam )
                std::rethrow_exception( spam );
        }
        catch
            ( const E & object )
        {
            return true;
        }
        catch
            ( ... )
        {
        }
        return false;

    }

    // Icing:

    template<class F>
    static Expected fromCode( F fun )
    {
        try
        {
            return Expected( fun() );
        }
        catch
            ( ... )
        {
            return fromException();
        }
    }

    // auto r = Expected<string>::fromCode( [] { ... } );
};

#endif // NONSTD_EXPECTED_ALEXANDRESCU_H_INCLUDED
<commit_msg>fixed expected regards to vs2015<commit_after>// Copyright (C) 2012 Andrei Alexandrescu.

#ifndef NONSTD_EXPECTED_ALEXANDRESCU_H_INCLUDED
#define NONSTD_EXPECTED_ALEXANDRESCU_H_INCLUDED

#include <algorithm>            // std::move()
#include <exception>            // std::exception_ptr

template<class T>
class Expected
{
    union
    {
        T ham;
        std::exception_ptr spam;
    };

    bool gotHam;

    Expected() {} //used internally

public:
    ~Expected()
    {
        if ( gotHam )
            ham.~T();
        else
            spam.~exception_ptr();
    }

    Expected( const T & rhs ) : ham( rhs ), gotHam( true ) {}

    Expected( T && rhs ) : ham( std::move( rhs ) ), gotHam( true ) {}

    Expected( const Expected & rhs )
        : gotHam( rhs.gotHam )
    {
        if( gotHam )
            new( &ham ) T ( rhs.ham );
        else
            new( &spam ) std::exception_ptr( rhs.spam );
    }

    Expected( Expected && rhs )
        : gotHam( rhs.gotHam )
    {
        if( gotHam )
            new( &ham ) T ( std::move( rhs.ham ) );
        else
            new( &spam ) std::exception_ptr( std::move( rhs.spam ) );

    }

    void swap( Expected & rhs )
    {
        if( gotHam )
        {
            if( rhs.gotHam )
            {
                using std::swap;
                swap( ham, rhs.ham );
            }
            else
            {
                auto t = std::move( rhs.spam );
                new( &rhs.ham ) T( std::move( ham ) );
                new( &spam ) std::exception_ptr( t );
                std::swap( gotHam, rhs.gotHam );
            }
        }
        else
        {
            if( rhs.gotHam )
            {
                rhs.swap( *this );
            }
            else
            {
                spam.swap( rhs.spam );
                std::swap( gotHam, rhs.gotHam );
            }
        }
    }

    template<class E>
    static Expected<T> fromException( const E & exception )
    {
        if( typeid( exception ) != typeid( E ) )
        {
            throw std::invalid_argument( "slicing detected" );
        }
        return fromException( std::make_exception_ptr( exception ) );
    }

    static Expected<T> fromException( std::exception_ptr p )
    {
        Expected<T> result;
        result.gotHam = false;

        new( &result.spam ) std::exception_ptr( std::move( p ) );

        return result;
    }

    static Expected<T> fromException()
    {
        return fromException( std::current_exception() );
    }

    // Access:

    bool valid() const
    {
        return gotHam;
    }

    T & get()
    {
        if( !gotHam )
            std::rethrow_exception( spam );
        return ham;

    }

    const T & get() const
    {
        if( !gotHam )
            std::rethrow_exception( spam );
        return ham;
    }

    // Probing for Type:

    template<class E>
    bool hasException() const
    {
        try
        {
            if( !gotHam )
                std::rethrow_exception( spam );
        }
        catch
            ( const E & object )
        {
            return true;
        }
        catch
            ( ... )
        {
        }
        return false;

    }

    // Icing:

    template<class F>
    static Expected fromCode( F fun )
    {
        try
        {
            return Expected( fun() );
        }
        catch
            ( ... )
        {
            return fromException();
        }
    }

    // auto r = Expected<string>::fromCode( [] { ... } );
};

#endif // NONSTD_EXPECTED_ALEXANDRESCU_H_INCLUDED
<|endoftext|>


<commit_before>/*************************************************
* Boost.Python module definition                 *
* (C) 1999-2006 The Botan Project                *
*************************************************/

#include <botan/rsa.h>
#include <botan/dsa.h>
#include <botan/dh.h>
#include <botan/look_pk.h>
using namespace Botan;

#include <boost/python.hpp>
namespace python = boost::python;

std::string encode_pub(const Public_Key* key,
                       const std::string& type)
   {
   if(type == "DER")
      {
      Pipe pipe;
      pipe.start_msg();
      X509::encode(*key, pipe, RAW_BER);
      pipe.end_msg();
      return pipe.read_all_as_string();
      }
   else if(type == "PEM")
      return X509::PEM_encode(*key);
   else
      throw Encoding_Error("Unknown key encoding method " + type);
   }

std::string encode_priv(const Private_Key* key,
                        const std::string& type)
   {
   if(type == "DER")
      {
      Pipe pipe;
      PKCS8::encode(*key, pipe, RAW_BER);
      return pipe.read_all_as_string();
      }
   else if(type == "PEM")
      return PKCS8::PEM_encode(*key);
   else
      throw Encoding_Error("Unknown key encoding method " + type);
   }

Private_Key* load_priv(const std::string& file, const std::string& pass)
   {
   return PKCS8::load_key(file, pass);
   }

Public_Key* load_public(const std::string& file)
   {
   return X509::load_key(file);
   }

std::string encrypt_string(const PK_Encryptor* enc, const std::string& in)
   {
   SecureVector<byte> cipher = enc->encrypt((const byte*)in.data(), in.length());
   return std::string((const char*)cipher.begin(), cipher.size());
   }

std::string decrypt_string(const PK_Decryptor* dec, const std::string& in)
   {
   SecureVector<byte> plain = dec->decrypt((const byte*)in.data(), in.length());
   return std::string((const char*)plain.begin(), plain.size());
   }

void export_pk()
   {
   python::def("private_key", load_priv,
               python::return_value_policy<python::manage_new_object>());
   python::def("public_key", load_public,
               python::return_value_policy<python::manage_new_object>());

   python::class_<Public_Key, boost::noncopyable>
      ("Public_Key", python::no_init)
      .add_property("name", &Public_Key::algo_name)
      .add_property("max_input_bits", &Public_Key::max_input_bits)
      .def("public_key", encode_pub);

   python::class_<PK_Encrypting_Key, python::bases<Public_Key>, boost::noncopyable>
      ("PK_Encrypting_Key", python::no_init);

   python::class_<Private_Key, python::bases<Public_Key>, boost::noncopyable>
      ("Private_Key", python::no_init)
      .def("private_key", encode_priv);

   python::class_<PK_Decrypting_Key, python::bases<Private_Key>, boost::noncopyable>
      ("PK_Decrypting_Key", python::no_init);

   python::class_<RSA_PublicKey, python::bases<PK_Encrypting_Key> >
      ("RSA_PublicKey", python::no_init);

   python::class_<DSA_PublicKey, python::bases<Public_Key> >
      ("DSA_PublicKey", python::no_init);

   python::class_<RSA_PrivateKey, python::bases<RSA_PublicKey, PK_Decrypting_Key> >
      ("RSA_PrivateKey", python::init<u32bit>());

   python::class_<PK_Encryptor, boost::noncopyable>
      ("PK_Encryptor", python::no_init)
      .def("__init__",
           python::make_constructor(get_pk_encryptor,
                                    python::with_custodian_and_ward_postcall<0, 1>()))
      .def("max_input", &PK_Encryptor::maximum_input_size)
      .def("encrypt", encrypt_string);

   /*
   python::class_<PK_Decryptor, boost::noncopyable>
      ("PK_Decryptor", python::no_init)
      .def("__init__", python::make_constructor(get_pk_decryptor))
      .def("decrypt", decrypt_string);
   */
   }
<commit_msg>Comment out some problematic code so at least the Python extension can build<commit_after>/*************************************************
* Boost.Python module definition                 *
* (C) 1999-2006 The Botan Project                *
*************************************************/

#include <botan/rsa.h>
#include <botan/dsa.h>
#include <botan/dh.h>
#include <botan/look_pk.h>
using namespace Botan;

#include <boost/python.hpp>
namespace python = boost::python;

std::string encode_pub(const Public_Key* key,
                       const std::string& type)
   {
   if(type == "DER")
      {
      Pipe pipe;
      pipe.start_msg();
      X509::encode(*key, pipe, RAW_BER);
      pipe.end_msg();
      return pipe.read_all_as_string();
      }
   else if(type == "PEM")
      return X509::PEM_encode(*key);
   else
      throw Encoding_Error("Unknown key encoding method " + type);
   }

std::string encode_priv(const Private_Key* key,
                        const std::string& type)
   {
   if(type == "DER")
      {
      Pipe pipe;
      PKCS8::encode(*key, pipe, RAW_BER);
      return pipe.read_all_as_string();
      }
   else if(type == "PEM")
      return PKCS8::PEM_encode(*key);
   else
      throw Encoding_Error("Unknown key encoding method " + type);
   }

Private_Key* load_priv(const std::string& file, const std::string& pass)
   {
   return PKCS8::load_key(file, pass);
   }

Public_Key* load_public(const std::string& file)
   {
   return X509::load_key(file);
   }

std::string encrypt_string(const PK_Encryptor* enc, const std::string& in)
   {
   SecureVector<byte> cipher = enc->encrypt((const byte*)in.data(), in.length());
   return std::string((const char*)cipher.begin(), cipher.size());
   }

std::string decrypt_string(const PK_Decryptor* dec, const std::string& in)
   {
   SecureVector<byte> plain = dec->decrypt((const byte*)in.data(), in.length());
   return std::string((const char*)plain.begin(), plain.size());
   }

void export_pk()
   {
   python::def("private_key", load_priv,
               python::return_value_policy<python::manage_new_object>());
   python::def("public_key", load_public,
               python::return_value_policy<python::manage_new_object>());

   python::class_<Public_Key, boost::noncopyable>
      ("Public_Key", python::no_init)
      .add_property("name", &Public_Key::algo_name)
      .add_property("max_input_bits", &Public_Key::max_input_bits)
      .def("public_key", encode_pub);

   python::class_<PK_Encrypting_Key, python::bases<Public_Key>, boost::noncopyable>
      ("PK_Encrypting_Key", python::no_init);

   python::class_<Private_Key, python::bases<Public_Key>, boost::noncopyable>
      ("Private_Key", python::no_init)
      .def("private_key", encode_priv);

   python::class_<PK_Decrypting_Key, python::bases<Private_Key>, boost::noncopyable>
      ("PK_Decrypting_Key", python::no_init);

   python::class_<RSA_PublicKey, python::bases<PK_Encrypting_Key> >
      ("RSA_PublicKey", python::no_init);

   python::class_<DSA_PublicKey, python::bases<Public_Key> >
      ("DSA_PublicKey", python::no_init);

   python::class_<RSA_PrivateKey, python::bases<RSA_PublicKey, PK_Decrypting_Key> >
      ("RSA_PrivateKey", python::init<u32bit>());

   /*
   python::class_<PK_Encryptor, boost::noncopyable>
      ("PK_Encryptor", python::no_init)
      .def("__init__",
           python::make_constructor(get_pk_encryptor,
                                    python::with_custodian_and_ward_postcall<0, 1>()))
      .def("max_input", &PK_Encryptor::maximum_input_size)
      .def("encrypt", encrypt_string);
   */

   /*
   python::class_<PK_Decryptor, boost::noncopyable>
      ("PK_Decryptor", python::no_init)
      .def("__init__", python::make_constructor(get_pk_decryptor))
      .def("decrypt", decrypt_string);
   */
   }
<|endoftext|>


<commit_before>/*****************************************************************************
 * Licensed to Qualys, Inc. (QUALYS) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * QUALYS licenses this file to You under the Apache License, Version 2.0
 * (the "License"); 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
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ****************************************************************************/

/**
 * @file
 * @brief IronBee Modules --- Error Page
 *
 * The Error Page module allows the user to select a custom error page.
 *
 * @author Sam Baskinger <sbaskinger@qualys.com>
 */

#include "ironbee_config_auto.h"

#include <ironbeepp/configuration_directives.hpp>
#include <ironbeepp/configuration_parser.hpp>
#include <ironbeepp/c_trampoline.hpp>
#include <ironbeepp/module.hpp>
#include <ironbeepp/module_bootstrap.hpp>
#include <ironbeepp/module_delegate.hpp>
#include <ironbeepp/transaction.hpp>

#include <ironbee/rule_engine.h>
#include <ironbee/string.h>
#include <ironbee/path.h>

#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/iostreams/device/mapped_file.hpp>

/* Enable PRId64 printf. */
extern "C" {
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include <inttypes.h>
}

/**
 * Error Page Module implementation.
 */
class ErrorPageModule : public IronBee::ModuleDelegate
{
public:
    /**
     * A version of @ref ib_rule_error_page_fn_t without the callback data.
     *
     * This will be used by @ref IronBee::make_c_trampoline to replace
     * IronBee's default error page function.
     *
     * @param[in] tx The transaction.
     * @param[out] page A pointer to the error page.
     * @param[out] page_sz The length of @a page.
     */
    typedef ib_status_t(error_page_fn_t)(
        ib_tx_t        *tx,
        const uint8_t **page,
        size_t         *page_sz);
    /**
     * Constructor.
     *
     * @param[in] module The IronBee++ module.
     */
    explicit ErrorPageModule(IronBee::Module module);

private:

    /**
     * Member to store the C trampoline information.
     *
     * Note that m_trampoline_pair.second must be destroyed.
     */
    std::pair<ib_rule_error_page_fn_t, void*> m_trampoline_pair;

    /**
     * Holds callback data associated with @ref ib_rule_error_page_fn_t.
     *
     * This member ensures that IronBee::delete_c_trampoline is called.
     */
    boost::shared_ptr<void> m_trampoline_data;

    /**
     * Produce an error page for the given transaction.
     *
     * @param[in] tx The transaction.
     * @param[out] page The page data is placed here.
     * @param[out] page_sz The page size is placed here.
     *
     * @returns
     * - IB_OK On success.
     * - IB_DECLINED If the error page is not available but not because
     *               of an error.
     * - Other on error.
     */
    ib_status_t error_page_fn(
        ib_tx_t        *tx,
        const uint8_t **page,
        size_t         *page_sz);

    /**
     * Implement the HTTP Status Code directive.
     *
     * @param[in] cp Configuration parser.
     * @param[in] name The directive.
     * @param[in] param1 The status code.
     * @param[in] param2 The error page file name.
     *
     * @throws @ref IronBee::exception.
     */
    void httpStatusCodeContentsDirective(
        IronBee::ConfigurationParser  cp,
        const char                   *name,
        const char                   *param1,
        const char                   *param2);
};

IBPP_BOOTSTRAP_MODULE_DELEGATE("ErrorPageModule", ErrorPageModule);

/**
 * A map from the status number to a file name.
 */
typedef std::map<ib_num_t, std::string> status_to_file_map_t;

/**
 * Context configuration value for the ErrorPageModule.
 */
struct ErrorPageCtxConfig {

    /**
     * The mapping from an HTTP status code to the file to return.
     */
    status_to_file_map_t status_to_file;

    /**
     * The mapping from an HTTP status code to the memory mapped file.
     */
    std::map<ib_num_t, boost::iostreams::mapped_file_source>
        status_to_mapped_file_source;
};

/* Implementation */

ErrorPageModule::ErrorPageModule(IronBee::Module module):
    /* Initialize the parent. */
    IronBee::ModuleDelegate(module),

    /* Build the C callback for ib_rule_set_error_page_fn(). */
    m_trampoline_pair(
        IronBee::make_c_trampoline<error_page_fn_t>(
            boost::bind(&ErrorPageModule::error_page_fn, this, _1, _2, _3))),

    m_trampoline_data(
        m_trampoline_pair.second,
        IronBee::delete_c_trampoline)
{

    /* Register the callback with the IronBee engine. */
    ib_rule_set_error_page_fn(
        module.engine().ib(),
        m_trampoline_pair.first,
        m_trampoline_pair.second);

    /* Setup the directive callbacks. */
    module.engine().register_configuration_directives().
        param2(
            "HttpStatusCodeContents",
            boost::bind(
                &ErrorPageModule::httpStatusCodeContentsDirective,
                this, _1, _2, _3, _4));

    module.set_configuration_data<ErrorPageCtxConfig>();
}

void ErrorPageModule::httpStatusCodeContentsDirective(
        IronBee::ConfigurationParser  cp,
        const char                   *name,
        const char                   *param1,
        const char                   *param2)
{
    ib_num_t     num;

    ErrorPageCtxConfig &cfg =
        module().configuration_data<ErrorPageCtxConfig>(cp.current_context());

    /* Convert the incoming parameter. */
    IronBee::throw_if_error(ib_string_to_num(param1, 10, &num));

    /* Set the mapping in the context configuration. */
    cfg.status_to_file[num] = ib_util_relative_file(
        ib_engine_pool_config_get(cp.engine().ib()),
        cp.current_file(),
        param2
    );

    try {
        cfg.status_to_mapped_file_source[num] =
            boost::iostreams::mapped_file_source(param2);
    }
    catch (const std::exception& e) {
        BOOST_THROW_EXCEPTION(
            IronBee::enoent()
                << IronBee::errinfo_what(e.what()));
    }
}

ib_status_t ErrorPageModule::error_page_fn(
    ib_tx_t *tx,
    const uint8_t **page,
    size_t *len)
{
    IronBee::Transaction ib_tx(tx);

    ErrorPageCtxConfig &cfg =
        module().configuration_data<ErrorPageCtxConfig>(
            ib_tx.context());

    status_to_file_map_t::iterator itr =
        cfg.status_to_file.find(ib_tx.ib()->block_status);

    /* If we can't find a mapping, decline. The default will take over. */
    if (itr == cfg.status_to_file.end()) {
        ib_log_debug2_tx(
            ib_tx.ib(),
            "No custom page mapped for status %" PRId64 " and context %s. "
            "Declining.",
            ib_tx.ib()->block_status,
            ib_tx.context().name());
        return IB_DECLINED;
    }

    const std::string& file = itr->second;
    const boost::iostreams::mapped_file_source &source =
        cfg.status_to_mapped_file_source[tx->block_status];

    ib_log_debug2_tx(
        ib_tx.ib(),
        "Using custom error page file %.*s.",
        static_cast<int>(file.length()),
        file.data());

    *page = reinterpret_cast<const uint8_t*>(source.data());
    *len = source.size();

    return IB_OK;
}
<commit_msg>error_page: Additional fix for relative paths.<commit_after>/*****************************************************************************
 * Licensed to Qualys, Inc. (QUALYS) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * QUALYS licenses this file to You under the Apache License, Version 2.0
 * (the "License"); 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
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ****************************************************************************/

/**
 * @file
 * @brief IronBee Modules --- Error Page
 *
 * The Error Page module allows the user to select a custom error page.
 *
 * @author Sam Baskinger <sbaskinger@qualys.com>
 */

#include "ironbee_config_auto.h"

#include <ironbeepp/configuration_directives.hpp>
#include <ironbeepp/configuration_parser.hpp>
#include <ironbeepp/c_trampoline.hpp>
#include <ironbeepp/module.hpp>
#include <ironbeepp/module_bootstrap.hpp>
#include <ironbeepp/module_delegate.hpp>
#include <ironbeepp/transaction.hpp>

#include <ironbee/rule_engine.h>
#include <ironbee/string.h>
#include <ironbee/path.h>

#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/iostreams/device/mapped_file.hpp>

/* Enable PRId64 printf. */
extern "C" {
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include <inttypes.h>
}

/**
 * Error Page Module implementation.
 */
class ErrorPageModule : public IronBee::ModuleDelegate
{
public:
    /**
     * A version of @ref ib_rule_error_page_fn_t without the callback data.
     *
     * This will be used by @ref IronBee::make_c_trampoline to replace
     * IronBee's default error page function.
     *
     * @param[in] tx The transaction.
     * @param[out] page A pointer to the error page.
     * @param[out] page_sz The length of @a page.
     */
    typedef ib_status_t(error_page_fn_t)(
        ib_tx_t        *tx,
        const uint8_t **page,
        size_t         *page_sz);
    /**
     * Constructor.
     *
     * @param[in] module The IronBee++ module.
     */
    explicit ErrorPageModule(IronBee::Module module);

private:

    /**
     * Member to store the C trampoline information.
     *
     * Note that m_trampoline_pair.second must be destroyed.
     */
    std::pair<ib_rule_error_page_fn_t, void*> m_trampoline_pair;

    /**
     * Holds callback data associated with @ref ib_rule_error_page_fn_t.
     *
     * This member ensures that IronBee::delete_c_trampoline is called.
     */
    boost::shared_ptr<void> m_trampoline_data;

    /**
     * Produce an error page for the given transaction.
     *
     * @param[in] tx The transaction.
     * @param[out] page The page data is placed here.
     * @param[out] page_sz The page size is placed here.
     *
     * @returns
     * - IB_OK On success.
     * - IB_DECLINED If the error page is not available but not because
     *               of an error.
     * - Other on error.
     */
    ib_status_t error_page_fn(
        ib_tx_t        *tx,
        const uint8_t **page,
        size_t         *page_sz);

    /**
     * Implement the HTTP Status Code directive.
     *
     * @param[in] cp Configuration parser.
     * @param[in] name The directive.
     * @param[in] param1 The status code.
     * @param[in] param2 The error page file name.
     *
     * @throws @ref IronBee::exception.
     */
    void httpStatusCodeContentsDirective(
        IronBee::ConfigurationParser  cp,
        const char                   *name,
        const char                   *param1,
        const char                   *param2);
};

IBPP_BOOTSTRAP_MODULE_DELEGATE("ErrorPageModule", ErrorPageModule);

/**
 * A map from the status number to a file name.
 */
typedef std::map<ib_num_t, std::string> status_to_file_map_t;

/**
 * Context configuration value for the ErrorPageModule.
 */
struct ErrorPageCtxConfig {

    /**
     * The mapping from an HTTP status code to the file to return.
     */
    status_to_file_map_t status_to_file;

    /**
     * The mapping from an HTTP status code to the memory mapped file.
     */
    std::map<ib_num_t, boost::iostreams::mapped_file_source>
        status_to_mapped_file_source;
};

/* Implementation */

ErrorPageModule::ErrorPageModule(IronBee::Module module):
    /* Initialize the parent. */
    IronBee::ModuleDelegate(module),

    /* Build the C callback for ib_rule_set_error_page_fn(). */
    m_trampoline_pair(
        IronBee::make_c_trampoline<error_page_fn_t>(
            boost::bind(&ErrorPageModule::error_page_fn, this, _1, _2, _3))),

    m_trampoline_data(
        m_trampoline_pair.second,
        IronBee::delete_c_trampoline)
{

    /* Register the callback with the IronBee engine. */
    ib_rule_set_error_page_fn(
        module.engine().ib(),
        m_trampoline_pair.first,
        m_trampoline_pair.second);

    /* Setup the directive callbacks. */
    module.engine().register_configuration_directives().
        param2(
            "HttpStatusCodeContents",
            boost::bind(
                &ErrorPageModule::httpStatusCodeContentsDirective,
                this, _1, _2, _3, _4));

    module.set_configuration_data<ErrorPageCtxConfig>();
}

void ErrorPageModule::httpStatusCodeContentsDirective(
        IronBee::ConfigurationParser  cp,
        const char                   *name,
        const char                   *param1,
        const char                   *param2)
{
    ib_num_t     num;

    ErrorPageCtxConfig &cfg =
        module().configuration_data<ErrorPageCtxConfig>(cp.current_context());

    /* Convert the incoming parameter. */
    IronBee::throw_if_error(ib_string_to_num(param1, 10, &num));

    /* Set the mapping in the context configuration. */
    cfg.status_to_file[num] = ib_util_relative_file(
        ib_engine_pool_config_get(cp.engine().ib()),
        cp.current_file(),
        param2
    );

    try {
        cfg.status_to_mapped_file_source[num] =
            boost::iostreams::mapped_file_source(cfg.status_to_file[num]);
    }
    catch (const std::exception& e) {
        BOOST_THROW_EXCEPTION(
            IronBee::enoent()
                << IronBee::errinfo_what(e.what()));
    }
}

ib_status_t ErrorPageModule::error_page_fn(
    ib_tx_t *tx,
    const uint8_t **page,
    size_t *len)
{
    IronBee::Transaction ib_tx(tx);

    ErrorPageCtxConfig &cfg =
        module().configuration_data<ErrorPageCtxConfig>(
            ib_tx.context());

    status_to_file_map_t::iterator itr =
        cfg.status_to_file.find(ib_tx.ib()->block_status);

    /* If we can't find a mapping, decline. The default will take over. */
    if (itr == cfg.status_to_file.end()) {
        ib_log_debug2_tx(
            ib_tx.ib(),
            "No custom page mapped for status %" PRId64 " and context %s. "
            "Declining.",
            ib_tx.ib()->block_status,
            ib_tx.context().name());
        return IB_DECLINED;
    }

    const std::string& file = itr->second;
    const boost::iostreams::mapped_file_source &source =
        cfg.status_to_mapped_file_source[tx->block_status];

    ib_log_debug2_tx(
        ib_tx.ib(),
        "Using custom error page file %.*s.",
        static_cast<int>(file.length()),
        file.data());

    *page = reinterpret_cast<const uint8_t*>(source.data());
    *len = source.size();

    return IB_OK;
}
<|endoftext|>


<commit_before>/*
* (C) 2017 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include "test_runner.h"
#include "tests.h"

#include <botan/version.h>
#include <botan/rotate.h>
#include <botan/loadstor.h>
#include <botan/cpuid.h>

namespace Botan_Tests {

Test_Runner::Test_Runner(std::ostream& out) : m_output(out) {}

namespace {

/*
* This is a fast, simple, deterministic PRNG that's used for running
* the tests. It is not intended to be cryptographically secure.
*/
class Testsuite_RNG final : public Botan::RandomNumberGenerator
   {
   public:
      std::string name() const override { return "Testsuite_RNG"; }

      void clear() override
         {
         m_a = m_b = m_c = m_d = 0;
         }

      void add_entropy(const uint8_t data[], size_t len) override
         {
         for(size_t i = 0; i != len; ++i)
            {
            m_a ^= data[i];
            m_b ^= i;
            mix();
            }
         }

      bool is_seeded() const override
         {
         return true;
         }

      void randomize(uint8_t out[], size_t len) override
         {
         for(size_t i = 0; i != len; ++i)
            {
            out[i] = static_cast<uint8_t>(m_a);
            mix();
            }
         }

      Testsuite_RNG(const std::vector<uint8_t>& seed, size_t test_counter = 0)
         {
         m_d = static_cast<uint32_t>(test_counter);

         add_entropy(seed.data(), seed.size());
         }
   private:
      void mix()
         {
         const size_t ROUNDS = 3;

         for(size_t i = 0; i != ROUNDS; ++i)
            {
            m_a += i;

            m_a = Botan::rotl<9>(m_a);
            m_b ^= m_a;
            m_d ^= m_c;

            m_a += m_d;
            m_c += m_b;
            m_c = Botan::rotl<23>(m_c);
            }
         }

      uint32_t m_a = 0, m_b = 0, m_c = 0, m_d = 0;
   };

}

int Test_Runner::run(const Test_Options& opts)
   {
   std::vector<std::string> req = opts.requested_tests();

   if(req.empty())
      {
      /*
      If nothing was requested on the command line, run everything. First
      run the "essentials" to smoke test, then everything else in
      alphabetical order.
      */
      req = {"block", "stream", "hash", "mac", "modes", "aead",
             "kdf", "pbkdf", "hmac_drbg", "util"
      };

      std::set<std::string> all_others = Botan_Tests::Test::registered_tests();

      if(opts.pkcs11_lib().empty())
         {
         // do not run pkcs11 tests by default unless pkcs11-lib set
         for(std::set<std::string>::iterator iter = all_others.begin(); iter != all_others.end();)
            {
            if((*iter).find("pkcs11") != std::string::npos)
               {
               iter = all_others.erase(iter);
               }
            else
               {
               ++iter;
               }
            }
         }

      for(auto f : req)
         {
         all_others.erase(f);
         }

      req.insert(req.end(), all_others.begin(), all_others.end());
      }
   else if(req.size() == 1 && req.at(0) == "pkcs11")
      {
      req = {"pkcs11-manage", "pkcs11-module", "pkcs11-slot", "pkcs11-session", "pkcs11-object", "pkcs11-rsa",
             "pkcs11-ecdsa", "pkcs11-ecdh", "pkcs11-rng", "pkcs11-x509"
      };
      }
   else
      {
      std::set<std::string> all = Botan_Tests::Test::registered_tests();
      for(auto const& r : req)
         {
         if(all.find(r) == all.end())
            {
            throw Botan_Tests::Test_Error("Unknown test suite: " + r);
            }
         }
      }

   output() << "Testing " << Botan::version_string() << "\n";
   output() << "Starting tests";

   if(!opts.pkcs11_lib().empty())
      {
      output() << " pkcs11 library:" << opts.pkcs11_lib();
      }

   if(!opts.provider().empty())
      {
      output() << " provider:" << opts.provider();
      }

   std::vector<uint8_t> seed = Botan::hex_decode(opts.drbg_seed());
   if(seed.empty())
      {
      const uint64_t ts = Botan_Tests::Test::timestamp();
      seed.resize(8);
      Botan::store_be(ts, seed.data());
      }

   output() << " drbg_seed:" << Botan::hex_encode(seed) << "\n";

   Botan_Tests::Test::set_test_options(opts);

   for(size_t i = 0; i != opts.test_runs(); ++i)
      {
      std::unique_ptr<Botan::RandomNumberGenerator> rng =
         std::unique_ptr<Botan::RandomNumberGenerator>(new Testsuite_RNG(seed, i));

      Botan_Tests::Test::set_test_rng(std::move(rng));

      const size_t failed = run_tests(req, i, opts.test_runs());
      if(failed > 0)
         return failed;
      }

   return 0;
   }

namespace {

std::string report_out(const std::vector<Botan_Tests::Test::Result>& results,
                       size_t& tests_failed,
                       size_t& tests_ran)
   {
   std::ostringstream out;

   std::map<std::string, Botan_Tests::Test::Result> combined;
   for(auto const& result : results)
      {
      const std::string who = result.who();
      auto i = combined.find(who);
      if(i == combined.end())
         {
         combined.insert(std::make_pair(who, Botan_Tests::Test::Result(who)));
         i = combined.find(who);
         }

      i->second.merge(result);
      }

   for(auto const& result : combined)
      {
      out << result.second.result_string();
      tests_failed += result.second.tests_failed();
      tests_ran += result.second.tests_run();
      }

   return out.str();
   }

}

size_t Test_Runner::run_tests(const std::vector<std::string>& tests_to_run,
                              size_t test_run,
                              size_t tot_test_runs)
   {
   size_t tests_ran = 0, tests_failed = 0;

   const uint64_t start_time = Botan_Tests::Test::timestamp();

   for(auto const& test_name : tests_to_run)
      {
      output() << test_name << ':' << std::endl;

      std::vector<Test::Result> results;

      try
         {
         if(test_name == "simd_32" && Botan::CPUID::has_simd_32() == false)
            {
            results.push_back(Test::Result::Note(test_name, "SIMD not available on this platform"));
            }
         else if(Test* test = Test::get_test(test_name))
            {
            std::vector<Test::Result> test_results = test->run();
            results.insert(results.end(), test_results.begin(), test_results.end());
            }
         else
            {
            results.push_back(Test::Result::Note(test_name, "Test missing or unavailable"));
            }
         }
      catch(std::exception& e)
         {
         results.push_back(Test::Result::Failure(test_name, e.what()));
         }
      catch(...)
         {
         results.push_back(Test::Result::Failure(test_name, "unknown exception"));
         }

      output() << report_out(results, tests_failed, tests_ran) << std::flush;
      }

   const uint64_t total_ns = Botan_Tests::Test::timestamp() - start_time;

   if(test_run == 0 && tot_test_runs == 1)
      output() << "Tests";
   else
      output() << "Test run " << (1+test_run) << "/" << tot_test_runs;

   output() << " complete ran " << tests_ran << " tests in "
            << Botan_Tests::Test::format_time(total_ns) << " ";

   if(tests_failed > 0)
      {
      output() << tests_failed << " tests failed";
      }
   else if(tests_ran > 0)
      {
      output() << "all tests ok";
      }

   output() << std::endl;

   return tests_failed;
   }

}

<commit_msg>Report CPUID flags in test runner<commit_after>/*
* (C) 2017 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include "test_runner.h"
#include "tests.h"

#include <botan/version.h>
#include <botan/rotate.h>
#include <botan/loadstor.h>
#include <botan/cpuid.h>

namespace Botan_Tests {

Test_Runner::Test_Runner(std::ostream& out) : m_output(out) {}

namespace {

/*
* This is a fast, simple, deterministic PRNG that's used for running
* the tests. It is not intended to be cryptographically secure.
*/
class Testsuite_RNG final : public Botan::RandomNumberGenerator
   {
   public:
      std::string name() const override { return "Testsuite_RNG"; }

      void clear() override
         {
         m_a = m_b = m_c = m_d = 0;
         }

      void add_entropy(const uint8_t data[], size_t len) override
         {
         for(size_t i = 0; i != len; ++i)
            {
            m_a ^= data[i];
            m_b ^= i;
            mix();
            }
         }

      bool is_seeded() const override
         {
         return true;
         }

      void randomize(uint8_t out[], size_t len) override
         {
         for(size_t i = 0; i != len; ++i)
            {
            out[i] = static_cast<uint8_t>(m_a);
            mix();
            }
         }

      Testsuite_RNG(const std::vector<uint8_t>& seed, size_t test_counter = 0)
         {
         m_d = static_cast<uint32_t>(test_counter);

         add_entropy(seed.data(), seed.size());
         }
   private:
      void mix()
         {
         const size_t ROUNDS = 3;

         for(size_t i = 0; i != ROUNDS; ++i)
            {
            m_a += i;

            m_a = Botan::rotl<9>(m_a);
            m_b ^= m_a;
            m_d ^= m_c;

            m_a += m_d;
            m_c += m_b;
            m_c = Botan::rotl<23>(m_c);
            }
         }

      uint32_t m_a = 0, m_b = 0, m_c = 0, m_d = 0;
   };

}

int Test_Runner::run(const Test_Options& opts)
   {
   std::vector<std::string> req = opts.requested_tests();

   if(req.empty())
      {
      /*
      If nothing was requested on the command line, run everything. First
      run the "essentials" to smoke test, then everything else in
      alphabetical order.
      */
      req = {"block", "stream", "hash", "mac", "modes", "aead",
             "kdf", "pbkdf", "hmac_drbg", "util"
      };

      std::set<std::string> all_others = Botan_Tests::Test::registered_tests();

      if(opts.pkcs11_lib().empty())
         {
         // do not run pkcs11 tests by default unless pkcs11-lib set
         for(std::set<std::string>::iterator iter = all_others.begin(); iter != all_others.end();)
            {
            if((*iter).find("pkcs11") != std::string::npos)
               {
               iter = all_others.erase(iter);
               }
            else
               {
               ++iter;
               }
            }
         }

      for(auto f : req)
         {
         all_others.erase(f);
         }

      req.insert(req.end(), all_others.begin(), all_others.end());
      }
   else if(req.size() == 1 && req.at(0) == "pkcs11")
      {
      req = {"pkcs11-manage", "pkcs11-module", "pkcs11-slot", "pkcs11-session", "pkcs11-object", "pkcs11-rsa",
             "pkcs11-ecdsa", "pkcs11-ecdh", "pkcs11-rng", "pkcs11-x509"
      };
      }
   else
      {
      std::set<std::string> all = Botan_Tests::Test::registered_tests();
      for(auto const& r : req)
         {
         if(all.find(r) == all.end())
            {
            throw Botan_Tests::Test_Error("Unknown test suite: " + r);
            }
         }
      }

   output() << "Testing " << Botan::version_string() << "\n";

   const std::string cpuid = Botan::CPUID::to_string();
   if(cpuid.size() > 0)
      output() << "CPU flags: " << cpuid << "\n";
   output() << "Starting tests";

   if(!opts.pkcs11_lib().empty())
      {
      output() << " pkcs11 library:" << opts.pkcs11_lib();
      }

   if(!opts.provider().empty())
      {
      output() << " provider:" << opts.provider();
      }

   std::vector<uint8_t> seed = Botan::hex_decode(opts.drbg_seed());
   if(seed.empty())
      {
      const uint64_t ts = Botan_Tests::Test::timestamp();
      seed.resize(8);
      Botan::store_be(ts, seed.data());
      }

   output() << " drbg_seed:" << Botan::hex_encode(seed) << "\n";

   Botan_Tests::Test::set_test_options(opts);

   for(size_t i = 0; i != opts.test_runs(); ++i)
      {
      std::unique_ptr<Botan::RandomNumberGenerator> rng =
         std::unique_ptr<Botan::RandomNumberGenerator>(new Testsuite_RNG(seed, i));

      Botan_Tests::Test::set_test_rng(std::move(rng));

      const size_t failed = run_tests(req, i, opts.test_runs());
      if(failed > 0)
         return failed;
      }

   return 0;
   }

namespace {

std::string report_out(const std::vector<Botan_Tests::Test::Result>& results,
                       size_t& tests_failed,
                       size_t& tests_ran)
   {
   std::ostringstream out;

   std::map<std::string, Botan_Tests::Test::Result> combined;
   for(auto const& result : results)
      {
      const std::string who = result.who();
      auto i = combined.find(who);
      if(i == combined.end())
         {
         combined.insert(std::make_pair(who, Botan_Tests::Test::Result(who)));
         i = combined.find(who);
         }

      i->second.merge(result);
      }

   for(auto const& result : combined)
      {
      out << result.second.result_string();
      tests_failed += result.second.tests_failed();
      tests_ran += result.second.tests_run();
      }

   return out.str();
   }

}

size_t Test_Runner::run_tests(const std::vector<std::string>& tests_to_run,
                              size_t test_run,
                              size_t tot_test_runs)
   {
   size_t tests_ran = 0, tests_failed = 0;

   const uint64_t start_time = Botan_Tests::Test::timestamp();

   for(auto const& test_name : tests_to_run)
      {
      output() << test_name << ':' << std::endl;

      std::vector<Test::Result> results;

      try
         {
         if(test_name == "simd_32" && Botan::CPUID::has_simd_32() == false)
            {
            results.push_back(Test::Result::Note(test_name, "SIMD not available on this platform"));
            }
         else if(Test* test = Test::get_test(test_name))
            {
            std::vector<Test::Result> test_results = test->run();
            results.insert(results.end(), test_results.begin(), test_results.end());
            }
         else
            {
            results.push_back(Test::Result::Note(test_name, "Test missing or unavailable"));
            }
         }
      catch(std::exception& e)
         {
         results.push_back(Test::Result::Failure(test_name, e.what()));
         }
      catch(...)
         {
         results.push_back(Test::Result::Failure(test_name, "unknown exception"));
         }

      output() << report_out(results, tests_failed, tests_ran) << std::flush;
      }

   const uint64_t total_ns = Botan_Tests::Test::timestamp() - start_time;

   if(test_run == 0 && tot_test_runs == 1)
      output() << "Tests";
   else
      output() << "Test run " << (1+test_run) << "/" << tot_test_runs;

   output() << " complete ran " << tests_ran << " tests in "
            << Botan_Tests::Test::format_time(total_ns) << " ";

   if(tests_failed > 0)
      {
      output() << tests_failed << " tests failed";
      }
   else if(tests_ran > 0)
      {
      output() << "all tests ok";
      }

   output() << std::endl;

   return tests_failed;
   }

}

<|endoftext|>


<commit_before>#include "Chip8.hpp"

Chip8::Chip8()
{

   // Set up the SFML stuff
   window.create(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Chip8 Emulator");
   window.setFramerateLimit(CLOCK_SPEED);
   pixel.setSize(sf::Vector2f(PIXEL_WIDTH, PIXEL_HEIGHT));
   pixel.setFillColor(sf::Color::White);

   // Initialize
   clearScreen();
   opcode = 0;
   for(int i = 0; i < MEMORY_SIZE; i++)
      memory[i] = 0;
   for(int i = 0; i < NUM_REGISTERS; i++)
      V[i] = 0;
   I = 0;
   pc = PROGRAM_START;
   delayTimer = 0;
   soundTimer = 0;
   for(int i = 0; i < STACK_SIZE; i++)
      stack[i] = 0;
   sp = 0;
   for(int i = 0; i < NUM_KEYS; i++)
      key[i] = 0;
   drawFlag = false;

   // Load fontset
   loadFontSet();

}

void Chip8::loadFontSet()
{

   unsigned char chip8_fontset[FONTSET_SIZE] =
   {
      0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
      0x20, 0x60, 0x20, 0x20, 0x70, // 1
      0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
      0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
      0x90, 0x90, 0xF0, 0x10, 0x10, // 4
      0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
      0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
      0xF0, 0x10, 0x20, 0x40, 0x40, // 7
      0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
      0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
      0xF0, 0x90, 0xF0, 0x90, 0x90, // A
      0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
      0xF0, 0x80, 0x80, 0x80, 0xF0, // C
      0xE0, 0x90, 0x90, 0x90, 0xE0, // D
      0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
      0xF0, 0x80, 0xF0, 0x80, 0x80  // F
   };

   for(int i = 0; i < FONTSET_SIZE; i++)
      memory[i] = chip8_fontset[i];

}

void Chip8::drawGraphics()
{

   for(int i = 0; i < PIXELS_X; i++)
   {
      for(int j = 0; j < PIXELS_Y; j++)
      {
         if(graphics[i + (j * PIXELS_X)])
         {
            pixel.setPosition(sf::Vector2f(i * PIXEL_WIDTH, j * PIXEL_HEIGHT));
            window.draw(pixel);
         }
      }
   }

}

void Chip8::tick()
{

   // Fetch opcode
   opcode = memory[pc] << 8 | memory[pc + 1];

   // Decode opcode
   switch(opcode & 0xF000)
   {
   case 0x0000:
      switch(opcode & 0x000F)
      {
      case 0x0000:   // 0x00E0: Clear screen
         clearScreen();
         break;
      case 0x000E:   // 0x00EE: Returns from subroutine
         pc = stack[--sp];
         break;
      }
   case 0x2000:   // 2NNN: Call subroutine at NNN
      stack[sp++] = pc;
      pc = opcode & 0x0FFF;
      break;
   case 0x8000:
      switch(opcode & 0x000F)
      {
      case 0x0004:   // 0x8XY4: Add VX to VY and set VF if needed
         if(V[(opcode & 0x00F0) >> 4] > (0xFF - V[(opcode & 0x0F00) >> 8]))   // Value at VY is larger than (max - value at VX)
            V[0xF] = 1;
         else
            V[0xF] = 0;
         V[(opcode & 0x0F00) >> 8] += V[(opcode & 0x00F0) >> 4];
         pc += 2;
         break;
      }
   case 0xA000:   // ANNN: Sets I to the address NNN
      I = opcode & 0x0FFF;
      pc += 2;
      break;

   default:
      std::cout<<"Unknown opcode: 0x"<<std::hex<<opcode<<"\n";
   }

   // Update timers
   if(delayTimer > 0)
      delayTimer--;

   if(soundTimer > 0)
   {
      std::cout<<"\a";
      soundTimer--;
   }

}

void Chip8::setKeys()
{
}

void Chip8::clearScreen()
{

   for(int i = 0; i < PIXELS_X * PIXELS_Y; i++)
      graphics[i] = 0;

}

void Chip8::printMemory()
{

   for(int i = 0; i < MEMORY_SIZE; i++)
      std::cout<<std::hex<<(int)memory[i];

}

void Chip8::loadProgram(std::string fileName)
{

   std::ifstream input(fileName, std::ifstream::binary);

   if(input)
   {
      // Get the length of the file
      input.seekg(0, input.end);
      int length = input.tellg();
      input.seekg(0, input.beg);

      // Make our buffer
      unsigned char *buffer = new unsigned char[length];

      // Read from the file
      input.read((char*)buffer, length);

      if(!input)
         std::cout<<"Error reading file: only "<<input.gcount()<<" could be read\n";

      // Move the program into memory
      for(int i = 0; i < length; i++)
         memory[i + PROGRAM_START] = buffer[i];

      input.close();
      delete[] buffer;
   }
   else
      std::cout<<"Error reading file: "<<fileName<<"\n";

}

void Chip8::run()
{

   sf::Clock clock;
   clock.restart();

   const sf::Time FRAMETIME = sf::microseconds(1000000.f / CLOCK_SPEED);      // No in-class initialization allowed?

   while(window.isOpen())
   {
      sf::Event event;
      while(window.pollEvent(event))
      {
         // Request for closing the window
         if(event.type == sf::Event::Closed)
            window.close();
      }

      // Clear the window
      window.clear();

      // The clock cycle for the CPU needs to be 60Hz
      sf::Time timeElapsed = clock.restart();
      if(timeElapsed > FRAMETIME)
         sf::sleep(FRAMETIME - timeElapsed);

      // Emulate
      tick();

      // Draw the graphics
      if(drawFlag)
         drawGraphics();

      // Store key press state
      setKeys();

      // Display the window
      window.display();
   }

}

Chip8::~Chip8()
{
}
<commit_msg>Added in some breaks, opcode 0xFX33<commit_after>#include "Chip8.hpp"

Chip8::Chip8()
{

   // Set up the SFML stuff
   window.create(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Chip8 Emulator");
   window.setFramerateLimit(CLOCK_SPEED);
   pixel.setSize(sf::Vector2f(PIXEL_WIDTH, PIXEL_HEIGHT));
   pixel.setFillColor(sf::Color::White);

   // Initialize
   clearScreen();
   opcode = 0;
   for(int i = 0; i < MEMORY_SIZE; i++)
      memory[i] = 0;
   for(int i = 0; i < NUM_REGISTERS; i++)
      V[i] = 0;
   I = 0;
   pc = PROGRAM_START;
   delayTimer = 0;
   soundTimer = 0;
   for(int i = 0; i < STACK_SIZE; i++)
      stack[i] = 0;
   sp = 0;
   for(int i = 0; i < NUM_KEYS; i++)
      key[i] = 0;
   drawFlag = false;

   // Load fontset
   loadFontSet();

}

void Chip8::loadFontSet()
{

   unsigned char chip8_fontset[FONTSET_SIZE] =
   {
      0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
      0x20, 0x60, 0x20, 0x20, 0x70, // 1
      0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
      0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
      0x90, 0x90, 0xF0, 0x10, 0x10, // 4
      0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
      0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
      0xF0, 0x10, 0x20, 0x40, 0x40, // 7
      0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
      0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
      0xF0, 0x90, 0xF0, 0x90, 0x90, // A
      0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
      0xF0, 0x80, 0x80, 0x80, 0xF0, // C
      0xE0, 0x90, 0x90, 0x90, 0xE0, // D
      0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
      0xF0, 0x80, 0xF0, 0x80, 0x80  // F
   };

   for(int i = 0; i < FONTSET_SIZE; i++)
      memory[i] = chip8_fontset[i];

}

void Chip8::drawGraphics()
{

   for(int i = 0; i < PIXELS_X; i++)
   {
      for(int j = 0; j < PIXELS_Y; j++)
      {
         if(graphics[i + (j * PIXELS_X)])
         {
            pixel.setPosition(sf::Vector2f(i * PIXEL_WIDTH, j * PIXEL_HEIGHT));
            window.draw(pixel);
         }
      }
   }

}

void Chip8::tick()
{

   // Fetch opcode
   opcode = memory[pc] << 8 | memory[pc + 1];

   // Decode opcode
   switch(opcode & 0xF000)
   {
   case 0x0000:
      switch(opcode & 0x000F)
      {
      case 0x0000:   // 0x00E0: Clear screen
         clearScreen();
         break;
      case 0x000E:   // 0x00EE: Returns from subroutine
         pc = stack[--sp];
         break;
      }
      break;
   case 0x2000:   // 0x2NNN: Call subroutine at NNN
      stack[sp++] = pc;
      pc = opcode & 0x0FFF;
      break;
   case 0x8000:
      switch(opcode & 0x000F)
      {
      case 0x0004:   // 0x8XY4: Add VX to VY and set VF if needed
         if(V[(opcode & 0x00F0) >> 4] > (0xFF - V[(opcode & 0x0F00) >> 8]))   // Value at VY is larger than (max - value at VX)
            V[0xF] = 1;
         else
            V[0xF] = 0;
         V[(opcode & 0x0F00) >> 8] += V[(opcode & 0x00F0) >> 4];
         pc += 2;
         break;
      }
      break;
   case 0xA000:   // 0xANNN: Sets I to the address NNN
      I = opcode & 0x0FFF;
      pc += 2;
      break;
   case 0xF000:
      switch(opcode & 0x00FF)
      {
      case 0x0033:   // 0xFX33: Stores the binary-coded decimal representation of VX at I, I + 1, and I + 2
         memory[I] = V[(opcode & 0x0F00) >> 8] / 100;
         memory[I + 1] = (V[(opcode & 0x0F00) >> 8] / 10) % 10;
         memory[I + 2] = (V[(opcode & 0x0F00) >> 8] % 100) % 10;
         pc += 2;
         break;
      }
      break;

   default:
      std::cout<<"Unknown opcode: 0x"<<std::hex<<opcode<<"\n";
   }

   // Update timers
   if(delayTimer > 0)
      delayTimer--;

   if(soundTimer > 0)
   {
      std::cout<<"\a";
      soundTimer--;
   }

}

void Chip8::setKeys()
{
}

void Chip8::clearScreen()
{

   for(int i = 0; i < PIXELS_X * PIXELS_Y; i++)
      graphics[i] = 0;

}

void Chip8::printMemory()
{

   for(int i = 0; i < MEMORY_SIZE; i++)
      std::cout<<std::hex<<(int)memory[i];

}

void Chip8::loadProgram(std::string fileName)
{

   std::ifstream input(fileName, std::ifstream::binary);

   if(input)
   {
      // Get the length of the file
      input.seekg(0, input.end);
      int length = input.tellg();
      input.seekg(0, input.beg);

      // Make our buffer
      unsigned char *buffer = new unsigned char[length];

      // Read from the file
      input.read((char*)buffer, length);

      if(!input)
         std::cout<<"Error reading file: only "<<input.gcount()<<" could be read\n";

      // Move the program into memory
      for(int i = 0; i < length; i++)
         memory[i + PROGRAM_START] = buffer[i];

      input.close();
      delete[] buffer;
   }
   else
      std::cout<<"Error reading file: "<<fileName<<"\n";

}

void Chip8::run()
{

   sf::Clock clock;
   clock.restart();

   const sf::Time FRAMETIME = sf::microseconds(1000000.f / CLOCK_SPEED);      // No in-class initialization allowed?

   while(window.isOpen())
   {
      sf::Event event;
      while(window.pollEvent(event))
      {
         // Request for closing the window
         if(event.type == sf::Event::Closed)
            window.close();
      }

      // Clear the window
      window.clear();

      // The clock cycle for the CPU needs to be 60Hz
      sf::Time timeElapsed = clock.restart();
      if(timeElapsed > FRAMETIME)
         sf::sleep(FRAMETIME - timeElapsed);

      // Emulate
      tick();

      // Draw the graphics
      if(drawFlag)
         drawGraphics();

      // Store key press state
      setKeys();

      // Display the window
      window.display();
   }

}

Chip8::~Chip8()
{
}
<|endoftext|>


<commit_before>#include <m3conf/tokenizer.h>
#include <test/XTest.h>

#define CHECK_TOKEN(toktype, val) XTS_ASSERT_DIFFERS(NULL, c); XTS_ASSERT_EQUALS(toktype, c->type); XTS_ASSERT_EQUALS(std::string(val), c->value); c = c->next;
#define CHECK_EOF() CHECK_TOKEN(TOK_EOF, ""); XTS_ASSERT_EQUALS(NULL, c);

XTS_TEST(TokenizerTests, testSingleChars) {
	struct Token* t;
	struct Token* c;
	std::string data = "{}=;";
	XTS_ASSERT_EQUALS(0, tokenize(data.c_str(), data.length(), &t));
	c = t;
	CHECK_TOKEN(TOK_LB, "{");
	CHECK_TOKEN(TOK_RB, "}");
	CHECK_TOKEN(TOK_EQ, "=");
	CHECK_TOKEN(TOK_SEMI, ";");
	CHECK_EOF();

	free_tokens(t);
}

XTS_TEST(TokenizerTests, testValidInt) {
	struct Token* t;
	struct Token* c;
	std::string data = "01234-44";
	XTS_ASSERT_EQUALS(0, tokenize(data.c_str(), data.length(), &t));
	c = t;
	CHECK_TOKEN(TOK_INT, "0");
	CHECK_TOKEN(TOK_INT, "1234");
	CHECK_TOKEN(TOK_INT, "-44");
	CHECK_EOF();

	free_tokens(t);
}

XTS_TEST(TokenizerTests, testValidStr) {
	struct Token* t;
	struct Token* c;
	std::string data = "\"aoeu\"\"\\\"quote\"\"#\\\\\\\"\"";
	XTS_ASSERT_EQUALS(0, tokenize(data.c_str(), data.length(), &t));
	c = t;
	CHECK_TOKEN(TOK_STR, "aoeu");
	CHECK_TOKEN(TOK_STR, "\"quote");
	CHECK_TOKEN(TOK_STR, "#\\\"");
	CHECK_EOF();

	free_tokens(t);
}

XTS_TEST(TokenizerTests, testValidId) {
	struct Token* t;
	struct Token* c;
	std::string data = "aoeu AOEU a1 section _ _._";
	XTS_ASSERT_EQUALS(0, tokenize(data.c_str(), data.length(), &t));
	c = t;
	CHECK_TOKEN(TOK_ID, "aoeu");
	CHECK_TOKEN(TOK_ID, "AOEU");
	CHECK_TOKEN(TOK_ID, "a1");
	CHECK_TOKEN(TOK_ID, "section");
	CHECK_TOKEN(TOK_ID, "_");
	CHECK_TOKEN(TOK_ID, "_._");
	CHECK_EOF();

	free_tokens(t);
}

XTS_TEST(TokenizerTests, testValidWhiteSpace) {
	struct Token* t;
	struct Token* c;
	std::string data = " \t\n\r";
	XTS_ASSERT_EQUALS(0, tokenize(data.c_str(), data.length(), &t));
	c = t;
	CHECK_EOF();

	free_tokens(t);
}

XTS_TEST(TokenizerTests, testValidComment) {
	struct Token* t;
	struct Token* c;
	std::string data = "#comment \t comment\r\n#comment";
	XTS_ASSERT_EQUALS(0, tokenize(data.c_str(), data.length(), &t));
	c = t;
	CHECK_EOF();

	free_tokens(t);
}

XTS_TEST(TokenizerTests, testChaining) {
	struct Token* t;
	struct Token* c;
	std::string data = "a1{-2a.;=0Q\"3\"3a}";
	XTS_ASSERT_EQUALS(0, tokenize(data.c_str(), data.length(), &t));
	c = t;
	CHECK_TOKEN(TOK_ID, "a1");
	CHECK_TOKEN(TOK_LB, "{");
	CHECK_TOKEN(TOK_INT, "-2");
	CHECK_TOKEN(TOK_ID, "a.");
	CHECK_TOKEN(TOK_SEMI, ";");
	CHECK_TOKEN(TOK_EQ, "=");
	CHECK_TOKEN(TOK_INT, "0");
	CHECK_TOKEN(TOK_ID, "Q");
	CHECK_TOKEN(TOK_STR, "3");
	CHECK_TOKEN(TOK_INT, "3");
	CHECK_TOKEN(TOK_ID, "a");
	CHECK_TOKEN(TOK_RB, "}");
	CHECK_EOF();

	free_tokens(t);
}

XTS_TEST(TokenizerTests, testCommentChaining) {
	struct Token* t;
	struct Token* c;
	std::string data = "x#a\nx.#b\n34#c\n0#d\n1#e\n";
	XTS_ASSERT_EQUALS(0, tokenize(data.c_str(), data.length(), &t));
	c = t;
	CHECK_TOKEN(TOK_ID, "x");
	CHECK_TOKEN(TOK_ID, "x.");
	CHECK_TOKEN(TOK_INT, "34");
	CHECK_TOKEN(TOK_INT, "0");
	CHECK_TOKEN(TOK_INT, "1");
	CHECK_EOF();

	free_tokens(t);
}
<commit_msg>Adding some invalidness tests.<commit_after>#include <m3conf/tokenizer.h>
#include <test/XTest.h>

#define CHECK_TOKEN(toktype, val) XTS_ASSERT_DIFFERS(NULL, c); XTS_ASSERT_EQUALS(toktype, c->type); XTS_ASSERT_EQUALS(std::string(val), c->value); c = c->next;
#define CHECK_EOF() CHECK_TOKEN(TOK_EOF, ""); XTS_ASSERT_EQUALS(NULL, c);

XTS_TEST(TokenizerTests, testSingleChars) {
	struct Token* t;
	struct Token* c;
	std::string data = "{}=;";
	XTS_ASSERT_EQUALS(0, tokenize(data.c_str(), data.length(), &t));
	c = t;
	CHECK_TOKEN(TOK_LB, "{");
	CHECK_TOKEN(TOK_RB, "}");
	CHECK_TOKEN(TOK_EQ, "=");
	CHECK_TOKEN(TOK_SEMI, ";");
	CHECK_EOF();

	free_tokens(t);
}

XTS_TEST(TokenizerTests, testValidInt) {
	struct Token* t;
	struct Token* c;
	std::string data = "01234-44";
	XTS_ASSERT_EQUALS(0, tokenize(data.c_str(), data.length(), &t));
	c = t;
	CHECK_TOKEN(TOK_INT, "0");
	CHECK_TOKEN(TOK_INT, "1234");
	CHECK_TOKEN(TOK_INT, "-44");
	CHECK_EOF();

	free_tokens(t);
}

XTS_TEST(TokenizerTests, testValidStr) {
	struct Token* t;
	struct Token* c;
	std::string data = "\"aoeu\"\"\\\"quote\"\"#\\\\\\\"\"";
	XTS_ASSERT_EQUALS(0, tokenize(data.c_str(), data.length(), &t));
	c = t;
	CHECK_TOKEN(TOK_STR, "aoeu");
	CHECK_TOKEN(TOK_STR, "\"quote");
	CHECK_TOKEN(TOK_STR, "#\\\"");
	CHECK_EOF();

	free_tokens(t);
}

XTS_TEST(TokenizerTests, testValidId) {
	struct Token* t;
	struct Token* c;
	std::string data = "aoeu AOEU a1 section _ _._";
	XTS_ASSERT_EQUALS(0, tokenize(data.c_str(), data.length(), &t));
	c = t;
	CHECK_TOKEN(TOK_ID, "aoeu");
	CHECK_TOKEN(TOK_ID, "AOEU");
	CHECK_TOKEN(TOK_ID, "a1");
	CHECK_TOKEN(TOK_ID, "section");
	CHECK_TOKEN(TOK_ID, "_");
	CHECK_TOKEN(TOK_ID, "_._");
	CHECK_EOF();

	free_tokens(t);
}

XTS_TEST(TokenizerTests, testValidWhiteSpace) {
	struct Token* t;
	struct Token* c;
	std::string data = " \t\n\r";
	XTS_ASSERT_EQUALS(0, tokenize(data.c_str(), data.length(), &t));
	c = t;
	CHECK_EOF();

	free_tokens(t);
}

XTS_TEST(TokenizerTests, testValidComment) {
	struct Token* t;
	struct Token* c;
	std::string data = "#comment \t comment\r\n#comment";
	XTS_ASSERT_EQUALS(0, tokenize(data.c_str(), data.length(), &t));
	c = t;
	CHECK_EOF();

	free_tokens(t);
}

XTS_TEST(TokenizerTests, testChaining) {
	struct Token* t;
	struct Token* c;
	std::string data = "a1{-2a.;=0Q\"3\"3a}";
	XTS_ASSERT_EQUALS(0, tokenize(data.c_str(), data.length(), &t));
	c = t;
	CHECK_TOKEN(TOK_ID, "a1");
	CHECK_TOKEN(TOK_LB, "{");
	CHECK_TOKEN(TOK_INT, "-2");
	CHECK_TOKEN(TOK_ID, "a.");
	CHECK_TOKEN(TOK_SEMI, ";");
	CHECK_TOKEN(TOK_EQ, "=");
	CHECK_TOKEN(TOK_INT, "0");
	CHECK_TOKEN(TOK_ID, "Q");
	CHECK_TOKEN(TOK_STR, "3");
	CHECK_TOKEN(TOK_INT, "3");
	CHECK_TOKEN(TOK_ID, "a");
	CHECK_TOKEN(TOK_RB, "}");
	CHECK_EOF();

	free_tokens(t);
}

XTS_TEST(TokenizerTests, testCommentChaining) {
	struct Token* t;
	struct Token* c;
	std::string data = "x#a\nx.#b\n34#c\n0#d\n1#e\n";
	XTS_ASSERT_EQUALS(0, tokenize(data.c_str(), data.length(), &t));
	c = t;
	CHECK_TOKEN(TOK_ID, "x");
	CHECK_TOKEN(TOK_ID, "x.");
	CHECK_TOKEN(TOK_INT, "34");
	CHECK_TOKEN(TOK_INT, "0");
	CHECK_TOKEN(TOK_INT, "1");
	CHECK_EOF();

	free_tokens(t);
}

XTS_TEST(TokenizerTests, testInvalidInt) {
	struct Token* t;
	std::string data;
	data = "-";
	XTS_ASSERT_EQUALS(2, tokenize(data.c_str(), data.length(), &t));
	data = "-a";
	XTS_ASSERT_EQUALS(2, tokenize(data.c_str(), data.length(), &t));
}

XTS_TEST(TokenizerTests, testInvalidStr) {
	struct Token* t;
	std::string data;
	data = "\"";
	XTS_ASSERT_EQUALS(2, tokenize(data.c_str(), data.length(), &t));
	data = "\"\\"; /* TODO: should this return 2? */
	XTS_ASSERT_EQUALS(3, tokenize(data.c_str(), data.length(), &t));
	data = "\"\\a";
	XTS_ASSERT_EQUALS(3, tokenize(data.c_str(), data.length(), &t));
	data = "\"\\\\\\\"";
	XTS_ASSERT_EQUALS(6, tokenize(data.c_str(), data.length(), &t));
}

XTS_TEST(TokenizerTests, testInvalidId) {
	struct Token* t;
	std::string data = ".";
	XTS_ASSERT_EQUALS(1, tokenize(data.c_str(), data.length(), &t));
}
<|endoftext|>


<commit_before>/*******************************************************************************
    Copyright (c) The Taichi Authors (2016- ). All Rights Reserved.
    The use of this software is governed by the LICENSE file.
*******************************************************************************/

#include <taichi/util.h>
#include <taichi/testing.h>
#include <taichi/math/svd.h>
#include <taichi/math/eigen.h>

TC_NAMESPACE_BEGIN

constexpr size_t page_size = (1 << 12);  // 4 KB page size by default

#if defined(TC_PLATFORM_UNIX)
#include <sys/mman.h>
#else
#include <windows.h>
#endif

// Cross-platform virtual memory allocator
class VirtualMemoryAllocator {
public:
  void *ptr;
  size_t size;
  explicit VirtualMemoryAllocator(size_t size) : size(size) {
// http://pages.cs.wisc.edu/~sifakis/papers/SPGrid.pdf Sec 3.1
#if defined(TC_PLATFORM_UNIX)
    ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE,
               MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
#else
    ptr = VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT,
                       PAGE_READWRITE);
#endif
    TC_ERROR_IF(ptr == MAP_FAILED, "Virtual memory allocation ({} B) failed.",
                size);
    TC_ERROR_IF(((uint64_t)ptr) % page_size != 0,
                "Allocated address ({:}) is not aligned by page size {}", ptr,
                page_size);
  }

  ~VirtualMemoryAllocator() {
#if defined(TC_PLATFORM_UNIX)
    if (munmap(ptr, size) != 0)
#else
      if (!VirtualFree(ptr, size, MEM_RELEASE))
#endif
      TC_ERROR("Failed to free virtual memory ({} B)", size);

  }
};

inline uint64 rand_int64() {
  return ((uint64)rand_int() << 32) + rand_int();
}

TC_TEST("Virtual Memory") {
  for (int i = 0; i < 3; i++) {
    // Allocate 1 TB of virtual memory
    std::size_t size = 1LL << 40;
    VirtualMemoryAllocator vm(size);
    // Touch 512 MB (1 << 29 B)
    for (int j = 0; j < (1 << 29) / page_size; j++) {
      void *target = vm.ptr + rand_int64() % size;
      uint8 val = *(uint8 *)target;
      CHECK(val == 0);
    }
  }

}

TC_NAMESPACE_END
<commit_msg>MSVC & OS X fixes<commit_after>/*******************************************************************************
    Copyright (c) The Taichi Authors (2016- ). All Rights Reserved.
    The use of this software is governed by the LICENSE file.
*******************************************************************************/

#include <taichi/util.h>
#include <taichi/testing.h>
#include <taichi/math/svd.h>
#include <taichi/math/eigen.h>

TC_NAMESPACE_BEGIN

constexpr size_t page_size = (1 << 12);  // 4 KB page size by default

#if defined(TC_PLATFORM_UNIX)
#include <sys/mman.h>
#else
#include <windows.h>
#endif

// Cross-platform virtual memory allocator
class VirtualMemoryAllocator {
public:
  void *ptr;
  size_t size;
  explicit VirtualMemoryAllocator(size_t size) : size(size) {
// http://pages.cs.wisc.edu/~sifakis/papers/SPGrid.pdf Sec 3.1
#if defined(TC_PLATFORM_UNIX)
    ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE,
               MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
    TC_ERROR_IF(ptr == MAP_FAILED, "Virtual memory allocation ({} B) failed.",
                size);
#else
    ptr = VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT,
                       PAGE_READWRITE);
    TC_ERROR_IF(ptr == nullptr, "Virtual memory allocation ({} B) failed.",
                size);
#endif
    TC_ERROR_IF(((uint64_t)ptr) % page_size != 0,
                "Allocated address ({:}) is not aligned by page size {}", ptr,
                page_size);
  }

  ~VirtualMemoryAllocator() {
#if defined(TC_PLATFORM_UNIX)
    if (munmap(ptr, size) != 0)
#else
      if (!VirtualFree(ptr, size, MEM_RELEASE))
#endif
      TC_ERROR("Failed to free virtual memory ({} B)", size);

  }
};

inline uint64 rand_int64() {
  return ((uint64)rand_int() << 32) + rand_int();
}

TC_TEST("Virtual Memory") {
  for (int i = 0; i < 3; i++) {
    // Allocate 1 TB of virtual memory
    std::size_t size = 1LL << 40;
    VirtualMemoryAllocator vm(size);
    // Touch 512 MB (1 << 29 B)
    for (int j = 0; j < (1 << 29) / page_size; j++) {
      uint8 val = *((uint8 *)vm.ptr + rand_int64() % size);
      CHECK(val == 0);
    }
  }

}

TC_NAMESPACE_END
<|endoftext|>


<commit_before>
#include "FFmpegImageStream.hpp"
#include "FFmpegAudioStream.hpp"
#include "FFmpegParameters.hpp"

#include <OpenThreads/ScopedLock>
#include <osg/Notify>

#include <memory>

#define STREAM_TIMEOUT_IN_SECONDS_TO_CONSIDER_IT_DEAD   10

namespace osgFFmpeg {

FFmpegImageStream::FFmpegImageStream() :
    m_decoder(0),
    m_commands(0),
    m_frame_published_flag(false),
    _lastUpdateTS(0.)
{
    setOrigin(osg::Image::TOP_LEFT);

    std::auto_ptr<FFmpegDecoder> decoder(new FFmpegDecoder);
    std::auto_ptr<CommandQueue> commands(new CommandQueue);

    m_decoder = decoder.release();
    m_commands = commands.release();
}

FFmpegImageStream::FFmpegImageStream(const FFmpegImageStream & image, const osg::CopyOp & copyop) :
    osg::ImageStream(image, copyop),
    OpenThreads::Thread()
{
    // TODO: probably incorrect or incomplete
}

FFmpegImageStream::~FFmpegImageStream()
{
    OSG_INFO<<"Destructing FFmpegImageStream..."<<std::endl;

    quit(true);

    OSG_INFO<<"Have done quit"<<std::endl;

    // release athe audio streams to make sure that the decoder doesn't retain any external
    // references.
    getAudioStreams().clear();

    // destroy the decoder and associated threads
    m_decoder = 0;

    delete m_commands;

    OSG_INFO<<"Destructed FFMpegImageStream."<<std::endl;
}

bool FFmpegImageStream::open(const std::string & filename, FFmpegParameters* parameters)
{
    setFileName(filename);

    if (! m_decoder->open(filename, parameters))
        return false;

    setImage(
        m_decoder->video_decoder().width(), m_decoder->video_decoder().height(), 1, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE,
        const_cast<unsigned char *>(m_decoder->video_decoder().image()), NO_DELETE
    );

    setPixelAspectRatio(m_decoder->video_decoder().pixelAspectRatio());

    OSG_NOTICE<<"ffmpeg::open("<<filename<<") size("<<s()<<", "<<t()<<") aspect ratio "<<m_decoder->video_decoder().pixelAspectRatio()<<std::endl;

#if 1
    // swscale is reported errors and then crashing when rescaling video of size less than 10 by 10.
    if (s()<=10 || t()<=10) return false;
#endif

    m_decoder->video_decoder().setUserData(this);
    m_decoder->video_decoder().setPublishCallback(publishNewFrame);

    if (m_decoder->audio_decoder().validContext())
    {
        OSG_NOTICE<<"Attaching FFmpegAudioStream"<<std::endl;

        getAudioStreams().push_back(new FFmpegAudioStream(m_decoder.get()));
    }

    _status = PAUSED;
    applyLoopingMode();

    start(); // start thread

    return true;
}

void FFmpegImageStream::play()
{
    m_commands->push(CMD_PLAY);

#if 0
    // Wait for at least one frame to be published before returning the call
    OpenThreads::ScopedLock<Mutex> lock(m_mutex);

    while (duration() > 0 && ! m_frame_published_flag)
        m_frame_published_cond.wait(&m_mutex);

#endif
}

void FFmpegImageStream::pause()
{
    m_commands->push(CMD_PAUSE);
}

void FFmpegImageStream::rewind()
{
    m_commands->push(CMD_REWIND);
}

void FFmpegImageStream::seek(double time) {
    m_seek_time = time;
    m_commands->push(CMD_SEEK);
}

void FFmpegImageStream::quit(bool waitForThreadToExit)
{
    // Stop the packet producer thread
    if (isRunning())
    {
        m_commands->push(CMD_STOP);

        if (waitForThreadToExit)
            join();
    }

    // Close the decoder (i.e. flush the decoder packet queues)
    m_decoder->close(waitForThreadToExit);
}

void FFmpegImageStream::setVolume(float volume)
{
    m_decoder->audio_decoder().setVolume(volume);
}

float FFmpegImageStream::getVolume() const
{
    return m_decoder->audio_decoder().getVolume();
}

double FFmpegImageStream::getCreationTime() const
{
    return m_decoder->creation_time();
}

double FFmpegImageStream::getLength() const
{
    return m_decoder->duration();
}

double FFmpegImageStream::getReferenceTime () const
{
    return m_decoder->reference();
}

double FFmpegImageStream::getCurrentTime() const
{
    return m_decoder->reference();
}

double FFmpegImageStream::getFrameRate() const
{
    return m_decoder->video_decoder().frameRate();
}

bool FFmpegImageStream::isImageTranslucent() const
{
    return m_decoder->video_decoder().alphaChannel();
}

void FFmpegImageStream::run()
{
    try
    {
        bool done = false;

        while (! done)
        {
            if (_status == PLAYING)
            {
                bool no_cmd;
                const Command cmd = m_commands->timedPop(no_cmd, 1);

                if (no_cmd)
                {
                    m_decoder->readNextPacket();
                }
                else
                    done = ! handleCommand(cmd);

                // Security check to detect (and stop) dead streams
                if ( _lastUpdateTS > 0. &&
                    osg::Timer::instance()->delta_s(_lastUpdateTS, osg::Timer::instance()->tick()) > STREAM_TIMEOUT_IN_SECONDS_TO_CONSIDER_IT_DEAD )
                {
                    _status = INVALID;
                    done = true;
                }
            }
            else
            {
                done = ! handleCommand(m_commands->pop());
            }
        }
    }

    catch (const std::exception & error)
    {
        OSG_WARN << "FFmpegImageStream::run : " << error.what() << std::endl;
    }

    catch (...)
    {
        OSG_WARN << "FFmpegImageStream::run : unhandled exception" << std::endl;
    }

    OSG_NOTICE<<"Finished FFmpegImageStream::run()"<<std::endl;

//    _status = INVALID;
}

void FFmpegImageStream::applyLoopingMode()
{
    m_decoder->loop(getLoopingMode() == LOOPING);
}

bool FFmpegImageStream::handleCommand(const Command cmd)
{
    switch (cmd)
    {
    case CMD_PLAY:
        cmdPlay();
        return true;

    case CMD_PAUSE:
        cmdPause();
        return true;

    case CMD_REWIND:
        cmdRewind();
        return true;

    case CMD_SEEK:
        cmdSeek(m_seek_time);
        return true;

    case CMD_STOP:
        return false;

    default:
        assert(false);
        return false;
    }
}

void FFmpegImageStream::cmdPlay()
{
    if (_status == PAUSED)
    {
        if (! m_decoder->audio_decoder().isRunning())
            m_decoder->audio_decoder().start();

        if (! m_decoder->video_decoder().isRunning())
            m_decoder->video_decoder().start();

        m_decoder->video_decoder().pause(false);
        m_decoder->audio_decoder().pause(false);

        _lastUpdateTS = 0.;
    }

    _status = PLAYING;
}

void FFmpegImageStream::cmdPause()
{
    if (_status == PLAYING)
    {
        m_decoder->video_decoder().pause(true);
        m_decoder->audio_decoder().pause(true);
    }

    _status = PAUSED;
}

void FFmpegImageStream::cmdRewind()
{
    m_decoder->rewind();
}

void FFmpegImageStream::cmdSeek(double time)
{
    m_decoder->seek(time);
}

void FFmpegImageStream::publishNewFrame(const FFmpegDecoderVideo &, void * user_data)
{
    FFmpegImageStream * const this_ = reinterpret_cast<FFmpegImageStream*>(user_data);

#if 1
    this_->setImage(
        this_->m_decoder->video_decoder().width(), this_->m_decoder->video_decoder().height(), 1, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE,
        const_cast<unsigned char *>(this_->m_decoder->video_decoder().image()), NO_DELETE
    );
#else
    /** \bug If viewer.realize() hasn't been already called, this doesn't work? */
    this_->dirty();
#endif

    // Store the timestamp of this update. Needed to check dead streams
    this_->_lastUpdateTS = osg::Timer::instance()->tick();

    OpenThreads::ScopedLock<Mutex> lock(this_->m_mutex);

    if (! this_->m_frame_published_flag)
    {
        this_->m_frame_published_flag = true;
        this_->m_frame_published_cond.signal();
    }
}

} // namespace osgFFmpeg
<commit_msg>Removed stray tab<commit_after>
#include "FFmpegImageStream.hpp"
#include "FFmpegAudioStream.hpp"
#include "FFmpegParameters.hpp"

#include <OpenThreads/ScopedLock>
#include <osg/Notify>

#include <memory>

#define STREAM_TIMEOUT_IN_SECONDS_TO_CONSIDER_IT_DEAD   10

namespace osgFFmpeg {

FFmpegImageStream::FFmpegImageStream() :
    m_decoder(0),
    m_commands(0),
    m_frame_published_flag(false),
    _lastUpdateTS(0.)
{
    setOrigin(osg::Image::TOP_LEFT);

    std::auto_ptr<FFmpegDecoder> decoder(new FFmpegDecoder);
    std::auto_ptr<CommandQueue> commands(new CommandQueue);

    m_decoder = decoder.release();
    m_commands = commands.release();
}

FFmpegImageStream::FFmpegImageStream(const FFmpegImageStream & image, const osg::CopyOp & copyop) :
    osg::ImageStream(image, copyop),
    OpenThreads::Thread()
{
    // TODO: probably incorrect or incomplete
}

FFmpegImageStream::~FFmpegImageStream()
{
    OSG_INFO<<"Destructing FFmpegImageStream..."<<std::endl;

    quit(true);

    OSG_INFO<<"Have done quit"<<std::endl;

    // release athe audio streams to make sure that the decoder doesn't retain any external
    // references.
    getAudioStreams().clear();

    // destroy the decoder and associated threads
    m_decoder = 0;

    delete m_commands;

    OSG_INFO<<"Destructed FFMpegImageStream."<<std::endl;
}

bool FFmpegImageStream::open(const std::string & filename, FFmpegParameters* parameters)
{
    setFileName(filename);

    if (! m_decoder->open(filename, parameters))
        return false;

    setImage(
        m_decoder->video_decoder().width(), m_decoder->video_decoder().height(), 1, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE,
        const_cast<unsigned char *>(m_decoder->video_decoder().image()), NO_DELETE
    );

    setPixelAspectRatio(m_decoder->video_decoder().pixelAspectRatio());

    OSG_NOTICE<<"ffmpeg::open("<<filename<<") size("<<s()<<", "<<t()<<") aspect ratio "<<m_decoder->video_decoder().pixelAspectRatio()<<std::endl;

#if 1
    // swscale is reported errors and then crashing when rescaling video of size less than 10 by 10.
    if (s()<=10 || t()<=10) return false;
#endif

    m_decoder->video_decoder().setUserData(this);
    m_decoder->video_decoder().setPublishCallback(publishNewFrame);

    if (m_decoder->audio_decoder().validContext())
    {
        OSG_NOTICE<<"Attaching FFmpegAudioStream"<<std::endl;

        getAudioStreams().push_back(new FFmpegAudioStream(m_decoder.get()));
    }

    _status = PAUSED;
    applyLoopingMode();

    start(); // start thread

    return true;
}

void FFmpegImageStream::play()
{
    m_commands->push(CMD_PLAY);

#if 0
    // Wait for at least one frame to be published before returning the call
    OpenThreads::ScopedLock<Mutex> lock(m_mutex);

    while (duration() > 0 && ! m_frame_published_flag)
        m_frame_published_cond.wait(&m_mutex);

#endif
}

void FFmpegImageStream::pause()
{
    m_commands->push(CMD_PAUSE);
}

void FFmpegImageStream::rewind()
{
    m_commands->push(CMD_REWIND);
}

void FFmpegImageStream::seek(double time) {
    m_seek_time = time;
    m_commands->push(CMD_SEEK);
}

void FFmpegImageStream::quit(bool waitForThreadToExit)
{
    // Stop the packet producer thread
    if (isRunning())
    {
        m_commands->push(CMD_STOP);

        if (waitForThreadToExit)
            join();
    }

    // Close the decoder (i.e. flush the decoder packet queues)
    m_decoder->close(waitForThreadToExit);
}

void FFmpegImageStream::setVolume(float volume)
{
    m_decoder->audio_decoder().setVolume(volume);
}

float FFmpegImageStream::getVolume() const
{
    return m_decoder->audio_decoder().getVolume();
}

double FFmpegImageStream::getCreationTime() const
{
    return m_decoder->creation_time();
}

double FFmpegImageStream::getLength() const
{
    return m_decoder->duration();
}

double FFmpegImageStream::getReferenceTime () const
{
    return m_decoder->reference();
}

double FFmpegImageStream::getCurrentTime() const
{
    return m_decoder->reference();
}

double FFmpegImageStream::getFrameRate() const
{
    return m_decoder->video_decoder().frameRate();
}

bool FFmpegImageStream::isImageTranslucent() const
{
    return m_decoder->video_decoder().alphaChannel();
}

void FFmpegImageStream::run()
{
    try
    {
        bool done = false;

        while (! done)
        {
            if (_status == PLAYING)
            {
                bool no_cmd;
                const Command cmd = m_commands->timedPop(no_cmd, 1);

                if (no_cmd)
                {
                    m_decoder->readNextPacket();
                }
                else
                    done = ! handleCommand(cmd);

                // Security check to detect (and stop) dead streams
                if ( _lastUpdateTS > 0. &&
                    osg::Timer::instance()->delta_s(_lastUpdateTS, osg::Timer::instance()->tick()) > STREAM_TIMEOUT_IN_SECONDS_TO_CONSIDER_IT_DEAD )
                {
                    _status = INVALID;
                    done = true;
                }
            }
            else
            {
                done = ! handleCommand(m_commands->pop());
            }
        }
    }

    catch (const std::exception & error)
    {
        OSG_WARN << "FFmpegImageStream::run : " << error.what() << std::endl;
    }

    catch (...)
    {
        OSG_WARN << "FFmpegImageStream::run : unhandled exception" << std::endl;
    }

    OSG_NOTICE<<"Finished FFmpegImageStream::run()"<<std::endl;

//    _status = INVALID;
}

void FFmpegImageStream::applyLoopingMode()
{
    m_decoder->loop(getLoopingMode() == LOOPING);
}

bool FFmpegImageStream::handleCommand(const Command cmd)
{
    switch (cmd)
    {
    case CMD_PLAY:
        cmdPlay();
        return true;

    case CMD_PAUSE:
        cmdPause();
        return true;

    case CMD_REWIND:
        cmdRewind();
        return true;

    case CMD_SEEK:
        cmdSeek(m_seek_time);
        return true;

    case CMD_STOP:
        return false;

    default:
        assert(false);
        return false;
    }
}

void FFmpegImageStream::cmdPlay()
{
    if (_status == PAUSED)
    {
        if (! m_decoder->audio_decoder().isRunning())
            m_decoder->audio_decoder().start();

        if (! m_decoder->video_decoder().isRunning())
            m_decoder->video_decoder().start();

        m_decoder->video_decoder().pause(false);
        m_decoder->audio_decoder().pause(false);

        _lastUpdateTS = 0.;
    }

    _status = PLAYING;
}

void FFmpegImageStream::cmdPause()
{
    if (_status == PLAYING)
    {
        m_decoder->video_decoder().pause(true);
        m_decoder->audio_decoder().pause(true);
    }

    _status = PAUSED;
}

void FFmpegImageStream::cmdRewind()
{
    m_decoder->rewind();
}

void FFmpegImageStream::cmdSeek(double time)
{
    m_decoder->seek(time);
}

void FFmpegImageStream::publishNewFrame(const FFmpegDecoderVideo &, void * user_data)
{
    FFmpegImageStream * const this_ = reinterpret_cast<FFmpegImageStream*>(user_data);

#if 1
    this_->setImage(
        this_->m_decoder->video_decoder().width(), this_->m_decoder->video_decoder().height(), 1, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE,
        const_cast<unsigned char *>(this_->m_decoder->video_decoder().image()), NO_DELETE
    );
#else
    /** \bug If viewer.realize() hasn't been already called, this doesn't work? */
    this_->dirty();
#endif

    // Store the timestamp of this update. Needed to check dead streams
    this_->_lastUpdateTS = osg::Timer::instance()->tick();

    OpenThreads::ScopedLock<Mutex> lock(this_->m_mutex);

    if (! this_->m_frame_published_flag)
    {
        this_->m_frame_published_flag = true;
        this_->m_frame_published_cond.signal();
    }
}

} // namespace osgFFmpeg
<|endoftext|>


<commit_before><commit_msg>We can put a few more lines inside the #ifndef IOS<commit_after><|endoftext|>