<commit_before>#include <boost/test/unit_test.hpp>

#include "base58.h"
#include "util.h"
#include "test/data/base58_encode_decode.json.h"
#include "test/data/base58_keys_valid.json.h"
#include "test/data/base58_keys_invalid.json.h"

#include <univalue.h>

extern UniValue read_json(const std::string& jsondata);

BOOST_AUTO_TEST_SUITE(base58_tests)

// Goal: test low-level base58 encoding functionality
BOOST_AUTO_TEST_CASE(base58_EncodeBase58)
{
    UniValue tests = read_json(std::string(json_tests::base58_encode_decode, json_tests::base58_encode_decode + sizeof(json_tests::base58_encode_decode)));
    for (unsigned int idx = 0; idx < tests.size(); idx++)
    {
        UniValue test = tests[idx];
        std::string strTest = test.write();
        if (test.size() < 2) // Allow for extra stuff (useful for comments)
        {
            BOOST_ERROR("Bad test: " << strTest);
            continue;
        }
        std::vector<unsigned char> sourcedata = ParseHex(test[0].get_str());
        std::string base58string = test[1].get_str();
        BOOST_CHECK_MESSAGE(
                    EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size()) == base58string,
                    strTest);
    }
}

// Goal: test low-level base58 decoding functionality
BOOST_AUTO_TEST_CASE(base58_DecodeBase58)
{
    UniValue tests = read_json(std::string(json_tests::base58_encode_decode, json_tests::base58_encode_decode + sizeof(json_tests::base58_encode_decode)));
    std::vector<unsigned char> result;

    for (unsigned int idx = 0; idx < tests.size(); idx++) {
        UniValue test = tests[idx];
        std::string strTest = test.write();

        if (test.size() < 2) // Allow for extra stuff (useful for comments)
        {
            BOOST_ERROR("Bad test: " << strTest);
            continue;
        }
        std::vector<unsigned char> expected = ParseHex(test[0].get_str());
        std::string base58string = test[1].get_str();
        BOOST_CHECK_MESSAGE(DecodeBase58(base58string, result), strTest);
        BOOST_CHECK_MESSAGE(result.size() == expected.size() && std::equal(result.begin(), result.end(), expected.begin()), strTest);
    }

    BOOST_CHECK(!DecodeBase58("invalid", result));
}

// Visitor to check address type
class TestAddrTypeVisitor
{
private:
    std::string exp_addrType;
public:
    TestAddrTypeVisitor(const std::string &exp_addrType) : exp_addrType(exp_addrType) { }
    bool operator()(const CKeyID &id) const
    {
        return (exp_addrType == "pubkey");
    }
    bool operator()(const CScriptID &id) const
    {
        return (exp_addrType == "script");
    }
    bool operator()(const CNoDestination &no) const
    {
        return (exp_addrType == "none");
    }
};

// Visitor to check address payload
class TestPayloadVisitor
{
private:
    std::vector<unsigned char> exp_payload;
public:
    TestPayloadVisitor(std::vector<unsigned char> &exp_payload) : exp_payload(exp_payload) { }
    bool operator()(const CKeyID &id) const
    {
        uint160 exp_key(exp_payload);
        return exp_key == id;
    }
    bool operator()(const CScriptID &id) const
    {
        uint160 exp_key(exp_payload);
        return exp_key == id;
    }
    bool operator()(const CNoDestination &no) const
    {
        return exp_payload.size() == 0;
    }
};

// Goal: check that parsed keys match test payload
BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
{
    UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
    std::vector<unsigned char> result;
    CBitcoinSecret secret;
    CBitcoinAddress addr;
    // Save global state
    bool fTestNet_stored = fTestNet;
    for (unsigned int idx = 0; idx < tests.size(); idx++) {
        UniValue test = tests[idx];
        std::string strTest = test.write();
        if (test.size() < 3) // Allow for extra stuff (useful for comments)
        {
            BOOST_ERROR("Bad test: " << strTest);
            continue;
        }
        std::string exp_base58string = test[0].get_str();
        std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
        const UniValue &metadata = test[2].get_obj();
        bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
        bool isTestnet = find_value(metadata, "isTestnet").get_bool();
        fTestNet = isTestnet; // Override testnet flag
        if(isPrivkey)
        {
            bool isCompressed = find_value(metadata, "isCompressed").get_bool();
            // Must be valid private key
            // Note: CBitcoinSecret::SetString tests isValid, whereas CBitcoinAddress does not!
            BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest);
            BOOST_CHECK_MESSAGE(secret.IsValid(), "!IsValid:" + strTest);
            bool fCompressedOut = false;
            CSecret privkey = secret.GetSecret(fCompressedOut);
            BOOST_CHECK_MESSAGE(fCompressedOut == isCompressed, "compressed mismatch:" + strTest);
            BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest);

            // Private key must be invalid public key
            addr.SetString(exp_base58string);
            BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid privkey as pubkey:" + strTest);
        }
        else
        {
            std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey"
            // Must be valid public key
            BOOST_CHECK_MESSAGE(addr.SetString(exp_base58string), "SetString:" + strTest);
            BOOST_CHECK_MESSAGE(addr.IsValid(), "!IsValid:" + strTest);
            BOOST_CHECK_MESSAGE(addr.IsScript() == (exp_addrType == "script"), "isScript mismatch" + strTest);
            CTxDestination dest = addr.Get();
            BOOST_CHECK_MESSAGE(std::visit(TestAddrTypeVisitor(exp_addrType), dest), "addrType mismatch" + strTest);

            // Public key must be invalid private key
            secret.SetString(exp_base58string);
            BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid pubkey as privkey:" + strTest);
        }
    }
    // Restore global state
    fTestNet = fTestNet_stored;
}

// Goal: check that generated keys match test vectors
BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
{
    UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
    std::vector<unsigned char> result;
    // Save global state
    bool fTestNet_stored = fTestNet;
    for (unsigned int idx = 0; idx < tests.size(); idx++) {
        UniValue test = tests[idx];
        std::string strTest = test.write();
        if (test.size() < 3) // Allow for extra stuff (useful for comments)
        {
            BOOST_ERROR("Bad test: " << strTest);
            continue;
        }
        std::string exp_base58string = test[0].get_str();
        std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
        const UniValue &metadata = test[2].get_obj();
        bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
        bool isTestnet = find_value(metadata, "isTestnet").get_bool();
        fTestNet = isTestnet; // Override testnet flag
        if(isPrivkey)
        {
            bool isCompressed = find_value(metadata, "isCompressed").get_bool();
            CBitcoinSecret secret;
            secret.SetSecret(CSecret(exp_payload.begin(), exp_payload.end()), isCompressed);
            BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest);
        }
        else
        {
            std::string exp_addrType = find_value(metadata, "addrType").get_str();
            CTxDestination dest;
            if(exp_addrType == "pubkey")
            {
                dest = CKeyID(uint160(exp_payload));
            }
            else if(exp_addrType == "script")
            {
                dest = CScriptID(uint160(exp_payload));
            }
            else if(exp_addrType == "none")
            {
                dest = CNoDestination();
            }
            else
            {
                BOOST_ERROR("Bad addrtype: " << strTest);
                continue;
            }
            CBitcoinAddress addrOut;
            BOOST_CHECK_MESSAGE(std::visit(CBitcoinAddressVisitor(&addrOut), dest), "encode dest: " + strTest);
            BOOST_CHECK_MESSAGE(addrOut.ToString() == exp_base58string, "mismatch: " + strTest + addrOut.ToString());
        }
    }

    // Visiting a CNoDestination must fail
    CBitcoinAddress dummyAddr;
    CTxDestination nodest = CNoDestination();
    BOOST_CHECK(!std::visit(CBitcoinAddressVisitor(&dummyAddr), nodest));

    // Restore global state
    fTestNet = fTestNet_stored;
}

// Goal: check that base58 parsing code is robust against a variety of corrupted data
BOOST_AUTO_TEST_CASE(base58_keys_invalid)
{
    UniValue tests = read_json(std::string(json_tests::base58_keys_invalid, json_tests::base58_keys_invalid + sizeof(json_tests::base58_keys_invalid)));
    std::vector<unsigned char> result;
    CBitcoinSecret secret;
    CBitcoinAddress addr;
    for (unsigned int idx = 0; idx < tests.size(); idx++) {
        UniValue test = tests[idx];
        std::string strTest = test.write();
        if (test.size() < 1) // Allow for extra stuff (useful for comments)
        {
            BOOST_ERROR("Bad test: " << strTest);
            continue;
        }
        std::string exp_base58string = test[0].get_str();

        // must be invalid as public and as private key
        addr.SetString(exp_base58string);
        BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid pubkey:" + strTest);
        secret.SetString(exp_base58string);
        BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest);
    }
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>test: DecodeBase58 skipping whitespace<commit_after>#include <boost/test/unit_test.hpp>

#include "base58.h"
#include "util.h"
#include "test/data/base58_encode_decode.json.h"
#include "test/data/base58_keys_valid.json.h"
#include "test/data/base58_keys_invalid.json.h"

#include <univalue.h>

extern UniValue read_json(const std::string& jsondata);

BOOST_AUTO_TEST_SUITE(base58_tests)

// Goal: test low-level base58 encoding functionality
BOOST_AUTO_TEST_CASE(base58_EncodeBase58)
{
    UniValue tests = read_json(std::string(json_tests::base58_encode_decode, json_tests::base58_encode_decode + sizeof(json_tests::base58_encode_decode)));
    for (unsigned int idx = 0; idx < tests.size(); idx++)
    {
        UniValue test = tests[idx];
        std::string strTest = test.write();
        if (test.size() < 2) // Allow for extra stuff (useful for comments)
        {
            BOOST_ERROR("Bad test: " << strTest);
            continue;
        }
        std::vector<unsigned char> sourcedata = ParseHex(test[0].get_str());
        std::string base58string = test[1].get_str();
        BOOST_CHECK_MESSAGE(
                    EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size()) == base58string,
                    strTest);
    }
}

