<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/hwpf/fapi2/include/fapi2_vpd_access.H $            */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2016,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
/// @file fapi2_vpd_access.H
/// @brief Common file that defines the vpd access functions that
/// platform code must implement.
///
//

#ifndef _FAPI2_VPDACCESS_H_
#define _FAPI2_VPDACCESS_H_

#include <return_code.H>
#include <target_types.H>
#include <vpd_access_defs.H>
#include <plat_vpd_access.H>

namespace fapi2
{

/// constants for VPD Info
constexpr uint64_t VPD_INFO_INVALID  = 0xffffffffffffffff;

/// @brief  Specialized class representing required VPDInfo to be used
///         in collecting VPD for the MCS target type.
/// @tparam T fapi2::TARGET_TYPE_MCS
template<>
class VPDInfo<TARGET_TYPE_MCS>
{
    public:
        // @brief VPDInfo constructor
        VPDInfo( const fapi2::MemVpdData& i_vpd_type)
            : iv_vpd_type(i_vpd_type),
              iv_size(VPD_INFO_INVALID),
              iv_freq_mhz(VPD_INFO_INVALID),
              iv_rank_count_dimm_0(VPD_INFO_INVALID),
              iv_rank_count_dimm_1(VPD_INFO_INVALID)
        {};
        // type of vpd field to return
        fapi2::MemVpdData_t iv_vpd_type;

        // size of the vpd data
        size_t   iv_size;
        uint64_t iv_freq_mhz;
        uint64_t iv_rank_count_dimm_0;
        uint64_t iv_rank_count_dimm_1;

};

/// @brief Return a blob of memory VPD data associated with the input target
/// @param[in] i_target     a valid fapi2 target
/// @param[in] io_vpd_info  fapi2::VPDInfo class that specifies which piece of data to return
/// @param[out] o_blob      the blob of raw data from the vpd
/// @return FAPI2_RC_SUCCESS if there's no problem
/// @note passing nullptr for o_blob will return the size of the keyword
///
/// Example:
/// fapi2::VPDInfo<fapi2::TARGET_TYPE_MCS> vpdInfo(MR_keyword);
/// vpdInfo.iv_freq = 2667;
///
/// uint8_t * blob = NULL;
///
/// FAPI_TRY(getVPD( mcs, vpdInfo, blob ));
/// blob = static_cast<uint8_t *>(malloc(vpdInfo.iv_size));
/// FAPI_TRY(getVPD( mcs, vpdInfo, blob ));
/// blob now contains the VPD data for the MCS.
///
template<TargetType T>
ReturnCode getVPD(const Target<T>& i_target,
                  VPDInfo<T>& io_vpd_info,
                  uint8_t* o_blob);

};
#endif
<commit_msg>fapi2: Update misc functions to honor platform value type.<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/hwpf/fapi2/include/fapi2_vpd_access.H $            */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2016,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
/// @file fapi2_vpd_access.H
/// @brief Common file that defines the vpd access functions that
/// platform code must implement.
///
//

#ifndef _FAPI2_VPDACCESS_H_
#define _FAPI2_VPDACCESS_H_

#include <return_code.H>
#include <target_types.H>
#include <vpd_access_defs.H>
#include <plat_vpd_access.H>

namespace fapi2
{

/// constants for VPD Info
constexpr uint64_t VPD_INFO_INVALID  = 0xffffffffffffffff;

/// @brief  Specialized class representing required VPDInfo to be used
///         in collecting VPD for the MCS target type.
/// @tparam T fapi2::TARGET_TYPE_MCS
template<>
class VPDInfo<TARGET_TYPE_MCS>
{
    public:
        // @brief VPDInfo constructor
        VPDInfo( const fapi2::MemVpdData& i_vpd_type)
            : iv_vpd_type(i_vpd_type),
              iv_size(VPD_INFO_INVALID),
              iv_freq_mhz(VPD_INFO_INVALID),
              iv_rank_count_dimm_0(VPD_INFO_INVALID),
              iv_rank_count_dimm_1(VPD_INFO_INVALID)
        {};
        // type of vpd field to return
        fapi2::MemVpdData_t iv_vpd_type;

        // size of the vpd data
        size_t   iv_size;
        uint64_t iv_freq_mhz;
        uint64_t iv_rank_count_dimm_0;
        uint64_t iv_rank_count_dimm_1;

};

/// @brief Return a blob of memory VPD data associated with the input target
/// @param[in] i_target     a valid fapi2 target
/// @param[in] io_vpd_info  fapi2::VPDInfo class that specifies which piece of data to return
/// @param[out] o_blob      the blob of raw data from the vpd
/// @return FAPI2_RC_SUCCESS if there's no problem
/// @note passing nullptr for o_blob will return the size of the keyword
///
/// Example:
/// fapi2::VPDInfo<fapi2::TARGET_TYPE_MCS> vpdInfo(MR_keyword);
/// vpdInfo.iv_freq = 2667;
///
/// uint8_t * blob = NULL;
///
/// FAPI_TRY(getVPD( mcs, vpdInfo, blob ));
/// blob = static_cast<uint8_t *>(malloc(vpdInfo.iv_size));
/// FAPI_TRY(getVPD( mcs, vpdInfo, blob ));
/// blob now contains the VPD data for the MCS.
///
template<TargetType T, typename V>
ReturnCode getVPD(const Target<T, V>& i_target,
                  VPDInfo<T>& io_vpd_info,
                  uint8_t* o_blob);

};
#endif
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2021 The Regents of the University of California.
 * All rights reserved.
 *
 * 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;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * 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 COPYRIGHT
 * OWNER 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 "cpu/testers/traffic_gen/gups_gen.hh"

#include <cstring>
#include <string>

#include "base/random.hh"
#include "debug/GUPSGen.hh"
#include "sim/sim_exit.hh"

