<commit_before>// Copyright (c) 2012-2016 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 "bloom.h"

#include "primitives/transaction.h"
#include "hash.h"
#include "script/script.h"
#include "script/standard.h"
#include "random.h"
#include "streams.h"

#include <math.h>
#include <stdlib.h>

#include <boost/foreach.hpp>

#define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455
#define LN2 0.6931471805599453094172321214581765680755001343602552

CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn, unsigned char nFlagsIn) :
    /**
     * The ideal size for a bloom filter with a given number of elements and false positive rate is:
     * - nElements * log(fp rate) / ln(2)^2
     * We ignore filter parameters which will create a bloom filter larger than the protocol limits
     */
    vData(std::min((unsigned int)(-1  / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8),
    /**
     * The ideal number of hash functions is filter size * ln(2) / number of elements
     * Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits
     * See https://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas
     */
    isFull(false),
    isEmpty(true),
    nHashFuncs(std::min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)),
    nTweak(nTweakIn),
    nFlags(nFlagsIn)
{
}

// Private constructor used by CRollingBloomFilter
CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn) :
    vData((unsigned int)(-1  / LN2SQUARED * nElements * log(nFPRate)) / 8),
    isFull(false),
    isEmpty(true),
    nHashFuncs((unsigned int)(vData.size() * 8 / nElements * LN2)),
    nTweak(nTweakIn),
    nFlags(BLOOM_UPDATE_NONE)
{
}

inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector<unsigned char>& vDataToHash) const
{
    // 0xFBA4C795 chosen as it guarantees a reasonable bit difference between nHashNum values.
    return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.size() * 8);
}

void CBloomFilter::insert(const std::vector<unsigned char>& vKey)
{
    if (isFull)
        return;
    for (unsigned int i = 0; i < nHashFuncs; i++)
    {
        unsigned int nIndex = Hash(i, vKey);
        // Sets bit nIndex of vData
        vData[nIndex >> 3] |= (1 << (7 & nIndex));
    }
    isEmpty = false;
}

void CBloomFilter::insert(const COutPoint& outpoint)
{
    CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
    stream << outpoint;
    std::vector<unsigned char> data(stream.begin(), stream.end());
    insert(data);
}

void CBloomFilter::insert(const uint256& hash)
{
    std::vector<unsigned char> data(hash.begin(), hash.end());
    insert(data);
}

bool CBloomFilter::contains(const std::vector<unsigned char>& vKey) const
{
    if (isFull)
        return true;
    if (isEmpty)
        return false;
    for (unsigned int i = 0; i < nHashFuncs; i++)
    {
        unsigned int nIndex = Hash(i, vKey);
        // Checks bit nIndex of vData
        if (!(vData[nIndex >> 3] & (1 << (7 & nIndex))))
            return false;
    }
    return true;
}

bool CBloomFilter::contains(const COutPoint& outpoint) const
{
    CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
    stream << outpoint;
    std::vector<unsigned char> data(stream.begin(), stream.end());
    return contains(data);
}

bool CBloomFilter::contains(const uint256& hash) const
{
    std::vector<unsigned char> data(hash.begin(), hash.end());
    return contains(data);
}

void CBloomFilter::clear()
{
    vData.assign(vData.size(),0);
    isFull = false;
    isEmpty = true;
}

void CBloomFilter::reset(unsigned int nNewTweak)
{
    clear();
    nTweak = nNewTweak;
}

bool CBloomFilter::IsWithinSizeConstraints() const
{
    return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS;
}

bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx)
{
    bool fFound = false;
    // Match if the filter contains the hash of tx
    //  for finding tx when they appear in a block
    if (isFull)
        return true;
    if (isEmpty)
        return false;
    const uint256& hash = tx.GetHash();
    if (contains(hash))
        fFound = true;

    for (unsigned int i = 0; i < tx.vout.size(); i++)
    {
        const CTxOut& txout = tx.vout[i];
        // Match if the filter contains any arbitrary script data element in any scriptPubKey in tx
        // If this matches, also add the specific output that was matched.
        // This means clients don't have to update the filter themselves when a new relevant tx
        // is discovered in order to find spending transactions, which avoids round-tripping and race conditions.
        CScript::const_iterator pc = txout.scriptPubKey.begin();
        std::vector<unsigned char> data;
        while (pc < txout.scriptPubKey.end())
        {
            opcodetype opcode;
            if (!txout.scriptPubKey.GetOp(pc, opcode, data))
                break;
            if (data.size() != 0 && contains(data))
            {
                fFound = true;
                if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL)
                    insert(COutPoint(hash, i));
                else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY)
                {
                    txnouttype type;
                    std::vector<std::vector<unsigned char> > vSolutions;
                    if (Solver(txout.scriptPubKey, type, vSolutions) &&
                            (type == TX_PUBKEY || type == TX_MULTISIG))
                        insert(COutPoint(hash, i));
                }
                break;
            }
        }
    }

    if (fFound)
        return true;

    BOOST_FOREACH(const CTxIn& txin, tx.vin)
    {
        // Match if the filter contains an outpoint tx spends
        if (contains(txin.prevout))
            return true;

        // Match if the filter contains any arbitrary script data element in any scriptSig in tx
        CScript::const_iterator pc = txin.scriptSig.begin();
        std::vector<unsigned char> data;
        while (pc < txin.scriptSig.end())
        {
            opcodetype opcode;
            if (!txin.scriptSig.GetOp(pc, opcode, data))
                break;
            if (data.size() != 0 && contains(data))
                return true;
        }
    }

    return false;
}

void CBloomFilter::UpdateEmptyFull()
{
    bool full = true;
    bool empty = true;
    for (unsigned int i = 0; i < vData.size(); i++)
    {
        full &= vData[i] == 0xff;
        empty &= vData[i] == 0;
    }
    isFull = full;
    isEmpty = empty;
}

CRollingBloomFilter::CRollingBloomFilter(unsigned int nElements, double fpRate)
{
    double logFpRate = log(fpRate);
    /* The optimal number of hash functions is log(fpRate) / log(0.5), but
     * restrict it to the range 1-50. */
    nHashFuncs = std::max(1, std::min((int)round(logFpRate / log(0.5)), 50));
    /* In this rolling bloom filter, we'll store between 2 and 3 generations of nElements / 2 entries. */
    nEntriesPerGeneration = (nElements + 1) / 2;
    uint32_t nMaxElements = nEntriesPerGeneration * 3;
    /* The maximum fpRate = pow(1.0 - exp(-nHashFuncs * nMaxElements / nFilterBits), nHashFuncs)
     * =>          pow(fpRate, 1.0 / nHashFuncs) = 1.0 - exp(-nHashFuncs * nMaxElements / nFilterBits)
     * =>          1.0 - pow(fpRate, 1.0 / nHashFuncs) = exp(-nHashFuncs * nMaxElements / nFilterBits)
     * =>          log(1.0 - pow(fpRate, 1.0 / nHashFuncs)) = -nHashFuncs * nMaxElements / nFilterBits
     * =>          nFilterBits = -nHashFuncs * nMaxElements / log(1.0 - pow(fpRate, 1.0 / nHashFuncs))
     * =>          nFilterBits = -nHashFuncs * nMaxElements / log(1.0 - exp(logFpRate / nHashFuncs))
     */
    uint32_t nFilterBits = (uint32_t)ceil(-1.0 * nHashFuncs * nMaxElements / log(1.0 - exp(logFpRate / nHashFuncs)));
    data.clear();
    /* For each data element we need to store 2 bits. If both bits are 0, the
     * bit is treated as unset. If the bits are (01), (10), or (11), the bit is
     * treated as set in generation 1, 2, or 3 respectively.
     * These bits are stored in separate integers: position P corresponds to bit
     * (P & 63) of the integers data[(P >> 6) * 2] and data[(P >> 6) * 2 + 1]. */
    data.resize(((nFilterBits + 63) / 64) << 1);
    reset();
}

/* Similar to CBloomFilter::Hash */
static inline uint32_t RollingBloomHash(unsigned int nHashNum, uint32_t nTweak, const std::vector<unsigned char>& vDataToHash) {
    return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash);
}

void CRollingBloomFilter::insert(const std::vector<unsigned char>& vKey)
{
    if (nEntriesThisGeneration == nEntriesPerGeneration) {
        nEntriesThisGeneration = 0;
        nGeneration++;
        if (nGeneration == 4) {
            nGeneration = 1;
        }
        uint64_t nGenerationMask1 = -(uint64_t)(nGeneration & 1);
        uint64_t nGenerationMask2 = -(uint64_t)(nGeneration >> 1);
        /* Wipe old entries that used this generation number. */
        for (uint32_t p = 0; p < data.size(); p += 2) {
            uint64_t p1 = data[p], p2 = data[p + 1];
            uint64_t mask = (p1 ^ nGenerationMask1) | (p2 ^ nGenerationMask2);
            data[p] = p1 & mask;
            data[p + 1] = p2 & mask;
        }
    }
    nEntriesThisGeneration++;

    for (int n = 0; n < nHashFuncs; n++) {
        uint32_t h = RollingBloomHash(n, nTweak, vKey);
        int bit = h & 0x3F;
        uint32_t pos = (h >> 6) % data.size();
        /* The lowest bit of pos is ignored, and set to zero for the first bit, and to one for the second. */
        data[pos & ~1] = (data[pos & ~1] & ~(((uint64_t)1) << bit)) | ((uint64_t)(nGeneration & 1)) << bit;
        data[pos | 1] = (data[pos | 1] & ~(((uint64_t)1) << bit)) | ((uint64_t)(nGeneration >> 1)) << bit;
    }
}

void CRollingBloomFilter::insert(const uint256& hash)
{
    std::vector<unsigned char> vData(hash.begin(), hash.end());
    insert(vData);
}

bool CRollingBloomFilter::contains(const std::vector<unsigned char>& vKey) const
{
    for (int n = 0; n < nHashFuncs; n++) {
        uint32_t h = RollingBloomHash(n, nTweak, vKey);
        int bit = h & 0x3F;
        uint32_t pos = (h >> 6) % data.size();
        /* If the relevant bit is not set in either data[pos & ~1] or data[pos | 1], the filter does not contain vKey */
        if (!(((data[pos & ~1] | data[pos | 1]) >> bit) & 1)) {
            return false;
        }
    }
    return true;
}

bool CRollingBloomFilter::contains(const uint256& hash) const
{
    std::vector<unsigned char> vData(hash.begin(), hash.end());
    return contains(vData);
}

void CRollingBloomFilter::reset()
{
    nTweak = GetRand(std::numeric_limits<unsigned int>::max());
    nEntriesThisGeneration = 0;
    nGeneration = 1;
    for (std::vector<uint64_t>::iterator it = data.begin(); it != data.end(); it++) {
        *it = 0;
    }
}
<commit_msg>Fix msvc compiler error C4146 (minus operator applied to unsigned type)<commit_after>// Copyright (c) 2012-2016 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 "bloom.h"

#include "primitives/transaction.h"
#include "hash.h"
#include "script/script.h"
#include "script/standard.h"
#include "random.h"
#include "streams.h"

#include <math.h>
#include <stdlib.h>

#include <boost/foreach.hpp>

#define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455
#define LN2 0.6931471805599453094172321214581765680755001343602552

CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn, unsigned char nFlagsIn) :
    /**
     * The ideal size for a bloom filter with a given number of elements and false positive rate is:
     * - nElements * log(fp rate) / ln(2)^2
     * We ignore filter parameters which will create a bloom filter larger than the protocol limits
     */
    vData(std::min((unsigned int)(-1  / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8),
    /**
     * The ideal number of hash functions is filter size * ln(2) / number of elements
     * Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits
     * See https://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas
     */
    isFull(false),
    isEmpty(true),
    nHashFuncs(std::min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)),
    nTweak(nTweakIn),
    nFlags(nFlagsIn)
{
}

// Private constructor used by CRollingBloomFilter
CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn) :
    vData((unsigned int)(-1  / LN2SQUARED * nElements * log(nFPRate)) / 8),
    isFull(false),
    isEmpty(true),
    nHashFuncs((unsigned int)(vData.size() * 8 / nElements * LN2)),
    nTweak(nTweakIn),
    nFlags(BLOOM_UPDATE_NONE)
{
}

inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector<unsigned char>& vDataToHash) const
{
    // 0xFBA4C795 chosen as it guarantees a reasonable bit difference between nHashNum values.
    return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.size() * 8);
}

void CBloomFilter::insert(const std::vector<unsigned char>& vKey)
{
    if (isFull)
        return;
    for (unsigned int i = 0; i < nHashFuncs; i++)
    {
        unsigned int nIndex = Hash(i, vKey);
        // Sets bit nIndex of vData
        vData[nIndex >> 3] |= (1 << (7 & nIndex));
    }
    isEmpty = false;
}

void CBloomFilter::insert(const COutPoint& outpoint)
{
    CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
    stream << outpoint;
    std::vector<unsigned char> data(stream.begin(), stream.end());
    insert(data);
}

void CBloomFilter::insert(const uint256& hash)
{
    std::vector<unsigned char> data(hash.begin(), hash.end());
    insert(data);
}

bool CBloomFilter::contains(const std::vector<unsigned char>& vKey) const
{
    if (isFull)
        return true;
    if (isEmpty)
        return false;
    for (unsigned int i = 0; i < nHashFuncs; i++)
    {
        unsigned int nIndex = Hash(i, vKey);
        // Checks bit nIndex of vData
        if (!(vData[nIndex >> 3] & (1 << (7 & nIndex))))
            return false;
    }
    return true;
}

bool CBloomFilter::contains(const COutPoint& outpoint) const
{
    CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
    stream << outpoint;
    std::vector<unsigned char> data(stream.begin(), stream.end());
    return contains(data);
}

bool CBloomFilter::contains(const uint256& hash) const
{
    std::vector<unsigned char> data(hash.begin(), hash.end());
    return contains(data);
}

void CBloomFilter::clear()
{
    vData.assign(vData.size(),0);
    isFull = false;
    isEmpty = true;
}

void CBloomFilter::reset(unsigned int nNewTweak)
{
    clear();
    nTweak = nNewTweak;
}

bool CBloomFilter::IsWithinSizeConstraints() const
{
    return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS;
}

bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx)
{
    bool fFound = false;
    // Match if the filter contains the hash of tx
    //  for finding tx when they appear in a block
    if (isFull)
        return true;
    if (isEmpty)
        return false;
    const uint256& hash = tx.GetHash();
    if (contains(hash))
        fFound = true;

    for (unsigned int i = 0; i < tx.vout.size(); i++)
    {
        const CTxOut& txout = tx.vout[i];
        // Match if the filter contains any arbitrary script data element in any scriptPubKey in tx
        // If this matches, also add the specific output that was matched.
        // This means clients don't have to update the filter themselves when a new relevant tx
        // is discovered in order to find spending transactions, which avoids round-tripping and race conditions.
        CScript::const_iterator pc = txout.scriptPubKey.begin();
        std::vector<unsigned char> data;
        while (pc < txout.scriptPubKey.end())
        {
            opcodetype opcode;
            if (!txout.scriptPubKey.GetOp(pc, opcode, data))
                break;
            if (data.size() != 0 && contains(data))
            {
                fFound = true;
                if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL)
                    insert(COutPoint(hash, i));
                else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY)
                {
                    txnouttype type;
                    std::vector<std::vector<unsigned char> > vSolutions;
                    if (Solver(txout.scriptPubKey, type, vSolutions) &&
                            (type == TX_PUBKEY || type == TX_MULTISIG))
                        insert(COutPoint(hash, i));
                }
                break;
            }
        }
    }

    if (fFound)
        return true;

    BOOST_FOREACH(const CTxIn& txin, tx.vin)
    {
        // Match if the filter contains an outpoint tx spends
        if (contains(txin.prevout))
            return true;

        // Match if the filter contains any arbitrary script data element in any scriptSig in tx
        CScript::const_iterator pc = txin.scriptSig.begin();
        std::vector<unsigned char> data;
        while (pc < txin.scriptSig.end())
        {
            opcodetype opcode;
            if (!txin.scriptSig.GetOp(pc, opcode, data))
                break;
            if (data.size() != 0 && contains(data))
                return true;
        }
    }

    return false;
}

void CBloomFilter::UpdateEmptyFull()
{
    bool full = true;
    bool empty = true;
    for (unsigned int i = 0; i < vData.size(); i++)
    {
        full &= vData[i] == 0xff;
        empty &= vData[i] == 0;
    }
    isFull = full;
    isEmpty = empty;
}

CRollingBloomFilter::CRollingBloomFilter(unsigned int nElements, double fpRate)
{
    double logFpRate = log(fpRate);
    /* The optimal number of hash functions is log(fpRate) / log(0.5), but
     * restrict it to the range 1-50. */
    nHashFuncs = std::max(1, std::min((int)round(logFpRate / log(0.5)), 50));
    /* In this rolling bloom filter, we'll store between 2 and 3 generations of nElements / 2 entries. */
    nEntriesPerGeneration = (nElements + 1) / 2;
    uint32_t nMaxElements = nEntriesPerGeneration * 3;
    /* The maximum fpRate = pow(1.0 - exp(-nHashFuncs * nMaxElements / nFilterBits), nHashFuncs)
     * =>          pow(fpRate, 1.0 / nHashFuncs) = 1.0 - exp(-nHashFuncs * nMaxElements / nFilterBits)
     * =>          1.0 - pow(fpRate, 1.0 / nHashFuncs) = exp(-nHashFuncs * nMaxElements / nFilterBits)
     * =>          log(1.0 - pow(fpRate, 1.0 / nHashFuncs)) = -nHashFuncs * nMaxElements / nFilterBits
     * =>          nFilterBits = -nHashFuncs * nMaxElements / log(1.0 - pow(fpRate, 1.0 / nHashFuncs))
     * =>          nFilterBits = -nHashFuncs * nMaxElements / log(1.0 - exp(logFpRate / nHashFuncs))
     */
    uint32_t nFilterBits = (uint32_t)ceil(-1.0 * nHashFuncs * nMaxElements / log(1.0 - exp(logFpRate / nHashFuncs)));
    data.clear();
    /* For each data element we need to store 2 bits. If both bits are 0, the
     * bit is treated as unset. If the bits are (01), (10), or (11), the bit is
     * treated as set in generation 1, 2, or 3 respectively.
     * These bits are stored in separate integers: position P corresponds to bit
     * (P & 63) of the integers data[(P >> 6) * 2] and data[(P >> 6) * 2 + 1]. */
    data.resize(((nFilterBits + 63) / 64) << 1);
    reset();
}

/* Similar to CBloomFilter::Hash */
static inline uint32_t RollingBloomHash(unsigned int nHashNum, uint32_t nTweak, const std::vector<unsigned char>& vDataToHash) {
    return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash);
}

void CRollingBloomFilter::insert(const std::vector<unsigned char>& vKey)
{
    if (nEntriesThisGeneration == nEntriesPerGeneration) {
        nEntriesThisGeneration = 0;
        nGeneration++;
        if (nGeneration == 4) {
            nGeneration = 1;
        }
        uint64_t nGenerationMask1 = 0 - (uint64_t)(nGeneration & 1);
        uint64_t nGenerationMask2 = 0 - (uint64_t)(nGeneration >> 1);
        /* Wipe old entries that used this generation number. */
        for (uint32_t p = 0; p < data.size(); p += 2) {
            uint64_t p1 = data[p], p2 = data[p + 1];
            uint64_t mask = (p1 ^ nGenerationMask1) | (p2 ^ nGenerationMask2);
            data[p] = p1 & mask;
            data[p + 1] = p2 & mask;
        }
    }
    nEntriesThisGeneration++;

    for (int n = 0; n < nHashFuncs; n++) {
        uint32_t h = RollingBloomHash(n, nTweak, vKey);
        int bit = h & 0x3F;
        uint32_t pos = (h >> 6) % data.size();
        /* The lowest bit of pos is ignored, and set to zero for the first bit, and to one for the second. */
        data[pos & ~1] = (data[pos & ~1] & ~(((uint64_t)1) << bit)) | ((uint64_t)(nGeneration & 1)) << bit;
        data[pos | 1] = (data[pos | 1] & ~(((uint64_t)1) << bit)) | ((uint64_t)(nGeneration >> 1)) << bit;
    }
}

void CRollingBloomFilter::insert(const uint256& hash)
{
    std::vector<unsigned char> vData(hash.begin(), hash.end());
    insert(vData);
}

bool CRollingBloomFilter::contains(const std::vector<unsigned char>& vKey) const
{
    for (int n = 0; n < nHashFuncs; n++) {
        uint32_t h = RollingBloomHash(n, nTweak, vKey);
        int bit = h & 0x3F;
        uint32_t pos = (h >> 6) % data.size();
        /* If the relevant bit is not set in either data[pos & ~1] or data[pos | 1], the filter does not contain vKey */
        if (!(((data[pos & ~1] | data[pos | 1]) >> bit) & 1)) {
            return false;
        }
    }
    return true;
}

bool CRollingBloomFilter::contains(const uint256& hash) const
{
    std::vector<unsigned char> vData(hash.begin(), hash.end());
    return contains(vData);
}

void CRollingBloomFilter::reset()
{
    nTweak = GetRand(std::numeric_limits<unsigned int>::max());
    nEntriesThisGeneration = 0;
    nGeneration = 1;
    for (std::vector<uint64_t>::iterator it = data.begin(); it != data.end(); it++) {
        *it = 0;
    }
}
<|endoftext|>


<commit_before>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "kernel/yosys.h"
#include "libs/sha1/sha1.h"
#include "backends/ilang/ilang_backend.h"

#if !defined(_WIN32) || defined(__MINGW32__)
#  include <sys/time.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <vector>
#include <list>

YOSYS_NAMESPACE_BEGIN

std::vector<FILE*> log_files;
std::vector<std::ostream*> log_streams;
FILE *log_errfile = NULL;
SHA1 *log_hasher = NULL;

bool log_time = false;
bool log_cmd_error_throw = false;
int log_verbose_level;

std::vector<int> header_count;
std::list<std::string> string_buf;
int string_buf_size = 0;

static struct timeval initial_tv = { 0, 0 };
static bool next_print_log = false;
static int log_newline_count = 0;

#if defined(_WIN32) && !defined(__MINGW32__)
// this will get time information and return it in timeval, simulating gettimeofday()
int gettimeofday(struct timeval *tv, struct timezone *tz)
{
	LARGE_INTEGER counter;
	LARGE_INTEGER freq;

	QueryPerformanceFrequency(&freq);
	QueryPerformanceCounter(&counter);

	counter.QuadPart *= 1000000;
	counter.QuadPart /= freq.QuadPart;

	tv->tv_sec = long(counter.QuadPart / 1000000);
	tv->tv_usec = counter.QuadPart % 1000000;

	return 0;
}
#endif

