<commit_before>/*
 * opencog/attentionbank/AttentionBank.cc
 *
 * Copyright (C) 2013,2017 Linas Vepstas <linasvepstas@gmail.com>
 * All Rights Reserved
 *
 * Written by Joel Pitt
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <functional>
#include <opencog/util/Config.h>
#include <opencog/util/mt19937ar.h>

#include <opencog/atoms/base/Handle.h>
#include <opencog/atomspace/AtomSpace.h>
#include "AttentionBank.h"
#include "AVUtils.h"

using namespace opencog;

AttentionBank::AttentionBank(AtomSpace* asp)
{
    // XXX FIXME -- should not use config() to get these values;
    // The user of this class should call config(), instead!

    startingFundsSTI = fundsSTI = config().get_double("STARTING_STI_FUNDS", 100000);
    startingFundsLTI = fundsLTI = config().get_double("STARTING_LTI_FUNDS", 100000);
    stiFundsBuffer = config().get_double("STI_FUNDS_BUFFER", 10000);
    ltiFundsBuffer = config().get_double("LTI_FUNDS_BUFFER", 10000);
    targetLTI = config().get_double("TARGET_LTI_FUNDS", 10000);
    targetSTI = config().get_double("TARGET_STI_FUNDS", 10000);
    STIAtomWage = config().get_double("ECAN_STARTING_ATOM_STI_WAGE", 10);
    LTIAtomWage = config().get_double("ECAN_STARTING_ATOM_LTI_WAGE", 10);
    maxAFSize = config().get_int("ECAN_MAX_AF_SIZE", 100);

    _remove_signal = &asp->atomRemovedSignal();
    _remove_connection = _remove_signal->connect(
            std::bind(&AttentionBank::remove_atom_from_bank, this,
                std::placeholders::_1));
}

AttentionBank::~AttentionBank()
{
    _remove_signal->disconnect(_remove_connection);
}

void AttentionBank::remove_atom_from_bank(const AtomPtr& atom)
{
    AFMutex.lock();
    auto it = std::find_if(attentionalFocus.begin(), attentionalFocus.end(),
                [atom](std::pair<Handle, AttentionValuePtr> p)
                { return p.first == Handle(atom);});

    if (it != attentionalFocus.end())
        attentionalFocus.erase(it);
    AFMutex.unlock();

   _importanceIndex.removeAtom(Handle(atom));
}

void AttentionBank::set_sti(const Handle& h, AttentionValue::sti_t stiValue)
{
    AttentionValuePtr oldav = get_av(h);
    AttentionValuePtr newav = AttentionValue::createAV(
        stiValue, oldav->getLTI(), oldav->getVLTI());

    _importanceIndex.updateImportance(h, oldav, newav);
    AVChanged(h, oldav, newav);
}

void AttentionBank::set_lti(const Handle& h, AttentionValue::lti_t ltiValue)
{
    AttentionValuePtr old_av = get_av(h);
    AttentionValuePtr new_av = AttentionValue::createAV(
        old_av->getSTI(), ltiValue, old_av->getVLTI());

    AVChanged(h, old_av, new_av);
}

void AttentionBank::change_vlti(const Handle& h, int unit)
{
    AttentionValuePtr old_av = get_av(h);
    AttentionValuePtr new_av = AttentionValue::createAV(
        old_av->getSTI(),
        old_av->getLTI(),
        old_av->getVLTI() + unit);

    AVChanged(h, old_av, new_av);
}

void AttentionBank::change_av(const Handle& h, const AttentionValuePtr& new_av)
{
    AttentionValuePtr old_av = get_av(h);
    _importanceIndex.updateImportance(h, old_av, new_av);
    AVChanged(h, old_av, new_av);
}

void AttentionBank::AVChanged(const Handle& h,
                              const AttentionValuePtr& old_av,
                              const AttentionValuePtr& new_av)
{
    _mtx.lock();
    // First, update the atom's actual AV.
    set_av(h, new_av);

    AttentionValue::sti_t oldSti = old_av->getSTI();
    AttentionValue::sti_t newSti = new_av->getSTI();

    // Add the old attention values to the AttentionBank funds and
    // subtract the new attention values from the AttentionBank funds
    fundsSTI += (oldSti - newSti);
    fundsLTI += (old_av->getLTI() - new_av->getLTI());

    _importanceIndex.update();

    _mtx.unlock();

    logger().fine("AVChanged: fundsSTI = %d, old_av: %d, new_av: %d",
                   fundsSTI, oldSti, newSti);

    // Notify any interested parties that the AV changed.
    _AVChangedSignal.emit(h, old_av, new_av);

    // update AF
    updateAttentionalFocus(h, old_av, new_av);
}

void AttentionBank::stimulate(const Handle& h, double stimulus)
{
    // XXX This is not protected or made atomic in any way ...
    // If two different threads stimulate the same atom at the same
    // time, then the calculations will be bad. Does it matter?
    AttentionValuePtr oldav(get_av(h));
    AttentionValue::sti_t sti   = oldav->getSTI();
    AttentionValue::lti_t lti   = oldav->getLTI();
    AttentionValue::vlti_t vlti = oldav->getVLTI();

    AttentionValue::sti_t stiWage = calculateSTIWage() * stimulus;
    AttentionValue::lti_t ltiWage = calculateLTIWage() * stimulus;
    AttentionValuePtr newav = AttentionValue::createAV(
           sti + stiWage, lti + ltiWage, vlti);
    _importanceIndex.updateImportance(h, oldav, newav);
    AVChanged(h, oldav, newav);

#ifdef ECAN_EXPERIMENT
    if(stimulusRec.find(h) != stimulusRec.end()){
        stimulusRec[h] += stimulus;
    } else{
        stimulusRec[h] = stimulus;
    }
#endif
}

AttentionValue::sti_t AttentionBank::calculateSTIWage()
{
    AttentionValue::sti_t funds = getSTIFunds();
    AttentionValue::sti_t diff  = funds - targetSTI;
    AttentionValue::sti_t ndiff = diff / stiFundsBuffer;
    ndiff = std::min(ndiff, 1.0);
    ndiff = std::max(ndiff, -1.0);

    return STIAtomWage + (STIAtomWage * ndiff);
}

AttentionValue::lti_t AttentionBank::calculateLTIWage()
{
    AttentionValue::lti_t funds = getLTIFunds();
    AttentionValue::lti_t diff  = funds - targetLTI;
    AttentionValue::lti_t ndiff = diff / ltiFundsBuffer;
    ndiff = std::min(ndiff, 1.0);
    ndiff = std::max(ndiff, -1.0);

    return LTIAtomWage + (LTIAtomWage * ndiff);
}

double AttentionBank::getNormalisedSTI(AttentionValuePtr av,
                                   bool average, bool clip) const
{
    double val;
    // get normalizer (maxSTI - attention boundary)
    AttentionValue::sti_t s = av->getSTI();
    if (s > get_af_max_sti()) {
        int normaliser = (int) getMaxSTI(average) - get_af_max_sti();
        if (normaliser == 0) return 0.0;
        val = (s - get_af_max_sti()) / (double) normaliser;
    } else {
        int normaliser = -((int) getMinSTI(average) + get_af_max_sti());
        if (normaliser == 0) return 0.0;
        val = (s + get_af_max_sti()) / (double) normaliser;

    }

    if (clip) return std::max(-1.0, std::min(val, 1.0));
    return val;
}

double AttentionBank::getNormalisedSTI(AttentionValuePtr av) const
{
    AttentionValue::sti_t s = av->getSTI();
    auto normaliser =
            s > get_af_max_sti() ? getMaxSTI() : getMinSTI();

    return (s / normaliser);
}

double AttentionBank::getNormalisedZeroToOneSTI(AttentionValuePtr av,
                                    bool average, bool clip) const
{
    AttentionValue::sti_t s = av->getSTI();
    int normaliser = getMaxSTI(average) - getMinSTI(average);

    if (normaliser == 0) return 0.0;

    double val = (s - getMinSTI(average)) / (double) normaliser;
    if (clip) return std::max(0.0, std::min(val, 1.0));
    return val;
}

/** Unique singleton instance (for now) */
// This implementation is pretty hokey, and is a stop-gap until some
// sort of more elegant way of managing the attentionbank is found.
// One of the issues is that access via this function can be CPU-wasteful.
AttentionBank& opencog::attentionbank(AtomSpace* asp)
{
    static AttentionBank* _instance = nullptr;
    static AtomSpace* _as = nullptr;

    // Protect setting and getting against thread races.
    // This is probably not needed.
    static std::map<AtomSpace*, AttentionBank*> banksy;
    static std::mutex art;
    std::unique_lock<std::mutex> graffiti(art);

    if (_as != asp and _instance) {
        delete _instance;
        _instance = nullptr;
        _as = nullptr;
    }
    if (asp and nullptr == _instance) {
        _instance = new AttentionBank(asp);
        _as = asp;
    }
    return *_instance;
}

bool AttentionBank::atom_is_in_AF(const Handle& h)
{
    auto it = std::find_if(attentionalFocus.begin(), attentionalFocus.end(),
            [h](std::pair<Handle, AttentionValuePtr> p)
            {
               if (p.first == h) return true;
               return false;
            });
    return  (it != attentionalFocus.end());
}

/**
 *  Updates list of top K important atoms based on STI value.
 */
