<commit_before>// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// 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.

/**
 * @file ReaderProxy.cpp
 *
 */

#include <fastrtps/rtps/writer/ReaderProxy.h>
#include <fastrtps/rtps/writer/StatefulWriter.h>
#include <fastrtps/utils/TimeConversion.h>
#include <fastrtps/rtps/writer/timedevent/NackSupressionDuration.h>
#include <fastrtps/log/Log.h>
#include <fastrtps/rtps/resources/AsyncWriterThread.h>
#include <fastrtps/rtps/history/WriterHistory.h>

#include <mutex>

#include <cassert>

using namespace eprosima::fastrtps::rtps;

ReaderProxy::ReaderProxy(
        const RemoteReaderAttributes& rdata,
        const WriterTimes& times,
        StatefulWriter* SW)
    : m_att(rdata)
    , mp_SFW(SW)
    , mp_nackSupression(nullptr)
    , m_lastAcknackCount(0)
    , mp_mutex(new std::recursive_mutex())
    , lastNackfragCount_(0)
{
    if(rdata.endpoint.reliabilityKind == RELIABLE)
    {
        mp_nackSupression = new NackSupressionDuration(this,TimeConv::Time_t2MilliSecondsDouble(times.nackSupressionDuration));
    }

    // Use remoteLocatorList as joint unicast + multicast locators
    m_att.endpoint.remoteLocatorList.assign(m_att.endpoint.unicastLocatorList);
    m_att.endpoint.remoteLocatorList.push_back(m_att.endpoint.multicastLocatorList);

    logInfo(RTPS_WRITER,"Reader Proxy created");
}

ReaderProxy::~ReaderProxy()
{
    destroy_timers();
    delete(mp_mutex);
}

void ReaderProxy::destroy_timers()
{
    if(mp_nackSupression != nullptr)
    {
        delete(mp_nackSupression);
        mp_nackSupression = nullptr;
    }
}

void ReaderProxy::addChange(const ChangeForReader_t& change)
{
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);

    assert(change.getSequenceNumber() > changesFromRLowMark_);
    assert(m_changesForReader.rbegin() != m_changesForReader.rend() ?
            change.getSequenceNumber() > m_changesForReader.rbegin()->getSequenceNumber() :
            true);

    // For best effort readers, changes are acked when being sent
    if(m_changesForReader.size() == 0 && change.getStatus() == ACKNOWLEDGED)
    {
        changesFromRLowMark_ = change.getSequenceNumber();
        return;
    }

    m_changesForReader.insert(change);
    //TODO (Ricardo) Remove this functionality from here. It is not his place.
    if (change.getStatus() == UNSENT)
    {
        AsyncWriterThread::wakeUp(mp_SFW);
    }
}

size_t ReaderProxy::countChangesForReader() const
{
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);
    return m_changesForReader.size();
}

bool ReaderProxy::change_is_acked(const SequenceNumber_t& sequence_number)
{
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);

    if(sequence_number <= changesFromRLowMark_)
        return true;

    auto chit = m_changesForReader.find(ChangeForReader_t(sequence_number));
    assert(chit != m_changesForReader.end());

    return !chit->isRelevant() || chit->getStatus() == ACKNOWLEDGED;
}

void ReaderProxy::acked_changes_set(const SequenceNumber_t& seqNum)
{
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);
    SequenceNumber_t future_low_mark = seqNum;

    if(seqNum > changesFromRLowMark_)
    {
        auto chit = m_changesForReader.find(seqNum);
        m_changesForReader.erase(m_changesForReader.begin(), chit);
    }
    else
    {
        // Special case. Currently only used on Builtin StatefulWriters
        // after losing lease duration.

        SequenceNumber_t current_sequence = seqNum;
        if(seqNum < mp_SFW->get_seq_num_min())
        {
            current_sequence = mp_SFW->get_seq_num_min();
        }
        future_low_mark = current_sequence;

        for(; current_sequence <= changesFromRLowMark_; ++current_sequence)
        {
            CacheChange_t* change = nullptr;

            if(mp_SFW->mp_history->get_change(current_sequence, mp_SFW->getGuid(), &change))
            {
                ChangeForReader_t cr(change);
                cr.setStatus(UNACKNOWLEDGED);
                m_changesForReader.insert(cr);
            }
            else
            {
                ChangeForReader_t cr;
                cr.setStatus(UNACKNOWLEDGED);
                cr.notValid();
                m_changesForReader.insert(cr);
            }
        }
    }

    changesFromRLowMark_ = future_low_mark - 1;
}

bool ReaderProxy::requested_changes_set(std::vector<SequenceNumber_t>& seqNumSet)
{
    bool isSomeoneWasSetRequested = false;
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);

    for(std::vector<SequenceNumber_t>::iterator sit=seqNumSet.begin();sit!=seqNumSet.end();++sit)
    {
        auto chit = m_changesForReader.find(ChangeForReader_t(*sit));

        if(chit != m_changesForReader.end() && UNACKNOWLEDGED == chit->getStatus())
        {
            ChangeForReader_t newch(*chit);
            newch.setStatus(REQUESTED);
            newch.markAllFragmentsAsUnsent();

            auto hint = m_changesForReader.erase(chit);

            m_changesForReader.insert(hint, newch);

            isSomeoneWasSetRequested = true;
        }
    }

    if(isSomeoneWasSetRequested)
    {
        logInfo(RTPS_WRITER,"Requested Changes: " << seqNumSet);
    }
    else if(!seqNumSet.empty())
    {
        logWarning(RTPS_WRITER,"Requested Changes: " << seqNumSet
                   << " not found (low mark: " << changesFromRLowMark_ << ")");
    }

    return isSomeoneWasSetRequested;
}

//TODO(Ricardo) Temporal
//std::vector<const ChangeForReader_t*> ReaderProxy::get_unsent_changes() const
std::vector<ChangeForReader_t*> ReaderProxy::get_unsent_changes()
{
    std::vector<ChangeForReader_t*> unsent_changes;
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);

    for(auto &change_for_reader : m_changesForReader)
        if(change_for_reader.getStatus() == UNSENT)
            unsent_changes.push_back(const_cast<ChangeForReader_t*>(&change_for_reader));

    return unsent_changes;
}

std::vector<const ChangeForReader_t*> ReaderProxy::get_requested_changes() const
{
    std::vector<const ChangeForReader_t*> unsent_changes;
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);

    auto it = m_changesForReader.begin();
    for (; it!= m_changesForReader.end(); ++it)
        if(it->getStatus() == REQUESTED)
            unsent_changes.push_back(&(*it));

    return unsent_changes;
}

void ReaderProxy::set_change_to_status(const SequenceNumber_t& seq_num, ChangeForReaderStatus_t status)
{
    if(seq_num <= changesFromRLowMark_)
    {
        return;
    }

    auto it = m_changesForReader.find(ChangeForReader_t(seq_num));
    bool mustWakeUpAsyncThread = false;

    if(it != m_changesForReader.end())
    {
        if(status == ACKNOWLEDGED && it == m_changesForReader.begin())
        {
            m_changesForReader.erase(it);
            changesFromRLowMark_ = seq_num;
        }
        else
        {
            ChangeForReader_t newch(*it);
            newch.setStatus(status);
            if (status == UNSENT) mustWakeUpAsyncThread = true;
            auto hint = m_changesForReader.erase(it);
            m_changesForReader.insert(hint, newch);
        }
    }

    if (mustWakeUpAsyncThread)
        AsyncWriterThread::wakeUp(mp_SFW);
}

bool ReaderProxy::mark_fragment_as_sent_for_change(const CacheChange_t* change, FragmentNumber_t fragment)
{
    if(change->sequenceNumber <= changesFromRLowMark_)
        return false;

    bool allFragmentsSent = false;
    auto it = m_changesForReader.find(ChangeForReader_t(change->sequenceNumber));

    bool mustWakeUpAsyncThread = false;

    if(it != m_changesForReader.end())
    {
        ChangeForReader_t newch(*it);
        newch.markFragmentsAsSent(fragment);
        if (newch.getUnsentFragments().isSetEmpty())
        {
            allFragmentsSent = true;
        }
        else
            mustWakeUpAsyncThread = true;
        auto hint = m_changesForReader.erase(it);
        m_changesForReader.insert(hint, newch);
    }

    if (mustWakeUpAsyncThread)
    {
        AsyncWriterThread::wakeUp(mp_SFW);
    }

    return allFragmentsSent;
}

void ReaderProxy::convert_status_on_all_changes(ChangeForReaderStatus_t previous, ChangeForReaderStatus_t next)
{
    assert(previous > next);

    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);
    bool mustWakeUpAsyncThread = false;

    auto it = m_changesForReader.begin();
    while(it != m_changesForReader.end())
    {
        if(it->getStatus() == previous)
        {
            if(next == ACKNOWLEDGED && it == m_changesForReader.begin())
            {
                changesFromRLowMark_ = it->getSequenceNumber();
                it = m_changesForReader.erase(it);
                continue;
            }
            else
            {
                // Note: we can perform this cast as we are not touching the sorting field (seq_num)
                const_cast<ChangeForReader_t&>(*it).setStatus(next);
                if (next == UNSENT)
                {
                    mustWakeUpAsyncThread = true;
                }
            }
        }

        ++it;
    }

    if (mustWakeUpAsyncThread)
    {
        AsyncWriterThread::wakeUp(mp_SFW);
    }
}

//TODO(Ricardo)
//void ReaderProxy::setNotValid(const CacheChange_t* change)
void ReaderProxy::setNotValid(CacheChange_t* change)
{
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);

    // Check sequence number is in the container, because it was not clean up.
    if(m_changesForReader.size() == 0 || change->sequenceNumber < m_changesForReader.begin()->getSequenceNumber())
        return;

    auto chit = m_changesForReader.find(ChangeForReader_t(change));

    // Element must be in the container. In other case, bug.
    assert(chit != m_changesForReader.end());

    // Note: we can perform this cast as we are not touching the sorting field (seq_num)
    auto & newch = const_cast<ChangeForReader_t&>(*chit);

    if(chit == m_changesForReader.begin())
    {
        assert(chit->getStatus() != ACKNOWLEDGED);

        // if it is the first element, set state to unacknowledge because from now reader has to confirm
        // it will not be expecting it.
        // Note: we can perform this cast as we are not touching the sorting field (seq_num)
        newch.setStatus(UNACKNOWLEDGED);
    }
    else
    {
        // In case its state is not ACKNOWLEDGED, set it to UNACKNOWLEDGE because from now reader has to confirm
        // it will not be expecting it.
        if (newch.getStatus() != ACKNOWLEDGED)
            newch.setStatus(UNACKNOWLEDGED);
    }
    newch.notValid();
}

bool ReaderProxy::thereIsUnacknowledged() const
{
    bool returnedValue = false;
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);

    for(auto it = m_changesForReader.begin(); it!=m_changesForReader.end(); ++it)
    {
        if(it->getStatus() == UNACKNOWLEDGED)
        {
            returnedValue = true;
            break;
        }
    }

    return returnedValue;
}

bool change_min(const ChangeForReader_t* ch1, const ChangeForReader_t* ch2)
{
    return ch1->getSequenceNumber() < ch2->getSequenceNumber();
}

bool ReaderProxy::minChange(std::vector<ChangeForReader_t*>* Changes,
        ChangeForReader_t* changeForReader)
{
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);
    *changeForReader = **std::min_element(Changes->begin(),Changes->end(),change_min);
    return true;
}

bool ReaderProxy::requested_fragment_set(SequenceNumber_t sequence_number, const FragmentNumberSet_t& frag_set)
{
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);

    // Locate the outbound change referenced by the NACK_FRAG
    auto changeIter = std::find_if(m_changesForReader.begin(), m_changesForReader.end(),
            [sequence_number](const ChangeForReader_t& change)
            {return change.getSequenceNumber() == sequence_number;});
    if (changeIter == m_changesForReader.end())
        return false;

    ChangeForReader_t newch(*changeIter);
    auto hint = m_changesForReader.erase(changeIter);
    newch.markFragmentsAsUnsent(frag_set);

    // If it was UNSENT, we shouldn't switch back to REQUESTED to prevent stalling.
    if (newch.getStatus() != UNSENT)
        newch.setStatus(REQUESTED);
    m_changesForReader.insert(hint, newch);

    return true;
}
<commit_msg>Refs #4720 Removing warning message (#425)<commit_after>// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// 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.

/**
 * @file ReaderProxy.cpp
 *
 */

#include <fastrtps/rtps/writer/ReaderProxy.h>
#include <fastrtps/rtps/writer/StatefulWriter.h>
#include <fastrtps/utils/TimeConversion.h>
#include <fastrtps/rtps/writer/timedevent/NackSupressionDuration.h>
#include <fastrtps/log/Log.h>
#include <fastrtps/rtps/resources/AsyncWriterThread.h>
#include <fastrtps/rtps/history/WriterHistory.h>