void logv(const char *format, va_list ap)
{
	while (format[0] == '\n' && format[1] != 0) {
		log("\n");
		format++;
	}

	std::string str = vstringf(format, ap);

	if (str.empty())
		return;

	size_t nnl_pos = str.find_last_not_of('\n');
	if (nnl_pos == std::string::npos)
		log_newline_count += GetSize(str);
	else
		log_newline_count = GetSize(str) - nnl_pos - 1;

	if (log_hasher)
		log_hasher->update(str);

	if (log_time)
	{
		std::string time_str;

		if (next_print_log || initial_tv.tv_sec == 0) {
			next_print_log = false;
			struct timeval tv;
			gettimeofday(&tv, NULL);
			if (initial_tv.tv_sec == 0)
				initial_tv = tv;
			if (tv.tv_usec < initial_tv.tv_usec) {
				tv.tv_sec--;
				tv.tv_usec += 1000000;
			}
			tv.tv_sec -= initial_tv.tv_sec;
			tv.tv_usec -= initial_tv.tv_usec;
			time_str += stringf("[%05d.%06d] ", int(tv.tv_sec), int(tv.tv_usec));
		}

		if (format[0] && format[strlen(format)-1] == '\n')
			next_print_log = true;

		for (auto f : log_files)
			fputs(time_str.c_str(), f);

		for (auto f : log_streams)
			*f << time_str;
	}

	for (auto f : log_files)
		fputs(str.c_str(), f);

	for (auto f : log_streams)
		*f << str;
}

void logv_header(const char *format, va_list ap)
{
	bool pop_errfile = false;

	log_spacer();
	if (header_count.size() > 0)
		header_count.back()++;

	if (int(header_count.size()) <= log_verbose_level && log_errfile != NULL) {
		log_files.push_back(log_errfile);
		pop_errfile = true;
	}

	for (int c : header_count)
		log("%d.", c);
	log(" ");
	logv(format, ap);
	log_flush();

	if (pop_errfile)
		log_files.pop_back();
}

void logv_error(const char *format, va_list ap)
{
	if (log_errfile != NULL)
		log_files.push_back(log_errfile);

	log("ERROR: ");
	logv(format, ap);
	log_flush();
	exit(1);
}

void log(const char *format, ...)
{
	va_list ap;
	va_start(ap, format);
	logv(format, ap);
	va_end(ap);
}

void log_header(const char *format, ...)
{
	va_list ap;
	va_start(ap, format);
	logv_header(format, ap);
	va_end(ap);
}

void log_error(const char *format, ...)
{
	va_list ap;
	va_start(ap, format);
	logv_error(format, ap);
}

void log_cmd_error(const char *format, ...)
{
	va_list ap;
	va_start(ap, format);

	if (log_cmd_error_throw) {
		log("ERROR: ");
		logv(format, ap);
		log_flush();
		throw log_cmd_error_exception();
	}

	logv_error(format, ap);
}

void log_spacer()
{
	while (log_newline_count < 2)
		log("\n");
}

void log_push()
{
	header_count.push_back(0);
}

void log_pop()
{
	header_count.pop_back();
	string_buf.clear();
	string_buf_size = 0;
	log_flush();
}

void log_reset_stack()
{
	while (header_count.size() > 1)
		header_count.pop_back();
	string_buf.clear();
	string_buf_size = 0;
	log_flush();
}

void log_flush()
{
	for (auto f : log_files)
		fflush(f);

	for (auto f : log_streams)
		f->flush();
}

void log_dump_val_worker(RTLIL::SigSpec v) {
	log("%s", log_signal(v));
}

const char *log_signal(const RTLIL::SigSpec &sig, bool autoint)
{
	std::stringstream buf;
	ILANG_BACKEND::dump_sigspec(buf, sig, autoint);

	if (string_buf_size < 100)
		string_buf_size++;
	else
		string_buf.pop_front();
	string_buf.push_back(buf.str());

	return string_buf.back().c_str();
}

const char *log_id(RTLIL::IdString str)
{
	const char *p = str.c_str();
	log_assert(RTLIL::IdString::global_refcount_storage_[str.index_] > 1);
	if (p[0] == '\\' && p[1] != '$' && p[1] != 0)
		return p+1;
	return p;
}

void log_cell(RTLIL::Cell *cell, std::string indent)
{
	std::stringstream buf;
	ILANG_BACKEND::dump_cell(buf, indent, cell);
	log("%s", buf.str().c_str());
}

// ---------------------------------------------------
// This is the magic behind the code coverage counters
// ---------------------------------------------------
#ifdef YOSYS_ENABLE_COVER

std::map<std::string, std::pair<std::string, int>> extra_coverage_data;

void cover_extra(std::string parent, std::string id, bool increment) {
	if (extra_coverage_data.count(id) == 0) {
		for (CoverData *p = __start_yosys_cover_list; p != __stop_yosys_cover_list; p++)
			if (p->id == parent)
				extra_coverage_data[id].first = stringf("%s:%d:%s", p->file, p->line, p->func);
		log_assert(extra_coverage_data.count(id));
	}
	if (increment)
		extra_coverage_data[id].second++;
}

std::map<std::string, std::pair<std::string, int>> get_coverage_data()
{
	std::map<std::string, std::pair<std::string, int>> coverage_data;

	for (auto &it : pass_register) {
		std::string key = stringf("passes.%s", it.first.c_str());
		coverage_data[key].first = stringf("%s:%d:%s", __FILE__, __LINE__, __FUNCTION__);
		coverage_data[key].second += it.second->call_counter;
	}

	for (auto &it : extra_coverage_data) {
		if (coverage_data.count(it.first))
			log("WARNING: found duplicate coverage id \"%s\".\n", it.first.c_str());
		coverage_data[it.first].first = it.second.first;
		coverage_data[it.first].second += it.second.second;
	}

	for (CoverData *p = __start_yosys_cover_list; p != __stop_yosys_cover_list; p++) {
		if (coverage_data.count(p->id))
			log("WARNING: found duplicate coverage id \"%s\".\n", p->id);
		coverage_data[p->id].first = stringf("%s:%d:%s", p->file, p->line, p->func);
		coverage_data[p->id].second += p->counter;
	}

	for (auto &it : coverage_data)
		if (!it.second.first.compare(0, strlen(YOSYS_SRC "/"), YOSYS_SRC "/"))
			it.second.first = it.second.first.substr(strlen(YOSYS_SRC "/"));

	return coverage_data;
}

#endif

YOSYS_NAMESPACE_END

<commit_msg>Use a cache for log_id() memory management<commit_after>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "kernel/yosys.h"
#include "libs/sha1/sha1.h"
#include "backends/ilang/ilang_backend.h"

#if !defined(_WIN32) || defined(__MINGW32__)
#  include <sys/time.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <vector>
#include <list>

YOSYS_NAMESPACE_BEGIN

std::vector<FILE*> log_files;
std::vector<std::ostream*> log_streams;
FILE *log_errfile = NULL;
SHA1 *log_hasher = NULL;

bool log_time = false;
bool log_cmd_error_throw = false;
int log_verbose_level;

std::vector<int> header_count;
std::set<RTLIL::IdString> log_id_cache;
std::list<std::string> string_buf;
int string_buf_size = 0;

static struct timeval initial_tv = { 0, 0 };
static bool next_print_log = false;
static int log_newline_count = 0;

#if defined(_WIN32) && !defined(__MINGW32__)
// this will get time information and return it in timeval, simulating gettimeofday()
int gettimeofday(struct timeval *tv, struct timezone *tz)
{
	LARGE_INTEGER counter;
	LARGE_INTEGER freq;

	QueryPerformanceFrequency(&freq);
	QueryPerformanceCounter(&counter);

	counter.QuadPart *= 1000000;
	counter.QuadPart /= freq.QuadPart;

	tv->tv_sec = long(counter.QuadPart / 1000000);
	tv->tv_usec = counter.QuadPart % 1000000;

	return 0;
}
#endif

void logv(const char *format, va_list ap)
{
	while (format[0] == '\n' && format[1] != 0) {
		log("\n");
		format++;
	}

	std::string str = vstringf(format, ap);

	if (str.empty())
		return;

	size_t nnl_pos = str.find_last_not_of('\n');
	if (nnl_pos == std::string::npos)
		log_newline_count += GetSize(str);
	else
		log_newline_count = GetSize(str) - nnl_pos - 1;

	if (log_hasher)
		log_hasher->update(str);

	if (log_time)
	{
		std::string time_str;

		if (next_print_log || initial_tv.tv_sec == 0) {
			next_print_log = false;
			struct timeval tv;
			gettimeofday(&tv, NULL);
			if (initial_tv.tv_sec == 0)
				initial_tv = tv;
			if (tv.tv_usec < initial_tv.tv_usec) {
				tv.tv_sec--;
				tv.tv_usec += 1000000;
			}
			tv.tv_sec -= initial_tv.tv_sec;
			tv.tv_usec -= initial_tv.tv_usec;
			time_str += stringf("[%05d.%06d] ", int(tv.tv_sec), int(tv.tv_usec));
		}

		if (format[0] && format[strlen(format)-1] == '\n')
			next_print_log = true;

		for (auto f : log_files)
			fputs(time_str.c_str(), f);

		for (auto f : log_streams)
			*f << time_str;
	}

	for (auto f : log_files)
		fputs(str.c_str(), f);

	for (auto f : log_streams)
		*f << str;
}

void logv_header(const char *format, va_list ap)
{
	bool pop_errfile = false;

	log_spacer();
	if (header_count.size() > 0)
		header_count.back()++;

	if (int(header_count.size()) <= log_verbose_level && log_errfile != NULL) {
		log_files.push_back(log_errfile);
		pop_errfile = true;
	}

	for (int c : header_count)
		log("%d.", c);
	log(" ");
	logv(format, ap);
	log_flush();

	if (pop_errfile)
		log_files.pop_back();
}

void logv_error(const char *format, va_list ap)
{
	if (log_errfile != NULL)
		log_files.push_back(log_errfile);

	log("ERROR: ");
	logv(format, ap);
	log_flush();
	exit(1);
}

void log(const char *format, ...)
{
	va_list ap;
	va_start(ap, format);
	logv(format, ap);
	va_end(ap);
}

void log_header(const char *format, ...)
{
	va_list ap;
	va_start(ap, format);
	logv_header(format, ap);
	va_end(ap);
}

void log_error(const char *format, ...)
{
	va_list ap;
	va_start(ap, format);
	logv_error(format, ap);
}

void log_cmd_error(const char *format, ...)
{
	va_list ap;
	va_start(ap, format);

	if (log_cmd_error_throw) {
		log("ERROR: ");
		logv(format, ap);
		log_flush();
		throw log_cmd_error_exception();
	}

	logv_error(format, ap);
}

void log_spacer()
{
	while (log_newline_count < 2)
		log("\n");
}

void log_push()
{
	header_count.push_back(0);
}

void log_pop()
{
	header_count.pop_back();
	log_id_cache.clear();
	string_buf.clear();
	string_buf_size = 0;
	log_flush();
}

void log_reset_stack()
{
	while (header_count.size() > 1)
		header_count.pop_back();
	log_id_cache.clear();
	string_buf.clear();
	string_buf_size = 0;
	log_flush();
}

void log_flush()
{
	for (auto f : log_files)
		fflush(f);

	for (auto f : log_streams)
		f->flush();
}

void log_dump_val_worker(RTLIL::SigSpec v) {
	log("%s", log_signal(v));
}

const char *log_signal(const RTLIL::SigSpec &sig, bool autoint)
{
	std::stringstream buf;
	ILANG_BACKEND::dump_sigspec(buf, sig, autoint);

	if (string_buf_size < 100)
		string_buf_size++;
	else
		string_buf.pop_front();
	string_buf.push_back(buf.str());

	return string_buf.back().c_str();
}

const char *log_id(RTLIL::IdString str)
{
	log_id_cache.insert(str);
	const char *p = str.c_str();
	if (p[0] == '\\' && p[1] != '$' && p[1] != 0)
		return p+1;
	return p;
}

void log_cell(RTLIL::Cell *cell, std::string indent)
{
	std::stringstream buf;
	ILANG_BACKEND::dump_cell(buf, indent, cell);
	log("%s", buf.str().c_str());
}