// Goal: test low-level base58 decoding functionality
BOOST_AUTO_TEST_CASE(base58_DecodeBase58)
{
    UniValue tests = read_json(std::string(json_tests::base58_encode_decode, json_tests::base58_encode_decode + sizeof(json_tests::base58_encode_decode)));
    std::vector<unsigned char> result;

    for (unsigned int idx = 0; idx < tests.size(); idx++) {
        UniValue test = tests[idx];
        std::string strTest = test.write();

        if (test.size() < 2) // Allow for extra stuff (useful for comments)
        {
            BOOST_ERROR("Bad test: " << strTest);
            continue;
        }
        std::vector<unsigned char> expected = ParseHex(test[0].get_str());
        std::string base58string = test[1].get_str();
        BOOST_CHECK_MESSAGE(DecodeBase58(base58string, result), strTest);
        BOOST_CHECK_MESSAGE(result.size() == expected.size() && std::equal(result.begin(), result.end(), expected.begin()), strTest);
    }

    BOOST_CHECK(!DecodeBase58("invalid", result));

    // check that DecodeBase58 skips whitespace, but still fails with unexpected non-whitespace at the end.
    BOOST_CHECK(!DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t a", result));
    BOOST_CHECK( DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t ", result));
    std::vector<unsigned char> expected = ParseHex("971a55");
    BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
}

// Visitor to check address type
class TestAddrTypeVisitor
{
private:
    std::string exp_addrType;
public:
    TestAddrTypeVisitor(const std::string &exp_addrType) : exp_addrType(exp_addrType) { }
    bool operator()(const CKeyID &id) const
    {
        return (exp_addrType == "pubkey");
    }
    bool operator()(const CScriptID &id) const
    {
        return (exp_addrType == "script");
    }
    bool operator()(const CNoDestination &no) const
    {
        return (exp_addrType == "none");
    }
};

// Visitor to check address payload
class TestPayloadVisitor
{
private:
    std::vector<unsigned char> exp_payload;
public:
    TestPayloadVisitor(std::vector<unsigned char> &exp_payload) : exp_payload(exp_payload) { }
    bool operator()(const CKeyID &id) const
    {
        uint160 exp_key(exp_payload);
        return exp_key == id;
    }
    bool operator()(const CScriptID &id) const
    {
        uint160 exp_key(exp_payload);
        return exp_key == id;
    }
    bool operator()(const CNoDestination &no) const
    {
        return exp_payload.size() == 0;
    }
};

// Goal: check that parsed keys match test payload
BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
{
    UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
    std::vector<unsigned char> result;
    CBitcoinSecret secret;
    CBitcoinAddress addr;
    // Save global state
    bool fTestNet_stored = fTestNet;
    for (unsigned int idx = 0; idx < tests.size(); idx++) {
        UniValue test = tests[idx];
        std::string strTest = test.write();
        if (test.size() < 3) // Allow for extra stuff (useful for comments)
        {
            BOOST_ERROR("Bad test: " << strTest);
            continue;
        }
        std::string exp_base58string = test[0].get_str();
        std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
        const UniValue &metadata = test[2].get_obj();
        bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
        bool isTestnet = find_value(metadata, "isTestnet").get_bool();
        fTestNet = isTestnet; // Override testnet flag
        if(isPrivkey)
        {
            bool isCompressed = find_value(metadata, "isCompressed").get_bool();
            // Must be valid private key
            // Note: CBitcoinSecret::SetString tests isValid, whereas CBitcoinAddress does not!
            BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest);
            BOOST_CHECK_MESSAGE(secret.IsValid(), "!IsValid:" + strTest);
            bool fCompressedOut = false;
            CSecret privkey = secret.GetSecret(fCompressedOut);
            BOOST_CHECK_MESSAGE(fCompressedOut == isCompressed, "compressed mismatch:" + strTest);
            BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest);

            // Private key must be invalid public key
            addr.SetString(exp_base58string);
            BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid privkey as pubkey:" + strTest);
        }
        else
        {
            std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey"
            // Must be valid public key
            BOOST_CHECK_MESSAGE(addr.SetString(exp_base58string), "SetString:" + strTest);
            BOOST_CHECK_MESSAGE(addr.IsValid(), "!IsValid:" + strTest);
            BOOST_CHECK_MESSAGE(addr.IsScript() == (exp_addrType == "script"), "isScript mismatch" + strTest);
            CTxDestination dest = addr.Get();
            BOOST_CHECK_MESSAGE(std::visit(TestAddrTypeVisitor(exp_addrType), dest), "addrType mismatch" + strTest);

            // Public key must be invalid private key
            secret.SetString(exp_base58string);
            BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid pubkey as privkey:" + strTest);
        }
    }
    // Restore global state
    fTestNet = fTestNet_stored;
}

// Goal: check that generated keys match test vectors
BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
{
    UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
    std::vector<unsigned char> result;
    // Save global state
    bool fTestNet_stored = fTestNet;
    for (unsigned int idx = 0; idx < tests.size(); idx++) {
        UniValue test = tests[idx];
        std::string strTest = test.write();
        if (test.size() < 3) // Allow for extra stuff (useful for comments)
        {
            BOOST_ERROR("Bad test: " << strTest);
            continue;
        }
        std::string exp_base58string = test[0].get_str();
        std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
        const UniValue &metadata = test[2].get_obj();
        bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
        bool isTestnet = find_value(metadata, "isTestnet").get_bool();
        fTestNet = isTestnet; // Override testnet flag
        if(isPrivkey)
        {
            bool isCompressed = find_value(metadata, "isCompressed").get_bool();
            CBitcoinSecret secret;
            secret.SetSecret(CSecret(exp_payload.begin(), exp_payload.end()), isCompressed);
            BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest);
        }
        else
        {
            std::string exp_addrType = find_value(metadata, "addrType").get_str();
            CTxDestination dest;
            if(exp_addrType == "pubkey")
            {
                dest = CKeyID(uint160(exp_payload));
            }
            else if(exp_addrType == "script")
            {
                dest = CScriptID(uint160(exp_payload));
            }
            else if(exp_addrType == "none")
            {
                dest = CNoDestination();
            }
            else
            {
                BOOST_ERROR("Bad addrtype: " << strTest);
                continue;
            }
            CBitcoinAddress addrOut;
            BOOST_CHECK_MESSAGE(std::visit(CBitcoinAddressVisitor(&addrOut), dest), "encode dest: " + strTest);
            BOOST_CHECK_MESSAGE(addrOut.ToString() == exp_base58string, "mismatch: " + strTest + addrOut.ToString());
        }
    }

    // Visiting a CNoDestination must fail
    CBitcoinAddress dummyAddr;
    CTxDestination nodest = CNoDestination();
    BOOST_CHECK(!std::visit(CBitcoinAddressVisitor(&dummyAddr), nodest));

    // Restore global state
    fTestNet = fTestNet_stored;
}

// Goal: check that base58 parsing code is robust against a variety of corrupted data
BOOST_AUTO_TEST_CASE(base58_keys_invalid)
{
    UniValue tests = read_json(std::string(json_tests::base58_keys_invalid, json_tests::base58_keys_invalid + sizeof(json_tests::base58_keys_invalid)));
    std::vector<unsigned char> result;
    CBitcoinSecret secret;
    CBitcoinAddress addr;
    for (unsigned int idx = 0; idx < tests.size(); idx++) {
        UniValue test = tests[idx];
        std::string strTest = test.write();
        if (test.size() < 1) // Allow for extra stuff (useful for comments)
        {
            BOOST_ERROR("Bad test: " << strTest);
            continue;
        }
        std::string exp_base58string = test[0].get_str();

        // must be invalid as public and as private key
        addr.SetString(exp_base58string);
        BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid pubkey:" + strTest);
        secret.SetString(exp_base58string);
        BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest);
    }
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>// Copyright (c) 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 <amount.h>
#include <arith_uint256.h>
#include <compressor.h>
#include <consensus/merkle.h>
#include <core_io.h>
#include <crypto/common.h>
#include <crypto/siphash.h>
#include <key_io.h>
#include <memusage.h>
#include <netbase.h>
#include <policy/settings.h>
#include <pow.h>
#include <pubkey.h>
#include <rpc/util.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <serialize.h>
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <uint256.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <util/time.h>
#include <version.h>

#include <cassert>
#include <limits>
#include <vector>

void initialize()
{
    SelectParams(CBaseChainParams::REGTEST);
}

void test_one_input(const std::vector<uint8_t>& buffer)
{
    if (buffer.size() < sizeof(uint256) + sizeof(uint160)) {
        return;
    }
    FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
    const uint256 u256(fuzzed_data_provider.ConsumeBytes<unsigned char>(sizeof(uint256)));
    const uint160 u160(fuzzed_data_provider.ConsumeBytes<unsigned char>(sizeof(uint160)));
    const uint64_t u64 = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
    const int64_t i64 = fuzzed_data_provider.ConsumeIntegral<int64_t>();
    const uint32_t u32 = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
    const int32_t i32 = fuzzed_data_provider.ConsumeIntegral<int32_t>();
    const uint16_t u16 = fuzzed_data_provider.ConsumeIntegral<uint16_t>();
    const int16_t i16 = fuzzed_data_provider.ConsumeIntegral<int16_t>();
    const uint8_t u8 = fuzzed_data_provider.ConsumeIntegral<uint8_t>();
    const int8_t i8 = fuzzed_data_provider.ConsumeIntegral<int8_t>();
    // We cannot assume a specific value of std::is_signed<char>::value:
    // ConsumeIntegral<char>() instead of casting from {u,}int8_t.
    const char ch = fuzzed_data_provider.ConsumeIntegral<char>();
    const bool b = fuzzed_data_provider.ConsumeBool();

    const Consensus::Params& consensus_params = Params().GetConsensus();
    (void)CheckProofOfWork(u256, u32, consensus_params);
    if (u64 <= MAX_MONEY) {
        const uint64_t compressed_money_amount = CompressAmount(u64);
        assert(u64 == DecompressAmount(compressed_money_amount));
        static const uint64_t compressed_money_amount_max = CompressAmount(MAX_MONEY - 1);
        assert(compressed_money_amount <= compressed_money_amount_max);
    } else {
        (void)CompressAmount(u64);
    }
    static const uint256 u256_min(uint256S("0000000000000000000000000000000000000000000000000000000000000000"));
    static const uint256 u256_max(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
    const std::vector<uint256> v256{u256, u256_min, u256_max};
    (void)ComputeMerkleRoot(v256);
    (void)CountBits(u64);
    (void)DecompressAmount(u64);
    (void)FormatISO8601Date(i64);
    (void)FormatISO8601DateTime(i64);
    (void)GetSizeOfCompactSize(u64);
    (void)GetSpecialScriptSize(u32);
    // (void)GetVirtualTransactionSize(i64, i64); // function defined only for a subset of int64_t inputs
    // (void)GetVirtualTransactionSize(i64, i64, u32); // function defined only for a subset of int64_t/uint32_t inputs
    (void)HexDigit(ch);
    (void)i64tostr(i64);
    (void)IsDigit(ch);
    (void)IsSpace(ch);
    (void)IsSwitchChar(ch);
    (void)itostr(i32);
    (void)memusage::DynamicUsage(ch);
    (void)memusage::DynamicUsage(i16);
    (void)memusage::DynamicUsage(i32);
    (void)memusage::DynamicUsage(i64);
    (void)memusage::DynamicUsage(i8);
    (void)memusage::DynamicUsage(u16);
    (void)memusage::DynamicUsage(u32);
    (void)memusage::DynamicUsage(u64);
    (void)memusage::DynamicUsage(u8);
    const unsigned char uch = static_cast<unsigned char>(u8);
    (void)memusage::DynamicUsage(uch);
    (void)MillisToTimeval(i64);
    const double d = ser_uint64_to_double(u64);
    assert(ser_double_to_uint64(d) == u64);
    const float f = ser_uint32_to_float(u32);
    assert(ser_float_to_uint32(f) == u32);
    (void)SighashToStr(uch);
    (void)SipHashUint256(u64, u64, u256);
    (void)SipHashUint256Extra(u64, u64, u256, u32);
    (void)ToLower(ch);

    const arith_uint256 au256 = UintToArith256(u256);
    assert(ArithToUint256(au256) == u256);
    assert(uint256S(au256.GetHex()) == u256);
    (void)au256.bits();
    (void)au256.GetCompact(/* fNegative= */ false);
    (void)au256.GetCompact(/* fNegative= */ true);
    (void)au256.getdouble();
    (void)au256.GetHex();
    (void)au256.GetLow64();
    (void)au256.size();
    (void)au256.ToString();

    const CKeyID key_id{u160};
    const CScriptID script_id{u160};
    // CTxDestination = CNoDestination ∪ PKHash ∪ ScriptHash ∪ WitnessV0ScriptHash ∪ WitnessV0KeyHash ∪ WitnessUnknown
    const PKHash pk_hash{u160};
    const ScriptHash script_hash{u160};
    const WitnessV0KeyHash witness_v0_key_hash{u160};
    const WitnessV0ScriptHash witness_v0_script_hash{u256};
    const std::vector<CTxDestination> destinations{pk_hash, script_hash, witness_v0_key_hash, witness_v0_script_hash};
    const SigningProvider store;
    for (const CTxDestination& destination : destinations) {
        (void)DescribeAddress(destination);
        (void)EncodeDestination(destination);
        (void)GetKeyForDestination(store, destination);
        (void)GetScriptForDestination(destination);
        (void)IsValidDestination(destination);
    }

    {
        CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION);

        uint256 deserialized_u256;
        stream << u256;
        stream >> deserialized_u256;
        assert(u256 == deserialized_u256 && stream.empty());

        uint160 deserialized_u160;
        stream << u160;
        stream >> deserialized_u160;
        assert(u160 == deserialized_u160 && stream.empty());

        uint64_t deserialized_u64;
        stream << u64;
        stream >> deserialized_u64;
        assert(u64 == deserialized_u64 && stream.empty());

        int64_t deserialized_i64;
        stream << i64;
        stream >> deserialized_i64;
        assert(i64 == deserialized_i64 && stream.empty());

        uint32_t deserialized_u32;
        stream << u32;
        stream >> deserialized_u32;
        assert(u32 == deserialized_u32 && stream.empty());

        int32_t deserialized_i32;
        stream << i32;
        stream >> deserialized_i32;
        assert(i32 == deserialized_i32 && stream.empty());

        uint16_t deserialized_u16;
        stream << u16;
        stream >> deserialized_u16;
        assert(u16 == deserialized_u16 && stream.empty());

        int16_t deserialized_i16;
        stream << i16;
        stream >> deserialized_i16;
        assert(i16 == deserialized_i16 && stream.empty());

        uint8_t deserialized_u8;
        stream << u8;
        stream >> deserialized_u8;
        assert(u8 == deserialized_u8 && stream.empty());

        int8_t deserialized_i8;
        stream << i8;
        stream >> deserialized_i8;
        assert(i8 == deserialized_i8 && stream.empty());

        char deserialized_ch;
        stream << ch;
        stream >> deserialized_ch;
        assert(ch == deserialized_ch && stream.empty());

        bool deserialized_b;
        stream << b;
        stream >> deserialized_b;
        assert(b == deserialized_b && stream.empty());
    }
}
<commit_msg>tests: Fuzz additional functions in the integer fuzzing harness<commit_after>// Copyright (c) 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 <amount.h>
#include <arith_uint256.h>
#include <compressor.h>
#include <consensus/merkle.h>
#include <core_io.h>
#include <crypto/common.h>
#include <crypto/siphash.h>
#include <key_io.h>
#include <memusage.h>
#include <netbase.h>
#include <policy/settings.h>
#include <pow.h>
#include <pubkey.h>
#include <rpc/util.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <serialize.h>
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <uint256.h>
#include <util/moneystr.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <util/time.h>
#include <version.h>