#include <mutex>

#include <cassert>

using namespace eprosima::fastrtps::rtps;

ReaderProxy::ReaderProxy(
        const RemoteReaderAttributes& rdata,
        const WriterTimes& times,
        StatefulWriter* SW)
    : m_att(rdata)
    , mp_SFW(SW)
    , mp_nackSupression(nullptr)
    , m_lastAcknackCount(0)
    , mp_mutex(new std::recursive_mutex())
    , lastNackfragCount_(0)
{
    if(rdata.endpoint.reliabilityKind == RELIABLE)
    {
        mp_nackSupression = new NackSupressionDuration(this,TimeConv::Time_t2MilliSecondsDouble(times.nackSupressionDuration));
    }

    // Use remoteLocatorList as joint unicast + multicast locators
    m_att.endpoint.remoteLocatorList.assign(m_att.endpoint.unicastLocatorList);
    m_att.endpoint.remoteLocatorList.push_back(m_att.endpoint.multicastLocatorList);

    logInfo(RTPS_WRITER,"Reader Proxy created");
}

ReaderProxy::~ReaderProxy()
{
    destroy_timers();
    delete(mp_mutex);
}

void ReaderProxy::destroy_timers()
{
    if(mp_nackSupression != nullptr)
    {
        delete(mp_nackSupression);
        mp_nackSupression = nullptr;
    }
}

void ReaderProxy::addChange(const ChangeForReader_t& change)
{
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);

    assert(change.getSequenceNumber() > changesFromRLowMark_);
    assert(m_changesForReader.rbegin() != m_changesForReader.rend() ?
            change.getSequenceNumber() > m_changesForReader.rbegin()->getSequenceNumber() :
            true);

    // For best effort readers, changes are acked when being sent
    if(m_changesForReader.size() == 0 && change.getStatus() == ACKNOWLEDGED)
    {
        changesFromRLowMark_ = change.getSequenceNumber();
        return;
    }

    m_changesForReader.insert(change);
    //TODO (Ricardo) Remove this functionality from here. It is not his place.
    if (change.getStatus() == UNSENT)
    {
        AsyncWriterThread::wakeUp(mp_SFW);
    }
}

size_t ReaderProxy::countChangesForReader() const
{
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);
    return m_changesForReader.size();
}

bool ReaderProxy::change_is_acked(const SequenceNumber_t& sequence_number)
{
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);

    if(sequence_number <= changesFromRLowMark_)
        return true;

    auto chit = m_changesForReader.find(ChangeForReader_t(sequence_number));
    assert(chit != m_changesForReader.end());

    return !chit->isRelevant() || chit->getStatus() == ACKNOWLEDGED;
}

void ReaderProxy::acked_changes_set(const SequenceNumber_t& seqNum)
{
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);
    SequenceNumber_t future_low_mark = seqNum;

    if(seqNum > changesFromRLowMark_)
    {
        auto chit = m_changesForReader.find(seqNum);
        m_changesForReader.erase(m_changesForReader.begin(), chit);
    }
    else
    {
        // Special case. Currently only used on Builtin StatefulWriters
        // after losing lease duration.

        SequenceNumber_t current_sequence = seqNum;
        if(seqNum < mp_SFW->get_seq_num_min())
        {
            current_sequence = mp_SFW->get_seq_num_min();
        }
        future_low_mark = current_sequence;

        for(; current_sequence <= changesFromRLowMark_; ++current_sequence)
        {
            CacheChange_t* change = nullptr;

            if(mp_SFW->mp_history->get_change(current_sequence, mp_SFW->getGuid(), &change))
            {
                ChangeForReader_t cr(change);
                cr.setStatus(UNACKNOWLEDGED);
                m_changesForReader.insert(cr);
            }
            else
            {
                ChangeForReader_t cr;
                cr.setStatus(UNACKNOWLEDGED);
                cr.notValid();
                m_changesForReader.insert(cr);
            }
        }
    }

    changesFromRLowMark_ = future_low_mark - 1;
}

bool ReaderProxy::requested_changes_set(std::vector<SequenceNumber_t>& seqNumSet)
{
    bool isSomeoneWasSetRequested = false;
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);

    for(std::vector<SequenceNumber_t>::iterator sit=seqNumSet.begin();sit!=seqNumSet.end();++sit)
    {
        auto chit = m_changesForReader.find(ChangeForReader_t(*sit));

        if(chit != m_changesForReader.end() && UNACKNOWLEDGED == chit->getStatus())
        {
            ChangeForReader_t newch(*chit);
            newch.setStatus(REQUESTED);
            newch.markAllFragmentsAsUnsent();

            auto hint = m_changesForReader.erase(chit);

            m_changesForReader.insert(hint, newch);

            isSomeoneWasSetRequested = true;
        }
    }

    if(isSomeoneWasSetRequested)
    {
        logInfo(RTPS_WRITER,"Requested Changes: " << seqNumSet);
    }

    return isSomeoneWasSetRequested;
}

//TODO(Ricardo) Temporal
//std::vector<const ChangeForReader_t*> ReaderProxy::get_unsent_changes() const
std::vector<ChangeForReader_t*> ReaderProxy::get_unsent_changes()
{
    std::vector<ChangeForReader_t*> unsent_changes;
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);

    for(auto &change_for_reader : m_changesForReader)
        if(change_for_reader.getStatus() == UNSENT)
            unsent_changes.push_back(const_cast<ChangeForReader_t*>(&change_for_reader));

    return unsent_changes;
}

std::vector<const ChangeForReader_t*> ReaderProxy::get_requested_changes() const
{
    std::vector<const ChangeForReader_t*> unsent_changes;
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);

    auto it = m_changesForReader.begin();
    for (; it!= m_changesForReader.end(); ++it)
        if(it->getStatus() == REQUESTED)
            unsent_changes.push_back(&(*it));

    return unsent_changes;
}

void ReaderProxy::set_change_to_status(const SequenceNumber_t& seq_num, ChangeForReaderStatus_t status)
{
    if(seq_num <= changesFromRLowMark_)
    {
        return;
    }

    auto it = m_changesForReader.find(ChangeForReader_t(seq_num));
    bool mustWakeUpAsyncThread = false;

    if(it != m_changesForReader.end())
    {
        if(status == ACKNOWLEDGED && it == m_changesForReader.begin())
        {
            m_changesForReader.erase(it);
            changesFromRLowMark_ = seq_num;
        }
        else
        {
            ChangeForReader_t newch(*it);
            newch.setStatus(status);
            if (status == UNSENT) mustWakeUpAsyncThread = true;
            auto hint = m_changesForReader.erase(it);
            m_changesForReader.insert(hint, newch);
        }
    }

    if (mustWakeUpAsyncThread)
        AsyncWriterThread::wakeUp(mp_SFW);
}

bool ReaderProxy::mark_fragment_as_sent_for_change(const CacheChange_t* change, FragmentNumber_t fragment)
{
    if(change->sequenceNumber <= changesFromRLowMark_)
        return false;

    bool allFragmentsSent = false;
    auto it = m_changesForReader.find(ChangeForReader_t(change->sequenceNumber));

    bool mustWakeUpAsyncThread = false;

    if(it != m_changesForReader.end())
    {
        ChangeForReader_t newch(*it);
        newch.markFragmentsAsSent(fragment);
        if (newch.getUnsentFragments().isSetEmpty())
        {
            allFragmentsSent = true;
        }
        else
            mustWakeUpAsyncThread = true;
        auto hint = m_changesForReader.erase(it);
        m_changesForReader.insert(hint, newch);
    }

    if (mustWakeUpAsyncThread)
    {
        AsyncWriterThread::wakeUp(mp_SFW);
    }

    return allFragmentsSent;
}

void ReaderProxy::convert_status_on_all_changes(ChangeForReaderStatus_t previous, ChangeForReaderStatus_t next)
{
    assert(previous > next);

    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);
    bool mustWakeUpAsyncThread = false;

    auto it = m_changesForReader.begin();
    while(it != m_changesForReader.end())
    {
        if(it->getStatus() == previous)
        {
            if(next == ACKNOWLEDGED && it == m_changesForReader.begin())
            {
                changesFromRLowMark_ = it->getSequenceNumber();
                it = m_changesForReader.erase(it);
                continue;
            }
            else
            {
                // Note: we can perform this cast as we are not touching the sorting field (seq_num)
                const_cast<ChangeForReader_t&>(*it).setStatus(next);
                if (next == UNSENT)
                {
                    mustWakeUpAsyncThread = true;
                }
            }
        }

        ++it;
    }

    if (mustWakeUpAsyncThread)
    {
        AsyncWriterThread::wakeUp(mp_SFW);
    }
}

//TODO(Ricardo)
//void ReaderProxy::setNotValid(const CacheChange_t* change)
void ReaderProxy::setNotValid(CacheChange_t* change)
{
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);

    // Check sequence number is in the container, because it was not clean up.
    if(m_changesForReader.size() == 0 || change->sequenceNumber < m_changesForReader.begin()->getSequenceNumber())
        return;

    auto chit = m_changesForReader.find(ChangeForReader_t(change));

    // Element must be in the container. In other case, bug.
    assert(chit != m_changesForReader.end());

    // Note: we can perform this cast as we are not touching the sorting field (seq_num)
    auto & newch = const_cast<ChangeForReader_t&>(*chit);

    if(chit == m_changesForReader.begin())
    {
        assert(chit->getStatus() != ACKNOWLEDGED);

        // if it is the first element, set state to unacknowledge because from now reader has to confirm
        // it will not be expecting it.
        // Note: we can perform this cast as we are not touching the sorting field (seq_num)
        newch.setStatus(UNACKNOWLEDGED);
    }
    else
    {
        // In case its state is not ACKNOWLEDGED, set it to UNACKNOWLEDGE because from now reader has to confirm
        // it will not be expecting it.
        if (newch.getStatus() != ACKNOWLEDGED)
            newch.setStatus(UNACKNOWLEDGED);
    }
    newch.notValid();
}

bool ReaderProxy::thereIsUnacknowledged() const
{
    bool returnedValue = false;
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);

    for(auto it = m_changesForReader.begin(); it!=m_changesForReader.end(); ++it)
    {
        if(it->getStatus() == UNACKNOWLEDGED)
        {
            returnedValue = true;
            break;
        }
    }

    return returnedValue;
}

bool change_min(const ChangeForReader_t* ch1, const ChangeForReader_t* ch2)
{
    return ch1->getSequenceNumber() < ch2->getSequenceNumber();
}

bool ReaderProxy::minChange(std::vector<ChangeForReader_t*>* Changes,
        ChangeForReader_t* changeForReader)
{
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);
    *changeForReader = **std::min_element(Changes->begin(),Changes->end(),change_min);
    return true;
}

bool ReaderProxy::requested_fragment_set(SequenceNumber_t sequence_number, const FragmentNumberSet_t& frag_set)
{
    std::lock_guard<std::recursive_mutex> guard(*mp_mutex);

    // Locate the outbound change referenced by the NACK_FRAG
    auto changeIter = std::find_if(m_changesForReader.begin(), m_changesForReader.end(),
            [sequence_number](const ChangeForReader_t& change)
            {return change.getSequenceNumber() == sequence_number;});
    if (changeIter == m_changesForReader.end())
        return false;

    ChangeForReader_t newch(*changeIter);
    auto hint = m_changesForReader.erase(changeIter);
    newch.markFragmentsAsUnsent(frag_set);

    // If it was UNSENT, we shouldn't switch back to REQUESTED to prevent stalling.
    if (newch.getStatus() != UNSENT)
        newch.setStatus(REQUESTED);
    m_changesForReader.insert(hint, newch);

    return true;
}
<|endoftext|>


<commit_before>#include "KakuroField.h"
#include "KakuroProblem.h"
#include <cstdio>