// ---------------------------------------------------
// This is the magic behind the code coverage counters
// ---------------------------------------------------
#ifdef YOSYS_ENABLE_COVER

std::map<std::string, std::pair<std::string, int>> extra_coverage_data;

void cover_extra(std::string parent, std::string id, bool increment) {
	if (extra_coverage_data.count(id) == 0) {
		for (CoverData *p = __start_yosys_cover_list; p != __stop_yosys_cover_list; p++)
			if (p->id == parent)
				extra_coverage_data[id].first = stringf("%s:%d:%s", p->file, p->line, p->func);
		log_assert(extra_coverage_data.count(id));
	}
	if (increment)
		extra_coverage_data[id].second++;
}

std::map<std::string, std::pair<std::string, int>> get_coverage_data()
{
	std::map<std::string, std::pair<std::string, int>> coverage_data;

	for (auto &it : pass_register) {
		std::string key = stringf("passes.%s", it.first.c_str());
		coverage_data[key].first = stringf("%s:%d:%s", __FILE__, __LINE__, __FUNCTION__);
		coverage_data[key].second += it.second->call_counter;
	}

	for (auto &it : extra_coverage_data) {
		if (coverage_data.count(it.first))
			log("WARNING: found duplicate coverage id \"%s\".\n", it.first.c_str());
		coverage_data[it.first].first = it.second.first;
		coverage_data[it.first].second += it.second.second;
	}

	for (CoverData *p = __start_yosys_cover_list; p != __stop_yosys_cover_list; p++) {
		if (coverage_data.count(p->id))
			log("WARNING: found duplicate coverage id \"%s\".\n", p->id);
		coverage_data[p->id].first = stringf("%s:%d:%s", p->file, p->line, p->func);
		coverage_data[p->id].second += p->counter;
	}

	for (auto &it : coverage_data)
		if (!it.second.first.compare(0, strlen(YOSYS_SRC "/"), YOSYS_SRC "/"))
			it.second.first = it.second.first.substr(strlen(YOSYS_SRC "/"));

	return coverage_data;
}

#endif

YOSYS_NAMESPACE_END

<|endoftext|>


<commit_before>//  Copyright (c) 2013, Robert McGibbon
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//////////////////////////////////////////////////////////////////////////////
// Calculation of the fused-sphere van der waals surface of a molecule.
//////////////////////////////////////////////////////////////////////////////

#include <math.h>
#include <algorithm>
#include <utility>
#include <vector>
#include <iostream>
#include <stdio.h>
#include <map>
#include <set>

#include "include/dotsphere.h"
// this is also included in the psi4 code as libmints/vector3.h
#include "include/vector3.h"

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

using namespace std;
using namespace psi;

// A. Bondi (1964). "van der Waals Volumes and Radii". J. Phys. Chem. 68: 441. doi:10.1021/j100785a001
static map<string, double> create_bondi_radii() {
    map<string, double> m;
    m["H"] = 1.2;
    m["C"] = 1.7;
    m["N"] = 1.55;
    m["O"] = 1.52;
    m["F"] = 1.47;
    m["P"] = 1.8;
    m["S"] = 1.8;
    m["Cl"] = 1.75;

    m["Ar"] = 1.88;
    m["As"] = 1.85;
    m["Br"] = 1.85;
    m["Cd"] = 1.62;
    m["Cu"] = 1.4;
    m["Ga"] = 1.87;
    m["Au"] = 1.66;
    m["He"] = 1.4;
    m["In"] = 1.93;
    m["I"] = 1.98;
    m["Kr"] = 2.02;
    m["Pb"] = 2.02;
    m["Li"] = 1.82;
    m["Mg"] = 1.73;
    m["Hg"] = 1.70;
    m["Ne"] = 1.54;
    m["Ni"] = 1.64;
    m["Pd"] = 1.63;
    m["Pt"] = 1.8;
    m["K"] = 2.75;
    m["Se"] = 1.90;
    m["Si"] = 2.1;
    m["Ag"] = 1.9;
    m["Na"] = 2.27;
    m["Te"] = 2.06;
    m["Tl"] = 1.96;
    m["Sn"] = 2.17;
    m["U"] = 1.86;
    m["Xe"] = 2.16;
    m["Zn"] = 1.37;
    return m;
}
static map<string, double> BONDI_RADII = create_bondi_radii();

// #############################################################################
// # Functions
// #############################################################################
//
vector<Vector3> vdw_surface(vector<Vector3> coordinates, vector<string> elements,
                            double scale_factor, double density) {
    // Compute points on the VDW surface of a molecule
    //
    // Parameters
    // ----------
    // coordinates : np.ndarray, shape=(n_atoms, 3)
    //     The cartesian coordinates of the nuclei, in units of ANGSTROMS
    // elements : list, shape=(n_atoms)
    //     The element symbols (C, H, etc) for each of the nuceli
    // scale_factor : float
    //     The points on the molecular surface are set at a distance of
    //     scale_factor * vdw_radius away from each of the atoms.
    // density : float
    //     The (approximate) number of points to generate per square angstrom
    //     of surface area. 1.0 is the default recommended by Kollman & Singh.
    if (coordinates.size() != elements.size()) {
        fprintf(stderr, "coordinate.size doesnot match elements.size");
        exit(1);
    }

    vector<Vector3> surfacepoints;
    vector<double> radii;

    for (size_t i = 0; i < elements.size(); i++) {
        // todo: check for error if element not in BONDI_RADII table
        if (BONDI_RADII.find(elements[i]) != BONDI_RADII.end()) {
            radii.push_back(BONDI_RADII[elements[i]] * scale_factor);
        } else {
            fprintf(stderr, "%s is not a supported element", elements[i].c_str());
            exit(1);
        }
    }

    for (size_t i = 0; i < coordinates.size(); i++) {
        // this could be optimized -- we don't need to compute the dotsphere.
        // at maximum we only need to compute it once per unique element / radius
        vector<Vector3> dots = dotsphere(density * ((4.0/3.0) * M_PI * radii[i]*radii[i]*radii[i]));

        for (size_t j = 0; j < dots.size(); j++)
            dots[j] = coordinates[i] + radii[i]*dots[j];

        // all of the atoms that i is close to
        typedef std::pair<double, size_t> Neighbor;
        vector<Neighbor> neighbors;
        for (size_t j = 0; j < coordinates.size(); j++) {
            if (i == j)
                continue;
            double d = (coordinates[i] - coordinates[j]).norm();
            if (d < (radii[i] + radii[j])) {
                neighbors.push_back(make_pair(d, j));
            }
        }
        sort(neighbors.begin(), neighbors.end());

        for (size_t k = 0; k < dots.size(); k++) {
            int accessible = 1;
            for (vector<Neighbor>::iterator it = neighbors.begin() ; it != neighbors.end(); ++it) {
                if ((coordinates[(*it).second] - dots[k]).norm() < radii[(*it).second]) {
                    accessible = 0;
                    break;
                }
            }
            if (accessible)
                surfacepoints.push_back(dots[k]);
        }
    }

    return surfacepoints;
}

// extern "C" void vdw_surface(double* coordinates, char* elements, int n_elements,
//                             double scale_factor, double density, double* out, int* n_out) {
//     vector<string> elements_;
//     vector<Vector3> coordinates_;
//
//     istringstream iss(elements);
//     string s;
//     for (int i = 0; i < n_elements; i++) {
//         getline(iss, s, ' ');
//         elements_.push_back(string(s));
//         coordinates_.push_back(Vector3(coordinates[3*i+0], coordinates[3*i+1], coordinates[3*i+2]));
//     }
//
//     vector<Vector3> points = vdw_surface(coordinates_, elements_, scale_factor, density);
//     for (size_t i = 0; i < points.size(); i++) {
//         out[3*i+0] = points[i][0];
//         out[3*i+1] = points[i][1];
//         out[3*i+2] = points[i][2];
//     }
//     *n_out = points.size();
// }
<commit_msg>Update vdwsurface.cc<commit_after>//////////////////////////////////////////////////////////////////////////////
// Calculation of the fused-sphere van der waals surface of a molecule.
//////////////////////////////////////////////////////////////////////////////

#include <math.h>
#include <algorithm>
#include <utility>
#include <vector>
#include <iostream>
#include <stdio.h>
#include <map>
#include <set>

#include "include/dotsphere.h"
// this is also included in the psi4 code as libmints/vector3.h
#include "include/vector3.h"

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

using namespace std;
using namespace psi;

// A. Bondi (1964). "van der Waals Volumes and Radii". J. Phys. Chem. 68: 441. doi:10.1021/j100785a001
static map<string, double> create_bondi_radii() {
    map<string, double> m;
    m["H"] = 1.2;
    m["C"] = 1.7;
    m["N"] = 1.55;
    m["O"] = 1.52;
    m["F"] = 1.47;
    m["P"] = 1.8;
    m["S"] = 1.8;
    m["Cl"] = 1.75;

    m["Ar"] = 1.88;
    m["As"] = 1.85;
    m["Br"] = 1.85;
    m["Cd"] = 1.62;
    m["Cu"] = 1.4;
    m["Ga"] = 1.87;
    m["Au"] = 1.66;
    m["He"] = 1.4;
    m["In"] = 1.93;
    m["I"] = 1.98;
    m["Kr"] = 2.02;
    m["Pb"] = 2.02;
    m["Li"] = 1.82;
    m["Mg"] = 1.73;
    m["Hg"] = 1.70;
    m["Ne"] = 1.54;
    m["Ni"] = 1.64;
    m["Pd"] = 1.63;
    m["Pt"] = 1.8;
    m["K"] = 2.75;
    m["Se"] = 1.90;
    m["Si"] = 2.1;
    m["Ag"] = 1.9;
    m["Na"] = 2.27;
    m["Te"] = 2.06;
    m["Tl"] = 1.96;
    m["Sn"] = 2.17;
    m["U"] = 1.86;
    m["Xe"] = 2.16;
    m["Zn"] = 1.37;
    return m;
}
static map<string, double> BONDI_RADII = create_bondi_radii();

// #############################################################################
// # Functions
// #############################################################################
//
vector<Vector3> vdw_surface(vector<Vector3> coordinates, vector<string> elements,
                            double scale_factor, double density) {
    // Compute points on the VDW surface of a molecule
    //
    // Parameters
    // ----------
    // coordinates : np.ndarray, shape=(n_atoms, 3)
    //     The cartesian coordinates of the nuclei, in units of ANGSTROMS
    // elements : list, shape=(n_atoms)
    //     The element symbols (C, H, etc) for each of the nuceli
    // scale_factor : float
    //     The points on the molecular surface are set at a distance of
    //     scale_factor * vdw_radius away from each of the atoms.
    // density : float
    //     The (approximate) number of points to generate per square angstrom
    //     of surface area. 1.0 is the default recommended by Kollman & Singh.
    if (coordinates.size() != elements.size()) {
        fprintf(stderr, "coordinate.size doesnot match elements.size");
        exit(1);
    }

    vector<Vector3> surfacepoints;
    vector<double> radii;

    for (size_t i = 0; i < elements.size(); i++) {
        // todo: check for error if element not in BONDI_RADII table
        if (BONDI_RADII.find(elements[i]) != BONDI_RADII.end()) {
            radii.push_back(BONDI_RADII[elements[i]] * scale_factor);
        } else {
            fprintf(stderr, "%s is not a supported element", elements[i].c_str());
            exit(1);
        }
    }

    for (size_t i = 0; i < coordinates.size(); i++) {
        // this could be optimized -- we don't need to compute the dotsphere.
        // at maximum we only need to compute it once per unique element / radius
        vector<Vector3> dots = dotsphere(density * ((4.0/3.0) * M_PI * radii[i]*radii[i]*radii[i]));

        for (size_t j = 0; j < dots.size(); j++)
            dots[j] = coordinates[i] + radii[i]*dots[j];

        // all of the atoms that i is close to
        typedef std::pair<double, size_t> Neighbor;
        vector<Neighbor> neighbors;
        for (size_t j = 0; j < coordinates.size(); j++) {
            if (i == j)
                continue;
            double d = (coordinates[i] - coordinates[j]).norm();
            if (d < (radii[i] + radii[j])) {
                neighbors.push_back(make_pair(d, j));
            }
        }
        sort(neighbors.begin(), neighbors.end());

        for (size_t k = 0; k < dots.size(); k++) {
            int accessible = 1;
            for (vector<Neighbor>::iterator it = neighbors.begin() ; it != neighbors.end(); ++it) {
                if ((coordinates[(*it).second] - dots[k]).norm() < radii[(*it).second]) {
                    accessible = 0;
                    break;
                }
            }
            if (accessible)
                surfacepoints.push_back(dots[k]);
        }
    }

    return surfacepoints;
}