void AttentionBank::updateAttentionalFocus(const Handle& h,
                    const AttentionValuePtr& old_av,
                    const AttentionValuePtr& new_av)
{
    std::lock_guard<std::mutex> lock(AFMutex);
    AttentionValue::sti_t sti = new_av->getSTI();
    auto least = attentionalFocus.begin(); // Atom to be removed from the AF
    bool insertable = false;
    auto it = std::find_if(attentionalFocus.begin(), attentionalFocus.end(),
            [h](std::pair<Handle, AttentionValuePtr> p)
            { return p.first == h;});

    // Update the STI value if atoms was already in AF
    if (it != attentionalFocus.end())
    {
        attentionalFocus.erase(it);
        attentionalFocus.insert(std::make_pair(h, new_av));
        return;
    }

    // Simply insert the new Atom if AF is not full yet.
    else if (attentionalFocus.size() < maxAFSize)
    {
        insertable = true;
    }

    // Remove the least sti valued atom in the AF and replace
    // it with the new atom holding a higher STI value.
    else if (sti > (least->second)->getSTI())
    {
        Handle hrm = least->first;
        AttentionValuePtr hrm_new_av = get_av(hrm);
        // Value recorded when this atom entered into AF
        AttentionValuePtr hrm_old_av = least->second;

        attentionalFocus.erase(least);
        AFCHSigl& afch = RemoveAFSignal();
        afch.emit(hrm, hrm_old_av, hrm_new_av);
        insertable = true;
    }

    // Insert the new atom in to AF and emit the AddAFSignal.
    if (insertable)
    {
        attentionalFocus.insert(std::make_pair(h, new_av));
        AFCHSigl& afch = AddAFSignal();
        afch.emit(h, old_av, new_av);
    }
}

Handle AttentionBank::getRandomAtomNotInAF(void)
{
    UnorderedHandleSet atoms_not_in_af = _importanceIndex.getHandleSet(0);

    {
        std::lock_guard<std::mutex> lock(AFMutex);
        for (const auto& hsp : attentionalFocus) {
            atoms_not_in_af.erase(hsp.first);
        }
    }

    if (atoms_not_in_af.empty())
        return Handle::UNDEFINED;

    auto it = atoms_not_in_af.cbegin();
    std::advance(it, randGen().randint(atoms_not_in_af.size()));
    return *it;
}
<commit_msg>emove one reason why things crash<commit_after>/*
 * opencog/attentionbank/AttentionBank.cc
 *
 * Copyright (C) 2013,2017 Linas Vepstas <linasvepstas@gmail.com>
 * All Rights Reserved
 *
 * Written by Joel Pitt
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <functional>
#include <opencog/util/Config.h>
#include <opencog/util/mt19937ar.h>

#include <opencog/atoms/base/Handle.h>
#include <opencog/atomspace/AtomSpace.h>
#include "AttentionBank.h"
#include "AVUtils.h"

using namespace opencog;

AttentionBank::AttentionBank(AtomSpace* asp)
{
    // XXX FIXME -- should not use config() to get these values;
    // The user of this class should call config(), instead!

    startingFundsSTI = fundsSTI = config().get_double("STARTING_STI_FUNDS", 100000);
    startingFundsLTI = fundsLTI = config().get_double("STARTING_LTI_FUNDS", 100000);
    stiFundsBuffer = config().get_double("STI_FUNDS_BUFFER", 10000);
    ltiFundsBuffer = config().get_double("LTI_FUNDS_BUFFER", 10000);
    targetLTI = config().get_double("TARGET_LTI_FUNDS", 10000);
    targetSTI = config().get_double("TARGET_STI_FUNDS", 10000);
    STIAtomWage = config().get_double("ECAN_STARTING_ATOM_STI_WAGE", 10);
    LTIAtomWage = config().get_double("ECAN_STARTING_ATOM_LTI_WAGE", 10);
    maxAFSize = config().get_int("ECAN_MAX_AF_SIZE", 100);

    _remove_signal = &asp->atomRemovedSignal();
    _remove_connection = _remove_signal->connect(
            std::bind(&AttentionBank::remove_atom_from_bank, this,
                std::placeholders::_1));
}

AttentionBank::~AttentionBank()
{
    // Making the call below leads to a crash in various situations.
    // This can occur when the cogserver is being shutdown, when
    // atomspaces are being deleted, and during Python unit tests.
    // Basically, the teardown sequence is somehow wrong, and I'm
    // too lazy to figure it out. Having a singleton isntance is
    // probably the main flaw; it does not play nice with the rest
    // of the code, which is working with several atomspaces.
    // _remove_signal->disconnect(_remove_connection);
}

void AttentionBank::remove_atom_from_bank(const AtomPtr& atom)
{
    AFMutex.lock();
    auto it = std::find_if(attentionalFocus.begin(), attentionalFocus.end(),
                [atom](std::pair<Handle, AttentionValuePtr> p)
                { return p.first == Handle(atom);});

    if (it != attentionalFocus.end())
        attentionalFocus.erase(it);
    AFMutex.unlock();

   _importanceIndex.removeAtom(Handle(atom));
}

void AttentionBank::set_sti(const Handle& h, AttentionValue::sti_t stiValue)
{
    AttentionValuePtr oldav = get_av(h);
    AttentionValuePtr newav = AttentionValue::createAV(
        stiValue, oldav->getLTI(), oldav->getVLTI());

    _importanceIndex.updateImportance(h, oldav, newav);
    AVChanged(h, oldav, newav);
}

void AttentionBank::set_lti(const Handle& h, AttentionValue::lti_t ltiValue)
{
    AttentionValuePtr old_av = get_av(h);
    AttentionValuePtr new_av = AttentionValue::createAV(
        old_av->getSTI(), ltiValue, old_av->getVLTI());

    AVChanged(h, old_av, new_av);
}

void AttentionBank::change_vlti(const Handle& h, int unit)
{
    AttentionValuePtr old_av = get_av(h);
    AttentionValuePtr new_av = AttentionValue::createAV(
        old_av->getSTI(),
        old_av->getLTI(),
        old_av->getVLTI() + unit);

    AVChanged(h, old_av, new_av);
}

void AttentionBank::change_av(const Handle& h, const AttentionValuePtr& new_av)
{
    AttentionValuePtr old_av = get_av(h);
    _importanceIndex.updateImportance(h, old_av, new_av);
    AVChanged(h, old_av, new_av);
}

void AttentionBank::AVChanged(const Handle& h,
                              const AttentionValuePtr& old_av,
                              const AttentionValuePtr& new_av)
{
    _mtx.lock();
    // First, update the atom's actual AV.
    set_av(h, new_av);

    AttentionValue::sti_t oldSti = old_av->getSTI();
    AttentionValue::sti_t newSti = new_av->getSTI();

    // Add the old attention values to the AttentionBank funds and
    // subtract the new attention values from the AttentionBank funds
    fundsSTI += (oldSti - newSti);
    fundsLTI += (old_av->getLTI() - new_av->getLTI());

    _importanceIndex.update();

    _mtx.unlock();

    logger().fine("AVChanged: fundsSTI = %d, old_av: %d, new_av: %d",
                   fundsSTI, oldSti, newSti);

    // Notify any interested parties that the AV changed.
    _AVChangedSignal.emit(h, old_av, new_av);

    // update AF
    updateAttentionalFocus(h, old_av, new_av);
}

void AttentionBank::stimulate(const Handle& h, double stimulus)
{
    // XXX This is not protected or made atomic in any way ...
    // If two different threads stimulate the same atom at the same
    // time, then the calculations will be bad. Does it matter?
    AttentionValuePtr oldav(get_av(h));
    AttentionValue::sti_t sti   = oldav->getSTI();
    AttentionValue::lti_t lti   = oldav->getLTI();
    AttentionValue::vlti_t vlti = oldav->getVLTI();

    AttentionValue::sti_t stiWage = calculateSTIWage() * stimulus;
    AttentionValue::lti_t ltiWage = calculateLTIWage() * stimulus;
    AttentionValuePtr newav = AttentionValue::createAV(
           sti + stiWage, lti + ltiWage, vlti);
    _importanceIndex.updateImportance(h, oldav, newav);
    AVChanged(h, oldav, newav);

#ifdef ECAN_EXPERIMENT
    if(stimulusRec.find(h) != stimulusRec.end()){
        stimulusRec[h] += stimulus;
    } else{
        stimulusRec[h] = stimulus;
    }
#endif
}

AttentionValue::sti_t AttentionBank::calculateSTIWage()
{
    AttentionValue::sti_t funds = getSTIFunds();
    AttentionValue::sti_t diff  = funds - targetSTI;
    AttentionValue::sti_t ndiff = diff / stiFundsBuffer;
    ndiff = std::min(ndiff, 1.0);
    ndiff = std::max(ndiff, -1.0);

    return STIAtomWage + (STIAtomWage * ndiff);
}

AttentionValue::lti_t AttentionBank::calculateLTIWage()
{
    AttentionValue::lti_t funds = getLTIFunds();
    AttentionValue::lti_t diff  = funds - targetLTI;
    AttentionValue::lti_t ndiff = diff / ltiFundsBuffer;
    ndiff = std::min(ndiff, 1.0);
    ndiff = std::max(ndiff, -1.0);

    return LTIAtomWage + (LTIAtomWage * ndiff);
}

double AttentionBank::getNormalisedSTI(AttentionValuePtr av,
                                   bool average, bool clip) const
{
    double val;
    // get normalizer (maxSTI - attention boundary)
    AttentionValue::sti_t s = av->getSTI();
    if (s > get_af_max_sti()) {
        int normaliser = (int) getMaxSTI(average) - get_af_max_sti();
        if (normaliser == 0) return 0.0;
        val = (s - get_af_max_sti()) / (double) normaliser;
    } else {
        int normaliser = -((int) getMinSTI(average) + get_af_max_sti());
        if (normaliser == 0) return 0.0;
        val = (s + get_af_max_sti()) / (double) normaliser;

    }

    if (clip) return std::max(-1.0, std::min(val, 1.0));
    return val;
}

double AttentionBank::getNormalisedSTI(AttentionValuePtr av) const
{
    AttentionValue::sti_t s = av->getSTI();
    auto normaliser =
            s > get_af_max_sti() ? getMaxSTI() : getMinSTI();

    return (s / normaliser);
}

double AttentionBank::getNormalisedZeroToOneSTI(AttentionValuePtr av,
                                    bool average, bool clip) const
{
    AttentionValue::sti_t s = av->getSTI();
    int normaliser = getMaxSTI(average) - getMinSTI(average);

    if (normaliser == 0) return 0.0;

    double val = (s - getMinSTI(average)) / (double) normaliser;
    if (clip) return std::max(0.0, std::min(val, 1.0));
    return val;
}

/** Unique singleton instance (for now) */
// This implementation is pretty hokey, and is a stop-gap until some
// sort of more elegant way of managing the attentionbank is found.
// One of the issues is that access via this function can be CPU-wasteful.
AttentionBank& opencog::attentionbank(AtomSpace* asp)
{
    static AttentionBank* _instance = nullptr;
    static AtomSpace* _as = nullptr;

    // Protect setting and getting against thread races.
    // This is probably not needed.
    static std::map<AtomSpace*, AttentionBank*> banksy;
    static std::mutex art;
    std::unique_lock<std::mutex> graffiti(art);

    if (_as != asp and _instance) {
        delete _instance;
        _instance = nullptr;
        _as = nullptr;
    }
    if (asp and nullptr == _instance) {
        _instance = new AttentionBank(asp);
        _as = asp;
    }
    return *_instance;
}