namespace Penciloid
{
KakuroField::KakuroField() : cells(nullptr), height(0), width(0), field_status(SolverStatus::NORMAL), num_undecided_cells(0)
{
}

KakuroField::~KakuroField()
{
	if (cells) delete[] cells;
}

void KakuroField::Init(int height_t, int width_t)
{
	height = height_t;
	width = width_t;

	if (cells) delete[] cells;

	cells = new KakuroCell[height * width * 2];
	field_status = SolverStatus::NORMAL;
	num_undecided_cells = 0;
}

void KakuroField::Init(KakuroProblem &prob)
{
	Init(prob.GetHeight(), prob.GetWidth());

	for (int y = 0; y < height; ++y) {
		for (int x = 0; x < width; ++x) {
			if (prob.IsNumberCell(y, x)) SetNumberCell(y, x);

			if (prob.IsHintAvailableVertical(y, x)) {
				int y_end = y + 1;
				for (; y_end < height && prob.IsNumberCell(y_end, x); ++y_end);

				for (int y2 = y + 1; y2 < y_end; ++y2) {
					cells[CellId(y2, x)].group_num_cells = y_end - (y + 1);
					cells[CellId(y2, x)].group_sum = prob.GetHintVertical(y, x);
					if (y2 == y_end - 1) cells[CellId(y2, x)].group_next_cell = CellId(y + 1, x);
					else cells[CellId(y2, x)].group_next_cell = CellId(y2 + 1, x);
				}
			}

			if (prob.IsHintAvailableHorizontal(y, x)) {
				int x_end = x + 1;
				for (; x_end < width && prob.IsNumberCell(y, x_end); ++x_end);

				for (int x2 = x + 1; x2 < x_end; ++x2) {
					cells[CellId(y, x2) | 1].group_num_cells = x_end - (x + 1);
					cells[CellId(y, x2) | 1].group_sum = prob.GetHintHorizontal(y, x);
					if (x2 == x_end - 1) cells[CellId(y, x2) | 1].group_next_cell = CellId(y, x + 1) | 1;
					else cells[CellId(y, x2) | 1].group_next_cell = CellId(y, x2 + 1) | 1;
				}
			}
		}
	}
}

int KakuroField::CheckAll()
{
	for (int i = 0; i < height * width * 2; ++i) {
		if (cells[i].group_next_cell != -1) CheckGroup(i);
	}

	return GetStatus();
}

void KakuroField::SetNumberCell(int y, int x)
{
	int id = CellId(y, x);

	if (cells[id].cell_value == CELL_HINT) {
		++num_undecided_cells;
		cells[id].cell_value = cells[id | 1].cell_value = CELL_UNDECIDED;
		cells[id].cell_candidate = cells[id | 1].cell_candidate = (2 << CELL_MAX_VALUE) - 2;
	}
}

int KakuroField::CheckGroup(int id)
{
	if (Next(id) == -1) return GetStatus();

	// TODO: desperately naive algorithm
	int already_used_values = 0;

	for (int current = id;;) {
		if (cells[current].cell_value >= 1) already_used_values |= 1 << cells[current].cell_value;
		if ((current = Next(current)) == id) break;
	}

	int possible_candidate = 0, imperative_candidate = (2 << CELL_MAX_VALUE) - 2;
	for (int bits = 2; bits < (2 << CELL_MAX_VALUE); bits += 2) {
		int sum = 0, num = 0;

		for (int i = 1; i <= CELL_MAX_VALUE; ++i) {
			if (bits & (1 << i)) {
				sum += i;
				++num;
			}
		}

		if ((bits & already_used_values) != already_used_values || sum != cells[id].group_sum || num != cells[id].group_num_cells) continue;

		possible_candidate |= bits;
		imperative_candidate &= bits;
	}

	for (int current = id;;) {
		UpdateCandidate(current, possible_candidate);
		if ((current = Next(current)) == id) break;
	}

	for (int i = 1; i <= CELL_MAX_VALUE; ++i) if (imperative_candidate & (1 << i)) {
		int candidate_loc = -1;

		for (int current = id;;) {
			if (cells[current].cell_candidate & (1 << i)) {
				if (candidate_loc == -1) candidate_loc = current;
				else {
					candidate_loc = -2;
					break;
				}
			}
			if ((current = Next(current)) == id) break;
		}

		if (candidate_loc == -1) return UpdateStatus(SolverStatus::INCONSISTENT);
		else if (candidate_loc != -2) UpdateCandidate(candidate_loc, 1 << i);
	}

	return GetStatus();
}

int KakuroField::UpdateCandidate(int id, int new_candidate)
{
	id &= ~1;
	new_candidate &= cells[id].cell_candidate;

	if (new_candidate == 0) return UpdateStatus(SolverStatus::INCONSISTENT);
	if (new_candidate == cells[id].cell_candidate) return UpdateStatus(0);

	cells[id].cell_candidate = cells[id | 1].cell_candidate = new_candidate;

	if (IsUniqueCandidate(new_candidate)) {
		int cand_value = 0;
		for (int i = 1; i <= CELL_MAX_VALUE; ++i) {
			if (new_candidate == 1 << i) {
				cand_value = i;
				break;
			}
		}

		cells[id].cell_value = cells[id | 1].cell_value = cand_value;
		if (--num_undecided_cells) UpdateStatus(SolverStatus::SUCCESS);

		// TODO: purge determined cells?
	}

	CheckGroup(id);
	CheckGroup(id | 1);

	return GetStatus();
}

void KakuroField::Debug()
{
	for (int i_ = 0; i_ <= 2 * height; ++i_) {
		if (i_ % 2 == 0) {
			fprintf(stderr, "+");
			for (int j = 0; j < width; ++j) fprintf(stderr, "---+");
			fprintf(stderr, "\n");
			continue;
		}

		int i = i_ / 2;
		fprintf(stderr, "|");
		for (int j = 0; j < width; ++j) {
			if (IsNumberCell(i, j)) fprintf(stderr, "   |");
			else {
				if (j == width - 1 || !IsNumberCell(i, j + 1)) fprintf(stderr, "\\  |");
				else fprintf(stderr, "\\%2d|", cells[CellId(i, j + 1) | 1].group_sum);
			}
		}
		fprintf(stderr, "\n");

		fprintf(stderr, "|");
		for (int j = 0; j < width; ++j) {
			if (IsNumberCell(i, j)) {
				if (GetCellValue(i, j) == CELL_UNDECIDED) fprintf(stderr, "   |");
				else fprintf(stderr, " %d |", GetCellValue(i, j));
			} else {
				fprintf(stderr, " \\ |");
			}
		}
		fprintf(stderr, "\n");

		fprintf(stderr, "|");
		for (int j = 0; j < width; ++j) {
			if (IsNumberCell(i, j)) fprintf(stderr, "   |");
			else {
				if (i == height - 1 || !IsNumberCell(i + 1, j)) fprintf(stderr, "  \\|");
				else fprintf(stderr, "%2d\\|", cells[CellId(i + 1, j)].group_sum);
			}
		}
		fprintf(stderr, "\n");
	}
	fflush(stderr);
}

}

<commit_msg>Bug fix in KakuroField<commit_after>#include "KakuroField.h"
#include "KakuroProblem.h"
#include <cstdio>

namespace Penciloid
{
KakuroField::KakuroField() : cells(nullptr), height(0), width(0), field_status(SolverStatus::NORMAL), num_undecided_cells(0)
{
}

KakuroField::~KakuroField()
{
	if (cells) delete[] cells;
}

void KakuroField::Init(int height_t, int width_t)
{
	height = height_t;
	width = width_t;

	if (cells) delete[] cells;

	cells = new KakuroCell[height * width * 2];
	field_status = SolverStatus::NORMAL;
	num_undecided_cells = 0;
}

void KakuroField::Init(KakuroProblem &prob)
{
	Init(prob.GetHeight(), prob.GetWidth());

	for (int y = 0; y < height; ++y) {
		for (int x = 0; x < width; ++x) {
			if (prob.IsNumberCell(y, x)) SetNumberCell(y, x);

			if (prob.IsHintAvailableVertical(y, x)) {
				int y_end = y + 1;
				for (; y_end < height && prob.IsNumberCell(y_end, x); ++y_end);

				for (int y2 = y + 1; y2 < y_end; ++y2) {
					cells[CellId(y2, x)].group_num_cells = y_end - (y + 1);
					cells[CellId(y2, x)].group_sum = prob.GetHintVertical(y, x);
					if (y2 == y_end - 1) cells[CellId(y2, x)].group_next_cell = CellId(y + 1, x);
					else cells[CellId(y2, x)].group_next_cell = CellId(y2 + 1, x);
				}
			}

			if (prob.IsHintAvailableHorizontal(y, x)) {
				int x_end = x + 1;
				for (; x_end < width && prob.IsNumberCell(y, x_end); ++x_end);

				for (int x2 = x + 1; x2 < x_end; ++x2) {
					cells[CellId(y, x2) | 1].group_num_cells = x_end - (x + 1);
					cells[CellId(y, x2) | 1].group_sum = prob.GetHintHorizontal(y, x);
					if (x2 == x_end - 1) cells[CellId(y, x2) | 1].group_next_cell = CellId(y, x + 1) | 1;
					else cells[CellId(y, x2) | 1].group_next_cell = CellId(y, x2 + 1) | 1;
				}
			}
		}
	}
}

int KakuroField::CheckAll()
{
	for (int i = 0; i < height * width * 2; ++i) {
		if (cells[i].group_next_cell != -1) CheckGroup(i);
	}

	return GetStatus();
}

void KakuroField::SetNumberCell(int y, int x)
{
	int id = CellId(y, x);

	if (cells[id].cell_value == CELL_HINT) {
		++num_undecided_cells;
		cells[id].cell_value = cells[id | 1].cell_value = CELL_UNDECIDED;
		cells[id].cell_candidate = cells[id | 1].cell_candidate = (2 << CELL_MAX_VALUE) - 2;
	}
}

int KakuroField::CheckGroup(int id)
{
	if (Next(id) == -1) return GetStatus();

	// TODO: desperately naive algorithm
	int already_used_values = 0;

	for (int current = id;;) {
		if (cells[current].cell_value >= 1) already_used_values |= 1 << cells[current].cell_value;
		if ((current = Next(current)) == id) break;
	}

	int possible_candidate = 0, imperative_candidate = (2 << CELL_MAX_VALUE) - 2;
	for (int bits = 2; bits < (2 << CELL_MAX_VALUE); bits += 2) {
		int sum = 0, num = 0;

		for (int i = 1; i <= CELL_MAX_VALUE; ++i) {
			if (bits & (1 << i)) {
				sum += i;
				++num;
			}
		}

		if ((bits & already_used_values) != already_used_values || sum != cells[id].group_sum || num != cells[id].group_num_cells) continue;

		possible_candidate |= bits ^ already_used_values;
		imperative_candidate &= bits ^ already_used_values;
	}

	for (int current = id;;) {
		if (cells[current].cell_value == CELL_UNDECIDED) UpdateCandidate(current, possible_candidate);
		if ((current = Next(current)) == id) break;
	}

	for (int i = 1; i <= CELL_MAX_VALUE; ++i) if (imperative_candidate & (1 << i)) {
		int candidate_loc = -1;

		for (int current = id;;) {
			if (cells[current].cell_candidate & (1 << i)) {
				if (candidate_loc == -1) candidate_loc = current;
				else {
					candidate_loc = -2;
					break;
				}
			}
			if ((current = Next(current)) == id) break;
		}

		if (candidate_loc == -1) return UpdateStatus(SolverStatus::INCONSISTENT);
		else if (candidate_loc != -2) UpdateCandidate(candidate_loc, 1 << i);
	}

	return GetStatus();
}

int KakuroField::UpdateCandidate(int id, int new_candidate)
{
	id &= ~1;
	new_candidate &= cells[id].cell_candidate;

	if (new_candidate == 0) {
		return UpdateStatus(SolverStatus::INCONSISTENT);
	} if (new_candidate == cells[id].cell_candidate) return UpdateStatus(0);

	cells[id].cell_candidate = cells[id | 1].cell_candidate = new_candidate;

	if (IsUniqueCandidate(new_candidate)) {
		int cand_value = 0;
		for (int i = 1; i <= CELL_MAX_VALUE; ++i) {
			if (new_candidate == 1 << i) {
				cand_value = i;
				break;
			}
		}

		cells[id].cell_value = cells[id | 1].cell_value = cand_value;
		if (--num_undecided_cells) UpdateStatus(SolverStatus::SUCCESS);

		// TODO: purge determined cells?
	}

	CheckGroup(id);
	CheckGroup(id | 1);

	return GetStatus();
}

void KakuroField::Debug()
{
	for (int i_ = 0; i_ <= 2 * height; ++i_) {
		if (i_ % 2 == 0) {
			fprintf(stderr, "+");
			for (int j = 0; j < width; ++j) fprintf(stderr, "---+");
			fprintf(stderr, "\n");
			continue;
		}

		int i = i_ / 2;
		fprintf(stderr, "|");
		for (int j = 0; j < width; ++j) {
			if (IsNumberCell(i, j)) fprintf(stderr, "   |");
			else {
				if (j == width - 1 || !IsNumberCell(i, j + 1)) fprintf(stderr, "\\  |");
				else fprintf(stderr, "\\%2d|", cells[CellId(i, j + 1) | 1].group_sum);
			}
		}
		fprintf(stderr, "\n");

		fprintf(stderr, "|");
		for (int j = 0; j < width; ++j) {
			if (IsNumberCell(i, j)) {
				if (GetCellValue(i, j) == CELL_UNDECIDED) fprintf(stderr, "   |");
				else fprintf(stderr, " %d |", GetCellValue(i, j));
			} else {
				fprintf(stderr, " \\ |");
			}
		}
		fprintf(stderr, "\n");

		fprintf(stderr, "|");
		for (int j = 0; j < width; ++j) {
			if (IsNumberCell(i, j)) fprintf(stderr, "   |");
			else {
				if (i == height - 1 || !IsNumberCell(i + 1, j)) fprintf(stderr, "  \\|");
				else fprintf(stderr, "%2d\\|", cells[CellId(i + 1, j)].group_sum);
			}
		}
		fprintf(stderr, "\n");
	}
	fflush(stderr);
}

}