#include <cassert>
#include <limits>
#include <vector>

void initialize()
{
    SelectParams(CBaseChainParams::REGTEST);
}

void test_one_input(const std::vector<uint8_t>& buffer)
{
    if (buffer.size() < sizeof(uint256) + sizeof(uint160)) {
        return;
    }
    FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
    const uint256 u256(fuzzed_data_provider.ConsumeBytes<unsigned char>(sizeof(uint256)));
    const uint160 u160(fuzzed_data_provider.ConsumeBytes<unsigned char>(sizeof(uint160)));
    const uint64_t u64 = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
    const int64_t i64 = fuzzed_data_provider.ConsumeIntegral<int64_t>();
    const uint32_t u32 = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
    const int32_t i32 = fuzzed_data_provider.ConsumeIntegral<int32_t>();
    const uint16_t u16 = fuzzed_data_provider.ConsumeIntegral<uint16_t>();
    const int16_t i16 = fuzzed_data_provider.ConsumeIntegral<int16_t>();
    const uint8_t u8 = fuzzed_data_provider.ConsumeIntegral<uint8_t>();
    const int8_t i8 = fuzzed_data_provider.ConsumeIntegral<int8_t>();
    // We cannot assume a specific value of std::is_signed<char>::value:
    // ConsumeIntegral<char>() instead of casting from {u,}int8_t.
    const char ch = fuzzed_data_provider.ConsumeIntegral<char>();
    const bool b = fuzzed_data_provider.ConsumeBool();

    const Consensus::Params& consensus_params = Params().GetConsensus();
    (void)CheckProofOfWork(u256, u32, consensus_params);
    if (u64 <= MAX_MONEY) {
        const uint64_t compressed_money_amount = CompressAmount(u64);
        assert(u64 == DecompressAmount(compressed_money_amount));
        static const uint64_t compressed_money_amount_max = CompressAmount(MAX_MONEY - 1);
        assert(compressed_money_amount <= compressed_money_amount_max);
    } else {
        (void)CompressAmount(u64);
    }
    static const uint256 u256_min(uint256S("0000000000000000000000000000000000000000000000000000000000000000"));
    static const uint256 u256_max(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
    const std::vector<uint256> v256{u256, u256_min, u256_max};
    (void)ComputeMerkleRoot(v256);
    (void)CountBits(u64);
    (void)DecompressAmount(u64);
    (void)FormatISO8601Date(i64);
    (void)FormatISO8601DateTime(i64);
    // FormatMoney(i) not defined when i == std::numeric_limits<int64_t>::min()
    if (i64 != std::numeric_limits<int64_t>::min()) {
        int64_t parsed_money;
        if (ParseMoney(FormatMoney(i64), parsed_money)) {
            assert(parsed_money == i64);
        }
    }
    (void)GetSizeOfCompactSize(u64);
    (void)GetSpecialScriptSize(u32);
    // (void)GetVirtualTransactionSize(i64, i64); // function defined only for a subset of int64_t inputs
    // (void)GetVirtualTransactionSize(i64, i64, u32); // function defined only for a subset of int64_t/uint32_t inputs
    (void)HexDigit(ch);
    (void)MoneyRange(i64);
    (void)i64tostr(i64);
    (void)IsDigit(ch);
    (void)IsSpace(ch);
    (void)IsSwitchChar(ch);
    (void)itostr(i32);
    (void)memusage::DynamicUsage(ch);
    (void)memusage::DynamicUsage(i16);
    (void)memusage::DynamicUsage(i32);
    (void)memusage::DynamicUsage(i64);
    (void)memusage::DynamicUsage(i8);
    (void)memusage::DynamicUsage(u16);
    (void)memusage::DynamicUsage(u32);
    (void)memusage::DynamicUsage(u64);
    (void)memusage::DynamicUsage(u8);
    const unsigned char uch = static_cast<unsigned char>(u8);
    (void)memusage::DynamicUsage(uch);
    (void)MillisToTimeval(i64);
    const double d = ser_uint64_to_double(u64);
    assert(ser_double_to_uint64(d) == u64);
    const float f = ser_uint32_to_float(u32);
    assert(ser_float_to_uint32(f) == u32);
    (void)SighashToStr(uch);
    (void)SipHashUint256(u64, u64, u256);
    (void)SipHashUint256Extra(u64, u64, u256, u32);
    (void)ToLower(ch);
    (void)ToUpper(ch);
    // ValueFromAmount(i) not defined when i == std::numeric_limits<int64_t>::min()
    if (i64 != std::numeric_limits<int64_t>::min()) {
        int64_t parsed_money;
        if (ParseMoney(ValueFromAmount(i64).getValStr(), parsed_money)) {
            assert(parsed_money == i64);
        }
    }

    const arith_uint256 au256 = UintToArith256(u256);
    assert(ArithToUint256(au256) == u256);
    assert(uint256S(au256.GetHex()) == u256);
    (void)au256.bits();
    (void)au256.GetCompact(/* fNegative= */ false);
    (void)au256.GetCompact(/* fNegative= */ true);
    (void)au256.getdouble();
    (void)au256.GetHex();
    (void)au256.GetLow64();
    (void)au256.size();
    (void)au256.ToString();

    const CKeyID key_id{u160};
    const CScriptID script_id{u160};
    // CTxDestination = CNoDestination ∪ PKHash ∪ ScriptHash ∪ WitnessV0ScriptHash ∪ WitnessV0KeyHash ∪ WitnessUnknown
    const PKHash pk_hash{u160};
    const ScriptHash script_hash{u160};
    const WitnessV0KeyHash witness_v0_key_hash{u160};
    const WitnessV0ScriptHash witness_v0_script_hash{u256};
    const std::vector<CTxDestination> destinations{pk_hash, script_hash, witness_v0_key_hash, witness_v0_script_hash};
    const SigningProvider store;
    for (const CTxDestination& destination : destinations) {
        (void)DescribeAddress(destination);
        (void)EncodeDestination(destination);
        (void)GetKeyForDestination(store, destination);
        (void)GetScriptForDestination(destination);
        (void)IsValidDestination(destination);
    }

    {
        CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION);

        uint256 deserialized_u256;
        stream << u256;
        stream >> deserialized_u256;
        assert(u256 == deserialized_u256 && stream.empty());

        uint160 deserialized_u160;
        stream << u160;
        stream >> deserialized_u160;
        assert(u160 == deserialized_u160 && stream.empty());

        uint64_t deserialized_u64;
        stream << u64;
        stream >> deserialized_u64;
        assert(u64 == deserialized_u64 && stream.empty());

        int64_t deserialized_i64;
        stream << i64;
        stream >> deserialized_i64;
        assert(i64 == deserialized_i64 && stream.empty());

        uint32_t deserialized_u32;
        stream << u32;
        stream >> deserialized_u32;
        assert(u32 == deserialized_u32 && stream.empty());

        int32_t deserialized_i32;
        stream << i32;
        stream >> deserialized_i32;
        assert(i32 == deserialized_i32 && stream.empty());

        uint16_t deserialized_u16;
        stream << u16;
        stream >> deserialized_u16;
        assert(u16 == deserialized_u16 && stream.empty());

        int16_t deserialized_i16;
        stream << i16;
        stream >> deserialized_i16;
        assert(i16 == deserialized_i16 && stream.empty());

        uint8_t deserialized_u8;
        stream << u8;
        stream >> deserialized_u8;
        assert(u8 == deserialized_u8 && stream.empty());

        int8_t deserialized_i8;
        stream << i8;
        stream >> deserialized_i8;
        assert(i8 == deserialized_i8 && stream.empty());

        char deserialized_ch;
        stream << ch;
        stream >> deserialized_ch;
        assert(ch == deserialized_ch && stream.empty());

        bool deserialized_b;
        stream << b;
        stream >> deserialized_b;
        assert(b == deserialized_b && stream.empty());
    }
}
<|endoftext|>


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

#include "consensus/merkle.h"
#include "test/test_bitcoin.h"
#include "test/test_random.h"

#include <boost/test/unit_test.hpp>

BOOST_FIXTURE_TEST_SUITE(merkle_tests, TestingSetup)