bool AttentionBank::atom_is_in_AF(const Handle& h)
{
    auto it = std::find_if(attentionalFocus.begin(), attentionalFocus.end(),
            [h](std::pair<Handle, AttentionValuePtr> p)
            {
               if (p.first == h) return true;
               return false;
            });
    return  (it != attentionalFocus.end());
}

/**
 *  Updates list of top K important atoms based on STI value.
 */
void AttentionBank::updateAttentionalFocus(const Handle& h,
                    const AttentionValuePtr& old_av,
                    const AttentionValuePtr& new_av)
{
    std::lock_guard<std::mutex> lock(AFMutex);
    AttentionValue::sti_t sti = new_av->getSTI();
    auto least = attentionalFocus.begin(); // Atom to be removed from the AF
    bool insertable = false;
    auto it = std::find_if(attentionalFocus.begin(), attentionalFocus.end(),
            [h](std::pair<Handle, AttentionValuePtr> p)
            { return p.first == h;});

    // Update the STI value if atoms was already in AF
    if (it != attentionalFocus.end())
    {
        attentionalFocus.erase(it);
        attentionalFocus.insert(std::make_pair(h, new_av));
        return;
    }

    // Simply insert the new Atom if AF is not full yet.
    else if (attentionalFocus.size() < maxAFSize)
    {
        insertable = true;
    }

    // Remove the least sti valued atom in the AF and replace
    // it with the new atom holding a higher STI value.
    else if (sti > (least->second)->getSTI())
    {
        Handle hrm = least->first;
        AttentionValuePtr hrm_new_av = get_av(hrm);
        // Value recorded when this atom entered into AF
        AttentionValuePtr hrm_old_av = least->second;

        attentionalFocus.erase(least);
        AFCHSigl& afch = RemoveAFSignal();
        afch.emit(hrm, hrm_old_av, hrm_new_av);
        insertable = true;
    }

    // Insert the new atom in to AF and emit the AddAFSignal.
    if (insertable)
    {
        attentionalFocus.insert(std::make_pair(h, new_av));
        AFCHSigl& afch = AddAFSignal();
        afch.emit(h, old_av, new_av);
    }
}

Handle AttentionBank::getRandomAtomNotInAF(void)
{
    UnorderedHandleSet atoms_not_in_af = _importanceIndex.getHandleSet(0);

    {
        std::lock_guard<std::mutex> lock(AFMutex);
        for (const auto& hsp : attentionalFocus) {
            atoms_not_in_af.erase(hsp.first);
        }
    }

    if (atoms_not_in_af.empty())
        return Handle::UNDEFINED;

    auto it = atoms_not_in_af.cbegin();
    std::advance(it, randGen().randint(atoms_not_in_af.size()));
    return *it;
}
<|endoftext|>


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

#include "TbusChannelControl.h"
#include "omnetpp.h"
#include "IPAddressResolver.h"
#include "IInterfaceTable.h"
#include "InterfaceEntry.h"
#include "IPv4InterfaceData.h"
#include "IPDatagram_m.h"
#include "TbusMobilePHY.h"
#include "VSimRTIAppPacket_m.h"
#include "TbusWorldView.h"
#include <map>

#ifdef TBUS_DEBUG
Register_GlobalConfigOption(CFGID_TBUS_DATABASE_FILE_DEBUG, "tbus-database", CFG_FILENAME, "", "Filename of TBUS database")
#endif /* TBUS_DEBUG */

Define_Module(TbusChannelControl);

/**
 * Empty constructor
 */
TbusChannelControl::TbusChannelControl() {}

/**
 * Empty destructor
 */
TbusChannelControl::~TbusChannelControl() {}

/**
 * Simulation initialization.
 * Stores gateIds for in and output
 */
void TbusChannelControl::initialize() {
	ChannelControl::initialize();

	routerInGate  = findGate("routerInGate");
	routerOutGate = findGate("routerOutGate");

#ifdef TBUS_DEBUG
	sqlite3* database;
	sqlite3_open_v2(ev.getConfig()->getAsFilename(CFGID_TBUS_DATABASE_FILE_DEBUG).c_str(), &database, SQLITE_OPEN_READONLY, NULL);

	sqlite3_stmt* statement;
	sqlite3_prepare_v2(database, "select timestamp from download_datarate order by timestamp asc;", -1, &statement, NULL);

	uint64_t time;
	uint64_t now = simTime().inUnit(SIMTIME_NS);

	while (sqlite3_step(statement) == SQLITE_ROW) {
		time = sqlite3_column_int64(statement, 0);

		if (time >= now) {
			scheduleAt(SimTime(time, SIMTIME_NS), new cMessage(NULL, 0));
		}
	}

	sqlite3_finalize(statement);

	sqlite3_close(database);
#endif /* TBUS_DEBUG */
}

/**
 * Register hostRef's first NIC IP from a NIC that is no loopback.
 * This is done for an IP to Host mapping in O(1).
 * @param hostRef Host to register
 */
void TbusChannelControl::registerIP(ChannelControl::HostRef hostRef) {
	IInterfaceTable* interfaceTable = IPAddressResolver().interfaceTableOf(hostRef->host);
	InterfaceEntry* ie = NULL;

	for (int i = 0; i < interfaceTable->getNumInterfaces(); i++) {
		ie = interfaceTable->getInterface(i);

		if (!ie->isLoopback()) {
			break;
		}
	}

	ASSERT2(ie != NULL, "No non-loopback interface found!");

	bool result = hostMap.insert(ip2host(ie->ipv4Data()->getIPAddress(), hostRef)).second;
	if (!result) {
		EV << "ERROR: Could not register host " << hostRef->host->getFullName() << " to TbusChannelControl, this host will not receive any data" << endl;
	} else {
		EV << "Registered " << hostRef->host->getFullName() << " with address " << ie->ipv4Data()->getIPAddress() << endl;
	}
}

/**
 * Handles messages send to TbusChannelControl via ordinary connections.
 * @param msg Message to send
 */
void TbusChannelControl::handleMessage(cMessage* msg) {
	// TODO: Handle router messages
#ifdef TBUS_DEBUG
	if (msg->isSelfMessage()) {
		TbusWorldView::getInstance()->performUpdateRound();
	}
#endif /* TBUS_DEBUG */
}

/**
 * Sends duplicates of the message to the corresponding receiver(s).
 * Only supports Broadcast and Unicast right now.
 * @param msg Message to send
 * @param h Host reference from sender
 */
void TbusChannelControl::sendToChannel(cMessage* msg, HostRef h) {
	Enter_Method_Silent();
	take(msg);

	// We only support VSimRTIAppPacket from now on
	IPDatagram* packet = check_and_cast<IPDatagram*>(msg);
//	VSimRTIAppPacket* packet = check_and_cast<VSimRTIAppPacket*>(msg);

	if (packet) {
		IPAddress ip = packet->getDestAddress();

		if (ip == IPAddress::ALLONES_ADDRESS) {
			// Broadcast
			EV << "Broadcasting message " << msg->getFullName() << endl;

			for (HostList::iterator it = hosts.begin(); it != hosts.end(); ++it) {
				if (strcmp(h->host->getFullName(), it->host->getFullName()) != 0) {
					sendDirect(msg->dup(), it->radioInGate);
				}
			}
		} else {
			// We suppose unicast here
			ip2hostMap::iterator dest = hostMap.find(ip);
			if (dest == hostMap.end()) {
				EV << "ERROR: Cannot find host with IP address " << ip << ", discarding packet" << endl;
			} else {
				EV << "Unicasting message " << msg->getFullName() << " to " << ip << endl;
				sendDirect(msg->dup(), dest->second->radioInGate);
			}
		}
	} else {
		throw cRuntimeError("Tbus Channel Control received non-IPDatagram packet!");
	}

	drop(msg);
	delete msg;
}
<commit_msg>Fix memory leak, messages are deleted after use<commit_after>//
// (c) 2014 Raphael Bialon <Raphael.Bialon@hhu.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
//

#include "TbusChannelControl.h"
#include "omnetpp.h"
#include "IPAddressResolver.h"
#include "IInterfaceTable.h"
#include "InterfaceEntry.h"
#include "IPv4InterfaceData.h"
#include "IPDatagram_m.h"
#include "TbusMobilePHY.h"
#include "VSimRTIAppPacket_m.h"
#include "TbusWorldView.h"
#include <map>

#ifdef TBUS_DEBUG
Register_GlobalConfigOption(CFGID_TBUS_DATABASE_FILE_DEBUG, "tbus-database", CFG_FILENAME, "", "Filename of TBUS database")
#endif /* TBUS_DEBUG */

Define_Module(TbusChannelControl);

/**
 * Empty constructor
 */