<|endoftext|>


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

#include "chrome/browser/extensions/extension_input_ui_api.h"

#include <string>

#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "base/values.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/extensions/extension_event_router.h"
#include "chrome/browser/profiles/profile.h"
#include "third_party/cros/chromeos_cros_api.h"
#include "third_party/cros/chromeos_input_method_ui.h"

namespace events {

const char kOnUpdateAuxiliaryText[] =
    "experimental.inputUI.onUpdateAuxiliaryText";
const char kOnUpdateLookupTable[] = "experimental.inputUI.onUpdateLookupTable";
const char kOnSetCursorLocation[] = "experimental.inputUI.onSetCursorLocation";

}  // namespace events

class InputUiController {
 public:
  explicit InputUiController(ExtensionInputUiEventRouter* router);
  ~InputUiController();

  void CandidateClicked(int index, int button, int flags);
  void CursorUp();
  void CursorDown();
  void PageUp();
  void PageDown();

 private:
  // The function is called when |HideAuxiliaryText| signal is received in
  // libcros. |ui_controller| is a void pointer to this object.
  static void OnHideAuxiliaryText(void* ui_controller);

  // The function is called when |HideLookupTable| signal is received in
  // libcros. |ui_controller| is a void pointer to this object.
  static void OnHideLookupTable(void* ui_controller);

  // The function is called when |SetCursorLocation| signal is received
  // in libcros. |ui_controller| is a void pointer to this object.
  static void OnSetCursorLocation(void* ui_controller,
                                  int x,
                                  int y,
                                  int width,
                                  int height);

  // The function is called when |UpdateAuxiliaryText| signal is received
  // in libcros. |ui_controller| is a void pointer to this object.
  static void OnUpdateAuxiliaryText(void* ui_controller,
                                    const std::string& utf8_text,
                                    bool visible);

  // The function is called when |UpdateLookupTable| signal is received
  // in libcros. |ui_controller| is a void pointer to this object.
  static void OnUpdateLookupTable(void* ui_controller,
      const chromeos::InputMethodLookupTable& lookup_table);

  // This function is called by libcros when ibus connects or disconnects.
  // |ui_controller| is a void pointer to this object.
  static void OnConnectionChange(void* ui_controller, bool connected);

  ExtensionInputUiEventRouter* router_;
  chromeos::InputMethodUiStatusConnection* ui_status_connection_;

  DISALLOW_COPY_AND_ASSIGN(InputUiController);
};

InputUiController::InputUiController(
    ExtensionInputUiEventRouter* router) :
  router_(router),
  ui_status_connection_(NULL) {
  if (!chromeos::CrosLibrary::Get()->EnsureLoaded())
    return;

  chromeos::InputMethodUiStatusMonitorFunctions functions;
  functions.hide_auxiliary_text =
      &InputUiController::OnHideAuxiliaryText;
  functions.hide_lookup_table =
      &InputUiController::OnHideLookupTable;
  functions.set_cursor_location =
      &InputUiController::OnSetCursorLocation;
  functions.update_auxiliary_text =
      &InputUiController::OnUpdateAuxiliaryText;
  functions.update_lookup_table =
      &InputUiController::OnUpdateLookupTable;
  ui_status_connection_ = chromeos::MonitorInputMethodUiStatus(functions, this);

  if (!ui_status_connection_)
    LOG(ERROR) << "chromeos::MonitorInputMethodUiStatus() failed!";
}

InputUiController::~InputUiController() {
  if (ui_status_connection_)
    chromeos::DisconnectInputMethodUiStatus(ui_status_connection_);
}

void InputUiController::CandidateClicked(
    int index, int button, int flags) {
  chromeos::NotifyCandidateClicked(ui_status_connection_, index, button, flags);
}

void InputUiController::CursorUp() {
  chromeos::NotifyCursorUp(ui_status_connection_);
}

void InputUiController::CursorDown() {
  chromeos::NotifyCursorDown(ui_status_connection_);
}

void InputUiController::PageUp() {
  chromeos::NotifyPageUp(ui_status_connection_);
}

void InputUiController::PageDown() {
  chromeos::NotifyPageDown(ui_status_connection_);
}

void InputUiController::OnHideAuxiliaryText(
    void* ui_controller) {
  InputUiController *self = static_cast<InputUiController*>(ui_controller);
  self->router_->OnHideAuxiliaryText();
}

void InputUiController::OnHideLookupTable(
    void* ui_controller) {
  InputUiController *self = static_cast<InputUiController*>(ui_controller);
  self->router_->OnHideLookupTable();
}

void InputUiController::OnSetCursorLocation(
    void* ui_controller,
    int x, int y, int width, int height) {
  InputUiController *self = static_cast<InputUiController*>(ui_controller);
  self->router_->OnSetCursorLocation(x, y, width, height);
}

void InputUiController::OnUpdateAuxiliaryText(
    void* ui_controller,
    const std::string& utf8_text,
    bool visible) {
  InputUiController *self = static_cast<InputUiController*>(ui_controller);
  if (!visible) {
    self->router_->OnHideAuxiliaryText();
  } else {
    self->router_->OnUpdateAuxiliaryText(utf8_text);
  }
}

void InputUiController::OnUpdateLookupTable(
    void* ui_controller,
    const chromeos::InputMethodLookupTable& lookup_table) {
  InputUiController *self = static_cast<InputUiController*>(ui_controller);
    self->router_->OnUpdateLookupTable(lookup_table);
}

ExtensionInputUiEventRouter*
ExtensionInputUiEventRouter::GetInstance() {
  return Singleton<ExtensionInputUiEventRouter>::get();
}

ExtensionInputUiEventRouter::ExtensionInputUiEventRouter()
  : profile_(NULL),
    ui_controller_(NULL) {
}

ExtensionInputUiEventRouter::~ExtensionInputUiEventRouter() {
}

void ExtensionInputUiEventRouter::Init() {
  if (ui_controller_.get() == NULL) {
    ui_controller_.reset(new InputUiController(this));
  }
}

void ExtensionInputUiEventRouter::Register(
    Profile* profile, const std::string& extension_id) {
  profile_ = profile;
  extension_id_ = extension_id;
}

void ExtensionInputUiEventRouter::CandidateClicked(Profile* profile,
    const std::string& extension_id, int index, int button) {
    if (profile_ != profile || extension_id_ != extension_id) {
      DLOG(WARNING) << "called from unregistered extension";
    }
    ui_controller_->CandidateClicked(index, button, 0);
}

void ExtensionInputUiEventRouter::CursorUp(Profile* profile,
    const std::string& extension_id) {
    if (profile_ != profile || extension_id_ != extension_id) {
      DLOG(WARNING) << "called from unregistered extension";
    }
    ui_controller_->CursorUp();
}

void ExtensionInputUiEventRouter::CursorDown(Profile* profile,
    const std::string& extension_id) {
    if (profile_ != profile || extension_id_ != extension_id) {
      DLOG(WARNING) << "called from unregistered extension";
    }
    ui_controller_->CursorDown();
}

void ExtensionInputUiEventRouter::PageUp(Profile* profile,
    const std::string& extension_id) {
    if (profile_ != profile || extension_id_ != extension_id) {
      DLOG(WARNING) << "called from unregistered extension";
    }
    ui_controller_->PageUp();
}

void ExtensionInputUiEventRouter::PageDown(Profile* profile,
    const std::string& extension_id) {
    if (profile_ != profile || extension_id_ != extension_id) {
      DLOG(WARNING) << "called from unregistered extension";
    }
    ui_controller_->PageDown();
}

void ExtensionInputUiEventRouter::OnHideAuxiliaryText() {
  OnUpdateAuxiliaryText("");
}

void ExtensionInputUiEventRouter::OnHideLookupTable() {
  if (profile_ == NULL || extension_id_.empty())
    return;

  DictionaryValue* dict = new DictionaryValue();
  dict->SetBoolean("visible", false);

  ListValue *candidates = new ListValue();
  dict->Set("candidates", candidates);

  ListValue args;
  args.Append(dict);

  std::string json_args;
  base::JSONWriter::Write(&args, false, &json_args);
  profile_->GetExtensionEventRouter()->DispatchEventToExtension(
    extension_id_, events::kOnUpdateLookupTable, json_args, profile_, GURL());
}

void ExtensionInputUiEventRouter::OnSetCursorLocation(
    int x, int y, int width, int height) {

  if (profile_ == NULL || extension_id_.empty())
    return;

  ListValue args;
  args.Append(Value::CreateIntegerValue(x));
  args.Append(Value::CreateIntegerValue(y));
  args.Append(Value::CreateIntegerValue(width));
  args.Append(Value::CreateIntegerValue(height));

  std::string json_args;
  base::JSONWriter::Write(&args, false, &json_args);
  profile_->GetExtensionEventRouter()->DispatchEventToExtension(
    extension_id_, events::kOnSetCursorLocation, json_args, profile_, GURL());
}

void ExtensionInputUiEventRouter::OnUpdateAuxiliaryText(
    const std::string& utf8_text) {
  if (profile_ == NULL || extension_id_.empty())
    return;

  ListValue args;
  args.Append(Value::CreateStringValue(utf8_text));

  std::string json_args;
  base::JSONWriter::Write(&args, false, &json_args);
  profile_->GetExtensionEventRouter()->DispatchEventToExtension(
    extension_id_, events::kOnUpdateAuxiliaryText, json_args, profile_, GURL());
}

void ExtensionInputUiEventRouter::OnUpdateLookupTable(
    const chromeos::InputMethodLookupTable& lookup_table) {
  if (profile_ == NULL || extension_id_.empty())
    return;

  DictionaryValue* dict = new DictionaryValue();
  dict->SetBoolean("visible", lookup_table.visible);

  if (lookup_table.visible) {
  }

  ListValue *candidates = new ListValue();
  for (size_t i = 0; i < lookup_table.candidates.size(); i++) {
    candidates->Append(Value::CreateStringValue(lookup_table.candidates[i]));
  }

  dict->Set("candidates", candidates);

  ListValue args;
  args.Append(dict);

  std::string json_args;
  base::JSONWriter::Write(&args, false, &json_args);
  profile_->GetExtensionEventRouter()->DispatchEventToExtension(
    extension_id_, events::kOnUpdateLookupTable, json_args, profile_, GURL());
}

bool RegisterInputUiFunction::RunImpl() {
  ExtensionInputUiEventRouter::GetInstance()->Register(
      profile(), extension_id());
  return true;
}

bool CandidateClickedInputUiFunction::RunImpl() {
  int index = 0;
  int button = 0;
  EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &index));
  EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(1, &button));

  ExtensionInputUiEventRouter::GetInstance()->CandidateClicked(
      profile(), extension_id(), index, button);

  return true;
}

bool CursorUpInputUiFunction::RunImpl() {
  ExtensionInputUiEventRouter::GetInstance()->CursorUp(
      profile(), extension_id());
  return true;
}

bool CursorDownInputUiFunction::RunImpl() {
  ExtensionInputUiEventRouter::GetInstance()->CursorDown(
      profile(), extension_id());
  return true;
}

bool PageUpInputUiFunction::RunImpl() {
  ExtensionInputUiEventRouter::GetInstance()->PageUp(
      profile(), extension_id());
  return true;
}

bool PageDownInputUiFunction::RunImpl() {
  ExtensionInputUiEventRouter::GetInstance()->PageDown(
      profile(), extension_id());
  return true;
}

<commit_msg>Only pass candidates in current page to extension.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/extension_input_ui_api.h"

#include <algorithm>
#include <string>

#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "base/values.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/extensions/extension_event_router.h"
#include "chrome/browser/profiles/profile.h"
#include "third_party/cros/chromeos_cros_api.h"
#include "third_party/cros/chromeos_input_method_ui.h"

namespace events {

const char kOnUpdateAuxiliaryText[] =
    "experimental.inputUI.onUpdateAuxiliaryText";
const char kOnUpdateLookupTable[] = "experimental.inputUI.onUpdateLookupTable";
const char kOnSetCursorLocation[] = "experimental.inputUI.onSetCursorLocation";

}  // namespace events

class InputUiController {
 public:
  explicit InputUiController(ExtensionInputUiEventRouter* router);
  ~InputUiController();

  void CandidateClicked(int index, int button, int flags);
  void CursorUp();
  void CursorDown();
  void PageUp();
  void PageDown();

 private:
  // The function is called when |HideAuxiliaryText| signal is received in
  // libcros. |ui_controller| is a void pointer to this object.
  static void OnHideAuxiliaryText(void* ui_controller);