// extern "C" void vdw_surface(double* coordinates, char* elements, int n_elements,
//                             double scale_factor, double density, double* out, int* n_out) {
//     vector<string> elements_;
//     vector<Vector3> coordinates_;
//
//     istringstream iss(elements);
//     string s;
//     for (int i = 0; i < n_elements; i++) {
//         getline(iss, s, ' ');
//         elements_.push_back(string(s));
//         coordinates_.push_back(Vector3(coordinates[3*i+0], coordinates[3*i+1], coordinates[3*i+2]));
//     }
//
//     vector<Vector3> points = vdw_surface(coordinates_, elements_, scale_factor, density);
//     for (size_t i = 0; i < points.size(); i++) {
//         out[3*i+0] = points[i][0];
//         out[3*i+1] = points[i][1];
//         out[3*i+2] = points[i][2];
//     }
//     *n_out = points.size();
// }
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013-2014 Nicholas Corgan (n.corgan@gmail.com)
 *
 * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
 * or copy at http://opensource.org/licenses/MIT)
 */

#include <boost/assign.hpp>
#include <boost/format.hpp>

#include <pkmn/enums.hpp>
#include <pkmn/paths.hpp>
#include <pkmn/trainer.hpp>
#include <pkmn/database/queries.hpp>
#include <pkmn/types/dict.hpp>
#include <pkmn/types/prng.hpp>

#include "bag_impl.hpp"
#include "team_pokemon_gen1impl.hpp"
#include "team_pokemon_gen2impl.hpp"
#include "team_pokemon_modernimpl.hpp"

#include "trainer_impl.hpp"

using namespace std;

namespace pkmn
{
    trainer::sptr trainer::make(unsigned int game, pokemon_text name, unsigned int gender)
    {
        return sptr(new trainer_impl(game, name, gender));
    }

    trainer::sptr trainer::make(std::string game, pokemon_text name, std::string gender)
    {
        return make(database::get_game_id(game),
                    name,
                    ((gender == "Female") ? Genders::FEMALE : Genders::MALE));
    }

    trainer_impl::trainer_impl(unsigned int game, pokemon_text name, unsigned int gender): trainer()
    {
        prng::sptr _rand_gen = prng::get(_generation);

        _game_id = game;
        _generation = database::get_generation(_game_id);
        _trainer_name = name;
        _gender_id = gender;
        _money = 0;

        /*
         * Public vs. secret ID's weren't a factor in Generations I-II
         */
        _trainer_id = (_generation < 3) ? (_rand_gen->lcrng() % 65535)
                                        : _rand_gen->lcrng();

        _party = pokemon_team_t(6);
        for(int i = 0; i < 6; i++)
        {
            _party[i] = team_pokemon::make(Species::NONE, _game_id, 1, Moves::NONE, Moves::NONE,
                                          Moves::NONE, Moves::NONE);
        }

        _bag = bag::make(_game_id);
    }

    pokemon_text trainer_impl::get_game() const {return database::get_game_name(_game_id);}

    unsigned int trainer_impl::get_generation() const {return _generation;}

    pokemon_text trainer_impl::get_name() const {return _trainer_name;}

    unsigned int trainer_impl::get_money() const {return _money;}

    pokemon_text trainer_impl::get_gender() const
    {
        return (_gender_id == Genders::MALE) ? "Male" : "Female";
    }

    unsigned int trainer_impl::get_id() const
    {
        return (_generation < 3) ? _tid.public_id
                                 : _trainer_id;
    }

    unsigned short trainer_impl::get_public_id() const {return _tid.public_id;}

    unsigned short trainer_impl::get_secret_id() const
    {
        return (_generation < 3) ? 0
                                 : _tid.secret_id;
    }

    void trainer_impl::set_name(pokemon_text name)
    {
        _trainer_name = (name.std_string().length() >= 1
                         and name.std_string().length() <= 7) ? name
                                                                : _trainer_name;
    }

    void trainer_impl::set_money(unsigned int money)
    {
        _money = (money > 999999) ? _money : money;
    }

    void trainer_impl::set_gender(pokemon_text gender)
    {
        if(gender == "Male") _gender_id = Genders::MALE;
        else if(gender == "Female") _gender_id = Genders::FEMALE;
    }

    void trainer_impl::set_id(unsigned int id) {_trainer_id = id;}

    void trainer_impl::set_public_id(unsigned short id) {_tid.public_id = id;}

    void trainer_impl::set_secret_id(unsigned short id) {_tid.secret_id = id;}

    team_pokemon::sptr trainer_impl::get_pokemon(unsigned int pos)
    {
        //If invalid position given, return invalid Pokemon
        if(pos == 0 or pos > 6) return team_pokemon::make(Species::INVALID, _game_id, 0, Moves::NONE,
                                                          Moves::NONE, Moves::NONE, Moves::NONE);
        else return _party[pos-1];
    }

    void trainer_impl::set_pokemon(unsigned int pos, team_pokemon::sptr t_pkmn)
    {
        //Check for valid position, don't do anything otherwise
        if(pos >= 1 and pos <= 6)
        {
            //TODO: more through check (items, forms, etc)
            if(database::get_version_group(_game_id) ==
               database::get_version_group(t_pkmn->get_game_id())) _party[pos-1] = t_pkmn;
        }
    }

    void trainer_impl::remove_pokemon(unsigned int pos)
    {
        unsigned int actual_pos = (pos > 6) ? 5 : (pos == 0) ? 0 : (pos-1);

        team_pokemon::sptr blank_pokemon = team_pokemon::make(Species::NONE, _game_id, 0, Moves::NONE,
                                           Moves::NONE, Moves::NONE, Moves::NONE);

        set_pokemon(actual_pos, blank_pokemon);

        //Move over any non-blank Pokemon in later positions
        for(int i = (actual_pos+1); i < 6; i++)
        {
            if(_party[i]->get_species_id() == Species::NONE) break;
            else
            {
                _party[i-1] = _party[i];
                _party[i] = blank_pokemon;
            }
        }
    }

    void trainer_impl::get_party(pokemon_team_t& party)
    {
        party = _party;
    }

    //TODO: allow for other trainers' Pokemon
    void trainer_impl::set_party(pokemon_team_t& party)
    {
        //Only set party if party and all Pokemon are valid
        if(party.size() != 6) return;
        if(party[0]->get_game_id() != _game_id or
           party[1]->get_game_id() != _game_id or
           party[2]->get_game_id() != _game_id or
           party[3]->get_game_id() != _game_id or
           party[4]->get_game_id() != _game_id or
           party[5]->get_game_id() != _game_id) return;

        _party = party;
    }

    bag::sptr trainer_impl::get_bag() const {return _bag;}

    unsigned int trainer_impl::get_game_id() const {return _game_id;}
} /* namespace pkmn */
<commit_msg>trainer: made sure _generation is set before using it to create PRNG<commit_after>/*
 * Copyright (c) 2013-2014 Nicholas Corgan (n.corgan@gmail.com)
 *
 * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
 * or copy at http://opensource.org/licenses/MIT)
 */

#include <boost/assign.hpp>
#include <boost/format.hpp>

#include <pkmn/enums.hpp>
#include <pkmn/paths.hpp>
#include <pkmn/trainer.hpp>
#include <pkmn/database/queries.hpp>
#include <pkmn/types/dict.hpp>
#include <pkmn/types/prng.hpp>

#include "bag_impl.hpp"
#include "team_pokemon_gen1impl.hpp"
#include "team_pokemon_gen2impl.hpp"
#include "team_pokemon_modernimpl.hpp"

#include "trainer_impl.hpp"

using namespace std;

namespace pkmn
{
    trainer::sptr trainer::make(unsigned int game, pokemon_text name, unsigned int gender)
    {
        return sptr(new trainer_impl(game, name, gender));
    }

    trainer::sptr trainer::make(std::string game, pokemon_text name, std::string gender)
    {
        return make(database::get_game_id(game),
                    name,
                    ((gender == "Female") ? Genders::FEMALE : Genders::MALE));
    }

    trainer_impl::trainer_impl(unsigned int game, pokemon_text name, unsigned int gender): trainer()
    {
        _game_id = game;
        _generation = database::get_generation(_game_id);
        _trainer_name = name;
        _gender_id = gender;
        _money = 0;

        prng::sptr _rand_gen = prng::get(_generation);

        /*
         * Public vs. secret ID's weren't a factor in Generations I-II
         */
        _trainer_id = (_generation < 3) ? (_rand_gen->lcrng() % 65535)
                                        : _rand_gen->lcrng();

        _party = pokemon_team_t(6);
        for(int i = 0; i < 6; i++)
        {
            _party[i] = team_pokemon::make(Species::NONE, _game_id, 1, Moves::NONE, Moves::NONE,
                                          Moves::NONE, Moves::NONE);
        }

        _bag = bag::make(_game_id);
    }

    pokemon_text trainer_impl::get_game() const {return database::get_game_name(_game_id);}

    unsigned int trainer_impl::get_generation() const {return _generation;}

    pokemon_text trainer_impl::get_name() const {return _trainer_name;}

    unsigned int trainer_impl::get_money() const {return _money;}

    pokemon_text trainer_impl::get_gender() const
    {
        return (_gender_id == Genders::MALE) ? "Male" : "Female";
    }

    unsigned int trainer_impl::get_id() const
    {
        return (_generation < 3) ? _tid.public_id
                                 : _trainer_id;
    }

    unsigned short trainer_impl::get_public_id() const {return _tid.public_id;}

    unsigned short trainer_impl::get_secret_id() const
    {
        return (_generation < 3) ? 0
                                 : _tid.secret_id;
    }

    void trainer_impl::set_name(pokemon_text name)
    {
        _trainer_name = (name.std_string().length() >= 1
                         and name.std_string().length() <= 7) ? name
                                                                : _trainer_name;
    }

    void trainer_impl::set_money(unsigned int money)
    {
        _money = (money > 999999) ? _money : money;
    }

    void trainer_impl::set_gender(pokemon_text gender)
    {
        if(gender == "Male") _gender_id = Genders::MALE;
        else if(gender == "Female") _gender_id = Genders::FEMALE;
    }

    void trainer_impl::set_id(unsigned int id) {_trainer_id = id;}

    void trainer_impl::set_public_id(unsigned short id) {_tid.public_id = id;}

    void trainer_impl::set_secret_id(unsigned short id) {_tid.secret_id = id;}

    team_pokemon::sptr trainer_impl::get_pokemon(unsigned int pos)
    {
        //If invalid position given, return invalid Pokemon
        if(pos == 0 or pos > 6) return team_pokemon::make(Species::INVALID, _game_id, 0, Moves::NONE,
                                                          Moves::NONE, Moves::NONE, Moves::NONE);
        else return _party[pos-1];
    }