TbusChannelControl::TbusChannelControl() {}

/**
 * Empty destructor
 */
TbusChannelControl::~TbusChannelControl() {}

/**
 * Simulation initialization.
 * Stores gateIds for in and output
 */
void TbusChannelControl::initialize() {
	ChannelControl::initialize();

	routerInGate  = findGate("routerInGate");
	routerOutGate = findGate("routerOutGate");

#ifdef TBUS_DEBUG
	sqlite3* database;
	sqlite3_open_v2(ev.getConfig()->getAsFilename(CFGID_TBUS_DATABASE_FILE_DEBUG).c_str(), &database, SQLITE_OPEN_READONLY, NULL);

	sqlite3_stmt* statement;
	sqlite3_prepare_v2(database, "select timestamp from download_datarate order by timestamp asc;", -1, &statement, NULL);

	uint64_t time;
	uint64_t now = simTime().inUnit(SIMTIME_NS);

	while (sqlite3_step(statement) == SQLITE_ROW) {
		time = sqlite3_column_int64(statement, 0);

		if (time >= now) {
			scheduleAt(SimTime(time, SIMTIME_NS), new cMessage(NULL, 0));
		}
	}

	sqlite3_finalize(statement);

	sqlite3_close(database);
#endif /* TBUS_DEBUG */
}

/**
 * Register hostRef's first NIC IP from a NIC that is no loopback.
 * This is done for an IP to Host mapping in O(1).
 * @param hostRef Host to register
 */
void TbusChannelControl::registerIP(ChannelControl::HostRef hostRef) {
	IInterfaceTable* interfaceTable = IPAddressResolver().interfaceTableOf(hostRef->host);
	InterfaceEntry* ie = NULL;

	for (int i = 0; i < interfaceTable->getNumInterfaces(); i++) {
		ie = interfaceTable->getInterface(i);

		if (!ie->isLoopback()) {
			break;
		}
	}

	ASSERT2(ie != NULL, "No non-loopback interface found!");

	bool result = hostMap.insert(ip2host(ie->ipv4Data()->getIPAddress(), hostRef)).second;
	if (!result) {
		EV << "ERROR: Could not register host " << hostRef->host->getFullName() << " to TbusChannelControl, this host will not receive any data" << endl;
	} else {
		EV << "Registered " << hostRef->host->getFullName() << " with address " << ie->ipv4Data()->getIPAddress() << endl;
	}
}

/**
 * Handles messages send to TbusChannelControl via ordinary connections.
 * @param msg Message to send
 */
void TbusChannelControl::handleMessage(cMessage* msg) {
	// TODO: Handle router messages
#ifdef TBUS_DEBUG
	if (msg->isSelfMessage()) {
		TbusWorldView::getInstance()->performUpdateRound();
	}
	delete msg;
#endif /* TBUS_DEBUG */
}

/**
 * Sends duplicates of the message to the corresponding receiver(s).
 * Only supports Broadcast and Unicast right now.
 * @param msg Message to send
 * @param h Host reference from sender
 */