  // The function is called when |HideLookupTable| signal is received in
  // libcros. |ui_controller| is a void pointer to this object.
  static void OnHideLookupTable(void* ui_controller);

  // The function is called when |SetCursorLocation| signal is received
  // in libcros. |ui_controller| is a void pointer to this object.
  static void OnSetCursorLocation(void* ui_controller,
                                  int x,
                                  int y,
                                  int width,
                                  int height);

  // The function is called when |UpdateAuxiliaryText| signal is received
  // in libcros. |ui_controller| is a void pointer to this object.
  static void OnUpdateAuxiliaryText(void* ui_controller,
                                    const std::string& utf8_text,
                                    bool visible);

  // The function is called when |UpdateLookupTable| signal is received
  // in libcros. |ui_controller| is a void pointer to this object.
  static void OnUpdateLookupTable(void* ui_controller,
      const chromeos::InputMethodLookupTable& lookup_table);

  // This function is called by libcros when ibus connects or disconnects.
  // |ui_controller| is a void pointer to this object.
  static void OnConnectionChange(void* ui_controller, bool connected);

  ExtensionInputUiEventRouter* router_;
  chromeos::InputMethodUiStatusConnection* ui_status_connection_;

  DISALLOW_COPY_AND_ASSIGN(InputUiController);
};

InputUiController::InputUiController(
    ExtensionInputUiEventRouter* router) :
  router_(router),
  ui_status_connection_(NULL) {
  if (!chromeos::CrosLibrary::Get()->EnsureLoaded())
    return;

  chromeos::InputMethodUiStatusMonitorFunctions functions;
  functions.hide_auxiliary_text =
      &InputUiController::OnHideAuxiliaryText;
  functions.hide_lookup_table =
      &InputUiController::OnHideLookupTable;
  functions.set_cursor_location =
      &InputUiController::OnSetCursorLocation;
  functions.update_auxiliary_text =
      &InputUiController::OnUpdateAuxiliaryText;
  functions.update_lookup_table =
      &InputUiController::OnUpdateLookupTable;
  ui_status_connection_ = chromeos::MonitorInputMethodUiStatus(functions, this);

  if (!ui_status_connection_)
    LOG(ERROR) << "chromeos::MonitorInputMethodUiStatus() failed!";
}

InputUiController::~InputUiController() {
  if (ui_status_connection_)
    chromeos::DisconnectInputMethodUiStatus(ui_status_connection_);
}

void InputUiController::CandidateClicked(
    int index, int button, int flags) {
  chromeos::NotifyCandidateClicked(ui_status_connection_, index, button, flags);
}

void InputUiController::CursorUp() {
  chromeos::NotifyCursorUp(ui_status_connection_);
}

void InputUiController::CursorDown() {
  chromeos::NotifyCursorDown(ui_status_connection_);
}

void InputUiController::PageUp() {
  chromeos::NotifyPageUp(ui_status_connection_);
}

void InputUiController::PageDown() {
  chromeos::NotifyPageDown(ui_status_connection_);
}

void InputUiController::OnHideAuxiliaryText(
    void* ui_controller) {
  InputUiController *self = static_cast<InputUiController*>(ui_controller);
  self->router_->OnHideAuxiliaryText();
}

void InputUiController::OnHideLookupTable(
    void* ui_controller) {
  InputUiController *self = static_cast<InputUiController*>(ui_controller);
  self->router_->OnHideLookupTable();
}

void InputUiController::OnSetCursorLocation(
    void* ui_controller,
    int x, int y, int width, int height) {
  InputUiController *self = static_cast<InputUiController*>(ui_controller);
  self->router_->OnSetCursorLocation(x, y, width, height);
}

void InputUiController::OnUpdateAuxiliaryText(
    void* ui_controller,
    const std::string& utf8_text,
    bool visible) {
  InputUiController *self = static_cast<InputUiController*>(ui_controller);
  if (!visible) {
    self->router_->OnHideAuxiliaryText();
  } else {
    self->router_->OnUpdateAuxiliaryText(utf8_text);
  }
}

void InputUiController::OnUpdateLookupTable(
    void* ui_controller,
    const chromeos::InputMethodLookupTable& lookup_table) {
  InputUiController *self = static_cast<InputUiController*>(ui_controller);
    self->router_->OnUpdateLookupTable(lookup_table);
}

ExtensionInputUiEventRouter*
ExtensionInputUiEventRouter::GetInstance() {
  return Singleton<ExtensionInputUiEventRouter>::get();
}

ExtensionInputUiEventRouter::ExtensionInputUiEventRouter()
  : profile_(NULL),
    ui_controller_(NULL) {
}

ExtensionInputUiEventRouter::~ExtensionInputUiEventRouter() {
}

void ExtensionInputUiEventRouter::Init() {
  if (ui_controller_.get() == NULL) {
    ui_controller_.reset(new InputUiController(this));
  }
}

void ExtensionInputUiEventRouter::Register(
    Profile* profile, const std::string& extension_id) {
  profile_ = profile;
  extension_id_ = extension_id;
}

void ExtensionInputUiEventRouter::CandidateClicked(Profile* profile,
    const std::string& extension_id, int index, int button) {
    if (profile_ != profile || extension_id_ != extension_id) {
      DLOG(WARNING) << "called from unregistered extension";
    }
    ui_controller_->CandidateClicked(index, button, 0);
}

void ExtensionInputUiEventRouter::CursorUp(Profile* profile,
    const std::string& extension_id) {
    if (profile_ != profile || extension_id_ != extension_id) {
      DLOG(WARNING) << "called from unregistered extension";
    }
    ui_controller_->CursorUp();
}

void ExtensionInputUiEventRouter::CursorDown(Profile* profile,
    const std::string& extension_id) {
    if (profile_ != profile || extension_id_ != extension_id) {
      DLOG(WARNING) << "called from unregistered extension";
    }
    ui_controller_->CursorDown();
}

void ExtensionInputUiEventRouter::PageUp(Profile* profile,
    const std::string& extension_id) {
    if (profile_ != profile || extension_id_ != extension_id) {
      DLOG(WARNING) << "called from unregistered extension";
    }
    ui_controller_->PageUp();
}

void ExtensionInputUiEventRouter::PageDown(Profile* profile,
    const std::string& extension_id) {
    if (profile_ != profile || extension_id_ != extension_id) {
      DLOG(WARNING) << "called from unregistered extension";
    }
    ui_controller_->PageDown();
}

void ExtensionInputUiEventRouter::OnHideAuxiliaryText() {
  OnUpdateAuxiliaryText("");
}

void ExtensionInputUiEventRouter::OnHideLookupTable() {
  if (profile_ == NULL || extension_id_.empty())
    return;

  DictionaryValue* dict = new DictionaryValue();
  dict->SetBoolean("visible", false);

  ListValue *candidates = new ListValue();
  dict->Set("candidates", candidates);

  ListValue args;
  args.Append(dict);

  std::string json_args;
  base::JSONWriter::Write(&args, false, &json_args);
  profile_->GetExtensionEventRouter()->DispatchEventToExtension(
    extension_id_, events::kOnUpdateLookupTable, json_args, profile_, GURL());
}

void ExtensionInputUiEventRouter::OnSetCursorLocation(
    int x, int y, int width, int height) {

  if (profile_ == NULL || extension_id_.empty())
    return;

  ListValue args;
  args.Append(Value::CreateIntegerValue(x));
  args.Append(Value::CreateIntegerValue(y));
  args.Append(Value::CreateIntegerValue(width));
  args.Append(Value::CreateIntegerValue(height));

  std::string json_args;
  base::JSONWriter::Write(&args, false, &json_args);
  profile_->GetExtensionEventRouter()->DispatchEventToExtension(
    extension_id_, events::kOnSetCursorLocation, json_args, profile_, GURL());
}

void ExtensionInputUiEventRouter::OnUpdateAuxiliaryText(
    const std::string& utf8_text) {
  if (profile_ == NULL || extension_id_.empty())
    return;

  ListValue args;
  args.Append(Value::CreateStringValue(utf8_text));

  std::string json_args;
  base::JSONWriter::Write(&args, false, &json_args);
  profile_->GetExtensionEventRouter()->DispatchEventToExtension(
    extension_id_, events::kOnUpdateAuxiliaryText, json_args, profile_, GURL());
}

void ExtensionInputUiEventRouter::OnUpdateLookupTable(
    const chromeos::InputMethodLookupTable& lookup_table) {
  if (profile_ == NULL || extension_id_.empty())
    return;

  DictionaryValue* dict = new DictionaryValue();
  dict->SetBoolean("visible", lookup_table.visible);

  if (lookup_table.visible) {
  }

  ListValue *candidates = new ListValue();

  size_t page = lookup_table.cursor_absolute_index / lookup_table.page_size;
  size_t begin = page * lookup_table.page_size;
  size_t end = std::min(begin + lookup_table.page_size,
      lookup_table.candidates.size());

  for (size_t i = begin; i < end; i++) {
    candidates->Append(Value::CreateStringValue(lookup_table.candidates[i]));
  }

  dict->Set("candidates", candidates);

  ListValue args;
  args.Append(dict);

  std::string json_args;
  base::JSONWriter::Write(&args, false, &json_args);
  profile_->GetExtensionEventRouter()->DispatchEventToExtension(
    extension_id_, events::kOnUpdateLookupTable, json_args, profile_, GURL());
}

bool RegisterInputUiFunction::RunImpl() {
  ExtensionInputUiEventRouter::GetInstance()->Register(
      profile(), extension_id());
  return true;
}

bool CandidateClickedInputUiFunction::RunImpl() {
  int index = 0;
  int button = 0;
  EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &index));
  EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(1, &button));

  ExtensionInputUiEventRouter::GetInstance()->CandidateClicked(
      profile(), extension_id(), index, button);

  return true;
}

bool CursorUpInputUiFunction::RunImpl() {
  ExtensionInputUiEventRouter::GetInstance()->CursorUp(
      profile(), extension_id());
  return true;
}

bool CursorDownInputUiFunction::RunImpl() {
  ExtensionInputUiEventRouter::GetInstance()->CursorDown(
      profile(), extension_id());
  return true;
}

bool PageUpInputUiFunction::RunImpl() {
  ExtensionInputUiEventRouter::GetInstance()->PageUp(
      profile(), extension_id());
  return true;
}

bool PageDownInputUiFunction::RunImpl() {
  ExtensionInputUiEventRouter::GetInstance()->PageDown(
      profile(), extension_id());
  return true;
}

<|endoftext|>


<commit_before>#include "mnist.h"

#include <algorithm>
#include <cstdint>
#include <fstream>
#include <memory>
#include <string>

#include "chainerx/array.h"
#include "chainerx/device.h"
#include "chainerx/dtype.h"
#include "chainerx/routines/creation.h"
#include "chainerx/shape.h"

namespace {

int32_t ReverseInt(int32_t i) {
    uint8_t c1 = i & 255;
    uint8_t c2 = (i >> 8) & 255;
    uint8_t c3 = (i >> 16) & 255;
    uint8_t c4 = (i >> 24) & 255;

    return (static_cast<int32_t>(c1) << 24) + (static_cast<int32_t>(c2) << 16) + (static_cast<int32_t>(c3) << 8) + c4;
}

std::unique_ptr<char[]> ReadFile(const std::string& filename) {
    std::ifstream ifs(filename.c_str(), std::ios::in | std::ios::binary | std::ios::ate);
    if (!ifs.is_open()) {
        throw std::runtime_error("Could not open " + filename);
    }

    size_t size = ifs.tellg();

    std::unique_ptr<char[]> buffer{new char[size]};

    ifs.seekg(0, std::ios::beg);
    ifs.read(buffer.get(), size);
    ifs.close();

    return buffer;
}

int32_t ReadHeader(const std::unique_ptr<char[]>& buffer, size_t offset) {
    return ReverseInt(*(reinterpret_cast<int32_t*>(buffer.get() + offset)));
}

chainerx::Array ReadArray(const std::unique_ptr<char[]>& buffer, const chainerx::Shape& shape, size_t offset) {
    int64_t n = shape.GetTotalSize();

    std::shared_ptr<uint8_t> data{new uint8_t[n], std::default_delete<uint8_t[]>{}};
    std::copy_n(reinterpret_cast<uint8_t*>(buffer.get() + offset), n, data.get());

    return chainerx::FromContiguousHostData(
            shape, chainerx::Dtype::kUInt8, static_cast<std::shared_ptr<void>>(data), chainerx::GetDefaultDevice());
}

}  // namespace

chainerx::Array ReadMnistImages(const std::string& filename) {
    std::unique_ptr<char[]> buffer = ReadFile(filename);

    int32_t n = ReadHeader(buffer, 4);
    int32_t height = ReadHeader(buffer, 8);
    int32_t width = ReadHeader(buffer, 12);

    return ReadArray(buffer, {n, height * width}, 16);
}