// Older version of the merkle root computation code, for comparison.
static uint256 BlockBuildMerkleTree(const CBlock &block, bool *fMutated, std::vector<uint256> &vMerkleTree)
{
    vMerkleTree.clear();
    vMerkleTree.reserve(block.vtx.size() * 2 + 16); // Safe upper bound for the number of total nodes.
    for (std::vector<CTransactionRef>::const_iterator it(block.vtx.begin()); it != block.vtx.end(); ++it)
        vMerkleTree.push_back((*it)->GetHash());
    int j = 0;
    bool mutated = false;
    for (int nSize = block.vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
    {
        for (int i = 0; i < nSize; i += 2)
        {
            int i2 = std::min(i + 1, nSize - 1);
            if (i2 == i + 1 && i2 + 1 == nSize && vMerkleTree[j + i] == vMerkleTree[j + i2])
            {
                // Two identical hashes at the end of the list at a particular level.
                mutated = true;
            }
            vMerkleTree.push_back(Hash(vMerkleTree[j + i].begin(), vMerkleTree[j + i].end(),
                vMerkleTree[j + i2].begin(), vMerkleTree[j + i2].end()));
        }
        j += nSize;
    }
    if (fMutated)
    {
        *fMutated = mutated;
    }
    return (vMerkleTree.empty() ? uint256() : vMerkleTree.back());
}

// Older version of the merkle branch computation code, for comparison.
static std::vector<uint256> BlockGetMerkleBranch(const CBlock &block,
    const std::vector<uint256> &vMerkleTree,
    int nIndex)
{
    std::vector<uint256> vMerkleBranch;
    int j = 0;
    for (int nSize = block.vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
    {
        int i = std::min(nIndex ^ 1, nSize - 1);
        vMerkleBranch.push_back(vMerkleTree[j + i]);
        nIndex >>= 1;
        j += nSize;
    }
    return vMerkleBranch;
}

static inline int ctz(uint32_t i)
{
    if (i == 0)
        return 0;
    int j = 0;
    while (!(i & 1))
    {
        j++;
        i >>= 1;
    }
    return j;
}

static uint256 ComputeRootFromCoinbaseProof(const uint256 &elem, const std::vector<uint256> &leaves)
{
    uint256 running = elem;
    for (auto i : leaves)
    {
        uint256 result;
        CHash256().Write(running.begin(), 32).Write(i.begin(), 32).Finalize(result.begin());
        running = result;
    }
    return running;
}

BOOST_AUTO_TEST_CASE(merkle_test)
{
    // Test the merkle proof code exhaustively for element 0 (coinbase), since this is used when communicating
    // mining block headers to mining pools.
    unsigned int MAX_LEAVES = 100000;
    unsigned int MAX_TRIES = 20;

    std::vector<uint256> leafData;
    leafData.reserve(MAX_LEAVES);

    for (unsigned int i = 0; i < MAX_LEAVES; i++)
    {
        // add a random leaf
        uint256 temp;
        for (uint j = 0; j < 32; j++)
            *(temp.begin() + j) = insecure_rand() & 255;
        leafData.push_back(temp);
    }

    for (unsigned int i = 0; i < MAX_TRIES; i++)
    {
        uint size = insecure_rand() % MAX_LEAVES;
        std::vector<uint256> leaves = leafData;
        leaves.resize(size);

        // printf("leaves: %u\n", (unsigned int) leaves.size());
        uint256 root = ComputeMerkleRoot(leaves);
        std::vector<uint256> mklProof = ComputeMerkleBranch(leaves, 0);

        // Test 2 ways of doing it
        uint256 r1 = ComputeMerkleRootFromBranch(leaves[0], mklProof, 0);
        uint256 r2 = ComputeRootFromCoinbaseProof(leaves[0], mklProof);

        BOOST_CHECK(root == r1);
        BOOST_CHECK(root == r2);
    }

    for (int i = 0; i < 32; i++)
    {
        // Try 32 block sizes: all sizes from 0 to 16 inclusive, and then 15 random sizes.
        int ntx = (i <= 16) ? i : 17 + (insecure_rand() % 4000);
        // Try up to 3 mutations.
        for (int mutate = 0; mutate <= 3; mutate++)
        {
            int duplicate1 = mutate >= 1 ? 1 << ctz(ntx) : 0; // The last how many transactions to duplicate first.
            // Duplication of the entire tree results in a different root (it adds a level).
            if (duplicate1 >= ntx)
                break;
            int ntx1 = ntx + duplicate1; // The resulting number of transactions after the first duplication.
            int duplicate2 = mutate >= 2 ? 1 << ctz(ntx1) : 0; // Likewise for the second mutation.
            if (duplicate2 >= ntx1)
                break;
            int ntx2 = ntx1 + duplicate2;
            int duplicate3 = mutate >= 3 ? 1 << ctz(ntx2) : 0; // And for the third mutation.
            if (duplicate3 >= ntx2)
                break;
            int ntx3 = ntx2 + duplicate3;
            // Build a block with ntx different transactions.
            CBlock block;
            block.vtx.resize(ntx);
            for (int j = 0; j < ntx; j++)
            {
                CMutableTransaction mtx;
                mtx.nLockTime = j;
                block.vtx[j] = MakeTransactionRef(std::move(mtx));
            }
            // Compute the root of the block before mutating it.
            bool unmutatedMutated = false;
            uint256 unmutatedRoot = BlockMerkleRoot(block, &unmutatedMutated);
            BOOST_CHECK(unmutatedMutated == false);
            // Optionally mutate by duplicating the last transactions, resulting in the same merkle root.
            block.vtx.resize(ntx3);
            for (int j = 0; j < duplicate1; j++)
            {
                block.vtx[ntx + j] = block.vtx[ntx + j - duplicate1];
            }
            for (int j = 0; j < duplicate2; j++)
            {
                block.vtx[ntx1 + j] = block.vtx[ntx1 + j - duplicate2];
            }
            for (int j = 0; j < duplicate3; j++)
            {
                block.vtx[ntx2 + j] = block.vtx[ntx2 + j - duplicate3];
            }
            // Compute the merkle root and merkle tree using the old mechanism.
            bool oldMutated = false;
            std::vector<uint256> merkleTree;
            uint256 oldRoot = BlockBuildMerkleTree(block, &oldMutated, merkleTree);
            // Compute the merkle root using the new mechanism.
            bool newMutated = false;
            uint256 newRoot = BlockMerkleRoot(block, &newMutated);
            BOOST_CHECK(oldRoot == newRoot);
            BOOST_CHECK(newRoot == unmutatedRoot);
            BOOST_CHECK((newRoot == uint256()) == (ntx == 0));
            BOOST_CHECK(oldMutated == newMutated);
            BOOST_CHECK(newMutated == !!mutate);
            // If no mutation was done (once for every ntx value), try up to 16 branches.
            if (mutate == 0)
            {
                for (int loop = 0; loop < std::min(ntx, 16); loop++)
                {
                    // If ntx <= 16, try all branches. Otherise, try 16 random ones.
                    int mtx = loop;
                    if (ntx > 16)
                    {
                        mtx = insecure_rand() % ntx;
                    }
                    std::vector<uint256> newBranch = BlockMerkleBranch(block, mtx);
                    std::vector<uint256> oldBranch = BlockGetMerkleBranch(block, merkleTree, mtx);
                    BOOST_CHECK(oldBranch == newBranch);
                    BOOST_CHECK(ComputeMerkleRootFromBranch(block.vtx[mtx]->GetHash(), newBranch, mtx) == oldRoot);
                }
            }
        }
    }
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Fixed Windows build<commit_after>// Copyright (c) 2015 The Bitcoin Core developers
// Copyright (c) 2015-2017 The Bitcoin Unlimited developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "consensus/merkle.h"
#include "test/test_bitcoin.h"
#include "test/test_random.h"

#include <boost/test/unit_test.hpp>

BOOST_FIXTURE_TEST_SUITE(merkle_tests, TestingSetup)

// Older version of the merkle root computation code, for comparison.
static uint256 BlockBuildMerkleTree(const CBlock &block, bool *fMutated, std::vector<uint256> &vMerkleTree)
{
    vMerkleTree.clear();
    vMerkleTree.reserve(block.vtx.size() * 2 + 16); // Safe upper bound for the number of total nodes.
    for (std::vector<CTransactionRef>::const_iterator it(block.vtx.begin()); it != block.vtx.end(); ++it)
        vMerkleTree.push_back((*it)->GetHash());
    int j = 0;
    bool mutated = false;
    for (int nSize = block.vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
    {
        for (int i = 0; i < nSize; i += 2)
        {
            int i2 = std::min(i + 1, nSize - 1);
            if (i2 == i + 1 && i2 + 1 == nSize && vMerkleTree[j + i] == vMerkleTree[j + i2])
            {
                // Two identical hashes at the end of the list at a particular level.
                mutated = true;
            }
            vMerkleTree.push_back(Hash(vMerkleTree[j + i].begin(), vMerkleTree[j + i].end(),
                vMerkleTree[j + i2].begin(), vMerkleTree[j + i2].end()));
        }
        j += nSize;
    }
    if (fMutated)
    {
        *fMutated = mutated;
    }
    return (vMerkleTree.empty() ? uint256() : vMerkleTree.back());
}

// Older version of the merkle branch computation code, for comparison.
static std::vector<uint256> BlockGetMerkleBranch(const CBlock &block,
    const std::vector<uint256> &vMerkleTree,
    int nIndex)
{
    std::vector<uint256> vMerkleBranch;
    int j = 0;
    for (int nSize = block.vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
    {
        int i = std::min(nIndex ^ 1, nSize - 1);
        vMerkleBranch.push_back(vMerkleTree[j + i]);
        nIndex >>= 1;
        j += nSize;
    }
    return vMerkleBranch;
}

static inline int ctz(uint32_t i)
{
    if (i == 0)
        return 0;
    int j = 0;
    while (!(i & 1))
    {
        j++;
        i >>= 1;
    }
    return j;
}

static uint256 ComputeRootFromCoinbaseProof(const uint256 &elem, const std::vector<uint256> &leaves)
{
    uint256 running = elem;
    for (auto i : leaves)
    {
        uint256 result;
        CHash256().Write(running.begin(), 32).Write(i.begin(), 32).Finalize(result.begin());
        running = result;
    }
    return running;
}

BOOST_AUTO_TEST_CASE(merkle_test)
{
    // Test the merkle proof code exhaustively for element 0 (coinbase), since this is used when communicating
    // mining block headers to mining pools.
    unsigned int MAX_LEAVES = 100000;
    unsigned int MAX_TRIES = 20;

    std::vector<uint256> leafData;
    leafData.reserve(MAX_LEAVES);

    for (unsigned int i = 0; i < MAX_LEAVES; i++)
    {
        // add a random leaf
        uint256 temp;
        for (uint32_t j = 0; j < 32; j++)
            *(temp.begin() + j) = insecure_rand() & 255;
        leafData.push_back(temp);
    }

    for (unsigned int i = 0; i < MAX_TRIES; i++)
    {
        uint32_t size = insecure_rand() % MAX_LEAVES;
        std::vector<uint256> leaves = leafData;
        leaves.resize(size);

        // printf("leaves: %u\n", (unsigned int) leaves.size());
        uint256 root = ComputeMerkleRoot(leaves);
        std::vector<uint256> mklProof = ComputeMerkleBranch(leaves, 0);

        // Test 2 ways of doing it
        uint256 r1 = ComputeMerkleRootFromBranch(leaves[0], mklProof, 0);
        uint256 r2 = ComputeRootFromCoinbaseProof(leaves[0], mklProof);

        BOOST_CHECK(root == r1);
        BOOST_CHECK(root == r2);
    }

    for (int i = 0; i < 32; i++)
    {
        // Try 32 block sizes: all sizes from 0 to 16 inclusive, and then 15 random sizes.
        int ntx = (i <= 16) ? i : 17 + (insecure_rand() % 4000);
        // Try up to 3 mutations.
        for (int mutate = 0; mutate <= 3; mutate++)
        {
            int duplicate1 = mutate >= 1 ? 1 << ctz(ntx) : 0; // The last how many transactions to duplicate first.
            // Duplication of the entire tree results in a different root (it adds a level).
            if (duplicate1 >= ntx)
                break;
            int ntx1 = ntx + duplicate1; // The resulting number of transactions after the first duplication.
            int duplicate2 = mutate >= 2 ? 1 << ctz(ntx1) : 0; // Likewise for the second mutation.
            if (duplicate2 >= ntx1)
                break;
            int ntx2 = ntx1 + duplicate2;
            int duplicate3 = mutate >= 3 ? 1 << ctz(ntx2) : 0; // And for the third mutation.
            if (duplicate3 >= ntx2)
                break;
            int ntx3 = ntx2 + duplicate3;
            // Build a block with ntx different transactions.
            CBlock block;
            block.vtx.resize(ntx);
            for (int j = 0; j < ntx; j++)
            {
                CMutableTransaction mtx;
                mtx.nLockTime = j;
                block.vtx[j] = MakeTransactionRef(std::move(mtx));
            }
            // Compute the root of the block before mutating it.
            bool unmutatedMutated = false;
            uint256 unmutatedRoot = BlockMerkleRoot(block, &unmutatedMutated);
            BOOST_CHECK(unmutatedMutated == false);
            // Optionally mutate by duplicating the last transactions, resulting in the same merkle root.
            block.vtx.resize(ntx3);
            for (int j = 0; j < duplicate1; j++)
            {
                block.vtx[ntx + j] = block.vtx[ntx + j - duplicate1];
            }
            for (int j = 0; j < duplicate2; j++)
            {
                block.vtx[ntx1 + j] = block.vtx[ntx1 + j - duplicate2];
            }
            for (int j = 0; j < duplicate3; j++)
            {
                block.vtx[ntx2 + j] = block.vtx[ntx2 + j - duplicate3];
            }
            // Compute the merkle root and merkle tree using the old mechanism.
            bool oldMutated = false;
            std::vector<uint256> merkleTree;
            uint256 oldRoot = BlockBuildMerkleTree(block, &oldMutated, merkleTree);
            // Compute the merkle root using the new mechanism.
            bool newMutated = false;
            uint256 newRoot = BlockMerkleRoot(block, &newMutated);
            BOOST_CHECK(oldRoot == newRoot);
            BOOST_CHECK(newRoot == unmutatedRoot);
            BOOST_CHECK((newRoot == uint256()) == (ntx == 0));
            BOOST_CHECK(oldMutated == newMutated);
            BOOST_CHECK(newMutated == !!mutate);
            // If no mutation was done (once for every ntx value), try up to 16 branches.
            if (mutate == 0)
            {
                for (int loop = 0; loop < std::min(ntx, 16); loop++)
                {
                    // If ntx <= 16, try all branches. Otherise, try 16 random ones.
                    int mtx = loop;
                    if (ntx > 16)
                    {
                        mtx = insecure_rand() % ntx;
                    }
                    std::vector<uint256> newBranch = BlockMerkleBranch(block, mtx);
                    std::vector<uint256> oldBranch = BlockGetMerkleBranch(block, merkleTree, mtx);
                    BOOST_CHECK(oldBranch == newBranch);
                    BOOST_CHECK(ComputeMerkleRootFromBranch(block.vtx[mtx]->GetHash(), newBranch, mtx) == oldRoot);
                }
            }
        }
    }
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>/************************************************************************/
/*                                                                      */
/*               Copyright 1998-2000 by Ullrich Koethe                  */
/*       Cognitive Systems Group, University of Hamburg, Germany        */
/*                                                                      */
/*    This file is part of the VIGRA computer vision library.           */
/*    ( Version 1.1.1, Feb 12 2001 )                                    */
/*    You may use, modify, and distribute this software according       */
/*    to the terms stated in the LICENSE file included in               */
/*    the VIGRA distribution.                                           */
/*                                                                      */
/*    The VIGRA Website is                                              */
/*        http://kogs-www.informatik.uni-hamburg.de/~koethe/vigra/      */
/*    Please direct questions, bug reports, and contributions to        */
/*        koethe@informatik.uni-hamburg.de                              */
/*                                                                      */
/*  THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR          */
/*  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED      */
/*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */
/*                                                                      */
/************************************************************************/

#ifndef VIGRA_ERROR_HXX
#define VIGRA_ERROR_HXX

#include <stdio.h>
#include "vigra/config.hxx"

#ifdef _MSC_VER
#  define snprintf _snprintf
#endif

/** @heading Error Reporting

    VIGRA defines the following exception classes:

    \begin{verbatim}
    namespace vigra {
        class ContractViolation : public std::exception;
        class PreconditionViolation : public ContractViolation;
        class PostconditionViolation : public ContractViolation;
        class InvariantViolation : public ContractViolation;
    }
    \end{verbatim}

    The following associated macros throw the corresponding exception if
    their PREDICATE evaluates to '#false#':

    \begin{verbatim}
    vigra_precondition(PREDICATE, MESSAGE);
    vigra_postcondition(PREDICATE, MESSAGE);
    vigra_invariant(PREDICATE, MESSAGE);
    \end{verbatim}

    The MESSAGE is passed to the exception and can be retrieved via
    the overloaded member function '#exception.what()#'. If the compiler
    flag '#NDEBUG#' is {\em not} defined, the file name and line number of
    the error are automatically included in the message.

    The following macro

    \begin{verbatim}
    vigra_fail(MESSAGE);
    \end{verbatim}

    unconditionally throws a '#std::runtime_error#' constructed from the message
    (along with file name and line number, if NDEBUG is not set).

    {\bf Usage:}

    Include-File:
    \URL[vigra/error.hxx]{../include/vigra/error.hxx}\\
    Namespace: vigra (except for the macros, of course)

    \begin{verbatim}
    int main(int argc, char ** argv)
    {
        try
        {
            const char* input_file_name = argv[1];

            // read input image
            vigra::ImageImportInfo info(input_file_name);

            // fail if input image is not grayscale
            vigra_precondition(info.isGrayscale(), "Input image must be grayscale");

            ...// process image
        }
        catch (std::exception & e)
        {
            std::cerr << e.what() << std::endl; // print message
            return 1;
        }

        return 0;
    }
    \end{verbatim}

    @memo Exceptions and assertions provided by VIGRA
**/

namespace vigra {

class ContractViolation : public StdException
{
  protected:
#ifdef NO_INLINE_STATIC_CONST_DEFINITION
    enum { bufsize_ = 1100 };
#else
    static const int bufsize_ = 1100;
#endif

  public:
    ContractViolation(char const * message)
    {
        snprintf(what_, bufsize_, "%s", message);
    }

    ContractViolation()
    {
        what_[0] = 0;
    }

    ContractViolation(ContractViolation const & o)
    {
        snprintf(what_, bufsize_, "%s", o.what_);
    }

    virtual const char * what() const throw()
    {
        return what_;
    }

    char what_[bufsize_];
};

class PreconditionViolation : public ContractViolation
{
  public:
    PreconditionViolation(char const * message, const char * file, int line)
    {
        snprintf( what_, bufsize_, "\nPrecondition Violation!\n%s\n(%s:%d)\n", message, file, line);
    }

    PreconditionViolation(char const * message)
    {
        snprintf( what_, bufsize_, "\nPrecondition Violation!\n%s\n", message);
    }
};

class PostconditionViolation : public ContractViolation
{
  public:
    PostconditionViolation(char const * message, const char * file, int line)
    {
        snprintf( what_, bufsize_, "\nPostcondition Violation!\n%s\n(%s:%d)\n", message, file, line);
    }

    PostconditionViolation(char const * message)
    {
        snprintf( what_, bufsize_, "\nPostcondition Violation!\n%s\n", message);
    }
};

class InvariantViolation : public ContractViolation
{
  public:
    InvariantViolation(char const * message, const char * file, int line)
    {
        snprintf( what_, bufsize_, "\nInvariant Violation!\n%s\n(%s:%d)\n", message, file, line);
    }

    InvariantViolation(char const * message)
    {
        snprintf( what_, bufsize_, "\nInvariant Violation!\n%s\n", message);
    }
};

#ifndef NDEBUG

inline
void throw_invariant_error(bool predicate, char const * message, char const * file, int line)
{
    if(!predicate)
	   throw vigra::InvariantViolation(message, file, line);
}

inline
void throw_precondition_error(bool predicate, char const * message, char const * file, int line)
{
    if(!predicate)
	   throw vigra::PreconditionViolation(message, file, line);
}

inline
void throw_postcondition_error(bool predicate, char const * message, char const * file, int line)
{
    if(!predicate)
	   throw vigra::PostconditionViolation(message, file, line);
}

inline
void throw_runtime_error(char const * message, char const * file, int line)
{
    char buf[1000];
    snprintf(buf, 1000, "%s (%s:%d)", message, file, line);
    throw std::runtime_error(buf);
}

#define vigra_precondition(PREDICATE, MESSAGE) vigra::throw_precondition_error((PREDICATE), MESSAGE, __FILE__, __LINE__)

#define vigra_postcondition(PREDICATE, MESSAGE) vigra::throw_postcondition_error((PREDICATE), MESSAGE, __FILE__, __LINE__)

#define vigra_invariant(PREDICATE, MESSAGE) vigra::throw_invariant_error((PREDICATE), MESSAGE, __FILE__, __LINE__)

#define vigra_fail(MESSAGE) vigra::throw_runtime_error(MESSAGE, __FILE__, __LINE__)

#else // NDEBUG

inline
void throw_invariant_error(bool predicate, char const * message)
{
    if(!predicate)
	   throw vigra::InvariantViolation(message);
}

inline
void throw_precondition_error(bool predicate, char const * message)
{
    if(!predicate)
	   throw vigra::PreconditionViolation(message);
}

inline
void throw_postcondition_error(bool predicate, char const * message)
{
    if(!predicate)
	   throw vigra::PostconditionViolation(message);
}

#define vigra_precondition(PREDICATE, MESSAGE) vigra::throw_precondition_error((PREDICATE), MESSAGE)

#define vigra_postcondition(PREDICATE, MESSAGE) vigra::throw_postcondition_error((PREDICATE), MESSAGE)

#define vigra_invariant(PREDICATE, MESSAGE) vigra::throw_invariant_error((PREDICATE), MESSAGE)

#define vigra_fail(MESSAGE) throw std::runtime_error(MESSAGE)

#endif // NDEBUG

} // namespace vigra

#endif // VIGRA_ERROR_HXX
<commit_msg>added missing ()<commit_after>/************************************************************************/
/*                                                                      */
/*               Copyright 1998-2000 by Ullrich Koethe                  */
/*       Cognitive Systems Group, University of Hamburg, Germany        */
/*                                                                      */
/*    This file is part of the VIGRA computer vision library.           */
/*    ( Version 1.1.1, Feb 12 2001 )                                    */
/*    You may use, modify, and distribute this software according       */
/*    to the terms stated in the LICENSE file included in               */
/*    the VIGRA distribution.                                           */
/*                                                                      */
/*    The VIGRA Website is                                              */
/*        http://kogs-www.informatik.uni-hamburg.de/~koethe/vigra/      */
/*    Please direct questions, bug reports, and contributions to        */
/*        koethe@informatik.uni-hamburg.de                              */
/*                                                                      */
/*  THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR          */
/*  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED      */
/*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */
/*                                                                      */
/************************************************************************/

#ifndef VIGRA_ERROR_HXX
#define VIGRA_ERROR_HXX

#include <stdio.h>
#include "vigra/config.hxx"

#ifdef _MSC_VER
#  define snprintf _snprintf
#endif

/** @heading Error Reporting

    VIGRA defines the following exception classes:

    \begin{verbatim}
    namespace vigra {
        class ContractViolation : public std::exception;
        class PreconditionViolation : public ContractViolation;
        class PostconditionViolation : public ContractViolation;
        class InvariantViolation : public ContractViolation;
    }
    \end{verbatim}

    The following associated macros throw the corresponding exception if
    their PREDICATE evaluates to '#false#':

    \begin{verbatim}
    vigra_precondition(PREDICATE, MESSAGE);
    vigra_postcondition(PREDICATE, MESSAGE);
    vigra_invariant(PREDICATE, MESSAGE);
    \end{verbatim}

    The MESSAGE is passed to the exception and can be retrieved via
    the overloaded member function '#exception.what()#'. If the compiler
    flag '#NDEBUG#' is {\em not} defined, the file name and line number of
    the error are automatically included in the message.

    The following macro

    \begin{verbatim}
    vigra_fail(MESSAGE);
    \end{verbatim}

    unconditionally throws a '#std::runtime_error#' constructed from the message
    (along with file name and line number, if NDEBUG is not set).

    {\bf Usage:}

    Include-File:
    \URL[vigra/error.hxx]{../include/vigra/error.hxx}\\
    Namespace: vigra (except for the macros, of course)

    \begin{verbatim}
    int main(int argc, char ** argv)
    {
        try
        {
            const char* input_file_name = argv[1];

            // read input image
            vigra::ImageImportInfo info(input_file_name);

            // fail if input image is not grayscale
            vigra_precondition(info.isGrayscale(), "Input image must be grayscale");

            ...// process image
        }
        catch (std::exception & e)
        {
            std::cerr << e.what() << std::endl; // print message
            return 1;
        }

        return 0;
    }
    \end{verbatim}

    @memo Exceptions and assertions provided by VIGRA
**/

namespace vigra {

class ContractViolation : public StdException
{
  protected:
#ifdef NO_INLINE_STATIC_CONST_DEFINITION
    enum { bufsize_ = 1100 };
#else
    static const int bufsize_ = 1100;
#endif

  public:
    ContractViolation(char const * message)
    {
        snprintf(what_, bufsize_, "%s", message);
    }

    ContractViolation()
    {
        what_[0] = 0;
    }

    ContractViolation(ContractViolation const & o)
    {
        snprintf(what_, bufsize_, "%s", o.what_);
    }

    virtual const char * what() const throw()
    {
        return what_;
    }

    char what_[bufsize_];
};

class PreconditionViolation : public ContractViolation
{
  public:
    PreconditionViolation(char const * message, const char * file, int line)
    {
        snprintf( what_, bufsize_, "\nPrecondition violation!\n%s\n(%s:%d)\n", message, file, line);
    }

    PreconditionViolation(char const * message)
    {
        snprintf( what_, bufsize_, "\nPrecondition violation!\n%s\n", message);
    }
};

class PostconditionViolation : public ContractViolation
{
  public:
    PostconditionViolation(char const * message, const char * file, int line)
    {
        snprintf( what_, bufsize_, "\nPostcondition violation!\n%s\n(%s:%d)\n", message, file, line);
    }

    PostconditionViolation(char const * message)
    {
        snprintf( what_, bufsize_, "\nPostcondition violation!\n%s\n", message);
    }
};

class InvariantViolation : public ContractViolation
{
  public:
    InvariantViolation(char const * message, const char * file, int line)
    {
        snprintf( what_, bufsize_, "\nInvariant violation!\n%s\n(%s:%d)\n", message, file, line);
    }

    InvariantViolation(char const * message)
    {
        snprintf( what_, bufsize_, "\nInvariant violation!\n%s\n", message);
    }
};

#ifndef NDEBUG

inline
void throw_invariant_error(bool predicate, char const * message, char const * file, int line)
{
    if(!predicate)
	   throw vigra::InvariantViolation(message, file, line);
}

inline
void throw_precondition_error(bool predicate, char const * message, char const * file, int line)
{
    if(!predicate)
	   throw vigra::PreconditionViolation(message, file, line);
}

inline
void throw_postcondition_error(bool predicate, char const * message, char const * file, int line)
{
    if(!predicate)
	   throw vigra::PostconditionViolation(message, file, line);
}

inline
void throw_runtime_error(char const * message, char const * file, int line)
{
    char buf[1000];
    snprintf(buf, 1000, "%s (%s:%d)", message, file, line);
    throw std::runtime_error(buf);
}

#define vigra_precondition(PREDICATE, MESSAGE) vigra::throw_precondition_error((PREDICATE), MESSAGE, __FILE__, __LINE__)

#define vigra_postcondition(PREDICATE, MESSAGE) vigra::throw_postcondition_error((PREDICATE), MESSAGE, __FILE__, __LINE__)

#define vigra_invariant(PREDICATE, MESSAGE) vigra::throw_invariant_error((PREDICATE), MESSAGE, __FILE__, __LINE__)

#define vigra_fail(MESSAGE) vigra::throw_runtime_error(MESSAGE, __FILE__, __LINE__)

#else // NDEBUG

inline
void throw_invariant_error(bool predicate, char const * message)
{
    if(!predicate)
	   throw vigra::InvariantViolation(message);
}

inline
void throw_precondition_error(bool predicate, char const * message)
{
    if(!predicate)
	   throw vigra::PreconditionViolation(message);
}

inline
void throw_postcondition_error(bool predicate, char const * message)
{
    if(!predicate)
	   throw vigra::PostconditionViolation(message);
}

#define vigra_precondition(PREDICATE, MESSAGE) vigra::throw_precondition_error((PREDICATE), MESSAGE)

#define vigra_postcondition(PREDICATE, MESSAGE) vigra::throw_postcondition_error((PREDICATE), MESSAGE)

#define vigra_invariant(PREDICATE, MESSAGE) vigra::throw_invariant_error((PREDICATE), MESSAGE)

#define vigra_fail(MESSAGE) throw std::runtime_error(MESSAGE)

#endif // NDEBUG

} // namespace vigra

#endif // VIGRA_ERROR_HXX
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include <swtypes.hxx>
#include <wordcountdialog.hxx>
#include <docstat.hxx>
#include <dialog.hrc>
#include <wordcountdialog.hrc>
#include <cmdid.h>
#include "vcl/msgbox.hxx" // RET_CANCEL
#include <swmodule.hxx>
#include <wview.hxx>
#include <sfx2/viewfrm.hxx>
#include <swwait.hxx>
#include <wrtsh.hxx>

//TODO, add asian/non-asian word count to UI when CJK mode is enabled.
SwWordCountDialog::SwWordCountDialog(Dialog* pParent)
    : vbox(pParent)
    , content_area(&vbox)
    , aCurrentSelection(&content_area, false, 3)
    , aCurrentSelectionText(&aCurrentSelection, SW_RES(FT_CURRENT))
    , aCurrentSelectionLine(&aCurrentSelection, SW_RES(FL_CURRENT))
    , aSelectionBox(&content_area, false, 7)
    , aSelectionRow1(&aSelectionBox)
    , aCurrentWordFT(&aSelectionRow1, SW_RES(FT_CURRENTWORD))
    , aCurrentWordFI(&aSelectionRow1, SW_RES(FI_CURRENTWORD))
    , aSelectionRow2(&aSelectionBox)
    , aCurrentCharacterFT(&aSelectionRow2, SW_RES(FT_CURRENTCHARACTER))
    , aCurrentCharacterFI(&aSelectionRow2, SW_RES(FI_CURRENTCHARACTER))
    , aSelectionRow3(&aSelectionBox)
    , aCurrentCharacterExcludingSpacesFT(&aSelectionRow3, SW_RES(FT_CURRENTCHARACTEREXCLUDINGSPACES))
    , aCurrentCharacterExcludingSpacesFI(&aSelectionRow3, SW_RES(FI_CURRENTCHARACTEREXCLUDINGSPACES))
    , aDoc(&content_area, false, 3)
    , aDocText(&aDoc, SW_RES(FT_DOC))
    , aDocLine(&aDoc, SW_RES(FL_DOC))
    , aDocBox(&content_area, false, 7)
    , aDocRow1(&aDocBox)
    , aDocWordFT(&aDocRow1, SW_RES(FT_DOCWORD))
    , aDocWordFI(&aDocRow1, SW_RES(FI_DOCWORD))
    , aDocRow2(&aDocBox)
    , aDocCharacterFT(&aDocRow2, SW_RES(FT_DOCCHARACTER))
    , aDocCharacterFI(&aDocRow2, SW_RES(FI_DOCCHARACTER))
    , aDocRow3(&aDocBox)
    , aDocCharacterExcludingSpacesFT(&aDocRow3, SW_RES(FT_DOCCHARACTEREXCLUDINGSPACES))
    , aDocCharacterExcludingSpacesFI(&aDocRow3, SW_RES(FI_DOCCHARACTEREXCLUDINGSPACES))
    , aBottomFL(&vbox, SW_RES(FL_BOTTOM))
    , action_area(&vbox)
    , aOK(&action_area, SW_RES(PB_OK))
    , aHelp(&action_area, SW_RES(PB_HELP))
{
    rtl::OUString sForceInitialSize(RTL_CONSTASCII_USTRINGPARAM("00000000"));
    aCurrentWordFI.SetText(sForceInitialSize);
    aCurrentCharacterFI.SetText(sForceInitialSize);
    aCurrentCharacterExcludingSpacesFI.SetText(sForceInitialSize);
    aDocWordFI.SetText(sForceInitialSize);
    aDocCharacterFI.SetText(sForceInitialSize);
    aDocCharacterExcludingSpacesFI.SetText(sForceInitialSize);

    rtl::OString sFill(RTL_CONSTASCII_STRINGPARAM("fill"));
    rtl::OString sExpand(RTL_CONSTASCII_STRINGPARAM("expand"));
    rtl::OString sPackType(RTL_CONSTASCII_STRINGPARAM("pack-type"));
    rtl::OString sBorderWidth(RTL_CONSTASCII_STRINGPARAM("border-width"));

    vbox.setChildProperty(sFill, true);
    //TO-DO, when vcontent_area belongs to dialog via builder, this becomes
    //content-area-border on the dialog
    vbox.setChildProperty(sBorderWidth, sal_Int32(7));

    action_area.setChildProperty(sFill, true);

    content_area.setChildProperty(sFill, true);
    content_area.setChildProperty(sExpand, true);

    aCurrentSelection.setChildProperty(sFill, true);
    aCurrentSelectionText.setChildProperty(sFill, true);
    aCurrentSelectionLine.setChildProperty(sFill, true);
    aCurrentSelectionLine.setChildProperty(sExpand, true);

    aSelectionBox.setChildProperty(sFill, true);
    aSelectionBox.setChildProperty(sBorderWidth, sal_Int32(7));

    aSelectionRow1.setChildProperty(sFill, true);
    aSelectionRow1.setChildProperty(sExpand, true);
    aCurrentWordFT.setChildProperty(sFill, true);
    aCurrentWordFI.setChildProperty(sFill, true);
    aCurrentWordFI.setChildProperty(sExpand, true);

    aSelectionRow2.setChildProperty(sFill, true);
    aSelectionRow2.setChildProperty(sExpand, true);
    aCurrentCharacterFT.setChildProperty(sFill, true);
    aCurrentCharacterFI.setChildProperty(sFill, true);
    aCurrentCharacterFI.setChildProperty(sExpand, true);
    aSelectionRow3.setChildProperty(sFill, true);
    aSelectionRow3.setChildProperty(sExpand, true);
    aCurrentCharacterExcludingSpacesFT.setChildProperty(sFill, true);
    aCurrentCharacterExcludingSpacesFI.setChildProperty(sFill, true);
    aCurrentCharacterExcludingSpacesFI.setChildProperty(sExpand, true);

    aDoc.setChildProperty(sFill, true);
    aDocText.setChildProperty(sFill, true);
    aDocLine.setChildProperty(sFill, true);
    aDocLine.setChildProperty(sExpand, true);

    aDocBox.setChildProperty(sFill, true);
    aDocBox.setChildProperty(sBorderWidth, sal_Int32(7));

    aDocRow1.setChildProperty(sFill, true);
    aDocRow1.setChildProperty(sExpand, true);
    aDocWordFT.setChildProperty(sFill, true);
    aDocWordFI.setChildProperty(sFill, true);
    aDocWordFI.setChildProperty(sExpand, true);

    aDocRow2.setChildProperty(sFill, true);
    aDocRow2.setChildProperty(sExpand, true);
    aDocCharacterFT.setChildProperty(sFill, true);
    aDocCharacterFI.setChildProperty(sFill, true);
    aDocCharacterFI.setChildProperty(sExpand, true);
    aDocRow3.setChildProperty(sFill, true);
    aDocRow3.setChildProperty(sExpand, true);
    aDocCharacterExcludingSpacesFT.setChildProperty(sFill, true);
    aDocCharacterExcludingSpacesFI.setChildProperty(sFill, true);
    aDocCharacterExcludingSpacesFI.setChildProperty(sExpand, true);

    aBottomFL.setChildProperty(sFill, true);
    aBottomFL.setChildProperty(sFill, true);

    aOK.setChildProperty<sal_Int32>(sPackType, VCL_PACK_END);
    aHelp.setChildProperty<sal_Int32>(sPackType, VCL_PACK_END);

    aOK.SetClickHdl(LINK(this, SwWordCountDialog, OkHdl));

    fprintf(stderr, "aOk is %p\n", &aOK);
    fprintf(stderr, "aHelp is %p\n", &aHelp);
    fprintf(stderr, "action_area is is %p\n", &action_area);
    fprintf(stderr, "aCurrentSelectionLine is is %p\n", &aCurrentSelectionLine);
    fprintf(stderr, "aCurrentSelectionText is is %p\n", &aCurrentSelectionText);
    fprintf(stderr, "aCurrentSelection is is %p\n", &aCurrentSelection);
    fprintf(stderr, "vbox is is %p\n", &vbox);

    pParent->SetMinOutputSizePixel(vbox.GetOptimalSize(WINDOWSIZE_PREFERRED));

#if OSL_DEBUG_LEVEL > 2
    aDocCharacterExcludingSpacesFT.SetControlBackground(Color(180,0,0));
    aDocCharacterExcludingSpacesFI.SetControlBackground(Color(0,180,0));
#endif
}

IMPL_LINK_NOARG(SwWordCountDialog, OkHdl)
{
    SfxViewFrame* pVFrame = ::GetActiveView()->GetViewFrame();
    if (pVFrame != NULL)
    {
        pVFrame->ToggleChildWindow(FN_WORDCOUNT_DIALOG);
    }
    return 0;
}

SwWordCountDialog::~SwWordCountDialog()
{
    ViewShell::SetCareWin( 0 );
}

void  SwWordCountDialog::SetValues(const SwDocStat& rCurrent, const SwDocStat& rDoc)
{
    aCurrentWordFI.SetText(     String::CreateFromInt32(rCurrent.nWord ));
    aCurrentCharacterFI.SetText(String::CreateFromInt32(rCurrent.nChar ));
    aCurrentCharacterExcludingSpacesFI.SetText(String::CreateFromInt32(rCurrent.nCharExcludingSpaces ));
    aDocWordFI.SetText(         String::CreateFromInt32(rDoc.nWord ));
    aDocCharacterFI.SetText(    String::CreateFromInt32(rDoc.nChar ));
    aDocCharacterExcludingSpacesFI.SetText(    String::CreateFromInt32(rDoc.nCharExcludingSpaces ));
}

SwWordCountFloatDlg::SwWordCountFloatDlg(SfxBindings* _pBindings,
                                         SfxChildWindow* pChild,
                                         Window *pParent,
                                         SfxChildWinInfo* pInfo)
    : SfxModelessDialog(_pBindings, pChild, pParent, SW_RES(DLG_WORDCOUNT)),
#if defined _MSC_VER
#pragma warning (disable : 4355)
#endif
      aDlg(this)
#if defined _MSC_VER
#pragma warning (default : 4355)
#endif
{
    FreeResource();
    Initialize(pInfo);
}

void SwWordCountFloatDlg::Activate()
{
    SfxModelessDialog::Activate();
}

void SwWordCountFloatDlg::UpdateCounts()
{
    SwWrtShell &rSh = ::GetActiveView()->GetWrtShell();
    SwDocStat aCurrCnt;
    SwDocStat aDocStat;
    {
        SwWait aWait( *::GetActiveView()->GetDocShell(), sal_True );
        rSh.StartAction();
        rSh.CountWords( aCurrCnt );
        aDocStat = rSh.GetUpdatedDocStat();
        rSh.EndAction();
    }
    aDlg.SetValues(aCurrCnt, aDocStat);
}

void SwWordCountFloatDlg::SetCounts(const SwDocStat &rCurrCnt, const SwDocStat &rDocStat)
{
    aDlg.SetValues(rCurrCnt, rDocStat);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>better initial size string for pre-update<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include <swtypes.hxx>
#include <wordcountdialog.hxx>
#include <docstat.hxx>
#include <dialog.hrc>
#include <wordcountdialog.hrc>
#include <cmdid.h>
#include "vcl/msgbox.hxx" // RET_CANCEL
#include <swmodule.hxx>
#include <wview.hxx>
#include <sfx2/viewfrm.hxx>
#include <swwait.hxx>
#include <wrtsh.hxx>

//TODO, add asian/non-asian word count to UI when CJK mode is enabled.
SwWordCountDialog::SwWordCountDialog(Dialog* pParent)
    : vbox(pParent)
    , content_area(&vbox)
    , aCurrentSelection(&content_area, false, 3)
    , aCurrentSelectionText(&aCurrentSelection, SW_RES(FT_CURRENT))
    , aCurrentSelectionLine(&aCurrentSelection, SW_RES(FL_CURRENT))
    , aSelectionBox(&content_area, false, 7)
    , aSelectionRow1(&aSelectionBox)
    , aCurrentWordFT(&aSelectionRow1, SW_RES(FT_CURRENTWORD))
    , aCurrentWordFI(&aSelectionRow1, SW_RES(FI_CURRENTWORD))
    , aSelectionRow2(&aSelectionBox)
    , aCurrentCharacterFT(&aSelectionRow2, SW_RES(FT_CURRENTCHARACTER))
    , aCurrentCharacterFI(&aSelectionRow2, SW_RES(FI_CURRENTCHARACTER))
    , aSelectionRow3(&aSelectionBox)
    , aCurrentCharacterExcludingSpacesFT(&aSelectionRow3, SW_RES(FT_CURRENTCHARACTEREXCLUDINGSPACES))
    , aCurrentCharacterExcludingSpacesFI(&aSelectionRow3, SW_RES(FI_CURRENTCHARACTEREXCLUDINGSPACES))
    , aDoc(&content_area, false, 3)
    , aDocText(&aDoc, SW_RES(FT_DOC))
    , aDocLine(&aDoc, SW_RES(FL_DOC))
    , aDocBox(&content_area, false, 7)
    , aDocRow1(&aDocBox)
    , aDocWordFT(&aDocRow1, SW_RES(FT_DOCWORD))
    , aDocWordFI(&aDocRow1, SW_RES(FI_DOCWORD))
    , aDocRow2(&aDocBox)
    , aDocCharacterFT(&aDocRow2, SW_RES(FT_DOCCHARACTER))
    , aDocCharacterFI(&aDocRow2, SW_RES(FI_DOCCHARACTER))
    , aDocRow3(&aDocBox)
    , aDocCharacterExcludingSpacesFT(&aDocRow3, SW_RES(FT_DOCCHARACTEREXCLUDINGSPACES))
    , aDocCharacterExcludingSpacesFI(&aDocRow3, SW_RES(FI_DOCCHARACTEREXCLUDINGSPACES))
    , aBottomFL(&vbox, SW_RES(FL_BOTTOM))
    , action_area(&vbox)
    , aOK(&action_area, SW_RES(PB_OK))
    , aHelp(&action_area, SW_RES(PB_HELP))
{
    rtl::OUString sForceInitialSize(RTL_CONSTASCII_USTRINGPARAM("        "));
    aCurrentWordFI.SetText(sForceInitialSize);
    aCurrentCharacterFI.SetText(sForceInitialSize);
    aCurrentCharacterExcludingSpacesFI.SetText(sForceInitialSize);
    aDocWordFI.SetText(sForceInitialSize);
    aDocCharacterFI.SetText(sForceInitialSize);
    aDocCharacterExcludingSpacesFI.SetText(sForceInitialSize);

    rtl::OString sFill(RTL_CONSTASCII_STRINGPARAM("fill"));
    rtl::OString sExpand(RTL_CONSTASCII_STRINGPARAM("expand"));
    rtl::OString sPackType(RTL_CONSTASCII_STRINGPARAM("pack-type"));
    rtl::OString sBorderWidth(RTL_CONSTASCII_STRINGPARAM("border-width"));

    vbox.setChildProperty(sFill, true);
    //TO-DO, when vcontent_area belongs to dialog via builder, this becomes
    //content-area-border on the dialog
    vbox.setChildProperty(sBorderWidth, sal_Int32(7));

    action_area.setChildProperty(sFill, true);

    content_area.setChildProperty(sFill, true);
    content_area.setChildProperty(sExpand, true);

    aCurrentSelection.setChildProperty(sFill, true);
    aCurrentSelectionText.setChildProperty(sFill, true);
    aCurrentSelectionLine.setChildProperty(sFill, true);
    aCurrentSelectionLine.setChildProperty(sExpand, true);

    aSelectionBox.setChildProperty(sFill, true);
    aSelectionBox.setChildProperty(sBorderWidth, sal_Int32(7));

    aSelectionRow1.setChildProperty(sFill, true);
    aSelectionRow1.setChildProperty(sExpand, true);
    aCurrentWordFT.setChildProperty(sFill, true);
    aCurrentWordFI.setChildProperty(sFill, true);
    aCurrentWordFI.setChildProperty(sExpand, true);

    aSelectionRow2.setChildProperty(sFill, true);
    aSelectionRow2.setChildProperty(sExpand, true);
    aCurrentCharacterFT.setChildProperty(sFill, true);
    aCurrentCharacterFI.setChildProperty(sFill, true);
    aCurrentCharacterFI.setChildProperty(sExpand, true);
    aSelectionRow3.setChildProperty(sFill, true);
    aSelectionRow3.setChildProperty(sExpand, true);
    aCurrentCharacterExcludingSpacesFT.setChildProperty(sFill, true);
    aCurrentCharacterExcludingSpacesFI.setChildProperty(sFill, true);
    aCurrentCharacterExcludingSpacesFI.setChildProperty(sExpand, true);

    aDoc.setChildProperty(sFill, true);
    aDocText.setChildProperty(sFill, true);
    aDocLine.setChildProperty(sFill, true);
    aDocLine.setChildProperty(sExpand, true);

    aDocBox.setChildProperty(sFill, true);
    aDocBox.setChildProperty(sBorderWidth, sal_Int32(7));

    aDocRow1.setChildProperty(sFill, true);
    aDocRow1.setChildProperty(sExpand, true);
    aDocWordFT.setChildProperty(sFill, true);
    aDocWordFI.setChildProperty(sFill, true);
    aDocWordFI.setChildProperty(sExpand, true);

    aDocRow2.setChildProperty(sFill, true);
    aDocRow2.setChildProperty(sExpand, true);
    aDocCharacterFT.setChildProperty(sFill, true);
    aDocCharacterFI.setChildProperty(sFill, true);
    aDocCharacterFI.setChildProperty(sExpand, true);
    aDocRow3.setChildProperty(sFill, true);
    aDocRow3.setChildProperty(sExpand, true);
    aDocCharacterExcludingSpacesFT.setChildProperty(sFill, true);
    aDocCharacterExcludingSpacesFI.setChildProperty(sFill, true);
    aDocCharacterExcludingSpacesFI.setChildProperty(sExpand, true);

    aBottomFL.setChildProperty(sFill, true);
    aBottomFL.setChildProperty(sFill, true);

    aOK.setChildProperty<sal_Int32>(sPackType, VCL_PACK_END);
    aHelp.setChildProperty<sal_Int32>(sPackType, VCL_PACK_END);

    aOK.SetClickHdl(LINK(this, SwWordCountDialog, OkHdl));

    fprintf(stderr, "aOk is %p\n", &aOK);
    fprintf(stderr, "aHelp is %p\n", &aHelp);
    fprintf(stderr, "action_area is is %p\n", &action_area);
    fprintf(stderr, "aCurrentSelectionLine is is %p\n", &aCurrentSelectionLine);
    fprintf(stderr, "aCurrentSelectionText is is %p\n", &aCurrentSelectionText);
    fprintf(stderr, "aCurrentSelection is is %p\n", &aCurrentSelection);
    fprintf(stderr, "vbox is is %p\n", &vbox);

    pParent->SetMinOutputSizePixel(vbox.GetOptimalSize(WINDOWSIZE_PREFERRED));

#if OSL_DEBUG_LEVEL > 2
    aDocCharacterExcludingSpacesFT.SetControlBackground(Color(180,0,0));
    aDocCharacterExcludingSpacesFI.SetControlBackground(Color(0,180,0));
#endif
}

IMPL_LINK_NOARG(SwWordCountDialog, OkHdl)
{
    SfxViewFrame* pVFrame = ::GetActiveView()->GetViewFrame();
    if (pVFrame != NULL)
    {
        pVFrame->ToggleChildWindow(FN_WORDCOUNT_DIALOG);
    }
    return 0;
}

SwWordCountDialog::~SwWordCountDialog()
{
    ViewShell::SetCareWin( 0 );
}

void  SwWordCountDialog::SetValues(const SwDocStat& rCurrent, const SwDocStat& rDoc)
{
    aCurrentWordFI.SetText(     String::CreateFromInt32(rCurrent.nWord ));
    aCurrentCharacterFI.SetText(String::CreateFromInt32(rCurrent.nChar ));
    aCurrentCharacterExcludingSpacesFI.SetText(String::CreateFromInt32(rCurrent.nCharExcludingSpaces ));
    aDocWordFI.SetText(         String::CreateFromInt32(rDoc.nWord ));
    aDocCharacterFI.SetText(    String::CreateFromInt32(rDoc.nChar ));
    aDocCharacterExcludingSpacesFI.SetText(    String::CreateFromInt32(rDoc.nCharExcludingSpaces ));
}

SwWordCountFloatDlg::SwWordCountFloatDlg(SfxBindings* _pBindings,
                                         SfxChildWindow* pChild,
                                         Window *pParent,
                                         SfxChildWinInfo* pInfo)
    : SfxModelessDialog(_pBindings, pChild, pParent, SW_RES(DLG_WORDCOUNT)),
#if defined _MSC_VER
#pragma warning (disable : 4355)
#endif
      aDlg(this)
#if defined _MSC_VER
#pragma warning (default : 4355)
#endif
{
    FreeResource();
    Initialize(pInfo);
}

void SwWordCountFloatDlg::Activate()
{
    SfxModelessDialog::Activate();
}

void SwWordCountFloatDlg::UpdateCounts()
{
    SwWrtShell &rSh = ::GetActiveView()->GetWrtShell();
    SwDocStat aCurrCnt;
    SwDocStat aDocStat;
    {
        SwWait aWait( *::GetActiveView()->GetDocShell(), sal_True );
        rSh.StartAction();
        rSh.CountWords( aCurrCnt );
        aDocStat = rSh.GetUpdatedDocStat();
        rSh.EndAction();
    }
    aDlg.SetValues(aCurrCnt, aDocStat);
}

void SwWordCountFloatDlg::SetCounts(const SwDocStat &rCurrCnt, const SwDocStat &rDocStat)
{
    aDlg.SetValues(rCurrCnt, rDocStat);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "modules/audio_processing/agc2/adaptive_digital_gain_applier.h"

#include <algorithm>

#include "common_audio/include/audio_util.h"
#include "modules/audio_processing/agc2/agc2_common.h"
#include "modules/audio_processing/logging/apm_data_dumper.h"
#include "rtc_base/numerics/safe_minmax.h"
#include "system_wrappers/include/metrics.h"

namespace webrtc {
namespace {

// This function maps input level to desired applied gain. We want to
// boost the signal so that peaks are at -kHeadroomDbfs. We can't
// apply more than kMaxGainDb gain.
float ComputeGainDb(float input_level_dbfs) {
  // If the level is very low, boost it as much as we can.
  if (input_level_dbfs < -(kHeadroomDbfs + kMaxGainDb)) {
    return kMaxGainDb;
  }

  // We expect to end up here most of the time: the level is below
  // -headroom, but we can boost it to -headroom.
  if (input_level_dbfs < -kHeadroomDbfs) {
    return -kHeadroomDbfs - input_level_dbfs;
  }

  // Otherwise, the level is too high and we can't boost. The
  // LevelEstimator is responsible for not reporting bogus gain
  // values.
  RTC_DCHECK_LE(input_level_dbfs, 0.f);
  return 0.f;
}

// We require 'gain + noise_level <= kMaxNoiseLevelDbfs'.
float LimitGainByNoise(float target_gain,
                       float input_noise_level_dbfs,
                       ApmDataDumper* apm_data_dumper) {
  const float noise_headroom_db = kMaxNoiseLevelDbfs - input_noise_level_dbfs;
  apm_data_dumper->DumpRaw("agc2_noise_headroom_db", noise_headroom_db);
  return std::min(target_gain, std::max(noise_headroom_db, 0.f));
}

float LimitGainByLowConfidence(float target_gain,
                               float last_gain,
                               float limiter_audio_level_dbfs,
                               bool estimate_is_confident) {
  if (estimate_is_confident ||
      limiter_audio_level_dbfs <= kLimiterThresholdForAgcGainDbfs) {
    return target_gain;
  }
  const float limiter_level_before_gain = limiter_audio_level_dbfs - last_gain;

  // Compute a new gain so that limiter_level_before_gain + new_gain <=
  // kLimiterThreshold.
  const float new_target_gain = std::max(
      kLimiterThresholdForAgcGainDbfs - limiter_level_before_gain, 0.f);
  return std::min(new_target_gain, target_gain);
}

// Computes how the gain should change during this frame.
// Return the gain difference in db to 'last_gain_db'.
float ComputeGainChangeThisFrameDb(float target_gain_db,
                                   float last_gain_db,
                                   bool gain_increase_allowed) {
  float target_gain_difference_db = target_gain_db - last_gain_db;
  if (!gain_increase_allowed) {
    target_gain_difference_db = std::min(target_gain_difference_db, 0.f);
  }

  return rtc::SafeClamp(target_gain_difference_db, -kMaxGainChangePerFrameDb,
                        kMaxGainChangePerFrameDb);
}
}  // namespace

SignalWithLevels::SignalWithLevels(AudioFrameView<float> float_frame)
    : float_frame(float_frame) {}
SignalWithLevels::SignalWithLevels(const SignalWithLevels&) = default;

AdaptiveDigitalGainApplier::AdaptiveDigitalGainApplier(
    ApmDataDumper* apm_data_dumper)
    : gain_applier_(false, DbToRatio(last_gain_db_)),
      apm_data_dumper_(apm_data_dumper) {}

void AdaptiveDigitalGainApplier::Process(SignalWithLevels signal_with_levels) {
  calls_since_last_gain_log_++;
  if (calls_since_last_gain_log_ == 100) {
    calls_since_last_gain_log_ = 0;
    RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc2.DigitalGainApplied",
                                last_gain_db_, 0, kMaxGainDb, kMaxGainDb + 1);
    RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc2.EstimatedNoiseLevel",
                                signal_with_levels.input_noise_level_dbfs, 0,
                                100, 101);
  }

  signal_with_levels.input_level_dbfs =
      std::min(signal_with_levels.input_level_dbfs, 0.f);

  RTC_DCHECK_GE(signal_with_levels.input_level_dbfs, -150.f);
  RTC_DCHECK_GE(signal_with_levels.float_frame.num_channels(), 1);
  RTC_DCHECK_GE(signal_with_levels.float_frame.samples_per_channel(), 1);

  const float target_gain_db = LimitGainByLowConfidence(
      LimitGainByNoise(ComputeGainDb(signal_with_levels.input_level_dbfs),
                       signal_with_levels.input_noise_level_dbfs,
                       apm_data_dumper_),
      last_gain_db_, signal_with_levels.limiter_audio_level_dbfs,
      signal_with_levels.estimate_is_confident);

  // Forbid increasing the gain when there is no speech.
  gain_increase_allowed_ = signal_with_levels.vad_result.speech_probability >
                           kVadConfidenceThreshold;

  const float gain_change_this_frame_db = ComputeGainChangeThisFrameDb(
      target_gain_db, last_gain_db_, gain_increase_allowed_);

  apm_data_dumper_->DumpRaw("agc2_want_to_change_by_db",
                            target_gain_db - last_gain_db_);
  apm_data_dumper_->DumpRaw("agc2_will_change_by_db",
                            gain_change_this_frame_db);

  // Optimization: avoid calling math functions if gain does not
  // change.
  if (gain_change_this_frame_db != 0.f) {
    gain_applier_.SetGainFactor(
        DbToRatio(last_gain_db_ + gain_change_this_frame_db));
  }
  gain_applier_.ApplyGain(signal_with_levels.float_frame);

  // Remember that the gain has changed for the next iteration.
  last_gain_db_ = last_gain_db_ + gain_change_this_frame_db;
  apm_data_dumper_->DumpRaw("agc2_applied_gain_db", last_gain_db_);
}
}  // namespace webrtc
<commit_msg>Reland of 'Bug in histogram metric reporting.'<commit_after>/*
 *  Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "modules/audio_processing/agc2/adaptive_digital_gain_applier.h"

#include <algorithm>

#include "common_audio/include/audio_util.h"
#include "modules/audio_processing/agc2/agc2_common.h"
#include "modules/audio_processing/logging/apm_data_dumper.h"
#include "rtc_base/numerics/safe_minmax.h"
#include "system_wrappers/include/metrics.h"

namespace webrtc {
namespace {

// This function maps input level to desired applied gain. We want to
// boost the signal so that peaks are at -kHeadroomDbfs. We can't
// apply more than kMaxGainDb gain.
float ComputeGainDb(float input_level_dbfs) {
  // If the level is very low, boost it as much as we can.
  if (input_level_dbfs < -(kHeadroomDbfs + kMaxGainDb)) {
    return kMaxGainDb;
  }

  // We expect to end up here most of the time: the level is below
  // -headroom, but we can boost it to -headroom.
  if (input_level_dbfs < -kHeadroomDbfs) {
    return -kHeadroomDbfs - input_level_dbfs;
  }

  // Otherwise, the level is too high and we can't boost. The
  // LevelEstimator is responsible for not reporting bogus gain
  // values.
  RTC_DCHECK_LE(input_level_dbfs, 0.f);
  return 0.f;
}

// We require 'gain + noise_level <= kMaxNoiseLevelDbfs'.
float LimitGainByNoise(float target_gain,
                       float input_noise_level_dbfs,
                       ApmDataDumper* apm_data_dumper) {
  const float noise_headroom_db = kMaxNoiseLevelDbfs - input_noise_level_dbfs;
  apm_data_dumper->DumpRaw("agc2_noise_headroom_db", noise_headroom_db);
  return std::min(target_gain, std::max(noise_headroom_db, 0.f));
}

float LimitGainByLowConfidence(float target_gain,
                               float last_gain,
                               float limiter_audio_level_dbfs,
                               bool estimate_is_confident) {
  if (estimate_is_confident ||
      limiter_audio_level_dbfs <= kLimiterThresholdForAgcGainDbfs) {
    return target_gain;
  }
  const float limiter_level_before_gain = limiter_audio_level_dbfs - last_gain;

  // Compute a new gain so that limiter_level_before_gain + new_gain <=
  // kLimiterThreshold.
  const float new_target_gain = std::max(
      kLimiterThresholdForAgcGainDbfs - limiter_level_before_gain, 0.f);
  return std::min(new_target_gain, target_gain);
}

// Computes how the gain should change during this frame.
// Return the gain difference in db to 'last_gain_db'.
float ComputeGainChangeThisFrameDb(float target_gain_db,
                                   float last_gain_db,
                                   bool gain_increase_allowed) {
  float target_gain_difference_db = target_gain_db - last_gain_db;
  if (!gain_increase_allowed) {
    target_gain_difference_db = std::min(target_gain_difference_db, 0.f);
  }

  return rtc::SafeClamp(target_gain_difference_db, -kMaxGainChangePerFrameDb,
                        kMaxGainChangePerFrameDb);
}
}  // namespace

SignalWithLevels::SignalWithLevels(AudioFrameView<float> float_frame)
    : float_frame(float_frame) {}
SignalWithLevels::SignalWithLevels(const SignalWithLevels&) = default;

AdaptiveDigitalGainApplier::AdaptiveDigitalGainApplier(
    ApmDataDumper* apm_data_dumper)
    : gain_applier_(false, DbToRatio(last_gain_db_)),
      apm_data_dumper_(apm_data_dumper) {}

void AdaptiveDigitalGainApplier::Process(SignalWithLevels signal_with_levels) {
  calls_since_last_gain_log_++;
  if (calls_since_last_gain_log_ == 100) {
    calls_since_last_gain_log_ = 0;
    RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc2.DigitalGainApplied",
                                last_gain_db_, 0, kMaxGainDb, kMaxGainDb + 1);
    RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc2.EstimatedNoiseLevel",
                                -signal_with_levels.input_noise_level_dbfs, 0,
                                100, 101);
  }

  signal_with_levels.input_level_dbfs =
      std::min(signal_with_levels.input_level_dbfs, 0.f);

  RTC_DCHECK_GE(signal_with_levels.input_level_dbfs, -150.f);
  RTC_DCHECK_GE(signal_with_levels.float_frame.num_channels(), 1);
  RTC_DCHECK_GE(signal_with_levels.float_frame.samples_per_channel(), 1);

  const float target_gain_db = LimitGainByLowConfidence(
      LimitGainByNoise(ComputeGainDb(signal_with_levels.input_level_dbfs),
                       signal_with_levels.input_noise_level_dbfs,
                       apm_data_dumper_),
      last_gain_db_, signal_with_levels.limiter_audio_level_dbfs,
      signal_with_levels.estimate_is_confident);

  // Forbid increasing the gain when there is no speech.
  gain_increase_allowed_ = signal_with_levels.vad_result.speech_probability >
                           kVadConfidenceThreshold;

  const float gain_change_this_frame_db = ComputeGainChangeThisFrameDb(
      target_gain_db, last_gain_db_, gain_increase_allowed_);

  apm_data_dumper_->DumpRaw("agc2_want_to_change_by_db",
                            target_gain_db - last_gain_db_);
  apm_data_dumper_->DumpRaw("agc2_will_change_by_db",
                            gain_change_this_frame_db);

  // Optimization: avoid calling math functions if gain does not
  // change.
  if (gain_change_this_frame_db != 0.f) {
    gain_applier_.SetGainFactor(
        DbToRatio(last_gain_db_ + gain_change_this_frame_db));
  }
  gain_applier_.ApplyGain(signal_with_levels.float_frame);

  // Remember that the gain has changed for the next iteration.
  last_gain_db_ = last_gain_db_ + gain_change_this_frame_db;
  apm_data_dumper_->DumpRaw("agc2_applied_gain_db", last_gain_db_);
}
}  // namespace webrtc
<|endoftext|>