void TbusChannelControl::sendToChannel(cMessage* msg, HostRef h) {
	Enter_Method_Silent();
	take(msg);

	// We only support VSimRTIAppPacket from now on
	IPDatagram* packet = check_and_cast<IPDatagram*>(msg);
//	VSimRTIAppPacket* packet = check_and_cast<VSimRTIAppPacket*>(msg);

	if (packet) {
		IPAddress ip = packet->getDestAddress();

		if (ip == IPAddress::ALLONES_ADDRESS) {
			// Broadcast
			EV << "Broadcasting message " << msg->getFullName() << endl;

			for (HostList::iterator it = hosts.begin(); it != hosts.end(); ++it) {
				if (strcmp(h->host->getFullName(), it->host->getFullName()) != 0) {
					sendDirect(msg->dup(), it->radioInGate);
				}
			}
		} else {
			// We suppose unicast here
			ip2hostMap::iterator dest = hostMap.find(ip);
			if (dest == hostMap.end()) {
				EV << "ERROR: Cannot find host with IP address " << ip << ", discarding packet" << endl;
			} else {
				EV << "Unicasting message " << msg->getFullName() << " to " << ip << endl;
				sendDirect(msg->dup(), dest->second->radioInGate);
			}
		}
	} else {
		throw cRuntimeError("Tbus Channel Control received non-IPDatagram packet!");
	}

	drop(msg);
	delete msg;
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2014-2018 AscEmu Team <http://www.ascemu.org>
This file is released under the MIT license. See README-MIT for more information.
*/

#include "Data/WoWObject.h"
#include "Data/WoWItem.h"
#include "Server/WUtil.h"
#include "Item.h"
#include "Storage/MySQLDataStore.hpp"
#include "Map/MapMgrDefines.hpp"
#include "Container.h"

void Item::init(uint32_t high, uint32_t low)
{
    memset(m_uint32Values, 0, sizeof(WoWItem));
    setObjectType(TYPEID_ITEM);
    setScale(1.f);
    setGuid(low, high);

    m_itemProperties = nullptr;
    m_owner = nullptr;
    loot = nullptr;
    locked = false;
    m_isDirty = true;
    random_prop = 0;
    random_suffix = 0;
    wrapped_item_id = 0;
    m_mapId = MAPID_NOT_IN_WORLD;
    m_zoneId = 0;
    m_objectUpdated = false;
    m_mapMgr = nullptr;
    m_factionTemplate = nullptr;
    m_factionEntry = nullptr;
    m_instanceId = INSTANCEID_NOT_IN_WORLD;
    m_inQueue = false;
    m_loadedFromDB = false;
}

void Item::create(uint32_t itemId, Player* owner)
{
    setEntry(itemId);

    if (owner != nullptr)
    {
        setOwner(owner);
        setContainerGuid(owner->getGuid());
    }

    setStackCount(1);

    m_itemProperties = sMySQLStore.getItemProperties(itemId);

    ARCEMU_ASSERT(m_itemProperties != nullptr);

    for (auto i = 0; i < 4; ++i)
        setSpellCharges(i, m_itemProperties->Spells[i].Charges);

    setDurability(m_itemProperties->MaxDurability);
    setMaxDurability(m_itemProperties->MaxDurability);

    m_owner = owner;
    if (m_itemProperties->LockId > 1)
        locked = true;
    else
        locked = false;
}

//////////////////////////////////////////////////////////////////////////////////////////
// WoWData

uint64_t Item::getOwnerGuid() const { return itemData()->owner_guid.guid; }
uint32_t Item::getOwnerGuidLow() const { return itemData()->owner_guid.parts.low; }
uint32_t Item::getOwnerGuidHigh() const { return itemData()->owner_guid.parts.high; }
void Item::setOwnerGuid(uint64_t guid) { write(itemData()->owner_guid.guid, guid); }

void Item::setContainerGuid(uint64_t guid) { write(itemData()->container_guid.guid, guid); }

uint64_t Item::getCreatorGuid() const { return itemData()->creator_guid.guid; }
void Item::setCreatorGuid(uint64_t guid) { write(itemData()->creator_guid.guid, guid); }

uint64_t Item::getGiftCreatorGuid() const { return itemData()->gift_creator_guid.guid; }
void Item::setGiftCreatorGuid(uint64_t guid) { write(itemData()->gift_creator_guid.guid, guid); }

uint32_t Item::getStackCount() const { return itemData()->stack_count; }
void Item::setStackCount(uint32_t count) { write(itemData()->stack_count, count); }
void Item::modStackCount(int32_t mod)
{
    int32_t newStackCount = getStackCount();
    newStackCount += mod;

    if (newStackCount < 0)
        newStackCount = 0;

    setStackCount(newStackCount);
}

void Item::setTextId(const uint32 textId)
{
	write(itemData()->item_text_id, textId);
}

uint32_t Item::getDuration() const { return itemData()->duration; }
void Item::setDuration(uint32_t seconds) { write(itemData()->duration, seconds); }

int32_t Item::getSpellCharges(uint8_t index) const { return itemData()->spell_charges[index]; }
void Item::setSpellCharges(uint8_t index, int32_t count)
{
    ARCEMU_ASSERT(index < WOWITEM_SPELL_CHARGES_COUNT)

    write(itemData()->spell_charges[index], count);
}

void Item::modSpellCharges(uint8_t index, int32_t mod)
{
    ARCEMU_ASSERT(index < WOWITEM_SPELL_CHARGES_COUNT)

    int32_t newSpellCharges = getSpellCharges(index);
    newSpellCharges += mod;

    if (newSpellCharges < 0)
        newSpellCharges = 0;

    setSpellCharges(index, newSpellCharges);
}

uint32_t Item::getFlags() const { return itemData()->flags; }
void Item::setFlags(uint32_t flags) { write(itemData()->flags, flags); }
void Item::addFlags(uint32_t flags) { setFlags(getFlags() | flags); }
void Item::removeFlags(uint32_t flags) { setFlags(getFlags() & ~flags); }
bool Item::hasFlags(uint32_t flags) const { return (getFlags() & flags) != 0; }

#if VERSION_STRING >= WotLK
uint32_t Item::getEnchantmentId(uint8_t index) const { return itemData()->enchantment[index].id; }
void Item::setEnchantmentId(uint8_t index, uint32_t id) { write(itemData()->enchantment[index].id, id); }

uint32_t Item::getEnchantmentDuration(uint8_t index) const { return itemData()->enchantment[index].duration; }
void Item::setEnchantmentDuration(uint8_t index, uint32_t duration) { write(itemData()->enchantment[index].duration, duration); }

uint32_t Item::getEnchantmentCharges(uint8_t index) const { return itemData()->enchantment[index].charges; }
void Item::setEnchantmentCharges(uint8_t index, uint32_t charges) { write(itemData()->enchantment[index].charges, charges); }
#endif

uint32_t Item::getPropertySeed() const { return itemData()->property_seed; }
void Item::setPropertySeed(uint32_t seed)
{
    write(itemData()->property_seed, seed);
    random_suffix = seed;
}

uint32_t Item::getRandomPropertiesId() const { return itemData()->random_properties_id; }
void Item:: setRandomPropertiesId(uint32_t id)
{
    write(itemData()->random_properties_id, id);
    random_prop = id;
}

uint32_t Item::getDurability() const { return itemData()->durability; }
void Item::setDurability(uint32_t durability) { write(itemData()->durability, durability); }

uint32_t Item::getMaxDurability() const { return itemData()->max_durability; }
void Item::setMaxDurability(uint32_t maxDurability) { write(itemData()->max_durability, maxDurability); }

#if VERSION_STRING >= WotLK
uint32_t Item::getCreatePlayedTime() const { return itemData()->create_played_time; }
void Item::setCreatePlayedTime(uint32_t time) { write(itemData()->create_played_time, time); }
#endif

//////////////////////////////////////////////////////////////////////////////////////////
// Misc

Player* Item::getOwner() const { return m_owner; }
void Item::setOwner(Player* owner)
{
    write(itemData()->owner_guid.guid, owner ? owner->getGuid() : 0UL);
    m_owner = owner;
}

void Item::setContainer(Container* container) { setContainerGuid(container ? container->getGuid() : 0UL); }

ItemProperties const* Item::getItemProperties() const { return m_itemProperties; }
void Item::setItemProperties(ItemProperties const* itemProperties) { m_itemProperties = itemProperties; }
<commit_msg>Fix build on non-BC platforms<commit_after>/*
Copyright (c) 2014-2018 AscEmu Team <http://www.ascemu.org>
This file is released under the MIT license. See README-MIT for more information.
*/

#include "Data/WoWObject.h"
#include "Data/WoWItem.h"
#include "Server/WUtil.h"
#include "Item.h"
#include "Storage/MySQLDataStore.hpp"
#include "Map/MapMgrDefines.hpp"
#include "Container.h"

void Item::init(uint32_t high, uint32_t low)
{
    memset(m_uint32Values, 0, sizeof(WoWItem));
    setObjectType(TYPEID_ITEM);
    setScale(1.f);
    setGuid(low, high);

    m_itemProperties = nullptr;
    m_owner = nullptr;
    loot = nullptr;
    locked = false;
    m_isDirty = true;
    random_prop = 0;
    random_suffix = 0;
    wrapped_item_id = 0;
    m_mapId = MAPID_NOT_IN_WORLD;
    m_zoneId = 0;
    m_objectUpdated = false;
    m_mapMgr = nullptr;
    m_factionTemplate = nullptr;
    m_factionEntry = nullptr;
    m_instanceId = INSTANCEID_NOT_IN_WORLD;
    m_inQueue = false;
    m_loadedFromDB = false;
}

void Item::create(uint32_t itemId, Player* owner)
{
    setEntry(itemId);

    if (owner != nullptr)
    {
        setOwner(owner);
        setContainerGuid(owner->getGuid());
    }

    setStackCount(1);

    m_itemProperties = sMySQLStore.getItemProperties(itemId);

    ARCEMU_ASSERT(m_itemProperties != nullptr);

    for (auto i = 0; i < 4; ++i)
        setSpellCharges(i, m_itemProperties->Spells[i].Charges);

    setDurability(m_itemProperties->MaxDurability);
    setMaxDurability(m_itemProperties->MaxDurability);

    m_owner = owner;
    if (m_itemProperties->LockId > 1)
        locked = true;
    else
        locked = false;
}

//////////////////////////////////////////////////////////////////////////////////////////
// WoWData

uint64_t Item::getOwnerGuid() const { return itemData()->owner_guid.guid; }
uint32_t Item::getOwnerGuidLow() const { return itemData()->owner_guid.parts.low; }
uint32_t Item::getOwnerGuidHigh() const { return itemData()->owner_guid.parts.high; }
void Item::setOwnerGuid(uint64_t guid) { write(itemData()->owner_guid.guid, guid); }

void Item::setContainerGuid(uint64_t guid) { write(itemData()->container_guid.guid, guid); }

uint64_t Item::getCreatorGuid() const { return itemData()->creator_guid.guid; }
void Item::setCreatorGuid(uint64_t guid) { write(itemData()->creator_guid.guid, guid); }

uint64_t Item::getGiftCreatorGuid() const { return itemData()->gift_creator_guid.guid; }
void Item::setGiftCreatorGuid(uint64_t guid) { write(itemData()->gift_creator_guid.guid, guid); }

uint32_t Item::getStackCount() const { return itemData()->stack_count; }
void Item::setStackCount(uint32_t count) { write(itemData()->stack_count, count); }
void Item::modStackCount(int32_t mod)
{
    int32_t newStackCount = getStackCount();
    newStackCount += mod;

    if (newStackCount < 0)
        newStackCount = 0;

    setStackCount(newStackCount);
}

#ifdef AE_TBC
void Item::setTextId(const uint32 textId)
{
	write(itemData()->item_text_id, textId);
}
#endif

uint32_t Item::getDuration() const { return itemData()->duration; }
void Item::setDuration(uint32_t seconds) { write(itemData()->duration, seconds); }

int32_t Item::getSpellCharges(uint8_t index) const { return itemData()->spell_charges[index]; }
void Item::setSpellCharges(uint8_t index, int32_t count)
{
    ARCEMU_ASSERT(index < WOWITEM_SPELL_CHARGES_COUNT)

    write(itemData()->spell_charges[index], count);
}

void Item::modSpellCharges(uint8_t index, int32_t mod)
{
    ARCEMU_ASSERT(index < WOWITEM_SPELL_CHARGES_COUNT)

    int32_t newSpellCharges = getSpellCharges(index);
    newSpellCharges += mod;

    if (newSpellCharges < 0)
        newSpellCharges = 0;

    setSpellCharges(index, newSpellCharges);
}

uint32_t Item::getFlags() const { return itemData()->flags; }
void Item::setFlags(uint32_t flags) { write(itemData()->flags, flags); }
void Item::addFlags(uint32_t flags) { setFlags(getFlags() | flags); }
void Item::removeFlags(uint32_t flags) { setFlags(getFlags() & ~flags); }
bool Item::hasFlags(uint32_t flags) const { return (getFlags() & flags) != 0; }

#if VERSION_STRING >= WotLK
uint32_t Item::getEnchantmentId(uint8_t index) const { return itemData()->enchantment[index].id; }
void Item::setEnchantmentId(uint8_t index, uint32_t id) { write(itemData()->enchantment[index].id, id); }

uint32_t Item::getEnchantmentDuration(uint8_t index) const { return itemData()->enchantment[index].duration; }
void Item::setEnchantmentDuration(uint8_t index, uint32_t duration) { write(itemData()->enchantment[index].duration, duration); }

uint32_t Item::getEnchantmentCharges(uint8_t index) const { return itemData()->enchantment[index].charges; }
void Item::setEnchantmentCharges(uint8_t index, uint32_t charges) { write(itemData()->enchantment[index].charges, charges); }
#endif

uint32_t Item::getPropertySeed() const { return itemData()->property_seed; }
void Item::setPropertySeed(uint32_t seed)
{
    write(itemData()->property_seed, seed);
    random_suffix = seed;
}

uint32_t Item::getRandomPropertiesId() const { return itemData()->random_properties_id; }
void Item:: setRandomPropertiesId(uint32_t id)
{
    write(itemData()->random_properties_id, id);
    random_prop = id;
}

uint32_t Item::getDurability() const { return itemData()->durability; }
void Item::setDurability(uint32_t durability) { write(itemData()->durability, durability); }

uint32_t Item::getMaxDurability() const { return itemData()->max_durability; }
void Item::setMaxDurability(uint32_t maxDurability) { write(itemData()->max_durability, maxDurability); }

#if VERSION_STRING >= WotLK
uint32_t Item::getCreatePlayedTime() const { return itemData()->create_played_time; }
void Item::setCreatePlayedTime(uint32_t time) { write(itemData()->create_played_time, time); }
#endif

//////////////////////////////////////////////////////////////////////////////////////////
// Misc

Player* Item::getOwner() const { return m_owner; }
void Item::setOwner(Player* owner)
{
    write(itemData()->owner_guid.guid, owner ? owner->getGuid() : 0UL);
    m_owner = owner;
}

void Item::setContainer(Container* container) { setContainerGuid(container ? container->getGuid() : 0UL); }

ItemProperties const* Item::getItemProperties() const { return m_itemProperties; }
void Item::setItemProperties(ItemProperties const* itemProperties) { m_itemProperties = itemProperties; }
<|endoftext|>


<commit_before>#include "window.h"

#include <stdexcept>
#include <cstring>

namespace ncurses
{
    Window::Window ( WINDOW* window ):
        window ( window ),
        fg_color ( DEFAULT ),
        bg_color ( DEFAULT )
    {
        getmaxyx ( window, height, width );
        getbegyx ( window, y, x );
    }

    Window::Window ( int x, int y, int width, int height ):
        height ( height ),
        width ( width ),
        x ( x ), y ( y ),
        fg_color ( DEFAULT ),
        bg_color ( DEFAULT )
    {
        window = newwin ( height, width, y, x );
    }

    Window::~Window ()
    {
        werase ( window );
        delwin ( window );
    }

    void Window::move ( int x, int y )
    {
        wmove ( window, y, x );
    }

    void Window::draw_string ( std::string s )
    {
        int x, y;
        getyx ( window, y, x );
        int free_space = get_width () - x + 1;
        if ( free_space > static_cast < int > ( s.size () ) && y < get_height () )
            waddstr ( window, s.c_str () );
        else if ( free_space > 0 && y < get_height () )
        {
            // Display at least as much as possible
            s.resize ( free_space );
            s.shrink_to_fit ();
            waddstr ( window, s.c_str () );
        }
    }

    void Window::draw_character ( char c )
    {
        waddch ( window, c );
    }

    void Window::set_color ( int fg, int bg )
    {
        set_fg_color ( fg );
        set_bg_color ( bg );
    }

    void Window::set_fg_color ( int fg_color )
    {
        if ( fg_color > -2 && fg_color < 8 )
        {
            this->fg_color = fg_color;
            wattron ( window, COLOR_PAIR ( get_color_id () ) );
        }
        else
            throw std::runtime_error ( "Error in ncurses::Window::set_fg_color : Bad Argument! fg_color out of range [ -1 | 7 ]" );
    }

    void Window::set_bg_color ( int bg_color )
    {
        if ( bg_color > -2 && bg_color < 8 )
        {
            this->bg_color = bg_color;
            wattron ( window, COLOR_PAIR ( get_color_id () ) );
        }
        else
            throw std::runtime_error ( "Error in ncurses::Window::set_bg_color : Bad Argument! bg_color out of range [ -1 | 7 ]" );
    }

    int Window::get_color_id () const
    {
        // Using this formula, we can compute the COLOR_PAIR id corresponding to bg- and fg-color
        return 2 + fg_color + 8 * ( 1 + bg_color );
    }

    void Window::set_color ( int color_id )
    {
       if ( color_id > 74 || color_id < 1 )
           throw std::runtime_error ( "Error in ncurses::Window::set_color : Bad argument! color_id > 73" );

       fg_color = ( color_id - 2 ) % 8;
       bg_color = ( ( ( color_id - 2 ) - fg_color ) ) / 8 - 1;
       wattron ( window, COLOR_PAIR ( color_id ) );
    }

    void Window::draw_border ()
    {
        box ( window, 0, 0 );
    }

    void Window::refresh ()
    {
        wrefresh ( window );
    }
}
<commit_msg>Keep window from skipping to the next line after adding partial string<commit_after>#include "window.h"

#include <stdexcept>
#include <cstring>

namespace ncurses
{
    Window::Window ( WINDOW* window ):
        window ( window ),
        fg_color ( DEFAULT ),
        bg_color ( DEFAULT )
    {
        getmaxyx ( window, height, width );
        getbegyx ( window, y, x );
    }

    Window::Window ( int x, int y, int width, int height ):
        height ( height ),
        width ( width ),
        x ( x ), y ( y ),
        fg_color ( DEFAULT ),
        bg_color ( DEFAULT )
    {
        window = newwin ( height, width, y, x );
    }

    Window::~Window ()
    {
        werase ( window );
        delwin ( window );
    }

    void Window::move ( int x, int y )
    {
        wmove ( window, y, x );
    }

    void Window::draw_string ( std::string s )
    {
        int x, y;
        getyx ( window, y, x );
        int free_space = get_width () - x + 1;
        if ( free_space > static_cast < int > ( s.size () ) && y < get_height () )
            waddstr ( window, s.c_str () );
        else if ( free_space > 1 && y < get_height () )
        {
            // This is neccessary so we don't have the last character on the
            // next line
            free_space -= 1;
            // Display at least as much as possible
            s.resize ( free_space );
            s.shrink_to_fit ();
            waddstr ( window, s.c_str () );
            // Put the cursor back up on the line
            move ( get_width () - 1, y );
        }
    }

    void Window::draw_character ( char c )
    {
        waddch ( window, c );
    }

    void Window::set_color ( int fg, int bg )
    {
        set_fg_color ( fg );
        set_bg_color ( bg );
    }

    void Window::set_fg_color ( int fg_color )
    {
        if ( fg_color > -2 && fg_color < 8 )
        {
            this->fg_color = fg_color;
            wattron ( window, COLOR_PAIR ( get_color_id () ) );
        }
        else
            throw std::runtime_error ( "Error in ncurses::Window::set_fg_color : Bad Argument! fg_color out of range [ -1 | 7 ]" );
    }

    void Window::set_bg_color ( int bg_color )
    {
        if ( bg_color > -2 && bg_color < 8 )
        {
            this->bg_color = bg_color;
            wattron ( window, COLOR_PAIR ( get_color_id () ) );
        }
        else
            throw std::runtime_error ( "Error in ncurses::Window::set_bg_color : Bad Argument! bg_color out of range [ -1 | 7 ]" );
    }

    int Window::get_color_id () const
    {
        // Using this formula, we can compute the COLOR_PAIR id corresponding to bg- and fg-color
        return 2 + fg_color + 8 * ( 1 + bg_color );
    }

    void Window::set_color ( int color_id )
    {
       if ( color_id > 74 || color_id < 1 )
           throw std::runtime_error ( "Error in ncurses::Window::set_color : Bad argument! color_id > 73" );

       fg_color = ( color_id - 2 ) % 8;
       bg_color = ( ( ( color_id - 2 ) - fg_color ) ) / 8 - 1;
       wattron ( window, COLOR_PAIR ( color_id ) );
    }

    void Window::draw_border ()
    {
        box ( window, 0, 0 );
    }

    void Window::refresh ()
    {
        wrefresh ( window );
    }
}
<|endoftext|>


<commit_before>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2015 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/base/byte_stream.h"

#include "xenia/base/assert.h"

namespace xe {

ByteStream::ByteStream(uint8_t* data, size_t data_length, size_t offset)
    : data_(data), data_length_(data_length), offset_(offset) {}

ByteStream::~ByteStream() = default;

void ByteStream::Advance(size_t num_bytes) { offset_ += num_bytes; }

void ByteStream::Read(uint8_t* buf, size_t len) {
  assert_true(offset_ < data_length_);

  std::memcpy(buf, data_ + offset_, len);
  Advance(len);
}

void ByteStream::Write(const uint8_t* buf, size_t len) {
  assert_true(offset_ < data_length_);

  std::memcpy(data_ + offset_, buf, len);
  Advance(len);
}

}  // namespace xe<commit_msg>byte_stream: add missing include for memcpy<commit_after>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2015 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/base/byte_stream.h"

#include <cstring>

#include "xenia/base/assert.h"

namespace xe {

ByteStream::ByteStream(uint8_t* data, size_t data_length, size_t offset)
    : data_(data), data_length_(data_length), offset_(offset) {}

ByteStream::~ByteStream() = default;

void ByteStream::Advance(size_t num_bytes) { offset_ += num_bytes; }

void ByteStream::Read(uint8_t* buf, size_t len) {
  assert_true(offset_ < data_length_);

  std::memcpy(buf, data_ + offset_, len);
  Advance(len);
}

void ByteStream::Write(const uint8_t* buf, size_t len) {
  assert_true(offset_ < data_length_);

  std::memcpy(data_ + offset_, buf, len);
  Advance(len);
}

}  // namespace xe
<|endoftext|>


<commit_before>// This file is part of the "x0" project, http://github.com/christianparpart/x0>
//   (c) 2009-2017 Christian Parpart <christian@parpart.family>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

#include <xzero/logging.h>
#include <xzero/ExceptionHandler.h>
#include <xzero/raft/Server.h>
#include <xzero/raft/StateMachine.h>
#include <xzero/raft/Discovery.h>
#include <xzero/raft/Transport.h>
#include <xzero/raft/Storage.h>
#include <xzero/util/BinaryReader.h>
#include <xzero/util/BinaryWriter.h>
#include <xzero/executor/PosixScheduler.h>
#include <xzero/testing.h>
#include <initializer_list>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <iostream>
#include <limits>

using namespace xzero;

class TestKeyValueStore : public raft::StateMachine { // {{{
 public:
  TestKeyValueStore();

  bool saveSnapshot(std::unique_ptr<OutputStream>&& output) override;
  bool loadSnapshot(std::unique_ptr<InputStream>&& input) override;
  void applyCommand(const raft::Command& serializedCmd) override;

  int get(int key) const;

 private:
  std::unordered_map<int, int> tuples_;
};

TestKeyValueStore::TestKeyValueStore()
    : tuples_() {
}

bool TestKeyValueStore::saveSnapshot(std::unique_ptr<OutputStream>&& output) {
  auto o = [&](const uint8_t* data, size_t len) {
    output->write((const char*) data, len);
  };
  BinaryWriter bw(o);
  for (const auto& pair: tuples_) {
    bw.writeVarUInt(pair.first);
    bw.writeVarUInt(pair.second);
  }
  return true;
}

bool TestKeyValueStore::loadSnapshot(std::unique_ptr<InputStream>&& input) {
  tuples_.clear();

  Buffer buffer;
  for (;;) {
    if (input->read(&buffer, 4096) <= 0) {
      break;
    }
  }

  BinaryReader reader((const uint8_t*) buffer.data(), buffer.size());

  while (reader.pending() > 0) {
    int a = reader.parseVarUInt();
    int b = reader.parseVarUInt();
    if (a < 0 || b < 0) {
      break;
    }
    tuples_[a] = b;
  }
  return true;
}

void TestKeyValueStore::applyCommand(const raft::Command& command) {
  BinaryReader reader(command.data(), command.size());
  int a = reader.parseVarUInt();
  int b = reader.parseVarUInt();
  tuples_[a] = b;
}

int TestKeyValueStore::get(int a) const {
  auto i = tuples_.find(a);
  if (i != tuples_.end())
    return i->second;
  else
    return -1;
}
// }}}
class TestServer { // {{{
 public:
  TestServer(raft::Id id,
             const raft::Discovery* discovery,
             Executor* executor);

  int get(int key) const;
  raft::RaftError set(int key, int value);

  raft::LocalTransport* transport() noexcept { return &transport_; }
  raft::Server* server() noexcept { return &raftServer_; }

 private:
  TestKeyValueStore stateMachine_;
  raft::MemoryStore storage_;
  raft::LocalTransport transport_;
  raft::Server raftServer_;
};

TestServer::TestServer(raft::Id id,
                       const raft::Discovery* discovery,
                       Executor* executor)
  : stateMachine_(),
    storage_(),
    transport_(id, executor),
    raftServer_(executor, id, &storage_, discovery, &transport_, &stateMachine_) {
}

int TestServer::get(int key) const {
  return stateMachine_.get(key);
}

raft::RaftError TestServer::set(int key, int value) {
  raft::Command cmd;
  cmd.push_back(key);
  cmd.push_back(value);
  return raftServer_.sendCommand(std::move(cmd));
}
// }}}
struct TestServerPod { // {{{
  PosixScheduler executor;
  const raft::StaticDiscovery discovery;
  std::vector<std::unique_ptr<TestServer>> servers;

  TestServerPod();

  void enableBreakOnConsensus();
  bool isConsensusReached() const;

  void startWithLeader(raft::Id id);
  void start();

  TestServer* getInstance(raft::Id id);
};

TestServerPod::TestServerPod()
  : executor(std::make_unique<CatchAndLogExceptionHandler>("TestServerPod")),
    discovery{ { 1, "127.0.0.1:4201" },
               { 2, "127.0.0.1:4202" },
               { 3, "127.0.0.1:4203" } },
    servers() {
  // create servers
  for (raft::Id id: discovery.listMembers()) {
    servers.emplace_back(new TestServer(id, &discovery, &executor));
  }

  // register (id,peer) tuples of server peers to this server
  for (auto& s: servers) {
    for (auto& t: servers) {
      s->transport()->setPeer(t->server()->id(), t->server());
    }

    s->server()->onLeaderChanged = [&](raft::Id oldLeaderId) {
      logDebug("TestServerPod", "onLeaderChanged[$0]: $1 ~> $2",
          s->server()->id(), oldLeaderId,
          s->server()->currentLeaderId());
    };
  }
}

void TestServerPod::enableBreakOnConsensus() {
  for (auto& s: servers) {
    s->server()->onStateChanged = [&](raft::Server* s, raft::ServerState oldState) {
      logDebug("TestServerPod", "onStateChanged[$0]: $1 ~> $2", s->id(), oldState, s->state());
      if (isConsensusReached()) {
        executor.breakLoop();
      }
    };
  }
}

bool TestServerPod::isConsensusReached() const {
  size_t leaderCount = 0;
  size_t followerCount = 0;

  for (const auto& s: servers) {
    switch (s->server()->state()) {
      case raft::ServerState::Leader:
        leaderCount++;
        break;
      case raft::ServerState::Follower:
        followerCount++;
        break;
      case raft::ServerState::Candidate:
        break;
    }
  }

  return leaderCount + followerCount == servers.size();
}

void TestServerPod::startWithLeader(raft::Id initialLeaderId) {
  for (auto& s: servers) {
    std::error_code ec = s->server()->startWithLeader(initialLeaderId);
    ASSERT_TRUE(!ec);
  };
}

void TestServerPod::start() {
  for (auto& s: servers) {
    std::error_code ec = s->server()->start();
    ASSERT_TRUE(!ec);
  };
}

TestServer* TestServerPod::getInstance(raft::Id id) {
  for (auto& i: servers) {
    if (i->server()->id() == id) {
      return i.get();
    }
  }

  return nullptr;
}
// }}}

TEST(raft_Server, leaderElection) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.start();
  pod.executor.runLoop();

  // now, leader election must have been taken place
  // 1 leader and 2 followers must exist

  EXPECT_TRUE(pod.isConsensusReached());
}