chainerx::Array ReadMnistLabels(const std::string& filename) {
    std::unique_ptr<char[]> buffer = ReadFile(filename);

    int32_t n = ReadHeader(buffer, 4);

    return ReadArray(buffer, {n}, 8);
}
<commit_msg>Simplify byte swapping<commit_after>#include "mnist.h"

#include <algorithm>
#include <cassert>
#include <cstdint>
#include <fstream>
#include <memory>
#include <string>

#include "chainerx/array.h"
#include "chainerx/device.h"
#include "chainerx/dtype.h"
#include "chainerx/routines/creation.h"
#include "chainerx/shape.h"

namespace {

std::unique_ptr<char[]> ReadFile(const std::string& filename) {
    std::ifstream ifs(filename.c_str(), std::ios::in | std::ios::binary | std::ios::ate);
    if (!ifs.is_open()) {
        throw std::runtime_error("Could not open " + filename);
    }

    size_t size = ifs.tellg();

    std::unique_ptr<char[]> buffer{new char[size]};

    ifs.seekg(0, std::ios::beg);
    ifs.read(buffer.get(), size);
    ifs.close();

    return buffer;
}

int32_t ReadHeader(const std::unique_ptr<char[]>& buffer, size_t offset) {
    uint32_t header = *reinterpret_cast<uint32_t*>(buffer.get() + offset);

    // Swap byte order.
    uint32_t b1 = header & 255;
    uint32_t b2 = (header >> 8) & 255;
    uint32_t b3 = (header >> 16) & 255;
    uint32_t b4 = (header >> 24) & 255;

    return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
}

chainerx::Array ReadArray(const std::unique_ptr<char[]>& buffer, const chainerx::Shape& shape, size_t offset) {
    int64_t n = shape.GetTotalSize();

    std::shared_ptr<uint8_t> data{new uint8_t[n], std::default_delete<uint8_t[]>{}};
    std::copy_n(reinterpret_cast<uint8_t*>(buffer.get() + offset), n, data.get());

    return chainerx::FromContiguousHostData(
            shape, chainerx::Dtype::kUInt8, static_cast<std::shared_ptr<void>>(data), chainerx::GetDefaultDevice());
}

}  // namespace

chainerx::Array ReadMnistImages(const std::string& filename) {
    std::unique_ptr<char[]> buffer = ReadFile(filename);

    int32_t n = ReadHeader(buffer, 4);
    int32_t height = ReadHeader(buffer, 8);
    int32_t width = ReadHeader(buffer, 12);

    assert(n == 60000 || n == 10000);
    assert(height == 28);
    assert(width == 28);

    return ReadArray(buffer, {n, height * width}, 16);
}

chainerx::Array ReadMnistLabels(const std::string& filename) {
    std::unique_ptr<char[]> buffer = ReadFile(filename);

    int32_t n = ReadHeader(buffer, 4);

    assert(n == 60000 || n == 10000);

    return ReadArray(buffer, {n}, 8);
}
<|endoftext|>


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

#include "chrome/browser/history/in_memory_history_backend.h"

#include <set>
#include <vector>

#include "base/command_line.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/history/history_notifications.h"
#include "chrome/browser/history/in_memory_database.h"
#include "chrome/browser/history/in_memory_url_index.h"
#include "chrome/browser/history/url_database.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_switches.h"
#include "content/common/notification_details.h"
#include "content/common/notification_source.h"

namespace history {

// If a page becomes starred we use this id in place of the real starred id.
// See note in OnURLsStarred.
static const StarID kBogusStarredID = 0x0FFFFFFF;

InMemoryHistoryBackend::InMemoryHistoryBackend()
    : profile_(NULL) {
}

InMemoryHistoryBackend::~InMemoryHistoryBackend() {
  if (index_.get())
    index_->ShutDown();
}

bool InMemoryHistoryBackend::Init(const FilePath& history_filename,
                                  const FilePath& history_dir,
                                  URLDatabase* db,
                                  const std::string& languages) {
  db_.reset(new InMemoryDatabase);
  bool success = db_->InitFromDisk(history_filename);
  if (!CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kDisableHistoryQuickProvider)) {
    index_.reset(new InMemoryURLIndex(history_dir));
    index_->Init(db, languages);
  }
  return success;
}

void InMemoryHistoryBackend::AttachToHistoryService(Profile* profile) {
  if (!db_.get()) {
    NOTREACHED();
    return;
  }

  profile_ = profile;

  // TODO(evanm): this is currently necessitated by generate_profile, which
  // runs without a browser process. generate_profile should really create
  // a browser process, at which point this check can then be nuked.
  if (!g_browser_process)
    return;

  // Register for the notifications we care about.
  // We only want notifications for the associated profile.
  Source<Profile> source(profile_);
  registrar_.Add(this, NotificationType::HISTORY_URL_VISITED, source);
  registrar_.Add(this, NotificationType::HISTORY_TYPED_URLS_MODIFIED, source);
  registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED, source);
  registrar_.Add(this, NotificationType::HISTORY_KEYWORD_SEARCH_TERM_UPDATED,
                 source);
  registrar_.Add(this, NotificationType::TEMPLATE_URL_REMOVED, source);
}

void InMemoryHistoryBackend::Observe(NotificationType type,
                                     const NotificationSource& source,
                                     const NotificationDetails& details) {
  switch (type.value) {
    case NotificationType::HISTORY_URL_VISITED: {
      Details<history::URLVisitedDetails> visited_details(details);
      PageTransition::Type primary_type =
          PageTransition::StripQualifier(visited_details->transition);
      if (visited_details->row.typed_count() > 0 ||
          primary_type == PageTransition::KEYWORD ||
          HasKeyword(visited_details->row.url())) {
        URLsModifiedDetails modified_details;
        modified_details.changed_urls.push_back(visited_details->row);
        OnTypedURLsModified(modified_details);
      }
      break;
    }
    case NotificationType::HISTORY_KEYWORD_SEARCH_TERM_UPDATED:
      OnKeywordSearchTermUpdated(
          *Details<history::KeywordSearchTermDetails>(details).ptr());
      break;
    case NotificationType::HISTORY_TYPED_URLS_MODIFIED:
      OnTypedURLsModified(
          *Details<history::URLsModifiedDetails>(details).ptr());
      break;
    case NotificationType::HISTORY_URLS_DELETED:
      OnURLsDeleted(*Details<history::URLsDeletedDetails>(details).ptr());
      break;
    case NotificationType::TEMPLATE_URL_REMOVED:
      db_->DeleteAllSearchTermsForKeyword(
          *(Details<TemplateURLID>(details).ptr()));
      break;
    default:
      // For simplicity, the unit tests send us all notifications, even when
      // we haven't registered for them, so don't assert here.
      break;
  }
}

void InMemoryHistoryBackend::OnTypedURLsModified(
    const URLsModifiedDetails& details) {
  DCHECK(db_.get());

  // Add or update the URLs.
  //
  // TODO(brettw) currently the rows in the in-memory database don't match the
  // IDs in the main database. This sucks. Instead of Add and Remove, we should
  // have Sync(), which would take the ID if it's given and add it.
  std::vector<history::URLRow>::const_iterator i;
  for (i = details.changed_urls.begin();
       i != details.changed_urls.end(); i++) {
    URLID id = db_->GetRowForURL(i->url(), NULL);
    if (id)
      db_->UpdateURLRow(id, *i);
    else
      id = db_->AddURL(*i);
    if (index_.get())
      index_->UpdateURL(id, *i);
  }
}

void InMemoryHistoryBackend::OnURLsDeleted(const URLsDeletedDetails& details) {
  DCHECK(db_.get());

  if (details.all_history) {
    // When all history is deleted, the individual URLs won't be listed. Just
    // create a new database to quickly clear everything out.
    db_.reset(new InMemoryDatabase);
    if (!db_->InitFromScratch())
      db_.reset();
    if (index_.get())
      index_->ReloadFromHistory(db_.get(), true);
    return;
  }

  // Delete all matching URLs in our database.
  for (std::set<GURL>::const_iterator i = details.urls.begin();
       i != details.urls.end(); ++i) {
    URLID id = db_->GetRowForURL(*i, NULL);
    if (id) {
      // We typically won't have most of them since we only have a subset of
      // history, so ignore errors.
      db_->DeleteURLRow(id);
      if (index_.get())
        index_->DeleteURL(id);
    }
  }
}

void InMemoryHistoryBackend::OnKeywordSearchTermUpdated(
    const KeywordSearchTermDetails& details) {
  // The url won't exist for new search terms (as the user hasn't typed it), so
  // we force it to be added. If we end up adding a URL it won't be
  // autocompleted as the typed count is 0.
  URLRow url_row;
  URLID url_id;
  if (!db_->GetRowForURL(details.url, &url_row)) {
    // Because this row won't have a typed count the title and other stuff
    // doesn't matter. If the user ends up typing the url we'll update the title
    // in OnTypedURLsModified.
    URLRow new_row(details.url);
    new_row.set_last_visit(base::Time::Now());
    url_id = db_->AddURL(new_row);
    if (!url_id)
      return;  // Error adding.
  } else {
    url_id = url_row.id();
  }

  db_->SetKeywordSearchTermsForURL(url_id, details.keyword_id, details.term);
}

bool InMemoryHistoryBackend::HasKeyword(const GURL& url) {
  URLID id = db_->GetRowForURL(url, NULL);
  if (!id)
    return false;

  return db_->GetKeywordSearchTermRow(id, NULL);
}

}  // namespace history
<commit_msg>history: Remove unused bookmark constant.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/history/in_memory_history_backend.h"

#include <set>
#include <vector>

#include "base/command_line.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/history/history_notifications.h"
#include "chrome/browser/history/in_memory_database.h"
#include "chrome/browser/history/in_memory_url_index.h"
#include "chrome/browser/history/url_database.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_switches.h"
#include "content/common/notification_details.h"
#include "content/common/notification_source.h"

namespace history {

InMemoryHistoryBackend::InMemoryHistoryBackend()
    : profile_(NULL) {
}

InMemoryHistoryBackend::~InMemoryHistoryBackend() {
  if (index_.get())
    index_->ShutDown();
}

bool InMemoryHistoryBackend::Init(const FilePath& history_filename,
                                  const FilePath& history_dir,
                                  URLDatabase* db,
                                  const std::string& languages) {
  db_.reset(new InMemoryDatabase);
  bool success = db_->InitFromDisk(history_filename);
  if (!CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kDisableHistoryQuickProvider)) {
    index_.reset(new InMemoryURLIndex(history_dir));
    index_->Init(db, languages);
  }
  return success;
}

void InMemoryHistoryBackend::AttachToHistoryService(Profile* profile) {
  if (!db_.get()) {
    NOTREACHED();
    return;
  }

  profile_ = profile;

  // TODO(evanm): this is currently necessitated by generate_profile, which
  // runs without a browser process. generate_profile should really create
  // a browser process, at which point this check can then be nuked.
  if (!g_browser_process)
    return;

  // Register for the notifications we care about.
  // We only want notifications for the associated profile.
  Source<Profile> source(profile_);
  registrar_.Add(this, NotificationType::HISTORY_URL_VISITED, source);
  registrar_.Add(this, NotificationType::HISTORY_TYPED_URLS_MODIFIED, source);
  registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED, source);
  registrar_.Add(this, NotificationType::HISTORY_KEYWORD_SEARCH_TERM_UPDATED,
                 source);
  registrar_.Add(this, NotificationType::TEMPLATE_URL_REMOVED, source);
}

void InMemoryHistoryBackend::Observe(NotificationType type,
                                     const NotificationSource& source,
                                     const NotificationDetails& details) {
  switch (type.value) {
    case NotificationType::HISTORY_URL_VISITED: {
      Details<history::URLVisitedDetails> visited_details(details);
      PageTransition::Type primary_type =
          PageTransition::StripQualifier(visited_details->transition);
      if (visited_details->row.typed_count() > 0 ||
          primary_type == PageTransition::KEYWORD ||
          HasKeyword(visited_details->row.url())) {
        URLsModifiedDetails modified_details;
        modified_details.changed_urls.push_back(visited_details->row);
        OnTypedURLsModified(modified_details);
      }
      break;
    }
    case NotificationType::HISTORY_KEYWORD_SEARCH_TERM_UPDATED:
      OnKeywordSearchTermUpdated(
          *Details<history::KeywordSearchTermDetails>(details).ptr());
      break;
    case NotificationType::HISTORY_TYPED_URLS_MODIFIED:
      OnTypedURLsModified(
          *Details<history::URLsModifiedDetails>(details).ptr());
      break;
    case NotificationType::HISTORY_URLS_DELETED:
      OnURLsDeleted(*Details<history::URLsDeletedDetails>(details).ptr());
      break;
    case NotificationType::TEMPLATE_URL_REMOVED:
      db_->DeleteAllSearchTermsForKeyword(
          *(Details<TemplateURLID>(details).ptr()));
      break;
    default:
      // For simplicity, the unit tests send us all notifications, even when
      // we haven't registered for them, so don't assert here.
      break;
  }
}