namespace gem5
{

GUPSGen::GUPSGen(const GUPSGenParams& params):
    ClockedObject(params),
    nextCreateEvent([this]{ createNextReq(); }, name()),
    nextSendEvent([this]{ sendNextReq(); }, name()),
    system(params.system),
    requestorId(system->getRequestorId(this)),
    port(name() + ".port", this),
    startAddr(params.start_addr),
    memSize(params.mem_size),
    updateLimit(params.update_limit),
    elementSize(sizeof(uint64_t)), // every element in the table is a uint64_t
    reqQueueSize(params.request_queue_size),
    initMemory(params.init_memory),
    stats(this)
{}

Port&
GUPSGen::getPort(const std::string &if_name, PortID idx)
{
    if (if_name != "port") {
        return ClockedObject::getPort(if_name, idx);
    } else {
        return port;
    }
}

void
GUPSGen::init()
{
    doneReading = false;
    onTheFlyRequests = 0;
    readRequests = 0;

    tableSize = memSize / elementSize;
    numUpdates = 4 * tableSize;
}

void
GUPSGen::startup()
{
    int block_size = 64; // Write the initial values in 64 byte blocks.
    uint64_t stride_size = block_size / elementSize;
    if (initMemory) {
        for (uint64_t start_index = 0; start_index < tableSize;
                                    start_index += stride_size) {
            uint8_t write_data[block_size];
            for (uint64_t offset = 0; offset < stride_size; offset++) {
                uint64_t value = start_index + offset;
                std::memcpy(write_data + offset * elementSize,
                            &value, elementSize);
            }
            Addr addr = indexToAddr(start_index);
            PacketPtr pkt = getWritePacket(addr, block_size, write_data);
            port.sendFunctionalPacket(pkt);
            delete pkt;
        }
    }
    schedule(nextCreateEvent, nextCycle());
}

Addr
GUPSGen::indexToAddr (uint64_t index)
{
    Addr ret = index * elementSize + startAddr;
    return ret;
}

PacketPtr
GUPSGen::getReadPacket(Addr addr, unsigned int size)
{
    RequestPtr req = std::make_shared<Request>(addr, size, 0, requestorId);
    // Dummy PC to have PC-based prefetchers latch on; get entropy into higher
    // bits
    req->setPC(((Addr)requestorId) << 2);

    // Embed it in a packet
    PacketPtr pkt = new Packet(req, MemCmd::ReadReq);
    pkt->allocate();

    return pkt;
}

PacketPtr
GUPSGen::getWritePacket(Addr addr, unsigned int size, uint8_t *data)
{
    RequestPtr req = std::make_shared<Request>(addr, size, 0,
                                               requestorId);
    // Dummy PC to have PC-based prefetchers latch on; get entropy into higher
    // bits
    req->setPC(((Addr)requestorId) << 2);

    PacketPtr pkt = new Packet(req, MemCmd::WriteReq);
    pkt->allocate();
    pkt->setData(data);

    return pkt;
}

void
GUPSGen::handleResponse(PacketPtr pkt)
{
    onTheFlyRequests--;
    DPRINTF(GUPSGen, "%s: onTheFlyRequests: %d.\n",
                __func__, onTheFlyRequests);
    if (pkt->isWrite()) {
        DPRINTF(GUPSGen, "%s: received a write resp. pkt->addr_range: %s,"
                        " pkt->data: %d\n", __func__,
                        pkt->getAddrRange().to_string(),
                        *pkt->getPtr<uint64_t>());
        stats.totalUpdates++;
        stats.totalWrites++;
        stats.totalBytesWritten += elementSize;
        stats.totalWriteLat += curTick() - exitTimes[pkt->req];

        exitTimes.erase(pkt->req);
        delete pkt;
    } else {
        DPRINTF(GUPSGen, "%s: received a read resp. pkt->addr_range: %s\n",
                        __func__, pkt->getAddrRange().to_string());

        stats.totalReads++;
        stats.totalBytesRead += elementSize;
        stats.totalReadLat += curTick() - exitTimes[pkt->req];

        exitTimes.erase(pkt->req);

        responsePool.push(pkt);
    }
    if (doneReading && requestPool.empty() && onTheFlyRequests == 0) {
        exitSimLoop(name() + " is finished updating the memory.\n");
        return;
    }

    if ((requestPool.size() < reqQueueSize) &&
        (!doneReading || !responsePool.empty()) &&
        !nextCreateEvent.scheduled())
    {
        schedule(nextCreateEvent, nextCycle());
    }
}

void
GUPSGen::wakeUp()
{
    if (!nextSendEvent.scheduled() && !requestPool.empty()) {
        schedule(nextSendEvent, nextCycle());
    }
}

void
GUPSGen::createNextReq()
{
    // Prioritize pending writes over reads
    // Write as soon as the data is read
    if (!responsePool.empty()) {
        PacketPtr pkt = responsePool.front();
        responsePool.pop();

        uint64_t *updated_value = pkt->getPtr<uint64_t>();
        DPRINTF(GUPSGen, "%s: Read value %lu from address %s", __func__,
                            *updated_value, pkt->getAddrRange().to_string());
        *updated_value ^= updateTable[pkt->req];
        updateTable.erase(pkt->req);
        Addr addr = pkt->getAddr();
        PacketPtr new_pkt = getWritePacket(addr,
                            elementSize, (uint8_t*) updated_value);
        delete pkt;
        requestPool.push(new_pkt);
    } else if (!doneReading) {
        // If no writes then read
        // Check to make sure we're not reading more than we should.
        assert (readRequests < numUpdates);

        uint64_t value = readRequests;
        uint64_t index = random_mt.random((int64_t) 0, tableSize);
        Addr addr = indexToAddr(index);
        PacketPtr pkt = getReadPacket(addr, elementSize);
        updateTable[pkt->req] = value;
        requestPool.push(pkt);
        readRequests++;

        if (readRequests >= numUpdates) {
            DPRINTF(GUPSGen, "%s: Done creating reads.\n", __func__);
            doneReading = true;
        }
        else if (readRequests == updateLimit && updateLimit != 0) {
            DPRINTF(GUPSGen, "%s: Update limit reached.\n", __func__);
            doneReading = true;
        }
    }

    if (!nextCreateEvent.scheduled() &&
        (requestPool.size() < reqQueueSize) &&
        (!doneReading || !responsePool.empty()))
    {
        schedule(nextCreateEvent, nextCycle());
    }

    if (!nextSendEvent.scheduled() && !requestPool.empty()) {
        schedule(nextSendEvent, nextCycle());
    }
}

void
GUPSGen::sendNextReq()
{
    if (!port.blocked()) {
        PacketPtr pkt = requestPool.front();

        exitTimes[pkt->req] = curTick();
        if (pkt->isWrite()) {
            DPRINTF(GUPSGen, "%s: Sent write pkt, pkt->addr_range: "
                    "%s, pkt->data: %lu.\n", __func__,
                    pkt->getAddrRange().to_string(),
                    *pkt->getPtr<uint64_t>());
        } else {
            DPRINTF(GUPSGen, "%s: Sent read pkt, pkt->addr_range: %s.\n",
                    __func__, pkt->getAddrRange().to_string());
        }
        port.sendTimingPacket(pkt);
        onTheFlyRequests++;
        DPRINTF(GUPSGen, "%s: onTheFlyRequests: %d.\n",
                __func__, onTheFlyRequests);
        requestPool.pop();
    }

    if (!nextCreateEvent.scheduled() &&
        (requestPool.size() < reqQueueSize) &&
        (!doneReading || !responsePool.empty()))
    {
        schedule(nextCreateEvent, nextCycle());
    }

    if (!nextSendEvent.scheduled()) {
        if (!requestPool.empty()) {
            schedule(nextSendEvent, nextCycle());
        }
    }
}

void
GUPSGen::GenPort::sendTimingPacket(PacketPtr pkt)
{
    panic_if(_blocked, "Should never try to send if blocked MemSide!");

    // If we can't send the packet across the port, store it for later.
    if (!sendTimingReq(pkt)) {
        DPRINTF(GUPSGen, "GenPort::%s: Packet blocked\n", __func__);
        blockedPacket = pkt;
        _blocked = true;
    } else {
        DPRINTF(GUPSGen, "GenPort::%s: Packet sent\n", __func__);
    }
}

void
GUPSGen::GenPort::sendFunctionalPacket(PacketPtr pkt)
{
    sendFunctional(pkt);
}

void
GUPSGen::GenPort::recvReqRetry()
{
    // We should have a blocked packet if this function is called.
    DPRINTF(GUPSGen, "GenPort::%s: Received a retry.\n", __func__);

    assert(_blocked && (blockedPacket != nullptr));
    // Try to resend it. It's possible that it fails again.
    _blocked = false;
    sendTimingPacket(blockedPacket);
    if (!_blocked){
        blockedPacket = nullptr;
    }

    owner->wakeUp();
}

bool
GUPSGen::GenPort::recvTimingResp(PacketPtr pkt)
{
    owner->handleResponse(pkt);
    return true;
}

GUPSGen::GUPSGenStat::GUPSGenStat(GUPSGen* parent):
    Stats::Group(parent),
    ADD_STAT(totalUpdates, UNIT_COUNT,
        "Total number of updates the generator made in the memory"),
    ADD_STAT(GUPS, UNIT_RATE(Stats::Units::Count, Stats::Units::Second),
        "Rate of billion updates per second"),
    ADD_STAT(totalReads, UNIT_COUNT,
        "Total number of read requests"),
    ADD_STAT(totalBytesRead, UNIT_BYTE,
        "Total number of bytes read"),
    ADD_STAT(avgReadBW, UNIT_RATE(Stats::Units::Byte, Stats::Units::Second),
        "Average read bandwidth received from memory"),
    ADD_STAT(totalReadLat, UNIT_TICK,
        "Total latency of read requests."),
    ADD_STAT(avgReadLat, UNIT_TICK,
        "Average latency for read requests"),
    ADD_STAT(totalWrites, UNIT_COUNT,
        "Total number of write requests"),
    ADD_STAT(totalBytesWritten, UNIT_BYTE,
        "Total number of bytes written"),
    ADD_STAT(avgWriteBW, UNIT_RATE(Stats::Units::Byte, Stats::Units::Second),
        "Average write bandwidth received from memory"),
    ADD_STAT(totalWriteLat, UNIT_TICK,
        "Total latency of write requests."),
    ADD_STAT(avgWriteLat, UNIT_TICK,
        "Average latency for write requests")
{}

void
GUPSGen::GUPSGenStat::regStats()
{
    GUPS.precision(8);
    avgReadBW.precision(2);
    avgReadLat.precision(2);
    avgWriteBW.precision(2);
    avgWriteLat.precision(2);

    GUPS = (totalUpdates / 1e9) / simSeconds;

    avgReadBW = totalBytesRead / simSeconds;
    avgReadLat = (totalReadLat) / totalReads;

    avgWriteBW = totalBytesWritten / simSeconds;
    avgWriteLat = (totalWriteLat) / totalWrites;
}

}
<commit_msg>cpu: Updating stats for GUPSGen<commit_after>/*
 * Copyright (c) 2021 The Regents of the University of California.
 * All rights reserved.
 *
 * 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;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * 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 COPYRIGHT
 * OWNER 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 "cpu/testers/traffic_gen/gups_gen.hh"

#include <cstring>
#include <string>

#include "base/random.hh"
#include "debug/GUPSGen.hh"
#include "sim/sim_exit.hh"

namespace gem5
{

GUPSGen::GUPSGen(const GUPSGenParams& params):
    ClockedObject(params),
    nextCreateEvent([this]{ createNextReq(); }, name()),
    nextSendEvent([this]{ sendNextReq(); }, name()),
    system(params.system),
    requestorId(system->getRequestorId(this)),
    port(name() + ".port", this),
    startAddr(params.start_addr),
    memSize(params.mem_size),
    updateLimit(params.update_limit),
    elementSize(sizeof(uint64_t)), // every element in the table is a uint64_t
    reqQueueSize(params.request_queue_size),
    initMemory(params.init_memory),
    stats(this)
{}

Port&
GUPSGen::getPort(const std::string &if_name, PortID idx)
{
    if (if_name != "port") {
        return ClockedObject::getPort(if_name, idx);
    } else {
        return port;
    }
}

void
GUPSGen::init()
{
    doneReading = false;
    onTheFlyRequests = 0;
    readRequests = 0;

    tableSize = memSize / elementSize;
    numUpdates = 4 * tableSize;
}

void
GUPSGen::startup()
{
    int block_size = 64; // Write the initial values in 64 byte blocks.
    uint64_t stride_size = block_size / elementSize;
    if (initMemory) {
        for (uint64_t start_index = 0; start_index < tableSize;
                                    start_index += stride_size) {
            uint8_t write_data[block_size];
            for (uint64_t offset = 0; offset < stride_size; offset++) {
                uint64_t value = start_index + offset;
                std::memcpy(write_data + offset * elementSize,
                            &value, elementSize);
            }
            Addr addr = indexToAddr(start_index);
            PacketPtr pkt = getWritePacket(addr, block_size, write_data);
            port.sendFunctionalPacket(pkt);
            delete pkt;
        }
    }
    schedule(nextCreateEvent, nextCycle());
}

Addr
GUPSGen::indexToAddr (uint64_t index)
{
    Addr ret = index * elementSize + startAddr;
    return ret;
}

PacketPtr
GUPSGen::getReadPacket(Addr addr, unsigned int size)
{
    RequestPtr req = std::make_shared<Request>(addr, size, 0, requestorId);
    // Dummy PC to have PC-based prefetchers latch on; get entropy into higher
    // bits
    req->setPC(((Addr)requestorId) << 2);

    // Embed it in a packet
    PacketPtr pkt = new Packet(req, MemCmd::ReadReq);
    pkt->allocate();

    return pkt;
}

PacketPtr
GUPSGen::getWritePacket(Addr addr, unsigned int size, uint8_t *data)
{
    RequestPtr req = std::make_shared<Request>(addr, size, 0,
                                               requestorId);
    // Dummy PC to have PC-based prefetchers latch on; get entropy into higher
    // bits
    req->setPC(((Addr)requestorId) << 2);

    PacketPtr pkt = new Packet(req, MemCmd::WriteReq);
    pkt->allocate();
    pkt->setData(data);

    return pkt;
}

void
GUPSGen::handleResponse(PacketPtr pkt)
{
    onTheFlyRequests--;
    DPRINTF(GUPSGen, "%s: onTheFlyRequests: %d.\n",
                __func__, onTheFlyRequests);
    if (pkt->isWrite()) {
        DPRINTF(GUPSGen, "%s: received a write resp. pkt->addr_range: %s,"
                        " pkt->data: %d\n", __func__,
                        pkt->getAddrRange().to_string(),
                        *pkt->getPtr<uint64_t>());
        stats.totalUpdates++;
        stats.totalWrites++;
        stats.totalBytesWritten += elementSize;
        stats.totalWriteLat += curTick() - exitTimes[pkt->req];

        exitTimes.erase(pkt->req);
        delete pkt;
    } else {
        DPRINTF(GUPSGen, "%s: received a read resp. pkt->addr_range: %s\n",
                        __func__, pkt->getAddrRange().to_string());

        stats.totalReads++;
        stats.totalBytesRead += elementSize;
        stats.totalReadLat += curTick() - exitTimes[pkt->req];

        exitTimes.erase(pkt->req);

        responsePool.push(pkt);
    }
    if (doneReading && requestPool.empty() && onTheFlyRequests == 0) {
        exitSimLoop(name() + " is finished updating the memory.\n");
        return;
    }

    if ((requestPool.size() < reqQueueSize) &&
        (!doneReading || !responsePool.empty()) &&
        !nextCreateEvent.scheduled())
    {
        schedule(nextCreateEvent, nextCycle());
    }
}

void
GUPSGen::wakeUp()
{
    if (!nextSendEvent.scheduled() && !requestPool.empty()) {
        schedule(nextSendEvent, nextCycle());
    }
}

void
GUPSGen::createNextReq()
{
    // Prioritize pending writes over reads
    // Write as soon as the data is read
    if (!responsePool.empty()) {
        PacketPtr pkt = responsePool.front();
        responsePool.pop();

        uint64_t *updated_value = pkt->getPtr<uint64_t>();
        DPRINTF(GUPSGen, "%s: Read value %lu from address %s", __func__,
                            *updated_value, pkt->getAddrRange().to_string());
        *updated_value ^= updateTable[pkt->req];
        updateTable.erase(pkt->req);
        Addr addr = pkt->getAddr();
        PacketPtr new_pkt = getWritePacket(addr,
                            elementSize, (uint8_t*) updated_value);
        delete pkt;
        requestPool.push(new_pkt);
    } else if (!doneReading) {
        // If no writes then read
        // Check to make sure we're not reading more than we should.
        assert (readRequests < numUpdates);

        uint64_t value = readRequests;
        uint64_t index = random_mt.random((int64_t) 0, tableSize);
        Addr addr = indexToAddr(index);
        PacketPtr pkt = getReadPacket(addr, elementSize);
        updateTable[pkt->req] = value;
        requestPool.push(pkt);
        readRequests++;

        if (readRequests >= numUpdates) {
            DPRINTF(GUPSGen, "%s: Done creating reads.\n", __func__);
            doneReading = true;
        }
        else if (readRequests == updateLimit && updateLimit != 0) {
            DPRINTF(GUPSGen, "%s: Update limit reached.\n", __func__);
            doneReading = true;
        }
    }

    if (!nextCreateEvent.scheduled() &&
        (requestPool.size() < reqQueueSize) &&
        (!doneReading || !responsePool.empty()))
    {
        schedule(nextCreateEvent, nextCycle());
    }

    if (!nextSendEvent.scheduled() && !requestPool.empty()) {
        schedule(nextSendEvent, nextCycle());
    }
}

void
GUPSGen::sendNextReq()
{
    if (!port.blocked()) {
        PacketPtr pkt = requestPool.front();

        exitTimes[pkt->req] = curTick();
        if (pkt->isWrite()) {
            DPRINTF(GUPSGen, "%s: Sent write pkt, pkt->addr_range: "
                    "%s, pkt->data: %lu.\n", __func__,
                    pkt->getAddrRange().to_string(),
                    *pkt->getPtr<uint64_t>());
        } else {
            DPRINTF(GUPSGen, "%s: Sent read pkt, pkt->addr_range: %s.\n",
                    __func__, pkt->getAddrRange().to_string());
        }
        port.sendTimingPacket(pkt);
        onTheFlyRequests++;
        DPRINTF(GUPSGen, "%s: onTheFlyRequests: %d.\n",
                __func__, onTheFlyRequests);
        requestPool.pop();
    }

    if (!nextCreateEvent.scheduled() &&
        (requestPool.size() < reqQueueSize) &&
        (!doneReading || !responsePool.empty()))
    {
        schedule(nextCreateEvent, nextCycle());
    }

    if (!nextSendEvent.scheduled()) {
        if (!requestPool.empty()) {
            schedule(nextSendEvent, nextCycle());
        }
    }
}

void
GUPSGen::GenPort::sendTimingPacket(PacketPtr pkt)
{
    panic_if(_blocked, "Should never try to send if blocked MemSide!");

    // If we can't send the packet across the port, store it for later.
    if (!sendTimingReq(pkt)) {
        DPRINTF(GUPSGen, "GenPort::%s: Packet blocked\n", __func__);
        blockedPacket = pkt;
        _blocked = true;
    } else {
        DPRINTF(GUPSGen, "GenPort::%s: Packet sent\n", __func__);
    }
}

void
GUPSGen::GenPort::sendFunctionalPacket(PacketPtr pkt)
{
    sendFunctional(pkt);
}

void
GUPSGen::GenPort::recvReqRetry()
{
    // We should have a blocked packet if this function is called.
    DPRINTF(GUPSGen, "GenPort::%s: Received a retry.\n", __func__);

    assert(_blocked && (blockedPacket != nullptr));
    // Try to resend it. It's possible that it fails again.
    _blocked = false;
    sendTimingPacket(blockedPacket);
    if (!_blocked){
        blockedPacket = nullptr;
    }

    owner->wakeUp();
}

bool
GUPSGen::GenPort::recvTimingResp(PacketPtr pkt)
{
    owner->handleResponse(pkt);
    return true;
}

GUPSGen::GUPSGenStat::GUPSGenStat(GUPSGen* parent):
    Stats::Group(parent),
    ADD_STAT(totalUpdates, statistics::units::Count::get(),
        "Total number of updates the generator made in the memory"),
    ADD_STAT(GUPS, statistics::units::Rate<statistics::units::Count,
                statistics::units::Second>::get(),
        "Rate of billion updates per second"),
    ADD_STAT(totalReads, statistics::units::Count::get(),
        "Total number of read requests"),
    ADD_STAT(totalBytesRead, statistics::units::Byte::get(),
        "Total number of bytes read"),
    ADD_STAT(avgReadBW, statistics::units::Rate<statistics::units::Byte,
                statistics::units::Second>::get(),
        "Average read bandwidth received from memory"),
    ADD_STAT(totalReadLat, statistics::units::Tick::get(),
        "Total latency of read requests."),
    ADD_STAT(avgReadLat, statistics::units::Tick::get(),
        "Average latency for read requests"),
    ADD_STAT(totalWrites, statistics::units::Count::get(),
        "Total number of write requests"),
    ADD_STAT(totalBytesWritten, statistics::units::Byte::get(),
        "Total number of bytes written"),
    ADD_STAT(avgWriteBW, statistics::units::Rate<statistics::units::Byte,
                statistics::units::Second>::get(),
        "Average write bandwidth received from memory"),
    ADD_STAT(totalWriteLat, statistics::units::Tick::get(),
        "Total latency of write requests."),
    ADD_STAT(avgWriteLat, statistics::units::Tick::get(),
        "Average latency for write requests")
{}

void
GUPSGen::GUPSGenStat::regStats()
{
    GUPS.precision(8);
    avgReadBW.precision(2);
    avgReadLat.precision(2);
    avgWriteBW.precision(2);
    avgWriteLat.precision(2);

    GUPS = (totalUpdates / 1e9) / simSeconds;

    avgReadBW = totalBytesRead / simSeconds;
    avgReadLat = (totalReadLat) / totalReads;

    avgWriteBW = totalBytesWritten / simSeconds;
    avgWriteLat = (totalWriteLat) / totalWrites;
}

}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: gui.cxx,v $
 *
 *  $Revision: 1.7 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-17 16:00:35 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_rsc.hxx"

#include <stdlib.h>
#include <stdio.h>
#ifdef MAC
#include <cursorctl.h>
#endif

#ifndef _RSCRSC_HXX
#include <rscrsc.hxx>
#endif
#ifndef _RSCDB_HXX
#include <rscdb.hxx>
#endif

/*************** G l o b a l e   V a r i a b l e n **********************/
static RscCompiler * pRscCompiler = NULL;
/****************************************************************/
/*                                                              */
/*  Function    :   ExitProgram()                               */
/*                                                              */
/*  Description :   Gibt die Temporaeren Dateien frei.          */
/****************************************************************/
#if defined( UNX ) || ( defined( PM2 ) && ( defined( TCPP ) || defined ( GCC )) ) || defined( MAC ) || defined (WTC) || defined (MTW)
        void ExitProgram( void ){
#else
#if defined( CSET )
    void _Optlink ExitProgram( void ){
#else
    void cdecl ExitProgram( void ){
#endif
#endif
    if( pRscCompiler )
        delete pRscCompiler;
}

#if defined( UNX ) || defined( MAC ) || ( defined( PM2 ) && ( defined( CSET ) || defined ( GCC ))) || defined (WTC) || defined(ICC)
int main ( int argc, char ** argv) {
#else
#if defined( MTW )
int  main ( int argc, char const ** argv) {
#else
int cdecl main ( int argc, char ** argv) {
#endif
#endif
#ifndef UNX
#ifdef CSET
    atexit( ExitProgram );
#else
    atexit( ExitProgram );
#endif
#endif
#ifdef MAC
    InitCursorCtl( 0 );
#endif
#if OSL_DEBUG_LEVEL > 1
    fprintf( stderr, "debugging %s\n", argv[0] );
#endif

    ERRTYPE     aError;

    InitRscCompiler();
    RscError*   pErrHdl    = new RscError();
#ifdef MTW
    RscCmdLine* pCmdLine   = new RscCmdLine( argc, (char **)argv, pErrHdl );
#else
    RscCmdLine* pCmdLine   = new RscCmdLine( argc, argv, pErrHdl );
#endif
    RscTypCont* pTypCont   = new RscTypCont( pErrHdl,
                                             pCmdLine->nByteOrder,
                                             pCmdLine->aPath,
                                             pCmdLine->nCommands );

    if( pErrHdl->nErrors )
        aError = ERR_ERROR;
    else{
        RscCompiler* pCompiler = new RscCompiler( pCmdLine, pTypCont );

        pRscCompiler = pCompiler;
        aError = pCompiler->Start();
        pRscCompiler = NULL;

        delete pCompiler;
    }

    delete pTypCont;
    delete pCmdLine;
    delete pErrHdl;
    delete pHS; // wird durch InitRscCompiler erzeugt

    if( aError.IsOk() )
        return( 0 );
    else
        return( 1 );
}

void RscExit( sal_uInt32 nExit )
{
    if( nExit )
        printf( "Program exit is %ud\n", (unsigned int)nExit );
    exit( nExit );
}
<commit_msg>INTEGRATION: CWS mingwport03 (1.6.48); FILE MERGED 2006/11/08 12:56:58 vg 1.6.48.2: RESYNC: (1.6-1.7); FILE MERGED 2006/09/13 14:22:28 vg 1.6.48.1: #i53572# MinGW port<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: gui.cxx,v $
 *
 *  $Revision: 1.8 $
 *
 *  last change: $Author: vg $ $Date: 2007-03-26 15:01:06 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_rsc.hxx"

#include <stdlib.h>
#include <stdio.h>
#ifdef MAC
#include <cursorctl.h>
#endif

#ifndef _RSCRSC_HXX
#include <rscrsc.hxx>
#endif
#ifndef _RSCDB_HXX
#include <rscdb.hxx>
#endif

/*************** G l o b a l e   V a r i a b l e n **********************/
static RscCompiler * pRscCompiler = NULL;
/****************************************************************/
/*                                                              */
/*  Function    :   ExitProgram()                               */
/*                                                              */
/*  Description :   Gibt die Temporaeren Dateien frei.          */
/****************************************************************/
#if defined( UNX ) || ( defined( PM2 ) && ( defined( TCPP ) || defined ( GCC )) ) || defined( MAC ) || defined (WTC) || defined (MTW) || defined(__MINGW32__)
        void ExitProgram( void ){
#else
#if defined( CSET )
    void _Optlink ExitProgram( void ){
#else
    void cdecl ExitProgram( void ){
#endif
#endif
    if( pRscCompiler )
        delete pRscCompiler;
}

#if defined( UNX ) || defined( MAC ) || ( defined( PM2 ) && ( defined( CSET ) || defined ( GCC ))) || defined (WTC) || defined(ICC) || defined(__MINGW32__)
int main ( int argc, char ** argv) {
#else
#if defined( MTW )
int  main ( int argc, char const ** argv) {
#else
int cdecl main ( int argc, char ** argv) {
#endif
#endif
#ifndef UNX
#ifdef CSET
    atexit( ExitProgram );
#else
    atexit( ExitProgram );
#endif
#endif
#ifdef MAC
    InitCursorCtl( 0 );
#endif
#if OSL_DEBUG_LEVEL > 1
    fprintf( stderr, "debugging %s\n", argv[0] );
#endif

    ERRTYPE     aError;

    InitRscCompiler();
    RscError*   pErrHdl    = new RscError();
#ifdef MTW
    RscCmdLine* pCmdLine   = new RscCmdLine( argc, (char **)argv, pErrHdl );
#else
    RscCmdLine* pCmdLine   = new RscCmdLine( argc, argv, pErrHdl );
#endif
    RscTypCont* pTypCont   = new RscTypCont( pErrHdl,
                                             pCmdLine->nByteOrder,
                                             pCmdLine->aPath,
                                             pCmdLine->nCommands );

    if( pErrHdl->nErrors )
        aError = ERR_ERROR;
    else{
        RscCompiler* pCompiler = new RscCompiler( pCmdLine, pTypCont );

        pRscCompiler = pCompiler;
        aError = pCompiler->Start();
        pRscCompiler = NULL;

        delete pCompiler;
    }

    delete pTypCont;
    delete pCmdLine;
    delete pErrHdl;
    delete pHS; // wird durch InitRscCompiler erzeugt

    if( aError.IsOk() )
        return( 0 );
    else
        return( 1 );
}

void RscExit( sal_uInt32 nExit )
{
    if( nExit )
        printf( "Program exit is %ud\n", (unsigned int)nExit );
    exit( nExit );
}
<|endoftext|>


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

#include "frame_buffer.h"
#include "packet.h"

#include <cassert>
#include <string.h>

#if defined(_WIN32)
    // VS 2005: Don't warn for default initialized arrays. See help for more info.
    #pragma warning(disable:4351)
#endif

namespace webrtc {

VCMFrameBuffer::VCMFrameBuffer()
  :
    _state(kStateFree),
    _frameCounted(false),
    _nackCount(0),
    _latestPacketTimeMs(-1) {
}

VCMFrameBuffer::~VCMFrameBuffer() {
}

VCMFrameBuffer::VCMFrameBuffer(VCMFrameBuffer& rhs)
:
VCMEncodedFrame(rhs),
_state(rhs._state),
_frameCounted(rhs._frameCounted),
_sessionInfo(),
_nackCount(rhs._nackCount),
_latestPacketTimeMs(rhs._latestPacketTimeMs)
{
    _sessionInfo = rhs._sessionInfo;
}

webrtc::FrameType
VCMFrameBuffer::FrameType() const
{
    return _sessionInfo.FrameType();
}

void
VCMFrameBuffer::SetPreviousFrameLoss()
{
    _sessionInfo.SetPreviousFrameLoss();
}

WebRtc_Word32
VCMFrameBuffer::GetLowSeqNum() const
{
    return _sessionInfo.LowSequenceNumber();
}

WebRtc_Word32
VCMFrameBuffer::GetHighSeqNum() const
{
    return _sessionInfo.HighSequenceNumber();
}

int VCMFrameBuffer::PictureId() const {
  return _sessionInfo.PictureId();
}

int VCMFrameBuffer::TemporalId() const {
  return _sessionInfo.TemporalId();
}

bool VCMFrameBuffer::LayerSync() const {
  return _sessionInfo.LayerSync();
}

int VCMFrameBuffer::Tl0PicId() const {
  return _sessionInfo.Tl0PicId();
}

bool VCMFrameBuffer::NonReference() const {
  return _sessionInfo.NonReference();
}

bool
VCMFrameBuffer::IsSessionComplete() const
{
    return _sessionInfo.complete();
}

// Insert packet
VCMFrameBufferEnum
VCMFrameBuffer::InsertPacket(const VCMPacket& packet, WebRtc_Word64 timeInMs,
                             bool enableDecodableState, WebRtc_UWord32 rttMS)
{
    if (_state == kStateDecoding)
    {
        // Do not insert packet
        return kIncomplete;
    }

    // Sanity to check if the frame has been freed. (Too old for example)
    if (_state == kStateFree)
    {
        return kStateError;
    }

    // is this packet part of this frame
    if (TimeStamp() && (TimeStamp() != packet.timestamp))
    {
        return kTimeStampError;
    }

    // sanity checks
    if (_size + packet.sizeBytes +
        (packet.insertStartCode ?  kH264StartCodeLengthBytes : 0 )
        > kMaxJBFrameSizeBytes)
    {
        return kSizeError;
    }
    if (NULL == packet.dataPtr && packet.sizeBytes > 0)
    {
        return kSizeError;
    }
    if (packet.dataPtr != NULL)
    {
        _payloadType = packet.payloadType;
    }

    if (kStateEmpty == _state)
    {
        // First packet (empty and/or media) inserted into this frame.
        // store some info and set some initial values.
        _timeStamp = packet.timestamp;
        _codec = packet.codec;
        if (packet.frameType != kFrameEmpty)
        {
            // first media packet
            SetState(kStateIncomplete);
        }
    }

    WebRtc_UWord32 requiredSizeBytes = Length() + packet.sizeBytes +
                   (packet.insertStartCode ? kH264StartCodeLengthBytes : 0);
    if (requiredSizeBytes >= _size)
    {
        const WebRtc_UWord8* prevBuffer = _buffer;
        const WebRtc_UWord32 increments = requiredSizeBytes /
                                          kBufferIncStepSizeBytes +
                                        (requiredSizeBytes %
                                         kBufferIncStepSizeBytes > 0);
        const WebRtc_UWord32 newSize = _size +
                                       increments * kBufferIncStepSizeBytes;
        if (newSize > kMaxJBFrameSizeBytes)
        {
            return kSizeError;
        }
        if (VerifyAndAllocate(newSize) == -1)
        {
            return kSizeError;
        }
        _sessionInfo.UpdateDataPointers(_buffer - prevBuffer);
    }

    CopyCodecSpecific(&packet.codecSpecificHeader);

    int retVal = _sessionInfo.InsertPacket(packet, _buffer,
                                           enableDecodableState,
                                           rttMS);
    if (retVal == -1)
    {
        return kSizeError;
    }
    else if (retVal == -2)
    {
        return kDuplicatePacket;
    }
    // update length
    _length = Length() + static_cast<WebRtc_UWord32>(retVal);

    _latestPacketTimeMs = timeInMs;

    if (_sessionInfo.complete()) {
      return kCompleteSession;
    } else if (_sessionInfo.decodable()) {
      SetState(kStateDecodable);
      return kDecodableSession;
    } else {
      // this layer is not complete
      if (_state == kStateComplete) {
        // we already have a complete layer
        // wait for all independent layers belonging to the same frame
        _state = kStateIncomplete;
      }
    }
    return kIncomplete;
}

WebRtc_Word64
VCMFrameBuffer::LatestPacketTimeMs()
{
    return _latestPacketTimeMs;
}

// Zero out all entries in list up to and including the (first)
// entry equal to _lowSeqNum
WebRtc_Word32
VCMFrameBuffer::ZeroOutSeqNum(WebRtc_Word32* list, WebRtc_Word32 num)
{
    if (_sessionInfo.ZeroOutSeqNum(list, num) != 0)
    {
       return -1;
    }
    return 0;
}

// Zero out all entries in list up to and including the (first) entry equal to
// _lowSeqNum. Hybrid mode: 1. Don't NACK FEC packets 2. Make a smart decision
// on whether to NACK or not

WebRtc_Word32
VCMFrameBuffer::ZeroOutSeqNumHybrid(WebRtc_Word32* list,
                                    WebRtc_Word32 num,
                                    WebRtc_UWord32 rttMs)
{
    return _sessionInfo.ZeroOutSeqNumHybrid(list, num, rttMs);
}

void
VCMFrameBuffer::IncrementNackCount()
{
    _nackCount++;
}

WebRtc_Word16
VCMFrameBuffer::GetNackCount() const
{
    return _nackCount;
}

bool
VCMFrameBuffer::HaveLastPacket() const
{
    return _sessionInfo.HaveLastPacket();
}

void
VCMFrameBuffer::Reset()
{
    _length = 0;
    _timeStamp = 0;
    _sessionInfo.Reset();
    _frameCounted = false;
    _payloadType = 0;
    _nackCount = 0;
    _latestPacketTimeMs = -1;
    _state = kStateFree;
    VCMEncodedFrame::Reset();
}

// Makes sure the session contains a decodable stream.
void
VCMFrameBuffer::MakeSessionDecodable()
{
    WebRtc_UWord32 retVal;
#ifdef INDEPENDENT_PARTITIONS
    if (_codec != kVideoCodecVP8) {
        retVal = _sessionInfo.MakeDecodable();
        _length -= retVal;
    }
#else
    retVal = _sessionInfo.MakeDecodable();
    _length -= retVal;
#endif
}

// Set state of frame
void
VCMFrameBuffer::SetState(VCMFrameBufferStateEnum state)
{
    if (_state == state)
    {
        return;
    }
    switch (state)
    {
    case kStateFree:
        // Reset everything
        // We can go to this state from all other states.
        // The one setting the state to free must ensure
        // that the frame is removed from the timestamp
        // ordered frame list in the jb.
        Reset();
        break;

    case kStateIncomplete:
        // we can go to this state from state kStateEmpty
        assert(_state == kStateEmpty ||
            _state == kStateDecoding);

        // Do nothing, we received a packet
        break;

    case kStateComplete:
        assert(_state == kStateEmpty ||
               _state == kStateIncomplete ||
               _state == kStateDecodable);

        break;

    case kStateEmpty:
        assert(_state == kStateFree);
        // Do nothing
        break;

    case kStateDecoding:
        // A frame might have received empty packets, or media packets might
        // have been removed when making the frame decodable. The frame can
        // still be set to decodable since it can be used to inform the
        // decoder of a frame loss.
        assert(_state == kStateComplete || _state == kStateIncomplete ||
               _state == kStateDecodable || _state == kStateEmpty);
        // Transfer frame information to EncodedFrame and create any codec
        // specific information
        RestructureFrameInformation();
        break;

    case kStateDecodable:
        assert(_state == kStateEmpty ||
               _state == kStateIncomplete);
        break;

    default:
        // Should never happen
        assert(!"FrameBuffer::SetState Incorrect frame buffer state as input");
        return;
    }
    _state = state;
}

void
VCMFrameBuffer::RestructureFrameInformation()
{
    PrepareForDecode();
    _frameType = ConvertFrameType(_sessionInfo.FrameType());
    _completeFrame = _sessionInfo.complete();
    _missingFrame = _sessionInfo.PreviousFrameLoss();
}

WebRtc_Word32
VCMFrameBuffer::ExtractFromStorage(const EncodedVideoData& frameFromStorage)
{
    _frameType = ConvertFrameType(frameFromStorage.frameType);
    _timeStamp = frameFromStorage.timeStamp;
    _payloadType = frameFromStorage.payloadType;
    _encodedWidth = frameFromStorage.encodedWidth;
    _encodedHeight = frameFromStorage.encodedHeight;
    _missingFrame = frameFromStorage.missingFrame;
    _completeFrame = frameFromStorage.completeFrame;
    _renderTimeMs = frameFromStorage.renderTimeMs;
    _codec = frameFromStorage.codec;
    const WebRtc_UWord8 *prevBuffer = _buffer;
    if (VerifyAndAllocate(frameFromStorage.payloadSize) < 0)
    {
        return VCM_MEMORY;
    }
    _sessionInfo.UpdateDataPointers(_buffer - prevBuffer);
    memcpy(_buffer, frameFromStorage.payloadData, frameFromStorage.payloadSize);
    _length = frameFromStorage.payloadSize;
    return VCM_OK;
}

int VCMFrameBuffer::NotDecodablePackets() const {
  return _sessionInfo.packets_not_decodable();
}

// Set counted status (as counted by JB or not)
void VCMFrameBuffer::SetCountedFrame(bool frameCounted)
{
    _frameCounted = frameCounted;
}

bool VCMFrameBuffer::GetCountedFrame() const
{
    return _frameCounted;
}

// Get current state of frame
VCMFrameBufferStateEnum
VCMFrameBuffer::GetState() const
{
    return _state;
}

// Get current state of frame
VCMFrameBufferStateEnum
VCMFrameBuffer::GetState(WebRtc_UWord32& timeStamp) const
{
    timeStamp = TimeStamp();
    return GetState();
}

bool
VCMFrameBuffer::IsRetransmitted() const
{
    return _sessionInfo.session_nack();
}

void
VCMFrameBuffer::PrepareForDecode()
{
#ifdef INDEPENDENT_PARTITIONS
    if (_codec == kVideoCodecVP8)
    {
        _length =
            _sessionInfo.BuildVP8FragmentationHeader(_buffer, _length,
                                                     &_fragmentation);
    }
    else
    {
        _length = _sessionInfo.PrepareForDecode(_buffer);
    }
#else
    _length = _sessionInfo.PrepareForDecode(_buffer);
#endif
}

}
<commit_msg>Updating the frame buffer return value in InsertPacket: Return NoError when a packet is inserted to a frame which is being decoded.  Review URL: http://webrtc-codereview.appspot.com/330027<commit_after>/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "frame_buffer.h"
#include "packet.h"

#include <cassert>
#include <string.h>

#if defined(_WIN32)
    // VS 2005: Don't warn for default initialized arrays. See help for more info.
    #pragma warning(disable:4351)
#endif

namespace webrtc {

VCMFrameBuffer::VCMFrameBuffer()
  :
    _state(kStateFree),
    _frameCounted(false),
    _nackCount(0),
    _latestPacketTimeMs(-1) {
}

VCMFrameBuffer::~VCMFrameBuffer() {
}

VCMFrameBuffer::VCMFrameBuffer(VCMFrameBuffer& rhs)
:
VCMEncodedFrame(rhs),
_state(rhs._state),
_frameCounted(rhs._frameCounted),
_sessionInfo(),
_nackCount(rhs._nackCount),
_latestPacketTimeMs(rhs._latestPacketTimeMs)
{
    _sessionInfo = rhs._sessionInfo;
}

webrtc::FrameType
VCMFrameBuffer::FrameType() const
{
    return _sessionInfo.FrameType();
}

void
VCMFrameBuffer::SetPreviousFrameLoss()
{
    _sessionInfo.SetPreviousFrameLoss();
}

WebRtc_Word32
VCMFrameBuffer::GetLowSeqNum() const
{
    return _sessionInfo.LowSequenceNumber();
}

WebRtc_Word32
VCMFrameBuffer::GetHighSeqNum() const
{
    return _sessionInfo.HighSequenceNumber();
}

int VCMFrameBuffer::PictureId() const {
  return _sessionInfo.PictureId();
}

int VCMFrameBuffer::TemporalId() const {
  return _sessionInfo.TemporalId();
}

bool VCMFrameBuffer::LayerSync() const {
  return _sessionInfo.LayerSync();
}

int VCMFrameBuffer::Tl0PicId() const {
  return _sessionInfo.Tl0PicId();
}

bool VCMFrameBuffer::NonReference() const {
  return _sessionInfo.NonReference();
}

bool
VCMFrameBuffer::IsSessionComplete() const
{
    return _sessionInfo.complete();
}

// Insert packet
VCMFrameBufferEnum
VCMFrameBuffer::InsertPacket(const VCMPacket& packet, WebRtc_Word64 timeInMs,
                             bool enableDecodableState, WebRtc_UWord32 rttMS)
{
    if (_state == kStateDecoding)
    {
        // Do not insert packet
        return kNoError;
    }

    // Sanity to check if the frame has been freed. (Too old for example)
    if (_state == kStateFree)
    {
        return kStateError;
    }

    // is this packet part of this frame
    if (TimeStamp() && (TimeStamp() != packet.timestamp))
    {
        return kTimeStampError;
    }

    // sanity checks
    if (_size + packet.sizeBytes +
        (packet.insertStartCode ?  kH264StartCodeLengthBytes : 0 )
        > kMaxJBFrameSizeBytes)
    {
        return kSizeError;
    }
    if (NULL == packet.dataPtr && packet.sizeBytes > 0)
    {
        return kSizeError;
    }
    if (packet.dataPtr != NULL)
    {
        _payloadType = packet.payloadType;
    }

    if (kStateEmpty == _state)
    {
        // First packet (empty and/or media) inserted into this frame.
        // store some info and set some initial values.
        _timeStamp = packet.timestamp;
        _codec = packet.codec;
        if (packet.frameType != kFrameEmpty)
        {
            // first media packet
            SetState(kStateIncomplete);
        }
    }

    WebRtc_UWord32 requiredSizeBytes = Length() + packet.sizeBytes +
                   (packet.insertStartCode ? kH264StartCodeLengthBytes : 0);
    if (requiredSizeBytes >= _size)
    {
        const WebRtc_UWord8* prevBuffer = _buffer;
        const WebRtc_UWord32 increments = requiredSizeBytes /
                                          kBufferIncStepSizeBytes +
                                        (requiredSizeBytes %
                                         kBufferIncStepSizeBytes > 0);
        const WebRtc_UWord32 newSize = _size +
                                       increments * kBufferIncStepSizeBytes;
        if (newSize > kMaxJBFrameSizeBytes)
        {
            return kSizeError;
        }
        if (VerifyAndAllocate(newSize) == -1)
        {
            return kSizeError;
        }
        _sessionInfo.UpdateDataPointers(_buffer - prevBuffer);
    }

    CopyCodecSpecific(&packet.codecSpecificHeader);

    int retVal = _sessionInfo.InsertPacket(packet, _buffer,
                                           enableDecodableState,
                                           rttMS);
    if (retVal == -1)
    {
        return kSizeError;
    }
    else if (retVal == -2)
    {
        return kDuplicatePacket;
    }
    // update length
    _length = Length() + static_cast<WebRtc_UWord32>(retVal);

    _latestPacketTimeMs = timeInMs;

    if (_sessionInfo.complete()) {
      return kCompleteSession;
    } else if (_sessionInfo.decodable()) {
      SetState(kStateDecodable);
      return kDecodableSession;
    } else {
      // this layer is not complete
      if (_state == kStateComplete) {
        // we already have a complete layer
        // wait for all independent layers belonging to the same frame
        _state = kStateIncomplete;
      }
    }
    return kIncomplete;
}

WebRtc_Word64
VCMFrameBuffer::LatestPacketTimeMs()
{
    return _latestPacketTimeMs;
}

// Zero out all entries in list up to and including the (first)
// entry equal to _lowSeqNum
WebRtc_Word32
VCMFrameBuffer::ZeroOutSeqNum(WebRtc_Word32* list, WebRtc_Word32 num)
{
    if (_sessionInfo.ZeroOutSeqNum(list, num) != 0)
    {
       return -1;
    }
    return 0;
}

// Zero out all entries in list up to and including the (first) entry equal to
// _lowSeqNum. Hybrid mode: 1. Don't NACK FEC packets 2. Make a smart decision
// on whether to NACK or not

WebRtc_Word32
VCMFrameBuffer::ZeroOutSeqNumHybrid(WebRtc_Word32* list,
                                    WebRtc_Word32 num,
                                    WebRtc_UWord32 rttMs)
{
    return _sessionInfo.ZeroOutSeqNumHybrid(list, num, rttMs);
}

void
VCMFrameBuffer::IncrementNackCount()
{
    _nackCount++;
}

WebRtc_Word16
VCMFrameBuffer::GetNackCount() const
{
    return _nackCount;
}

bool
VCMFrameBuffer::HaveLastPacket() const
{
    return _sessionInfo.HaveLastPacket();
}

void
VCMFrameBuffer::Reset()
{
    _length = 0;
    _timeStamp = 0;
    _sessionInfo.Reset();
    _frameCounted = false;
    _payloadType = 0;
    _nackCount = 0;
    _latestPacketTimeMs = -1;
    _state = kStateFree;
    VCMEncodedFrame::Reset();
}

// Makes sure the session contains a decodable stream.
void
VCMFrameBuffer::MakeSessionDecodable()
{
    WebRtc_UWord32 retVal;
#ifdef INDEPENDENT_PARTITIONS
    if (_codec != kVideoCodecVP8) {
        retVal = _sessionInfo.MakeDecodable();
        _length -= retVal;
    }
#else
    retVal = _sessionInfo.MakeDecodable();
    _length -= retVal;
#endif
}

// Set state of frame
void
VCMFrameBuffer::SetState(VCMFrameBufferStateEnum state)
{
    if (_state == state)
    {
        return;
    }
    switch (state)
    {
    case kStateFree:
        // Reset everything
        // We can go to this state from all other states.
        // The one setting the state to free must ensure
        // that the frame is removed from the timestamp
        // ordered frame list in the jb.
        Reset();
        break;

    case kStateIncomplete:
        // we can go to this state from state kStateEmpty
        assert(_state == kStateEmpty ||
            _state == kStateDecoding);

        // Do nothing, we received a packet
        break;

    case kStateComplete:
        assert(_state == kStateEmpty ||
               _state == kStateIncomplete ||
               _state == kStateDecodable);

        break;

    case kStateEmpty:
        assert(_state == kStateFree);
        // Do nothing
        break;

    case kStateDecoding:
        // A frame might have received empty packets, or media packets might
        // have been removed when making the frame decodable. The frame can
        // still be set to decodable since it can be used to inform the
        // decoder of a frame loss.
        assert(_state == kStateComplete || _state == kStateIncomplete ||
               _state == kStateDecodable || _state == kStateEmpty);
        // Transfer frame information to EncodedFrame and create any codec
        // specific information
        RestructureFrameInformation();
        break;

    case kStateDecodable:
        assert(_state == kStateEmpty ||
               _state == kStateIncomplete);
        break;

    default:
        // Should never happen
        assert(!"FrameBuffer::SetState Incorrect frame buffer state as input");
        return;
    }
    _state = state;
}

void
VCMFrameBuffer::RestructureFrameInformation()
{
    PrepareForDecode();
    _frameType = ConvertFrameType(_sessionInfo.FrameType());
    _completeFrame = _sessionInfo.complete();
    _missingFrame = _sessionInfo.PreviousFrameLoss();
}

WebRtc_Word32
VCMFrameBuffer::ExtractFromStorage(const EncodedVideoData& frameFromStorage)
{
    _frameType = ConvertFrameType(frameFromStorage.frameType);
    _timeStamp = frameFromStorage.timeStamp;
    _payloadType = frameFromStorage.payloadType;
    _encodedWidth = frameFromStorage.encodedWidth;
    _encodedHeight = frameFromStorage.encodedHeight;
    _missingFrame = frameFromStorage.missingFrame;
    _completeFrame = frameFromStorage.completeFrame;
    _renderTimeMs = frameFromStorage.renderTimeMs;
    _codec = frameFromStorage.codec;
    const WebRtc_UWord8 *prevBuffer = _buffer;
    if (VerifyAndAllocate(frameFromStorage.payloadSize) < 0)
    {
        return VCM_MEMORY;
    }
    _sessionInfo.UpdateDataPointers(_buffer - prevBuffer);
    memcpy(_buffer, frameFromStorage.payloadData, frameFromStorage.payloadSize);
    _length = frameFromStorage.payloadSize;
    return VCM_OK;
}

int VCMFrameBuffer::NotDecodablePackets() const {
  return _sessionInfo.packets_not_decodable();
}

// Set counted status (as counted by JB or not)
void VCMFrameBuffer::SetCountedFrame(bool frameCounted)
{
    _frameCounted = frameCounted;
}

bool VCMFrameBuffer::GetCountedFrame() const
{
    return _frameCounted;
}

// Get current state of frame
VCMFrameBufferStateEnum
VCMFrameBuffer::GetState() const
{
    return _state;
}

// Get current state of frame
VCMFrameBufferStateEnum
VCMFrameBuffer::GetState(WebRtc_UWord32& timeStamp) const
{
    timeStamp = TimeStamp();
    return GetState();
}

bool
VCMFrameBuffer::IsRetransmitted() const
{
    return _sessionInfo.session_nack();
}

void
VCMFrameBuffer::PrepareForDecode()
{
#ifdef INDEPENDENT_PARTITIONS
    if (_codec == kVideoCodecVP8)
    {
        _length =
            _sessionInfo.BuildVP8FragmentationHeader(_buffer, _length,
                                                     &_fragmentation);
    }
    else
    {
        _length = _sessionInfo.PrepareForDecode(_buffer);
    }
#else
    _length = _sessionInfo.PrepareForDecode(_buffer);
#endif
}

}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qmlpixmapcache_p.h"

#include "qfxperf_p_p.h"

#include <qmlengine.h>
#include <private/qmlglobal_p.h>

#include <QCoreApplication>
#include <QImageReader>
#include <QHash>
#include <QNetworkReply>
#include <QPixmapCache>
#include <QFile>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QtCore/qdebug.h>
#include <private/qobject_p.h>

QT_BEGIN_NAMESPACE

class QmlImageReader : public QThread
{
    Q_OBJECT
public:
    QmlImageReader(QObject *parent=0);
    ~QmlImageReader();

    void read(QmlPixmapReply *rep);
    void cancel(QmlPixmapReply *rep);

protected:
    void run();

private:
    struct Job {
        Job() : reply(0), error(false) {}
        QmlPixmapReply *reply;
        QImage img;
        bool error;
    };

    void loadImage(Job &job);

    QList<Job> jobs;
    QMutex mutex;
    QWaitCondition haveJob;
    bool quit;
};

class QmlImageDecodeEvent : public QEvent
{
public:
    QmlImageDecodeEvent(bool err, QImage &img) : QEvent(QEvent::User), error(err), image(img) {}

    bool error;
    QImage image;
};

Q_GLOBAL_STATIC(QmlImageReader, qmlImageReader)

QmlImageReader::QmlImageReader(QObject *parent) : QThread(parent), quit(false)
{
    start(QThread::LowPriority);
}

QmlImageReader::~QmlImageReader()
{
    quit = true;
    haveJob.wakeOne();
}

void QmlImageReader::read(QmlPixmapReply *reply)
{
    mutex.lock();
    Job job;
    job.reply = reply;
    jobs.append(job);
    if (jobs.count() == 1)
        haveJob.wakeOne();
    mutex.unlock();
}

void QmlImageReader::cancel(QmlPixmapReply *reply)
{
    mutex.lock();
    QList<Job>::iterator it = jobs.begin();
    while (it != jobs.end()) {
        if ((*it).reply == reply) {
            jobs.erase(it);
            break;
        }
        ++it;
    }
    mutex.unlock();
}

void QmlImageReader::loadImage(Job &job)
{
    QImageReader imgio(job.reply->device());
    if (imgio.read(&job.img)) {
        job.error = false;
    } else {
        job.error = true;
        qWarning() << imgio.errorString();
    }
}

void QmlImageReader::run()
{
    while (1) {
        mutex.lock();
        if (!jobs.count())
            haveJob.wait(&mutex);
        if (quit)
            break;
        Job runningJob = jobs.takeFirst();
        runningJob.reply->addRef();
        mutex.unlock();

        loadImage(runningJob);
        QCoreApplication::postEvent(runningJob.reply, new QmlImageDecodeEvent(runningJob.error, runningJob.img));
    }
}

static bool readImage(QIODevice *dev, QPixmap *pixmap)
{
    QImageReader imgio(dev);

//#define QT_TEST_SCALED_SIZE
#ifdef QT_TEST_SCALED_SIZE
    /*
    Some mechanism is needed for loading images at a limited size, especially
    for remote images. Loading only thumbnails of remote progressive JPEG
    images can be efficient. (Qt jpeg handler does not do so currently)
    */

    QSize limit(60,60);
    QSize sz = imgio.size();
    if (sz.width() > limit.width() || sz.height() > limit.height()) {
        sz.scale(limit,Qt::KeepAspectRatio);
        imgio.setScaledSize(sz);
    }
#endif

    QImage img;
    if (imgio.read(&img)) {
#ifdef QT_TEST_SCALED_SIZE
        if (!sz.isValid())
            img = img.scaled(limit,Qt::KeepAspectRatio);
#endif
        *pixmap = QPixmap::fromImage(img);
        return true;
    } else {
        qWarning() << imgio.errorString();
        return false;
    }
}

/*!
    \internal
    \class QmlPixmapCache
    \brief Enacapsultes a pixmap for QmlGraphics items.

    This class is NOT reentrant.
 */

static QString toLocalFileOrQrc(const QUrl& url)
{
    QString r = url.toLocalFile();
    if (r.isEmpty() && url.scheme() == QLatin1String("qrc"))
        r = QLatin1Char(':') + url.path();
    return r;
}

typedef QHash<QString, QmlPixmapReply *> QmlPixmapReplyHash;
static QmlPixmapReplyHash qmlActivePixmapReplies;

class QmlPixmapReplyPrivate : public QObjectPrivate
{
    Q_DECLARE_PUBLIC(QmlPixmapReply)

public:
    QmlPixmapReplyPrivate(const QString &url, QNetworkReply *r)
        : QObjectPrivate(), refCount(1), urlKey(url), reply(r), status(QmlPixmapReply::Loading) {
    }

    int refCount;
    QString urlKey;
    QNetworkReply *reply;
    QPixmap pixmap; // ensure reference to pixmap so QPixmapCache does not discard
    QImage image;
    QmlPixmapReply::Status status;
};

QmlPixmapReply::QmlPixmapReply(const QString &key, QNetworkReply *reply)
  : QObject(*new QmlPixmapReplyPrivate(key, reply), 0)
{
    Q_D(QmlPixmapReply);

    static int replyDownloadProgress = -1;
    static int replyFinished = -1;
    static int thisDownloadProgress = -1;
    static int thisNetworkRequestDone = -1;

    if (replyDownloadProgress == -1) {
        replyDownloadProgress = QNetworkReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)");
        replyFinished = QNetworkReply::staticMetaObject.indexOfSignal("finished()");
        thisDownloadProgress = QmlPixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)");
        thisNetworkRequestDone = QmlPixmapReply::staticMetaObject.indexOfSlot("networkRequestDone()");
    }

    QMetaObject::connect(d->reply, replyDownloadProgress, this, thisDownloadProgress, Qt::DirectConnection);
    QMetaObject::connect(d->reply, replyFinished, this, thisNetworkRequestDone);
}

QmlPixmapReply::~QmlPixmapReply()
{
    Q_D(QmlPixmapReply);
    if (d->status == Decoding)
        qmlImageReader()->cancel(this);
    delete d->reply;
}

void QmlPixmapReply::networkRequestDone()
{
    Q_D(QmlPixmapReply);
    if (d->reply->error()) {
        d->pixmap = QPixmap();
        d->status = Error;
        QPixmapCache::insert(d->urlKey, d->pixmap);
        qWarning() << "Network error loading" << d->urlKey << d->reply->errorString();
        emit finished();
    } else {
        qmlImageReader()->read(this);
        d->status = Decoding;
    }
}

bool QmlPixmapReply::event(QEvent *event)
{
    Q_D(QmlPixmapReply);
    if (event->type() == QEvent::User) {
        if (!release(true)) {
            QmlImageDecodeEvent *de = static_cast<QmlImageDecodeEvent*>(event);
            d->status = de->error ? Error : Ready;
            if (d->status == Ready) {
                d->pixmap = QPixmap::fromImage(de->image);
                d->image = QImage();
            } else {
                qWarning() << "Error decoding" << d->urlKey;
            }
            QPixmapCache::insert(d->urlKey, d->pixmap);
            emit finished();
        }
        return true;
    }

    return QObject::event(event);
}

QmlPixmapReply::Status QmlPixmapReply::status() const
{
    Q_D(const QmlPixmapReply);
    return d->status;
}

QIODevice *QmlPixmapReply::device()
{
    Q_D(QmlPixmapReply);
    return d->reply;
}

void QmlPixmapReply::addRef()
{
    Q_D(QmlPixmapReply);
    ++d->refCount;
}

bool QmlPixmapReply::release(bool defer)
{
    Q_D(QmlPixmapReply);
    Q_ASSERT(d->refCount > 0);
    --d->refCount;
    if (d->refCount == 0) {
        qmlActivePixmapReplies.remove(d->urlKey);
        if (defer)
            deleteLater();
        else
            delete this;
        return true;
    }

    return false;
}

/*!
    Finds the cached pixmap corresponding to \a url.
    If the image is a network resource and has not yet
    been retrieved and cached, request() must be called.

    Returns Ready, or Error if the image has been retrieved,
    otherwise the current retrieval status.
*/
QmlPixmapReply::Status QmlPixmapCache::get(const QUrl& url, QPixmap *pixmap)
{
    QmlPixmapReply::Status status = QmlPixmapReply::Unrequested;

#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
    QString lf = toLocalFileOrQrc(url);
    if (!lf.isEmpty()) {
        status = QmlPixmapReply::Ready;
        if (!QPixmapCache::find(lf,pixmap)) {
            QFile f(lf);
            if (f.open(QIODevice::ReadOnly)) {
                if (!readImage(&f, pixmap)) {
                    qWarning() << "Format error loading" << url;
                    *pixmap = QPixmap();
                    status = QmlPixmapReply::Error;
                }
            } else {
                qWarning() << "Cannot open" << url;
                *pixmap = QPixmap();
                status = QmlPixmapReply::Error;
            }
            if (status == QmlPixmapReply::Ready)
                QPixmapCache::insert(lf, *pixmap);
        }
        return status;
    }
#endif

    QString key = url.toString();
    QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies.find(key);
    if (QPixmapCache::find(key, pixmap)) {
        if (iter != qmlActivePixmapReplies.end()) {
            status = (*iter)->status();
            (*iter)->release();
        } else {
            status = pixmap->isNull() ? QmlPixmapReply::Error : QmlPixmapReply::Ready;
        }
    } else if (iter != qmlActivePixmapReplies.end()) {
        status = QmlPixmapReply::Loading;
    }

    return status;
}

/*!
    Starts a network request to load \a url.

    Returns a QmlPixmapReply.  Caller should connect to QmlPixmapReply::finished()
    and call get() when the image is available.

    The returned QmlPixmapReply will be deleted when all request() calls are
    matched by a corresponding get() call.
*/
QmlPixmapReply *QmlPixmapCache::request(QmlEngine *engine, const QUrl &url)
{
    QString key = url.toString();
    QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies.find(key);
    if (iter == qmlActivePixmapReplies.end()) {
        QNetworkRequest req(url);
        req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
        QmlPixmapReply *item = new QmlPixmapReply(key, engine->networkAccessManager()->get(req));
        iter = qmlActivePixmapReplies.insert(key, item);
    } else {
        (*iter)->addRef();
    }

    return (*iter);
}

/*!
    Cancels a previous call to request().

    May also cancel loading (eg. if no other pending request).

    Any connections from the QmlPixmapReply returned by request() to \a obj will be
    disconnected.
*/
void QmlPixmapCache::cancel(const QUrl& url, QObject *obj)
{
    QString key = url.toString();
    QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies.find(key);
    if (iter == qmlActivePixmapReplies.end())
        return;

    QmlPixmapReply *reply = *iter;
    if (obj)
        QObject::disconnect(reply, 0, obj, 0);
    reply->release();
}

/*!
    This function is mainly for test verification. It returns the number of
    requests that are still unfinished.
*/
int QmlPixmapCache::pendingRequests()
{
    return qmlActivePixmapReplies.count();
}

#include <qmlpixmapcache.moc>

QT_END_NAMESPACE
<commit_msg>Ensure pixmaps can be retrieved even if they do not fit in the QPixmapCache. Fixes QT-2280<commit_after>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qmlpixmapcache_p.h"

#include "qfxperf_p_p.h"

#include <qmlengine.h>
#include <private/qmlglobal_p.h>

#include <QCoreApplication>
#include <QImageReader>
#include <QHash>
#include <QNetworkReply>
#include <QPixmapCache>
#include <QFile>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QtCore/qdebug.h>
#include <private/qobject_p.h>

QT_BEGIN_NAMESPACE

class QmlImageReader : public QThread
{
    Q_OBJECT
public:
    QmlImageReader(QObject *parent=0);
    ~QmlImageReader();

    void read(QmlPixmapReply *rep);
    void cancel(QmlPixmapReply *rep);

protected:
    void run();

private:
    struct Job {
        Job() : reply(0), error(false) {}
        QmlPixmapReply *reply;
        QImage img;
        bool error;
    };

    void loadImage(Job &job);

    QList<Job> jobs;
    QMutex mutex;
    QWaitCondition haveJob;
    bool quit;
};

class QmlImageDecodeEvent : public QEvent
{
public:
    QmlImageDecodeEvent(bool err, QImage &img) : QEvent(QEvent::User), error(err), image(img) {}

    bool error;
    QImage image;
};

Q_GLOBAL_STATIC(QmlImageReader, qmlImageReader)

QmlImageReader::QmlImageReader(QObject *parent) : QThread(parent), quit(false)
{
    start(QThread::LowPriority);
}

QmlImageReader::~QmlImageReader()
{
    quit = true;
    haveJob.wakeOne();
}

void QmlImageReader::read(QmlPixmapReply *reply)
{
    mutex.lock();
    Job job;
    job.reply = reply;
    jobs.append(job);
    if (jobs.count() == 1)
        haveJob.wakeOne();
    mutex.unlock();
}

void QmlImageReader::cancel(QmlPixmapReply *reply)
{
    mutex.lock();
    QList<Job>::iterator it = jobs.begin();
    while (it != jobs.end()) {
        if ((*it).reply == reply) {
            jobs.erase(it);
            break;
        }
        ++it;
    }
    mutex.unlock();
}

void QmlImageReader::loadImage(Job &job)
{
    QImageReader imgio(job.reply->device());
    if (imgio.read(&job.img)) {
        job.error = false;
    } else {
        job.error = true;
        qWarning() << imgio.errorString();
    }
}

void QmlImageReader::run()
{
    while (1) {
        mutex.lock();
        if (!jobs.count())
            haveJob.wait(&mutex);
        if (quit)
            break;
        Job runningJob = jobs.takeFirst();
        runningJob.reply->addRef();
        mutex.unlock();

        loadImage(runningJob);
        QCoreApplication::postEvent(runningJob.reply, new QmlImageDecodeEvent(runningJob.error, runningJob.img));
    }
}

static bool readImage(QIODevice *dev, QPixmap *pixmap)
{
    QImageReader imgio(dev);

//#define QT_TEST_SCALED_SIZE
#ifdef QT_TEST_SCALED_SIZE
    /*
    Some mechanism is needed for loading images at a limited size, especially
    for remote images. Loading only thumbnails of remote progressive JPEG
    images can be efficient. (Qt jpeg handler does not do so currently)
    */

    QSize limit(60,60);
    QSize sz = imgio.size();
    if (sz.width() > limit.width() || sz.height() > limit.height()) {
        sz.scale(limit,Qt::KeepAspectRatio);
        imgio.setScaledSize(sz);
    }
#endif

    QImage img;
    if (imgio.read(&img)) {
#ifdef QT_TEST_SCALED_SIZE
        if (!sz.isValid())
            img = img.scaled(limit,Qt::KeepAspectRatio);
#endif
        *pixmap = QPixmap::fromImage(img);
        return true;
    } else {
        qWarning() << imgio.errorString();
        return false;
    }
}

/*!
    \internal
    \class QmlPixmapCache
    \brief Enacapsultes a pixmap for QmlGraphics items.

    This class is NOT reentrant.
 */

static QString toLocalFileOrQrc(const QUrl& url)
{
    QString r = url.toLocalFile();
    if (r.isEmpty() && url.scheme() == QLatin1String("qrc"))
        r = QLatin1Char(':') + url.path();
    return r;
}

typedef QHash<QString, QmlPixmapReply *> QmlPixmapReplyHash;
static QmlPixmapReplyHash qmlActivePixmapReplies;

class QmlPixmapReplyPrivate : public QObjectPrivate
{
    Q_DECLARE_PUBLIC(QmlPixmapReply)

public:
    QmlPixmapReplyPrivate(const QString &url, QNetworkReply *r)
        : QObjectPrivate(), refCount(1), urlKey(url), reply(r), status(QmlPixmapReply::Loading) {
    }

    int refCount;
    QString urlKey;
    QNetworkReply *reply;
    QPixmap pixmap; // ensure reference to pixmap so QPixmapCache does not discard
    QImage image;
    QmlPixmapReply::Status status;
};

QmlPixmapReply::QmlPixmapReply(const QString &key, QNetworkReply *reply)
  : QObject(*new QmlPixmapReplyPrivate(key, reply), 0)
{
    Q_D(QmlPixmapReply);

    static int replyDownloadProgress = -1;
    static int replyFinished = -1;
    static int thisDownloadProgress = -1;
    static int thisNetworkRequestDone = -1;

    if (replyDownloadProgress == -1) {
        replyDownloadProgress = QNetworkReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)");
        replyFinished = QNetworkReply::staticMetaObject.indexOfSignal("finished()");
        thisDownloadProgress = QmlPixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)");
        thisNetworkRequestDone = QmlPixmapReply::staticMetaObject.indexOfSlot("networkRequestDone()");
    }

    QMetaObject::connect(d->reply, replyDownloadProgress, this, thisDownloadProgress, Qt::DirectConnection);
    QMetaObject::connect(d->reply, replyFinished, this, thisNetworkRequestDone);
}

QmlPixmapReply::~QmlPixmapReply()
{
    Q_D(QmlPixmapReply);
    if (d->status == Decoding)
        qmlImageReader()->cancel(this);
    delete d->reply;
}

void QmlPixmapReply::networkRequestDone()
{
    Q_D(QmlPixmapReply);
    if (d->reply->error()) {
        d->pixmap = QPixmap();
        d->status = Error;
        QPixmapCache::insert(d->urlKey, d->pixmap);
        qWarning() << "Network error loading" << d->urlKey << d->reply->errorString();
        emit finished();
    } else {
        qmlImageReader()->read(this);
        d->status = Decoding;
    }
}

bool QmlPixmapReply::event(QEvent *event)
{
    Q_D(QmlPixmapReply);
    if (event->type() == QEvent::User) {
        if (!release(true)) {
            QmlImageDecodeEvent *de = static_cast<QmlImageDecodeEvent*>(event);
            d->status = de->error ? Error : Ready;
            if (d->status == Ready) {
                d->pixmap = QPixmap::fromImage(de->image);
                d->image = QImage();
            } else {
                qWarning() << "Error decoding" << d->urlKey;
            }
            QPixmapCache::insert(d->urlKey, d->pixmap); // note: may fail (returns false)
            emit finished();
        }
        return true;
    }

    return QObject::event(event);
}

QmlPixmapReply::Status QmlPixmapReply::status() const
{
    Q_D(const QmlPixmapReply);
    return d->status;
}

QIODevice *QmlPixmapReply::device()
{
    Q_D(QmlPixmapReply);
    return d->reply;
}

void QmlPixmapReply::addRef()
{
    Q_D(QmlPixmapReply);
    ++d->refCount;
}

bool QmlPixmapReply::release(bool defer)
{
    Q_D(QmlPixmapReply);
    Q_ASSERT(d->refCount > 0);
    --d->refCount;
    if (d->refCount == 0) {
        qmlActivePixmapReplies.remove(d->urlKey);
        if (defer)
            deleteLater();
        else
            delete this;
        return true;
    }

    return false;
}

/*!
    Finds the cached pixmap corresponding to \a url.
    If the image is a network resource and has not yet
    been retrieved and cached, request() must be called.

    Returns Ready, or Error if the image has been retrieved,
    otherwise the current retrieval status.
*/
QmlPixmapReply::Status QmlPixmapCache::get(const QUrl& url, QPixmap *pixmap)
{
    QmlPixmapReply::Status status = QmlPixmapReply::Unrequested;

#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
    QString lf = toLocalFileOrQrc(url);
    if (!lf.isEmpty()) {
        status = QmlPixmapReply::Ready;
        if (!QPixmapCache::find(lf,pixmap)) {
            QFile f(lf);
            if (f.open(QIODevice::ReadOnly)) {
                if (!readImage(&f, pixmap)) {
                    qWarning() << "Format error loading" << url;
                    *pixmap = QPixmap();
                    status = QmlPixmapReply::Error;
                }
            } else {
                qWarning() << "Cannot open" << url;
                *pixmap = QPixmap();
                status = QmlPixmapReply::Error;
            }
            if (status == QmlPixmapReply::Ready)
                QPixmapCache::insert(lf, *pixmap);
        }
        return status;
    }
#endif

    QString key = url.toString();

    QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies.find(key);
    if (iter != qmlActivePixmapReplies.end() && (*iter)->status() == QmlPixmapReply::Ready) {
        // Must check this, since QPixmapCache::insert may have failed.
        *pixmap = (*iter)->d_func()->pixmap;
        status = (*iter)->status();
        (*iter)->release();
    } else if (QPixmapCache::find(key, pixmap)) {
        if (iter != qmlActivePixmapReplies.end()) {
            status = (*iter)->status();
            (*iter)->release();
        } else {
            status = pixmap->isNull() ? QmlPixmapReply::Error : QmlPixmapReply::Ready;
        }
    } else if (iter != qmlActivePixmapReplies.end()) {
        status = QmlPixmapReply::Loading;
    }

    return status;
}

/*!
    Starts a network request to load \a url.

    Returns a QmlPixmapReply.  Caller should connect to QmlPixmapReply::finished()
    and call get() when the image is available.

    The returned QmlPixmapReply will be deleted when all request() calls are
    matched by a corresponding get() call.
*/
QmlPixmapReply *QmlPixmapCache::request(QmlEngine *engine, const QUrl &url)
{
    QString key = url.toString();
    QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies.find(key);
    if (iter == qmlActivePixmapReplies.end()) {
        QNetworkRequest req(url);
        req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
        QmlPixmapReply *item = new QmlPixmapReply(key, engine->networkAccessManager()->get(req));
        iter = qmlActivePixmapReplies.insert(key, item);
    } else {
        (*iter)->addRef();
    }

    return (*iter);
}

/*!
    Cancels a previous call to request().

    May also cancel loading (eg. if no other pending request).

    Any connections from the QmlPixmapReply returned by request() to \a obj will be
    disconnected.
*/
void QmlPixmapCache::cancel(const QUrl& url, QObject *obj)
{
    QString key = url.toString();
    QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies.find(key);
    if (iter == qmlActivePixmapReplies.end())
        return;

    QmlPixmapReply *reply = *iter;
    if (obj)
        QObject::disconnect(reply, 0, obj, 0);
    reply->release();
}

/*!
    This function is mainly for test verification. It returns the number of
    requests that are still unfinished.
*/
int QmlPixmapCache::pendingRequests()
{
    return qmlActivePixmapReplies.count();
}

#include <qmlpixmapcache.moc>

QT_END_NAMESPACE
<|endoftext|>


<commit_before><commit_msg>Fix a few warnings in the tracked object comparator<commit_after><|endoftext|>