TEST(raft_Server, startWithLeader) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.startWithLeader(1);
  pod.executor.runLoop();
  ASSERT_TRUE(pod.isConsensusReached());
  ASSERT_TRUE(pod.getInstance(1)->server()->isLeader());
}

TEST(raft_Server, AppendEntries) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  //pod.startWithLeader(1);
  pod.start();
  pod.executor.runLoop();
  ASSERT_TRUE(pod.isConsensusReached());

  // TODO ...
}
<commit_msg>[xzero] raft: some more work towards AppendEntries command distribution<commit_after>// This file is part of the "x0" project, http://github.com/christianparpart/x0>
//   (c) 2009-2017 Christian Parpart <christian@parpart.family>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

#include <xzero/logging.h>
#include <xzero/ExceptionHandler.h>
#include <xzero/raft/Server.h>
#include <xzero/raft/StateMachine.h>
#include <xzero/raft/Discovery.h>
#include <xzero/raft/Transport.h>
#include <xzero/raft/Storage.h>
#include <xzero/util/BinaryReader.h>
#include <xzero/util/BinaryWriter.h>
#include <xzero/executor/PosixScheduler.h>
#include <xzero/testing.h>
#include <initializer_list>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <iostream>
#include <limits>

using namespace xzero;

class TestKeyValueStore : public raft::StateMachine { // {{{
 public:
  TestKeyValueStore();