void InMemoryHistoryBackend::OnTypedURLsModified(
    const URLsModifiedDetails& details) {
  DCHECK(db_.get());

  // Add or update the URLs.
  //
  // TODO(brettw) currently the rows in the in-memory database don't match the
  // IDs in the main database. This sucks. Instead of Add and Remove, we should
  // have Sync(), which would take the ID if it's given and add it.
  std::vector<history::URLRow>::const_iterator i;
  for (i = details.changed_urls.begin();
       i != details.changed_urls.end(); i++) {
    URLID id = db_->GetRowForURL(i->url(), NULL);
    if (id)
      db_->UpdateURLRow(id, *i);
    else
      id = db_->AddURL(*i);
    if (index_.get())
      index_->UpdateURL(id, *i);
  }
}

void InMemoryHistoryBackend::OnURLsDeleted(const URLsDeletedDetails& details) {
  DCHECK(db_.get());

  if (details.all_history) {
    // When all history is deleted, the individual URLs won't be listed. Just
    // create a new database to quickly clear everything out.
    db_.reset(new InMemoryDatabase);
    if (!db_->InitFromScratch())
      db_.reset();
    if (index_.get())
      index_->ReloadFromHistory(db_.get(), true);
    return;
  }

  // Delete all matching URLs in our database.
  for (std::set<GURL>::const_iterator i = details.urls.begin();
       i != details.urls.end(); ++i) {
    URLID id = db_->GetRowForURL(*i, NULL);
    if (id) {
      // We typically won't have most of them since we only have a subset of
      // history, so ignore errors.
      db_->DeleteURLRow(id);
      if (index_.get())
        index_->DeleteURL(id);
    }
  }
}

void InMemoryHistoryBackend::OnKeywordSearchTermUpdated(
    const KeywordSearchTermDetails& details) {
  // The url won't exist for new search terms (as the user hasn't typed it), so
  // we force it to be added. If we end up adding a URL it won't be
  // autocompleted as the typed count is 0.
  URLRow url_row;
  URLID url_id;
  if (!db_->GetRowForURL(details.url, &url_row)) {
    // Because this row won't have a typed count the title and other stuff
    // doesn't matter. If the user ends up typing the url we'll update the title
    // in OnTypedURLsModified.
    URLRow new_row(details.url);
    new_row.set_last_visit(base::Time::Now());
    url_id = db_->AddURL(new_row);
    if (!url_id)
      return;  // Error adding.
  } else {
    url_id = url_row.id();
  }

  db_->SetKeywordSearchTermsForURL(url_id, details.keyword_id, details.term);
}

bool InMemoryHistoryBackend::HasKeyword(const GURL& url) {
  URLID id = db_->GetRowForURL(url, NULL);
  if (!id)
    return false;

  return db_->GetKeywordSearchTermRow(id, NULL);
}

}  // namespace history
<|endoftext|>


<commit_before>#include "modules/battery.hpp"
#include "drawtypes/animation.hpp"
#include "drawtypes/label.hpp"
#include "drawtypes/progressbar.hpp"
#include "drawtypes/ramp.hpp"
#include "utils/file.hpp"
#include "utils/math.hpp"

#include "modules/meta/base.inl"

POLYBAR_NS

namespace modules {
  template class module<battery_module>;

  template <typename ValueReader>
  typename ValueReader::return_type read(ValueReader& reader) {
    std::lock_guard<ValueReader> guard(reader);
    return reader.read();
  }

  /**
   * Bootstrap module by setting up required components
   */
  battery_module::battery_module(const bar_settings& bar, string name_)
      : inotify_module<battery_module>(bar, move(name_)) {
    // Load configuration values
    m_fullat = m_conf.get(name(), "full-at", m_fullat);
    m_interval = m_conf.get<decltype(m_interval)>(name(), "poll-interval", 5s);
    m_lastpoll = chrono::system_clock::now();

    auto path_adapter = string_util::replace(PATH_ADAPTER, "%adapter%", m_conf.get(name(), "adapter", "ADP1"s)) + "/";
    auto path_battery = string_util::replace(PATH_BATTERY, "%battery%", m_conf.get(name(), "battery", "BAT0"s)) + "/";

    // Make state reader
    if (file_util::exists((m_fstate = path_adapter + "online"))) {
      m_state_reader = make_unique<state_reader>([=] { return file_util::contents(m_fstate).erase(1) == "1"; });
    } else if (file_util::exists((m_fstate = path_battery + "status"))) {
      m_state_reader = make_unique<state_reader>([=] { return file_util::contents(m_fstate).erase(8) == "Charging"; });
    } else {
      throw module_error("No suitable way to get current charge state");
    }

    // Make capacity reader
    if ((m_fcapnow = file_util::pick({path_battery + "charge_now", path_battery + "energy_now"})).empty()) {
      throw module_error("No suitable way to get current capacity value");
    } else if ((m_fcapfull = file_util::pick({path_battery + "charge_full", path_battery + "energy_full"})).empty()) {
      throw module_error("No suitable way to get max capacity value");
    }

    m_capacity_reader = make_unique<capacity_reader>([=] {
      auto cap_now = std::strtoul(file_util::contents(m_fcapnow).c_str(), nullptr, 10);
      auto cap_max = std::strtoul(file_util::contents(m_fcapfull).c_str(), nullptr, 10);
      return math_util::percentage(cap_now, 0UL, cap_max);
    });

    // Make rate reader
    if ((m_fvoltage = file_util::pick({path_battery + "voltage_now"})).empty()) {
      throw module_error("No suitable way to get current voltage value");
    } else if ((m_frate = file_util::pick({path_battery + "current_now", path_battery + "power_now"})).empty()) {
      throw module_error("No suitable way to get current charge rate value");
    }

    m_rate_reader = make_unique<rate_reader>([this] {
      unsigned long rate{std::strtoul(file_util::contents(m_frate).c_str(), nullptr, 10) / 1000UL};
      unsigned long volt{std::strtoul(file_util::contents(m_fvoltage).c_str(), nullptr, 10) / 1000UL};
      unsigned long now{std::strtoul(file_util::contents(m_fcapnow).c_str(), nullptr, 10) / 1000UL};
      unsigned long max{std::strtoul(file_util::contents(m_fcapfull).c_str(), nullptr, 10) / 1000UL};
      unsigned long cap{read(*m_state_reader) ? max - now : now};

      if (rate && volt && cap) {
        return 3600UL * (cap * 1000UL / volt) / (rate * 1000UL / volt);
      } else {
        return 0UL;
      }
    });

    // Load state and capacity level
    m_state = current_state();
    m_percentage = current_percentage(m_state);

    // Add formats and elements
    m_formatter->add(FORMAT_CHARGING, TAG_LABEL_CHARGING,
        {TAG_BAR_CAPACITY, TAG_RAMP_CAPACITY, TAG_ANIMATION_CHARGING, TAG_LABEL_CHARGING});
    m_formatter->add(
        FORMAT_DISCHARGING, TAG_LABEL_DISCHARGING, {TAG_BAR_CAPACITY, TAG_RAMP_CAPACITY, TAG_LABEL_DISCHARGING});
    m_formatter->add(FORMAT_FULL, TAG_LABEL_FULL, {TAG_BAR_CAPACITY, TAG_RAMP_CAPACITY, TAG_LABEL_FULL});

    if (m_formatter->has(TAG_ANIMATION_CHARGING, FORMAT_CHARGING)) {
      m_animation_charging = load_animation(m_conf, name(), TAG_ANIMATION_CHARGING);
    }
    if (m_formatter->has(TAG_BAR_CAPACITY)) {
      m_bar_capacity = load_progressbar(m_bar, m_conf, name(), TAG_BAR_CAPACITY);
    }
    if (m_formatter->has(TAG_RAMP_CAPACITY)) {
      m_ramp_capacity = load_ramp(m_conf, name(), TAG_RAMP_CAPACITY);
    }
    if (m_formatter->has(TAG_LABEL_CHARGING, FORMAT_CHARGING)) {
      m_label_charging = load_optional_label(m_conf, name(), TAG_LABEL_CHARGING, "%percentage%");
    }
    if (m_formatter->has(TAG_LABEL_DISCHARGING, FORMAT_DISCHARGING)) {
      m_label_discharging = load_optional_label(m_conf, name(), TAG_LABEL_DISCHARGING, "%percentage%");
    }
    if (m_formatter->has(TAG_LABEL_FULL, FORMAT_FULL)) {
      m_label_full = load_optional_label(m_conf, name(), TAG_LABEL_FULL, "%percentage%");
    }

    // Create inotify watches
    watch(m_fcapnow, IN_ACCESS);
    watch(m_fstate, IN_ACCESS);

    // Setup time if token is used
    if ((m_label_charging && m_label_charging->has_token("%time%")) ||
        (m_label_discharging && m_label_discharging->has_token("%time%"))) {
      if (!m_bar.locale.empty()) {
        setlocale(LC_TIME, m_bar.locale.c_str());
      }
      m_timeformat = m_conf.get(name(), "time-format", "%H:%M:%S"s);
    }
  }

  /**
   * Dispatch the subthread used to update the
   * charging animation when the module is started
   */
  void battery_module::start() {
    this->inotify_module::start();
    m_subthread = thread(&battery_module::subthread, this);
  }

  /**
   * Release wake lock when stopping the module
   */
  void battery_module::teardown() {
    if (m_subthread.joinable()) {
      m_subthread.join();
    }
  }

  /**
   * Idle between polling inotify watches for events.
   *
   * If the defined interval has been reached, trigger a manual
   * poll in case the inotify events aren't fired.
   *
   * This fallback is needed because some systems won't
   * report inotify events for files on sysfs.
   */
  void battery_module::idle() {
    if (m_interval.count() > 0) {
      auto now = chrono::system_clock::now();
      if (chrono::duration_cast<decltype(m_interval)>(now - m_lastpoll) > m_interval) {
        m_lastpoll = now;
        m_log.info("%s: Polling values (inotify fallback)", name());
        read(*m_capacity_reader);
      }
    }

    this->inotify_module::idle();
  }

  /**
   * Update values when tracked files have changed
   */
  bool battery_module::on_event(inotify_event* event) {
    auto state = current_state();
    auto percentage = current_percentage(state);

    // Reset timer to avoid unnecessary polling
    m_lastpoll = chrono::system_clock::now();

    if (event != nullptr) {
      m_log.trace("%s: Inotify event reported for %s", name(), event->filename);

      if (state == m_state && percentage == m_percentage && m_unchanged--) {
        return false;
      }

      m_unchanged = SKIP_N_UNCHANGED;
    }

    m_state = state;
    m_percentage = percentage;

    const auto label = [this] {
      if (m_state == state::FULL) {
        return m_label_full;
      } else if (m_state == state::DISCHARGING) {
        return m_label_discharging;
      } else {
        return m_label_charging;
      }
    }();

    label->reset_tokens();
    label->replace_token("%percentage%", to_string(m_percentage) + "%");

    if (m_state != state::FULL && !m_timeformat.empty()) {
      label->replace_token("%time%", current_time());
    }

    return true;
  }

  /**
   * Get the output format based on state
   */
  string battery_module::get_format() const {
    if (m_state == state::CHARGING) {
      return FORMAT_CHARGING;
    } else if (m_state == state::DISCHARGING) {
      return FORMAT_DISCHARGING;
    } else {
      return FORMAT_FULL;
    }
  }

  /**
   * Generate module output using defined drawtypes
   */
  bool battery_module::build(builder* builder, const string& tag) const {
    if (tag == TAG_ANIMATION_CHARGING) {
      builder->node(m_animation_charging->get());
    } else if (tag == TAG_BAR_CAPACITY) {
      builder->node(m_bar_capacity->output(m_percentage));
    } else if (tag == TAG_RAMP_CAPACITY) {
      builder->node(m_ramp_capacity->get_by_percentage(m_percentage));
    } else if (tag == TAG_LABEL_CHARGING) {
      builder->node(m_label_charging);
    } else if (tag == TAG_LABEL_DISCHARGING) {
      builder->node(m_label_discharging);
    } else if (tag == TAG_LABEL_FULL) {
      builder->node(m_label_full);
    } else {
      return false;
    }

    return true;
  }

  /**
   * Get the current battery state
   */
  battery_module::state battery_module::current_state() {
    if (!read(*m_state_reader)) {
      return state::DISCHARGING;
    } else if (read(*m_capacity_reader) < m_fullat) {
      return state::CHARGING;
    } else {
      return state::FULL;
    }
  }

  /**
   * Get the current capacity level
   */
  int battery_module::current_percentage(state state) {
    int percentage{read(*m_capacity_reader)};
    if (state == state::FULL && percentage >= m_fullat) {
      percentage = 100;
    }
    return percentage;
  }