    void trainer_impl::set_pokemon(unsigned int pos, team_pokemon::sptr t_pkmn)
    {
        //Check for valid position, don't do anything otherwise
        if(pos >= 1 and pos <= 6)
        {
            //TODO: more through check (items, forms, etc)
            if(database::get_version_group(_game_id) ==
               database::get_version_group(t_pkmn->get_game_id())) _party[pos-1] = t_pkmn;
        }
    }

    void trainer_impl::remove_pokemon(unsigned int pos)
    {
        unsigned int actual_pos = (pos > 6) ? 5 : (pos == 0) ? 0 : (pos-1);

        team_pokemon::sptr blank_pokemon = team_pokemon::make(Species::NONE, _game_id, 0, Moves::NONE,
                                           Moves::NONE, Moves::NONE, Moves::NONE);

        set_pokemon(actual_pos, blank_pokemon);

        //Move over any non-blank Pokemon in later positions
        for(int i = (actual_pos+1); i < 6; i++)
        {
            if(_party[i]->get_species_id() == Species::NONE) break;
            else
            {
                _party[i-1] = _party[i];
                _party[i] = blank_pokemon;
            }
        }
    }

    void trainer_impl::get_party(pokemon_team_t& party)
    {
        party = _party;
    }

    //TODO: allow for other trainers' Pokemon
    void trainer_impl::set_party(pokemon_team_t& party)
    {
        //Only set party if party and all Pokemon are valid
        if(party.size() != 6) return;
        if(party[0]->get_game_id() != _game_id or
           party[1]->get_game_id() != _game_id or
           party[2]->get_game_id() != _game_id or
           party[3]->get_game_id() != _game_id or
           party[4]->get_game_id() != _game_id or
           party[5]->get_game_id() != _game_id) return;

        _party = party;
    }

    bag::sptr trainer_impl::get_bag() const {return _bag;}

    unsigned int trainer_impl::get_game_id() const {return _game_id;}
} /* namespace pkmn */
<|endoftext|>


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

#include "Input.hxx"
#include "was/async/Error.hxx"
#include "event/PipeEvent.hxx"
#include "istream/istream.hxx"
#include "istream/UnusedPtr.hxx"
#include "istream/Result.hxx"
#include "istream/Bucket.hxx"
#include "pool/pool.hxx"
#include "fb_pool.hxx"
#include "SliceFifoBuffer.hxx"
#include "io/Buffered.hxx"
#include "io/SpliceSupport.hxx"
#include "system/Error.hxx"
#include "util/ScopeExit.hxx"
#include "util/Exception.hxx"

#include <errno.h>
#include <string.h>
#include <unistd.h>

class WasInput final : Istream {
	PipeEvent event;

	WasInputHandler &handler;

	SliceFifoBuffer buffer;

	uint64_t received = 0, length;

	bool direct = false;

	bool enabled = false;

	bool closed = false, known_length = false;

public:
	WasInput(struct pool &p, EventLoop &event_loop, FileDescriptor fd,
		 WasInputHandler &_handler) noexcept
		:Istream(p),
		 event(event_loop, BIND_THIS_METHOD(EventCallback), fd),
		 handler(_handler) {
	}

	void Free(std::exception_ptr ep) noexcept;

	using Istream::Destroy;

	void DestroyUnused() noexcept {
		assert(!HasHandler());
		assert(!closed);
		assert(!buffer.IsDefined());

		Destroy();
	}

	UnusedIstreamPtr Enable() noexcept {
		assert(!enabled);
		enabled = true;
		ScheduleRead();
		return UnusedIstreamPtr(this);
	}

	void Disable() noexcept {
		event.Cancel();
	}

	bool SetLength(uint64_t _length) noexcept;
	void PrematureThrow(uint64_t _length);
	void Premature(uint64_t _length) noexcept;

private:
	bool HasPipe() const noexcept {
		return event.IsDefined();
	}

	FileDescriptor GetPipe() noexcept {
		return event.GetFileDescriptor();
	}

	bool CanRelease() const {
		return known_length && received == length;
	}

	/**
	 * @return false if the #WasInput has been destroyed
	 */
	bool ReleasePipe() noexcept {
		assert(HasPipe());

		event.Cancel();
		event.ReleaseFileDescriptor();

		return handler.WasInputRelease();
	}

	/**
	 * @return false if the #WasInput has been destroyed
	 */
	bool CheckReleasePipe() noexcept {
		return !CanRelease() || ReleasePipe();
	}

	void ScheduleRead() noexcept {
		assert(HasPipe());
		assert(!buffer.IsDefined() || !buffer.IsFull());

		event.ScheduleRead();
	}

	void AbortError(std::exception_ptr ep) noexcept {
		buffer.FreeIfDefined();
		event.Cancel();

		/* protect against recursive Free() call within the istream
		   handler */
		closed = true;

		handler.WasInputError();
		DestroyError(ep);
	}

	void AbortError(const char *msg) noexcept {
		AbortError(std::make_exception_ptr(WasProtocolError(msg)));
	}

	void Eof() noexcept {
		assert(known_length);
		assert(received == length);
		assert(!buffer.IsDefined());

		event.Cancel();

		handler.WasInputEof();
		DestroyEof();
	}

	bool CheckEof() noexcept {
		if (CanRelease() && buffer.empty()) {
			Eof();
			return true;
		} else
			return false;
	}

	/**
	 * Consume data from the input buffer.
	 *
	 * @return false if the handler blocks or if this object has been
	 * destroyed
	 */
	bool SubmitBuffer() noexcept {
		auto r = buffer.Read();
		if (!r.empty()) {
			size_t nbytes = InvokeData(r.data, r.size);
			if (nbytes == 0)
				return false;

			buffer.Consume(nbytes);
			buffer.FreeIfEmpty();
		}

		if (CheckEof())
			return false;

		return true;
	}

	/*
	 * socket i/o
	 *
	 */

	/**
	 * Throws on error.
	 */
	void ReadToBuffer();

	bool TryBuffered() noexcept;
	bool TryDirect() noexcept;

	void TryRead() noexcept {
		if (direct) {
			if (SubmitBuffer() && buffer.empty())
				TryDirect();
		} else {
			TryBuffered();
		}
	}

	void EventCallback(unsigned events) noexcept;

	/* virtual methods from class Istream */

	void _SetDirect(FdTypeMask mask) noexcept override {
		direct = (mask & ISTREAM_TO_PIPE) != 0;
	}

	off_t _GetAvailable(bool partial) noexcept override {
		if (known_length)
			return length - received + buffer.GetAvailable();
		else if (partial)
			return buffer.GetAvailable();
		else
			return -1;
	}

	void _Read() noexcept override {
		if (SubmitBuffer())
			TryRead();
	}

	void _FillBucketList(IstreamBucketList &list) override;
	size_t _ConsumeBucketList(size_t nbytes) noexcept override;

	void _Close() noexcept override {
		buffer.FreeIfDefined();
		event.Cancel();

		/* protect against recursive Free() call within the istream
		   handler */
		closed = true;

		handler.WasInputClose(received);

		Destroy();
	}
};

void
WasInput::ReadToBuffer()
{
	buffer.AllocateIfNull(fb_pool_get());

	size_t max_length = FB_SIZE;
	if (known_length) {
		uint64_t rest = length - received;
		if (rest < (uint64_t)max_length)
			max_length = rest;

		if (max_length == 0)
			/* all the data we need is already in the buffer */
			return;
	}

	ssize_t nbytes = read_to_buffer(GetPipe().Get(), buffer, max_length);
	assert(nbytes != -2);

	if (nbytes == 0)
		throw WasProtocolError("server closed the data connection");

	if (nbytes < 0) {
		const int e = errno;

		if (e == EAGAIN) {
			buffer.FreeIfEmpty();
			ScheduleRead();
			return;
		}

		throw MakeErrno(e, "read error on WAS data connection");
	}

	received += nbytes;

	if (buffer.IsFull())
		event.CancelRead();
}

inline bool
WasInput::TryBuffered() noexcept
{
	if (HasPipe()) {
		try {
			ReadToBuffer();
		} catch (...) {
			AbortError(std::current_exception());
			return false;
		}

		if (!CheckReleasePipe())
			return false;
	}

	if (SubmitBuffer()) {
		assert(!buffer.IsDefinedAndFull());

		if (HasPipe())
			ScheduleRead();
	}

	return true;
}

inline bool
WasInput::TryDirect() noexcept
{
	assert(buffer.empty());
	assert(!buffer.IsDefined());

	size_t max_length = 0x1000000;
	if (known_length) {
		uint64_t rest = length - received;
		if (rest < (uint64_t)max_length)
			max_length = rest;
	}

	ssize_t nbytes = InvokeDirect(FdType::FD_PIPE, GetPipe().Get(), max_length);
	if (nbytes == ISTREAM_RESULT_BLOCKING) {
		event.CancelRead();
		return false;
	}

	if (nbytes == ISTREAM_RESULT_EOF || nbytes == ISTREAM_RESULT_CLOSED)
		return false;

	if (nbytes < 0) {
		const int e = errno;

		if (e == EAGAIN) {
			ScheduleRead();
			return false;
		}

		AbortError(std::make_exception_ptr(MakeErrno(e,
							     "read error on WAS data connection")));
		return false;
	}

	received += nbytes;

	if (!CheckReleasePipe())
		return false;

	if (CheckEof())
		return false;

	ScheduleRead();
	return true;
}

/*
 * libevent callback
 *
 */

inline void
WasInput::EventCallback(unsigned) noexcept
{
	assert(HasPipe());

	TryRead();
}

/*
 * constructor
 *
 */

WasInput *
was_input_new(struct pool &pool, EventLoop &event_loop, FileDescriptor fd,
	      WasInputHandler &handler) noexcept
{
	assert(fd.IsDefined());

	return NewFromPool<WasInput>(pool, pool, event_loop, fd,
				     handler);
}

inline void
WasInput::Free(std::exception_ptr ep) noexcept
{
	assert(ep || closed || !enabled);

	buffer.FreeIfDefined();

	event.Cancel();

	if (!closed && enabled)
		DestroyError(ep);
}

void
was_input_free(WasInput *input, std::exception_ptr ep) noexcept
{
	input->Free(ep);
}

void
was_input_free_unused(WasInput *input) noexcept
{
	input->DestroyUnused();
}

UnusedIstreamPtr
was_input_enable(WasInput &input) noexcept
{
	return input.Enable();
}

void
was_input_disable(WasInput &input) noexcept
{
	input.Disable();
}

inline bool
WasInput::SetLength(uint64_t _length) noexcept
{
	if (known_length) {
		if (_length == length)
			return true;

		// TODO: don't invoke Istream::DestroyError() if not yet enabled
		AbortError("wrong input length announced");
		return false;
	}

	if (_length < received) {
		/* this length must be bogus, because we already received more than that from the socket */
		AbortError("announced length is too small");
		return false;
	}

	length = _length;
	known_length = true;

	if (!CheckReleasePipe())
		return false;

	if (enabled && CheckEof())
		return false;

	return true;
}

bool
was_input_set_length(WasInput *input, uint64_t length) noexcept
{
	return input->SetLength(length);
}

void
WasInput::PrematureThrow(uint64_t _length)
{
	buffer.FreeIfDefined();
	event.Cancel();

	if (known_length && _length > length)
		throw WasProtocolError("announced premature length is too large");

	if (_length < received)
		throw WasProtocolError("announced premature length is too small");

	uint64_t remaining = _length - received;

	while (remaining > 0) {
		uint8_t discard_buffer[4096];
		size_t size = std::min(remaining, uint64_t(sizeof(discard_buffer)));
		ssize_t nbytes = GetPipe().Read(discard_buffer, size);
		if (nbytes < 0)
			throw NestException(std::make_exception_ptr(MakeErrno("Read error")),
					    WasError("read error on WAS data connection"));

		if (nbytes == 0)
			throw WasProtocolError("server closed the WAS data connection");

		remaining -= nbytes;
	}
}