  bool saveSnapshot(std::unique_ptr<OutputStream>&& output) override;
  bool loadSnapshot(std::unique_ptr<InputStream>&& input) override;
  void applyCommand(const raft::Command& serializedCmd) override;

  int get(int key) const;

 private:
  std::unordered_map<int, int> tuples_;
};

TestKeyValueStore::TestKeyValueStore()
    : tuples_() {
}

bool TestKeyValueStore::saveSnapshot(std::unique_ptr<OutputStream>&& output) {
  auto o = [&](const uint8_t* data, size_t len) {
    output->write((const char*) data, len);
  };
  BinaryWriter bw(o);
  for (const auto& pair: tuples_) {
    bw.writeVarUInt(pair.first);
    bw.writeVarUInt(pair.second);
  }
  return true;
}

bool TestKeyValueStore::loadSnapshot(std::unique_ptr<InputStream>&& input) {
  tuples_.clear();

  Buffer buffer;
  for (;;) {
    if (input->read(&buffer, 4096) <= 0) {
      break;
    }
  }

  BinaryReader reader((const uint8_t*) buffer.data(), buffer.size());

  while (reader.pending() > 0) {
    int a = reader.parseVarUInt();
    int b = reader.parseVarUInt();
    if (a < 0 || b < 0) {
      break;
    }
    tuples_[a] = b;
  }
  return true;
}

void TestKeyValueStore::applyCommand(const raft::Command& command) {
  logDebug("TestKeyValueStore", "applyCommand: $0", StringUtil::hexPrint(command.data(), command.size()));
  BinaryReader reader(command.data(), command.size());
  int a = reader.parseVarUInt();
  int b = reader.parseVarUInt();
  logDebug("TestKeyValueStore", "-> applyCommand: $0 = $1", a, b);
  tuples_[a] = b;
}

int TestKeyValueStore::get(int a) const {
  auto i = tuples_.find(a);
  if (i != tuples_.end())
    return i->second;
  else
    return -1;
}
// }}}
class TestServer { // {{{
 public:
  TestServer(raft::Id id,
             const raft::Discovery* discovery,
             Executor* executor);

  int get(int key) const;
  raft::RaftError set(int key, int value);

  raft::LocalTransport* transport() noexcept { return &transport_; }
  raft::Server* server() noexcept { return &raftServer_; }

 private:
  TestKeyValueStore stateMachine_;
  raft::MemoryStore storage_;
  raft::LocalTransport transport_;
  raft::Server raftServer_;
};

TestServer::TestServer(raft::Id id,
                       const raft::Discovery* discovery,
                       Executor* executor)
  : stateMachine_(),
    storage_(),
    transport_(id, executor),
    raftServer_(executor, id, &storage_, discovery, &transport_, &stateMachine_) {
}

int TestServer::get(int key) const {
  return stateMachine_.get(key);
}

raft::RaftError TestServer::set(int key, int value) {
  raft::Command cmd;

  auto o = [&](const uint8_t* data, size_t len) {
    for (size_t i = 0; i < len; ++i) {
      cmd.push_back(data[i]);
    }
  };

  BinaryWriter bw(o);
  bw.writeVarUInt(key);
  bw.writeVarUInt(value);

  logDebug("TestServer", "set($0, $1): $2", key, value,
      StringUtil::hexPrint(cmd.data(), cmd.size()));

  return raftServer_.sendCommand(std::move(cmd));
}
// }}}
struct TestServerPod { // {{{
  PosixScheduler executor;
  const raft::StaticDiscovery discovery;
  std::vector<std::unique_ptr<TestServer>> servers;

  TestServerPod();

  void enableBreakOnConsensus();
  bool isConsensusReached() const;

  void startWithLeader(raft::Id id);
  void start();

  TestServer* getInstance(raft::Id id);
};

TestServerPod::TestServerPod()
  : executor(std::make_unique<CatchAndLogExceptionHandler>("TestServerPod")),
    discovery{ { 1, "127.0.0.1:4201" },
               { 2, "127.0.0.1:4202" },
               { 3, "127.0.0.1:4203" } },
    servers() {
  // create servers
  for (raft::Id id: discovery.listMembers()) {
    servers.emplace_back(new TestServer(id, &discovery, &executor));
  }

  // register (id,peer) tuples of server peers to this server
  for (auto& s: servers) {
    for (auto& t: servers) {
      s->transport()->setPeer(t->server()->id(), t->server());
    }

    s->server()->onLeaderChanged = [&](raft::Id oldLeaderId) {
      logDebug("TestServerPod", "onLeaderChanged[$0]: $1 ~> $2",
          s->server()->id(), oldLeaderId,
          s->server()->currentLeaderId());
    };
  }
}

void TestServerPod::enableBreakOnConsensus() {
  for (auto& s: servers) {
    s->server()->onStateChanged = [&](raft::Server* s, raft::ServerState oldState) {
      logDebug("TestServerPod", "onStateChanged[$0]: $1 ~> $2", s->id(), oldState, s->state());
      if (isConsensusReached()) {
        executor.breakLoop();
      }
    };
  }
}