  /**
   * Get estimate of remaining time until fully dis-/charged
   */
  string battery_module::current_time() {
    struct tm t {
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr
    };

    chrono::seconds sec{read(*m_rate_reader)};
    if (sec.count() > 0) {
      t.tm_hour = chrono::duration_cast<chrono::hours>(sec).count();
      sec -= chrono::seconds{3600 * t.tm_hour};
      t.tm_min = chrono::duration_cast<chrono::minutes>(sec).count();
      sec -= chrono::seconds{60 * t.tm_min};
      t.tm_sec = chrono::duration_cast<chrono::seconds>(sec).count();
    }

    char buffer[256]{0};
    strftime(buffer, sizeof(buffer), m_timeformat.c_str(), &t);
    return {buffer};
  }

  /**
   * Subthread runner that emit update events
   * to refresh <animation-charging> in case it is used.
   */
  void battery_module::subthread() {
    chrono::duration<double> dur{0.0};

    if (m_animation_charging) {
      dur += chrono::milliseconds{m_animation_charging->framerate()};
    } else {
      dur += 1s;
    }

    while (running()) {
      for (int i = 0; running() && i < dur.count(); ++i) {
        if (m_state == state::CHARGING) {
          broadcast();
        }
        sleep(dur);
      }
    }

    m_log.trace("%s: End of subthread", name());
  }
}

POLYBAR_NS_END
<commit_msg>fix(battery): Use full namespace<commit_after>#include "modules/battery.hpp"
#include "drawtypes/animation.hpp"
#include "drawtypes/label.hpp"
#include "drawtypes/progressbar.hpp"
#include "drawtypes/ramp.hpp"
#include "utils/file.hpp"
#include "utils/math.hpp"

#include "modules/meta/base.inl"

POLYBAR_NS

namespace modules {
  template class module<battery_module>;

  template <typename ValueReader>
  typename ValueReader::return_type read(ValueReader& reader) {
    std::lock_guard<ValueReader> guard(reader);
    return reader.read();
  }

  /**
   * Bootstrap module by setting up required components
   */
  battery_module::battery_module(const bar_settings& bar, string name_)
      : inotify_module<battery_module>(bar, move(name_)) {
    // Load configuration values
    m_fullat = m_conf.get(name(), "full-at", m_fullat);
    m_interval = m_conf.get<decltype(m_interval)>(name(), "poll-interval", 5s);
    m_lastpoll = chrono::system_clock::now();

    auto path_adapter = string_util::replace(PATH_ADAPTER, "%adapter%", m_conf.get(name(), "adapter", "ADP1"s)) + "/";
    auto path_battery = string_util::replace(PATH_BATTERY, "%battery%", m_conf.get(name(), "battery", "BAT0"s)) + "/";

    // Make state reader
    if (file_util::exists((m_fstate = path_adapter + "online"))) {
      m_state_reader = make_unique<state_reader>([=] { return file_util::contents(m_fstate).erase(1) == "1"; });
    } else if (file_util::exists((m_fstate = path_battery + "status"))) {
      m_state_reader = make_unique<state_reader>([=] { return file_util::contents(m_fstate).erase(8) == "Charging"; });
    } else {
      throw module_error("No suitable way to get current charge state");
    }

    // Make capacity reader
    if ((m_fcapnow = file_util::pick({path_battery + "charge_now", path_battery + "energy_now"})).empty()) {
      throw module_error("No suitable way to get current capacity value");
    } else if ((m_fcapfull = file_util::pick({path_battery + "charge_full", path_battery + "energy_full"})).empty()) {
      throw module_error("No suitable way to get max capacity value");
    }

    m_capacity_reader = make_unique<capacity_reader>([=] {
      auto cap_now = std::strtoul(file_util::contents(m_fcapnow).c_str(), nullptr, 10);
      auto cap_max = std::strtoul(file_util::contents(m_fcapfull).c_str(), nullptr, 10);
      return math_util::percentage(cap_now, 0UL, cap_max);
    });

    // Make rate reader
    if ((m_fvoltage = file_util::pick({path_battery + "voltage_now"})).empty()) {
      throw module_error("No suitable way to get current voltage value");
    } else if ((m_frate = file_util::pick({path_battery + "current_now", path_battery + "power_now"})).empty()) {
      throw module_error("No suitable way to get current charge rate value");
    }

    m_rate_reader = make_unique<rate_reader>([this] {
      unsigned long rate{std::strtoul(file_util::contents(m_frate).c_str(), nullptr, 10) / 1000UL};
      unsigned long volt{std::strtoul(file_util::contents(m_fvoltage).c_str(), nullptr, 10) / 1000UL};
      unsigned long now{std::strtoul(file_util::contents(m_fcapnow).c_str(), nullptr, 10) / 1000UL};
      unsigned long max{std::strtoul(file_util::contents(m_fcapfull).c_str(), nullptr, 10) / 1000UL};
      unsigned long cap{read(*m_state_reader) ? max - now : now};

      if (rate && volt && cap) {
        return 3600UL * (cap * 1000UL / volt) / (rate * 1000UL / volt);
      } else {
        return 0UL;
      }
    });

    // Load state and capacity level
    m_state = current_state();
    m_percentage = current_percentage(m_state);

    // Add formats and elements
    m_formatter->add(FORMAT_CHARGING, TAG_LABEL_CHARGING,
        {TAG_BAR_CAPACITY, TAG_RAMP_CAPACITY, TAG_ANIMATION_CHARGING, TAG_LABEL_CHARGING});
    m_formatter->add(
        FORMAT_DISCHARGING, TAG_LABEL_DISCHARGING, {TAG_BAR_CAPACITY, TAG_RAMP_CAPACITY, TAG_LABEL_DISCHARGING});
    m_formatter->add(FORMAT_FULL, TAG_LABEL_FULL, {TAG_BAR_CAPACITY, TAG_RAMP_CAPACITY, TAG_LABEL_FULL});

    if (m_formatter->has(TAG_ANIMATION_CHARGING, FORMAT_CHARGING)) {
      m_animation_charging = load_animation(m_conf, name(), TAG_ANIMATION_CHARGING);
    }
    if (m_formatter->has(TAG_BAR_CAPACITY)) {
      m_bar_capacity = load_progressbar(m_bar, m_conf, name(), TAG_BAR_CAPACITY);
    }
    if (m_formatter->has(TAG_RAMP_CAPACITY)) {
      m_ramp_capacity = load_ramp(m_conf, name(), TAG_RAMP_CAPACITY);
    }
    if (m_formatter->has(TAG_LABEL_CHARGING, FORMAT_CHARGING)) {
      m_label_charging = load_optional_label(m_conf, name(), TAG_LABEL_CHARGING, "%percentage%");
    }
    if (m_formatter->has(TAG_LABEL_DISCHARGING, FORMAT_DISCHARGING)) {
      m_label_discharging = load_optional_label(m_conf, name(), TAG_LABEL_DISCHARGING, "%percentage%");
    }
    if (m_formatter->has(TAG_LABEL_FULL, FORMAT_FULL)) {
      m_label_full = load_optional_label(m_conf, name(), TAG_LABEL_FULL, "%percentage%");
    }

    // Create inotify watches
    watch(m_fcapnow, IN_ACCESS);
    watch(m_fstate, IN_ACCESS);

    // Setup time if token is used
    if ((m_label_charging && m_label_charging->has_token("%time%")) ||
        (m_label_discharging && m_label_discharging->has_token("%time%"))) {
      if (!m_bar.locale.empty()) {
        setlocale(LC_TIME, m_bar.locale.c_str());
      }
      m_timeformat = m_conf.get(name(), "time-format", "%H:%M:%S"s);
    }
  }

  /**
   * Dispatch the subthread used to update the
   * charging animation when the module is started
   */
  void battery_module::start() {
    this->inotify_module::start();
    m_subthread = thread(&battery_module::subthread, this);
  }

  /**
   * Release wake lock when stopping the module
   */
  void battery_module::teardown() {
    if (m_subthread.joinable()) {
      m_subthread.join();
    }
  }

  /**
   * Idle between polling inotify watches for events.
   *
   * If the defined interval has been reached, trigger a manual
   * poll in case the inotify events aren't fired.
   *
   * This fallback is needed because some systems won't
   * report inotify events for files on sysfs.
   */
  void battery_module::idle() {
    if (m_interval.count() > 0) {
      auto now = chrono::system_clock::now();
      if (chrono::duration_cast<decltype(m_interval)>(now - m_lastpoll) > m_interval) {
        m_lastpoll = now;
        m_log.info("%s: Polling values (inotify fallback)", name());
        read(*m_capacity_reader);
      }
    }

    this->inotify_module::idle();
  }

  /**
   * Update values when tracked files have changed
   */
  bool battery_module::on_event(inotify_event* event) {
    auto state = current_state();
    auto percentage = current_percentage(state);

    // Reset timer to avoid unnecessary polling
    m_lastpoll = chrono::system_clock::now();

    if (event != nullptr) {
      m_log.trace("%s: Inotify event reported for %s", name(), event->filename);

      if (state == m_state && percentage == m_percentage && m_unchanged--) {
        return false;
      }

      m_unchanged = SKIP_N_UNCHANGED;
    }

    m_state = state;
    m_percentage = percentage;

    const auto label = [this] {
      if (m_state == battery_module::state::FULL) {
        return m_label_full;
      } else if (m_state == battery_module::state::DISCHARGING) {
        return m_label_discharging;
      } else {
        return m_label_charging;
      }
    }();

    label->reset_tokens();
    label->replace_token("%percentage%", to_string(m_percentage) + "%");

    if (m_state != battery_module::state::FULL && !m_timeformat.empty()) {
      label->replace_token("%time%", current_time());
    }

    return true;
  }

  /**
   * Get the output format based on state
   */
  string battery_module::get_format() const {
    if (m_state == battery_module::state::CHARGING) {
      return FORMAT_CHARGING;
    } else if (m_state == battery_module::state::DISCHARGING) {
      return FORMAT_DISCHARGING;
    } else {
      return FORMAT_FULL;
    }
  }

  /**
   * Generate module output using defined drawtypes
   */
  bool battery_module::build(builder* builder, const string& tag) const {
    if (tag == TAG_ANIMATION_CHARGING) {
      builder->node(m_animation_charging->get());
    } else if (tag == TAG_BAR_CAPACITY) {
      builder->node(m_bar_capacity->output(m_percentage));
    } else if (tag == TAG_RAMP_CAPACITY) {
      builder->node(m_ramp_capacity->get_by_percentage(m_percentage));
    } else if (tag == TAG_LABEL_CHARGING) {
      builder->node(m_label_charging);
    } else if (tag == TAG_LABEL_DISCHARGING) {
      builder->node(m_label_discharging);
    } else if (tag == TAG_LABEL_FULL) {
      builder->node(m_label_full);
    } else {
      return false;
    }

    return true;
  }

  /**
   * Get the current battery state
   */
  battery_module::state battery_module::current_state() {
    if (!read(*m_state_reader)) {
      return battery_module::state::DISCHARGING;
    } else if (read(*m_capacity_reader) < m_fullat) {
      return battery_module::state::CHARGING;
    } else {
      return battery_module::state::FULL;
    }
  }

  /**
   * Get the current capacity level
   */
  int battery_module::current_percentage(state state) {
    int percentage{read(*m_capacity_reader)};
    if (state == battery_module::state::FULL && percentage >= m_fullat) {
      percentage = 100;
    }
    return percentage;
  }

  /**
   * Get estimate of remaining time until fully dis-/charged
   */
  string battery_module::current_time() {
    struct tm t {
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr
    };

    chrono::seconds sec{read(*m_rate_reader)};
    if (sec.count() > 0) {
      t.tm_hour = chrono::duration_cast<chrono::hours>(sec).count();
      sec -= chrono::seconds{3600 * t.tm_hour};
      t.tm_min = chrono::duration_cast<chrono::minutes>(sec).count();
      sec -= chrono::seconds{60 * t.tm_min};
      t.tm_sec = chrono::duration_cast<chrono::seconds>(sec).count();
    }

    char buffer[256]{0};
    strftime(buffer, sizeof(buffer), m_timeformat.c_str(), &t);
    return {buffer};
  }

  /**
   * Subthread runner that emit update events
   * to refresh <animation-charging> in case it is used.
   */
  void battery_module::subthread() {
    chrono::duration<double> dur{0.0};

    if (m_animation_charging) {
      dur += chrono::milliseconds{m_animation_charging->framerate()};
    } else {
      dur += 1s;
    }

    while (running()) {
      for (int i = 0; running() && i < dur.count(); ++i) {
        if (m_state == battery_module::state::CHARGING) {
          broadcast();
        }
        sleep(dur);
      }
    }

    m_log.trace("%s: End of subthread", name());
  }
}

POLYBAR_NS_END
<|endoftext|>