inline void
WasInput::Premature(uint64_t _length) noexcept
{
	try {
		PrematureThrow(_length);
	} catch (...) {
		/* protocol error - notify our WasInputHandler */
		handler.WasInputError();
		DestroyError(std::current_exception());
		return;
	}

	/* recovery was successful */

	/* first, release the pipe (which cannot be released
	   already) */
	if (!ReleasePipe())
		/* WasInputHandler::WasInputRelease() has failed and
		   everything has been cleaned up already */
		return;

	/* pretend this is end-of-file, which will cause the
	   WasInputHandler to allow reusing the connection */
	handler.WasInputEof();

	/* finall let our IstreamHandler know that the stream was
	   closed prematurely */
	DestroyError(std::make_exception_ptr(WasProtocolError("premature end of WAS response")));
}

void
was_input_premature(WasInput *input, uint64_t length) noexcept
{
	input->Premature(length);
}

void
was_input_premature_throw(WasInput *input, uint64_t length)
{
	AtScopeExit(input) { input->Destroy(); };
	input->PrematureThrow(length);
	throw WasProtocolError("premature end of WAS response");
}

void
WasInput::_FillBucketList(IstreamBucketList &list)
{
	auto r = buffer.Read();
	if (r.empty()) {
		if (!HasPipe())
			return;

		try {
			ReadToBuffer();
		} catch (...) {
			handler.WasInputError();
			Destroy();
			throw;
		}

		if (!CheckReleasePipe()) {
			// TODO: deal with this condition properly or improve error message
			handler.WasInputError();
			Destroy();
			throw std::runtime_error("WAS peer failed");
		}

		r = buffer.Read();
		if (r.empty()) {
			list.SetMore();
			return;
		}
	}

	list.Push(r.ToVoid());

	if (!known_length || received < length)
		list.SetMore();
}