bool TestServerPod::isConsensusReached() const {
  size_t leaderCount = 0;
  size_t followerCount = 0;

  for (const auto& s: servers) {
    switch (s->server()->state()) {
      case raft::ServerState::Leader:
        leaderCount++;
        break;
      case raft::ServerState::Follower:
        followerCount++;
        break;
      case raft::ServerState::Candidate:
        break;
    }
  }

  return leaderCount + followerCount == servers.size();
}

void TestServerPod::startWithLeader(raft::Id initialLeaderId) {
  for (auto& s: servers) {
    std::error_code ec = s->server()->startWithLeader(initialLeaderId);
    ASSERT_TRUE(!ec);
  };
}

void TestServerPod::start() {
  for (auto& s: servers) {
    std::error_code ec = s->server()->start();
    ASSERT_TRUE(!ec);
  };
}

TestServer* TestServerPod::getInstance(raft::Id id) {
  for (auto& i: servers) {
    if (i->server()->id() == id) {
      return i.get();
    }
  }

  return nullptr;
}
// }}}

TEST(raft_Server, leaderElection) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.start();
  pod.executor.runLoop();

  // now, leader election must have been taken place
  // 1 leader and 2 followers must exist

  EXPECT_TRUE(pod.isConsensusReached());
}

TEST(raft_Server, startWithLeader) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.startWithLeader(1);
  pod.executor.runLoop();
  ASSERT_TRUE(pod.isConsensusReached());
  ASSERT_TRUE(pod.getInstance(1)->server()->isLeader());
}

TEST(raft_Server, AppendEntries) {
  TestServerPod pod;
  pod.enableBreakOnConsensus();
  pod.startWithLeader(1);
  pod.executor.runLoop();
  ASSERT_TRUE(pod.isConsensusReached());
  ASSERT_TRUE(pod.getInstance(1)->server()->isLeader());

  raft::RaftError err = pod.getInstance(1)->set(2, 4);
  ASSERT_EQ(raft::RaftError::Success, err);

  pod.executor.runLoopOnce();

  // ASSERT_EQ(4, pod.getInstance(1)->get(2));
  // ASSERT_EQ(4, pod.getInstance(2)->get(2));
  // ASSERT_EQ(4, pod.getInstance(3)->get(2));
}
<|endoftext|>


<commit_before><commit_msg>correction affichage inverseL<commit_after><|endoftext|>


<commit_before><commit_msg>fix: Correct minor typo (remove whitespace)<commit_after><|endoftext|>


<commit_before>#include "types.h"
#include "kernel.hh"
#include "mnode.hh"
#include "weakcache.hh"
#include "atomic_util.hh"
#include "percpu.hh"

static weakcache<u64, mnode, 257> mnode_cache;
static percpu<u64> next_inumber;

struct inumber
{
  u64 v_;
  static const int type_bits = 4;
  static const int cpu_bits = 8;

  inumber(u64 v) : v_(v) {}
  inumber(u8 type, u64 cpu, u64 count)
    : v_(type | (cpu << type_bits) | (count << (type_bits + cpu_bits)))
  {
    assert(type < (1 << type_bits));
    assert(cpu < (1 << cpu_bits));
  }

  u8 type() {
    return v_ & ((1 << type_bits) - 1);
  }
};

sref<mnode>
mnode::get(u64 inum)
{
  for (;;) {
    sref<mnode> m = mnode_cache.lookup(inum);
    if (m) {
      // wait for the mnode to be loaded from disk
      while (!m->valid_) {
        /* spin */
      }
      return m;
    }

    switch (inumber(inum).type()) {
    case types::dir:
      m = sref<mnode>::transfer(new mdir(inum));
      break;

    case types::file:
      m = sref<mnode>::transfer(new mfile(inum));
      break;

    case types::dev:
      m = sref<mnode>::transfer(new mdev(inum));
      break;

    default:
      panic("unknown type in inum 0x%lx", inum);
    }

    if (mnode_cache.insert(inum, m.get())) {
      m->cache_pin(true);
      // XXX read from disk
      m->valid_ = true;
      return m;
    }
  }
}

sref<mnode>
mnode::alloc(u8 type)
{
  auto counter = next_inumber.load();
  return mnode::get(inumber(type, myid(), (*counter)++).v_);
}

mnode::mnode(u64 inum) : inum_(inum), cache_pin_(false), valid_(false)
{
}

u8
mnode::type() const
{
  return inumber(inum_).type();
}

void
mnode::cache_pin(bool flag)
{
  if (cache_pin_ == flag || !cmpxch(&cache_pin_, !flag, flag))
    return;

  if (flag)
    inc();
  else
    dec();
}

void
mnode::onzero()
{
  mnode_cache.cleanup(weakref_);
  delete this;
}

void
mnode::linkcount::onzero()
{
  mnode* m = container_from_member(this, &mnode::nlink_);
  m->cache_pin(false);
}

void
mfile::resizer::resize_nogrow(u64 size)
{
  assert(PGROUNDUP(size) <= PGROUNDUP(mf_->size_));
  mf_->size_ = size;
  auto begin = mf_->pages_.find(PGROUNDUP(mf_->size_) / PGSIZE);
  auto end = mf_->pages_.find(maxidx);
  auto lock = mf_->pages_.acquire(begin, end);
  mf_->pages_.unset(begin, end);
}

void
mfile::resizer::resize_append(u64 size, sref<page_info> pi)
{
  assert(PGROUNDUP(mf_->size_) / PGSIZE + 1 == PGROUNDUP(size) / PGSIZE);
  auto it = mf_->pages_.find(PGROUNDUP(mf_->size_) / PGSIZE);
  auto lock = mf_->pages_.acquire(it);
  mf_->pages_.fill(it, page_state(pi));
  mf_->size_ = size;
}

sref<page_info>
mfile::get_page(u64 pageidx)
{
  auto it = pages_.find(pageidx);
  if (!it.is_set()) {
    if (pageidx < PGROUNDUP(size_) / PGSIZE) {
      // XXX read from disk
    }

    return sref<page_info>();
  }

  auto lock = pages_.acquire(it);
  return it->pg;
}
<commit_msg>make read/read scale again<commit_after>#include "types.h"
#include "kernel.hh"
#include "mnode.hh"
#include "weakcache.hh"
#include "atomic_util.hh"
#include "percpu.hh"

static weakcache<u64, mnode, 257> mnode_cache;
static percpu<u64> next_inumber;

struct inumber
{
  u64 v_;
  static const int type_bits = 4;
  static const int cpu_bits = 8;

  inumber(u64 v) : v_(v) {}
  inumber(u8 type, u64 cpu, u64 count)
    : v_(type | (cpu << type_bits) | (count << (type_bits + cpu_bits)))
  {
    assert(type < (1 << type_bits));
    assert(cpu < (1 << cpu_bits));
  }

  u8 type() {
    return v_ & ((1 << type_bits) - 1);
  }
};

sref<mnode>
mnode::get(u64 inum)
{
  for (;;) {
    sref<mnode> m = mnode_cache.lookup(inum);
    if (m) {
      // wait for the mnode to be loaded from disk
      while (!m->valid_) {
        /* spin */
      }
      return m;
    }

    switch (inumber(inum).type()) {
    case types::dir:
      m = sref<mnode>::transfer(new mdir(inum));
      break;

    case types::file:
      m = sref<mnode>::transfer(new mfile(inum));
      break;

    case types::dev:
      m = sref<mnode>::transfer(new mdev(inum));
      break;

    default:
      panic("unknown type in inum 0x%lx", inum);
    }

    if (mnode_cache.insert(inum, m.get())) {
      m->cache_pin(true);
      // XXX read from disk
      m->valid_ = true;
      return m;
    }
  }
}

sref<mnode>
mnode::alloc(u8 type)
{
  auto counter = next_inumber.load();
  return mnode::get(inumber(type, myid(), (*counter)++).v_);
}

mnode::mnode(u64 inum) : inum_(inum), cache_pin_(false), valid_(false)
{
}

u8
mnode::type() const
{
  return inumber(inum_).type();
}

void
mnode::cache_pin(bool flag)
{
  if (cache_pin_ == flag || !cmpxch(&cache_pin_, !flag, flag))
    return;

  if (flag)
    inc();
  else
    dec();
}

void
mnode::onzero()
{
  mnode_cache.cleanup(weakref_);
  delete this;
}

void
mnode::linkcount::onzero()
{
  mnode* m = container_from_member(this, &mnode::nlink_);
  m->cache_pin(false);
}

void
mfile::resizer::resize_nogrow(u64 size)
{
  assert(PGROUNDUP(size) <= PGROUNDUP(mf_->size_));
  mf_->size_ = size;
  auto begin = mf_->pages_.find(PGROUNDUP(mf_->size_) / PGSIZE);
  auto end = mf_->pages_.find(maxidx);
  auto lock = mf_->pages_.acquire(begin, end);
  mf_->pages_.unset(begin, end);
}

void
mfile::resizer::resize_append(u64 size, sref<page_info> pi)
{
  assert(PGROUNDUP(mf_->size_) / PGSIZE + 1 == PGROUNDUP(size) / PGSIZE);
  auto it = mf_->pages_.find(PGROUNDUP(mf_->size_) / PGSIZE);
  auto lock = mf_->pages_.acquire(it);
  mf_->pages_.fill(it, page_state(pi));
  mf_->size_ = size;
}

sref<page_info>
mfile::get_page(u64 pageidx)
{
  auto it = pages_.find(pageidx);
  if (!it.is_set()) {
    if (pageidx < PGROUNDUP(size_) / PGSIZE) {
      // XXX read from disk
    }

    return sref<page_info>();
  }

  /*
   * Ensure the page_info object is not garbage-collected by refcache,
   * by preventing the local core from going through a refcache epoch.
   * Here, we assume that all stores to sref::ptr_ are atomic, and we
   * will either see a valid pointer or nullptr.
   */
  scoped_cli cli;
  page_info* pi = it->pg.get();
  return sref<page_info>::newref(pi);
}
<|endoftext|>