size_t
WasInput::_ConsumeBucketList(size_t nbytes) noexcept
{
	size_t consumed = std::min(buffer.GetAvailable(), nbytes);

	buffer.Consume(consumed);
	buffer.FreeIfEmpty();

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

#include "Input.hxx"
#include "was/async/Error.hxx"
#include "event/PipeEvent.hxx"
#include "istream/istream.hxx"
#include "istream/UnusedPtr.hxx"
#include "istream/Result.hxx"
#include "istream/Bucket.hxx"
#include "pool/pool.hxx"
#include "fb_pool.hxx"
#include "SliceFifoBuffer.hxx"
#include "io/Buffered.hxx"
#include "io/SpliceSupport.hxx"
#include "system/Error.hxx"
#include "util/ScopeExit.hxx"
#include "util/Exception.hxx"

#include <errno.h>
#include <string.h>
#include <unistd.h>

class WasInput final : Istream {
	PipeEvent event;

	WasInputHandler &handler;

	SliceFifoBuffer buffer;

	uint64_t received = 0, length;

	bool direct = false;

	bool enabled = false;

	bool closed = false, known_length = false;

public:
	WasInput(struct pool &p, EventLoop &event_loop, FileDescriptor fd,
		 WasInputHandler &_handler) noexcept
		:Istream(p),
		 event(event_loop, BIND_THIS_METHOD(EventCallback), fd),
		 handler(_handler) {
	}

	void Free(std::exception_ptr ep) noexcept;

	using Istream::Destroy;

	void DestroyUnused() noexcept {
		assert(!HasHandler());
		assert(!closed);
		assert(!buffer.IsDefined());

		Destroy();
	}

	UnusedIstreamPtr Enable() noexcept {
		assert(!enabled);
		enabled = true;
		ScheduleRead();
		return UnusedIstreamPtr(this);
	}

	void Disable() noexcept {
		event.Cancel();
	}

	bool SetLength(uint64_t _length) noexcept;
	void PrematureThrow(uint64_t _length);
	void Premature(uint64_t _length) noexcept;

private:
	bool HasPipe() const noexcept {
		return event.IsDefined();
	}

	FileDescriptor GetPipe() noexcept {
		return event.GetFileDescriptor();
	}

	bool CanRelease() const {
		return known_length && received == length;
	}

	/**
	 * @return false if the #WasInput has been destroyed
	 */
	bool ReleasePipe() noexcept {
		assert(HasPipe());

		event.Cancel();
		event.ReleaseFileDescriptor();

		return handler.WasInputRelease();
	}

	/**
	 * @return false if the #WasInput has been destroyed
	 */
	bool CheckReleasePipe() noexcept {
		return !CanRelease() || ReleasePipe();
	}

	void ScheduleRead() noexcept {
		assert(HasPipe());
		assert(!buffer.IsDefined() || !buffer.IsFull());

		event.ScheduleRead();
	}

	void AbortError(std::exception_ptr ep) noexcept {
		buffer.FreeIfDefined();
		event.Cancel();

		/* protect against recursive Free() call within the istream
		   handler */
		closed = true;

		handler.WasInputError();
		DestroyError(ep);
	}

	void AbortError(const char *msg) noexcept {
		AbortError(std::make_exception_ptr(WasProtocolError(msg)));
	}

	void Eof() noexcept {
		assert(known_length);
		assert(received == length);
		assert(!buffer.IsDefined());

		event.Cancel();

		handler.WasInputEof();
		DestroyEof();
	}

	bool CheckEof() noexcept {
		if (CanRelease() && buffer.empty()) {
			Eof();
			return true;
		} else
			return false;
	}

	/**
	 * Consume data from the input buffer.
	 *
	 * @return false if the handler blocks or if this object has been
	 * destroyed
	 */
	bool SubmitBuffer() noexcept {
		auto r = buffer.Read();
		if (!r.empty()) {
			size_t nbytes = InvokeData(r.data, r.size);
			if (nbytes == 0)
				return false;

			buffer.Consume(nbytes);
			buffer.FreeIfEmpty();
		}

		if (CheckEof())
			return false;

		return true;
	}

	/*
	 * socket i/o
	 *
	 */

	/**
	 * Throws on error.
	 */
	void ReadToBuffer();

	bool TryBuffered() noexcept;
	bool TryDirect() noexcept;

	void TryRead() noexcept {
		if (direct) {
			if (SubmitBuffer() && buffer.empty())
				TryDirect();
		} else {
			TryBuffered();
		}
	}

	void EventCallback(unsigned events) noexcept;

	/* virtual methods from class Istream */

	void _SetDirect(FdTypeMask mask) noexcept override {
		direct = (mask & ISTREAM_TO_PIPE) != 0;
	}

	off_t _GetAvailable(bool partial) noexcept override {
		if (known_length)
			return length - received + buffer.GetAvailable();
		else if (partial)
			return buffer.GetAvailable();
		else
			return -1;
	}

	void _Read() noexcept override {
		if (SubmitBuffer())
			TryRead();
	}

	void _FillBucketList(IstreamBucketList &list) override;
	size_t _ConsumeBucketList(size_t nbytes) noexcept override;

	void _Close() noexcept override {
		buffer.FreeIfDefined();
		event.Cancel();

		/* protect against recursive Free() call within the istream
		   handler */
		closed = true;

		handler.WasInputClose(received);

		Destroy();
	}
};

void
WasInput::ReadToBuffer()
{
	size_t max_length = FB_SIZE;
	if (known_length) {
		uint64_t rest = length - received;
		if (rest < (uint64_t)max_length)
			max_length = rest;

		if (max_length == 0)
			/* all the data we need is already in the buffer */
			return;
	}

	buffer.AllocateIfNull(fb_pool_get());

	ssize_t nbytes = read_to_buffer(GetPipe().Get(), buffer, max_length);
	assert(nbytes != -2);

	if (nbytes == 0)
		throw WasProtocolError("server closed the data connection");

	if (nbytes < 0) {
		const int e = errno;

		if (e == EAGAIN) {
			buffer.FreeIfEmpty();
			ScheduleRead();
			return;
		}

		throw MakeErrno(e, "read error on WAS data connection");
	}

	received += nbytes;

	if (buffer.IsFull())
		event.CancelRead();
}

inline bool
WasInput::TryBuffered() noexcept
{
	if (HasPipe()) {
		try {
			ReadToBuffer();
		} catch (...) {
			AbortError(std::current_exception());
			return false;
		}

		if (!CheckReleasePipe())
			return false;
	}

	if (SubmitBuffer()) {
		assert(!buffer.IsDefinedAndFull());

		if (HasPipe())
			ScheduleRead();
	}

	return true;
}

inline bool
WasInput::TryDirect() noexcept
{
	assert(buffer.empty());
	assert(!buffer.IsDefined());

	size_t max_length = 0x1000000;
	if (known_length) {
		uint64_t rest = length - received;
		if (rest < (uint64_t)max_length)
			max_length = rest;
	}

	ssize_t nbytes = InvokeDirect(FdType::FD_PIPE, GetPipe().Get(), max_length);
	if (nbytes == ISTREAM_RESULT_BLOCKING) {
		event.CancelRead();
		return false;
	}

	if (nbytes == ISTREAM_RESULT_EOF || nbytes == ISTREAM_RESULT_CLOSED)
		return false;

	if (nbytes < 0) {
		const int e = errno;

		if (e == EAGAIN) {
			ScheduleRead();
			return false;
		}

		AbortError(std::make_exception_ptr(MakeErrno(e,
							     "read error on WAS data connection")));
		return false;
	}

	received += nbytes;

	if (!CheckReleasePipe())
		return false;

	if (CheckEof())
		return false;

	ScheduleRead();
	return true;
}

/*
 * libevent callback
 *
 */

inline void
WasInput::EventCallback(unsigned) noexcept
{
	assert(HasPipe());

	TryRead();
}

/*
 * constructor
 *
 */

WasInput *
was_input_new(struct pool &pool, EventLoop &event_loop, FileDescriptor fd,
	      WasInputHandler &handler) noexcept
{
	assert(fd.IsDefined());

	return NewFromPool<WasInput>(pool, pool, event_loop, fd,
				     handler);
}

inline void
WasInput::Free(std::exception_ptr ep) noexcept
{
	assert(ep || closed || !enabled);

	buffer.FreeIfDefined();

	event.Cancel();

	if (!closed && enabled)
		DestroyError(ep);
}

void
was_input_free(WasInput *input, std::exception_ptr ep) noexcept
{
	input->Free(ep);
}

void
was_input_free_unused(WasInput *input) noexcept
{
	input->DestroyUnused();
}

UnusedIstreamPtr
was_input_enable(WasInput &input) noexcept
{
	return input.Enable();
}

void
was_input_disable(WasInput &input) noexcept
{
	input.Disable();
}

inline bool
WasInput::SetLength(uint64_t _length) noexcept
{
	if (known_length) {
		if (_length == length)
			return true;

		// TODO: don't invoke Istream::DestroyError() if not yet enabled
		AbortError("wrong input length announced");
		return false;
	}

	if (_length < received) {
		/* this length must be bogus, because we already received more than that from the socket */
		AbortError("announced length is too small");
		return false;
	}

	length = _length;
	known_length = true;

	if (!CheckReleasePipe())
		return false;

	if (enabled && CheckEof())
		return false;

	return true;
}

bool
was_input_set_length(WasInput *input, uint64_t length) noexcept
{
	return input->SetLength(length);
}

void
WasInput::PrematureThrow(uint64_t _length)
{
	buffer.FreeIfDefined();
	event.Cancel();

	if (known_length && _length > length)
		throw WasProtocolError("announced premature length is too large");

	if (_length < received)
		throw WasProtocolError("announced premature length is too small");

	uint64_t remaining = _length - received;

	while (remaining > 0) {
		uint8_t discard_buffer[4096];
		size_t size = std::min(remaining, uint64_t(sizeof(discard_buffer)));
		ssize_t nbytes = GetPipe().Read(discard_buffer, size);
		if (nbytes < 0)
			throw NestException(std::make_exception_ptr(MakeErrno("Read error")),
					    WasError("read error on WAS data connection"));

		if (nbytes == 0)
			throw WasProtocolError("server closed the WAS data connection");

		remaining -= nbytes;
	}
}

inline void
WasInput::Premature(uint64_t _length) noexcept
{
	try {
		PrematureThrow(_length);
	} catch (...) {
		/* protocol error - notify our WasInputHandler */
		handler.WasInputError();
		DestroyError(std::current_exception());
		return;
	}

	/* recovery was successful */

	/* first, release the pipe (which cannot be released
	   already) */
	if (!ReleasePipe())
		/* WasInputHandler::WasInputRelease() has failed and
		   everything has been cleaned up already */
		return;

	/* pretend this is end-of-file, which will cause the
	   WasInputHandler to allow reusing the connection */
	handler.WasInputEof();

	/* finall let our IstreamHandler know that the stream was
	   closed prematurely */
	DestroyError(std::make_exception_ptr(WasProtocolError("premature end of WAS response")));
}

void
was_input_premature(WasInput *input, uint64_t length) noexcept
{
	input->Premature(length);
}

void
was_input_premature_throw(WasInput *input, uint64_t length)
{
	AtScopeExit(input) { input->Destroy(); };
	input->PrematureThrow(length);
	throw WasProtocolError("premature end of WAS response");
}

void
WasInput::_FillBucketList(IstreamBucketList &list)
{
	auto r = buffer.Read();
	if (r.empty()) {
		if (!HasPipe())
			return;

		try {
			ReadToBuffer();
		} catch (...) {
			handler.WasInputError();
			Destroy();
			throw;
		}

		if (!CheckReleasePipe()) {
			// TODO: deal with this condition properly or improve error message
			handler.WasInputError();
			Destroy();
			throw std::runtime_error("WAS peer failed");
		}

		r = buffer.Read();
		if (r.empty()) {
			list.SetMore();
			return;
		}
	}

	list.Push(r.ToVoid());

	if (!known_length || received < length)
		list.SetMore();
}

size_t
WasInput::_ConsumeBucketList(size_t nbytes) noexcept
{
	size_t consumed = std::min(buffer.GetAvailable(), nbytes);

	buffer.Consume(consumed);
	buffer.FreeIfEmpty();

	return Consumed(consumed);
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Jolla Mobile. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "webwindow.h"

#include <QDebug>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QResizeEvent>
#include <QThread>

#include "webview.h"

WebWindow::WebWindow(QWindow* parent)
    : QWindow(parent)
    , web_view_(nullptr)
    , clear_surface_task_(nullptr) {
  setSurfaceType(QWindow::OpenGLSurface);
  QSurfaceFormat format(requestedFormat());
  format.setAlphaBufferSize(0);
  setFormat(format);
  create();
}

WebWindow::~WebWindow() {
  QMutexLocker lock(&clear_surface_task_mutex_);
  if (clear_surface_task_) {
    QMozContext::GetInstance()->CancelTask(clear_surface_task_);
  }
}

void
WebWindow::SetActiveWebView(WebView* wv) {
  if (web_view_ && wv == web_view_) {
    qWarning() << "Tryig to activate already active webview";
    return;
  }

  if (web_view_ && web_view_ != wv) {
    disconnect(web_view_, &WebView::titleChanged,
               this, &WebWindow::OnTitleChanged);
    web_view_->setActive(false);
  }

  web_view_ = wv;

  if (web_view_) {
    web_view_->setActive(true);
    web_view_->update();

    connect(web_view_, &WebView::titleChanged,
            this, &WebWindow::OnTitleChanged);

    web_view_->setSize(QSizeF(width(), height()));
    web_view_->updateContentOrientation(Qt::PortraitOrientation);
  } else {
    ClearWindowSurface();
  }
}

QOpenGLContext*
WebWindow::GLContext() const {
  static QOpenGLContext* context = nullptr;
  if (!context) {
    context = new QOpenGLContext();
    context->setFormat(requestedFormat());
    if (!context->create())
      qFatal("Failed to create QOpenGLContext!");
  }
  return context;
}

bool
WebWindow::ClearWindowSurface() {
  QMutexLocker lock(&clear_surface_task_mutex_);
  if (clear_surface_task_) {
      return true;
  }
  clear_surface_task_ = QMozContext::GetInstance()->PostCompositorTask(
      &WebWindow::ClearWindowSurfaceImpl, this);
  return clear_surface_task_ != 0;
}

void
WebWindow::resizeEvent(QResizeEvent* evt) {
  if (web_view_) {
    web_view_->setSize(evt->size());
    web_view_->updateContentOrientation(Qt::PortraitOrientation);
  }
}

void
WebWindow::focusInEvent(QFocusEvent* evt) {
  if (web_view_)
    web_view_->focusInEvent(evt);
}

void
WebWindow::focusOutEvent(QFocusEvent* evt) {
  if (web_view_)
    web_view_->focusOutEvent(evt);
}

void
WebWindow::touchEvent(QTouchEvent* evt) {
  if (web_view_)
    web_view_->touchEvent(evt);
}

void
WebWindow::mouseMoveEvent(QMouseEvent* evt) {
  // Let Qt synthesize touch event
  evt->ignore();
}

void
WebWindow::mousePressEvent(QMouseEvent* evt) {
  // Let Qt synthesize touch event
  evt->ignore();
}

void
WebWindow::mouseReleaseEvent(QMouseEvent* evt) {
  // Let Qt synthesize touch event
  evt->ignore();
}

void
WebWindow::keyPressEvent(QKeyEvent* evt) {
  if (web_view_)
    web_view_->keyPressEvent(evt);
}

void
WebWindow::keyReleaseEvent(QKeyEvent* evt) {
  if (web_view_)
    web_view_->keyReleaseEvent(evt);
}

void
WebWindow::exposeEvent(QExposeEvent*) {
  if (QWindow::isExposed()) {
    if (web_view_) {
      web_view_->update();
    } else {
      ClearWindowSurface();
    }
  }
}

void
WebWindow::OnTitleChanged() {
  Q_ASSERT(web_view_);
  setTitle(web_view_->title());
}

void
WebWindow::ClearWindowSurfaceImpl(void* data) {
  WebWindow* ww = static_cast<WebWindow*>(data);
  QMutexLocker lock(&ww->clear_surface_task_mutex_);

  QOpenGLContext* context = ww->GLContext();
  // The GL context should always be used from the same thread in which it was created.
  Q_ASSERT(context->thread() == QThread::currentThread());
  context->makeCurrent(ww);
  QOpenGLFunctions* funcs = context->functions();
  Q_ASSERT(funcs);
  funcs->glClearColor(1.0, 1.0, 1.0, 0.0);
  funcs->glClear(GL_COLOR_BUFFER_BIT);
  context->swapBuffers(ww);

  ww->clear_surface_task_ = 0;
}
<commit_msg>Reset GL scissor box before clearing the compositing surface.<commit_after>// Copyright (c) 2015 Jolla Mobile. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "webwindow.h"

#include <QDebug>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QResizeEvent>
#include <QThread>

#include "webview.h"

WebWindow::WebWindow(QWindow* parent)
    : QWindow(parent)
    , web_view_(nullptr)
    , clear_surface_task_(nullptr) {
  setSurfaceType(QWindow::OpenGLSurface);
  QSurfaceFormat format(requestedFormat());
  format.setAlphaBufferSize(0);
  setFormat(format);
  create();
}

WebWindow::~WebWindow() {
  QMutexLocker lock(&clear_surface_task_mutex_);
  if (clear_surface_task_) {
    QMozContext::GetInstance()->CancelTask(clear_surface_task_);
  }
}

void
WebWindow::SetActiveWebView(WebView* wv) {
  if (web_view_ && wv == web_view_) {
    qWarning() << "Tryig to activate already active webview";
    return;
  }

  if (web_view_ && web_view_ != wv) {
    disconnect(web_view_, &WebView::titleChanged,
               this, &WebWindow::OnTitleChanged);
    web_view_->setActive(false);
  }

  web_view_ = wv;

  if (web_view_) {
    web_view_->setActive(true);
    web_view_->update();

    connect(web_view_, &WebView::titleChanged,
            this, &WebWindow::OnTitleChanged);

    web_view_->setSize(QSizeF(width(), height()));
    web_view_->updateContentOrientation(Qt::PortraitOrientation);
  } else {
    ClearWindowSurface();
  }
}

QOpenGLContext*
WebWindow::GLContext() const {
  static QOpenGLContext* context = nullptr;
  if (!context) {
    context = new QOpenGLContext();
    context->setFormat(requestedFormat());
    if (!context->create())
      qFatal("Failed to create QOpenGLContext!");
  }
  return context;
}

bool
WebWindow::ClearWindowSurface() {
  QMutexLocker lock(&clear_surface_task_mutex_);
  if (clear_surface_task_) {
      return true;
  }
  clear_surface_task_ = QMozContext::GetInstance()->PostCompositorTask(
      &WebWindow::ClearWindowSurfaceImpl, this);
  return clear_surface_task_ != 0;
}

void
WebWindow::resizeEvent(QResizeEvent* evt) {
  if (web_view_) {
    web_view_->setSize(evt->size());
    web_view_->updateContentOrientation(Qt::PortraitOrientation);
  }
}

void
WebWindow::focusInEvent(QFocusEvent* evt) {
  if (web_view_)
    web_view_->focusInEvent(evt);
}

void
WebWindow::focusOutEvent(QFocusEvent* evt) {
  if (web_view_)
    web_view_->focusOutEvent(evt);
}

void
WebWindow::touchEvent(QTouchEvent* evt) {
  if (web_view_)
    web_view_->touchEvent(evt);
}

void
WebWindow::mouseMoveEvent(QMouseEvent* evt) {
  // Let Qt synthesize touch event
  evt->ignore();
}

void
WebWindow::mousePressEvent(QMouseEvent* evt) {
  // Let Qt synthesize touch event
  evt->ignore();
}

void
WebWindow::mouseReleaseEvent(QMouseEvent* evt) {
  // Let Qt synthesize touch event
  evt->ignore();
}

void
WebWindow::keyPressEvent(QKeyEvent* evt) {
  if (web_view_)
    web_view_->keyPressEvent(evt);
}

void
WebWindow::keyReleaseEvent(QKeyEvent* evt) {
  if (web_view_)
    web_view_->keyReleaseEvent(evt);
}

void
WebWindow::exposeEvent(QExposeEvent*) {
  if (QWindow::isExposed()) {
    if (web_view_) {
      web_view_->update();
    } else {
      ClearWindowSurface();
    }
  }
}

void
WebWindow::OnTitleChanged() {
  Q_ASSERT(web_view_);
  setTitle(web_view_->title());
}

void
WebWindow::ClearWindowSurfaceImpl(void* data) {
  WebWindow* ww = static_cast<WebWindow*>(data);
  QMutexLocker lock(&ww->clear_surface_task_mutex_);

  QOpenGLContext* context = ww->GLContext();
  // The GL context should always be used from the same thread in which it was created.
  Q_ASSERT(context->thread() == QThread::currentThread());
  context->makeCurrent(ww);
  QOpenGLFunctions* funcs = context->functions();
  Q_ASSERT(funcs);
  funcs->glScissor(0, 0, ww->width(), ww->height());
  funcs->glClearColor(1.0, 1.0, 1.0, 0.0);
  funcs->glClear(GL_COLOR_BUFFER_BIT);
  context->swapBuffers(ww);

  ww->clear_surface_task_ = 0;
}
<|endoftext|>