<commit_before>/*
 * timers.cpp
 *
 * Implementation for some generic timers defined mostly in timers.h.
 */

#include <chrono>
#include <iostream>

#include "timers.hpp"
#include "stats.hpp"

using namespace std;

/* execute a 1-cycle loop 'iters' times */
#if UARCH_BENCH_PORTABLE
bench2_f portable_add_chain;
#define CAL_FN portable_add_chain
#else
extern "C" bench2_f add_calibration_x86;
#define CAL_FN add_calibration_x86
#endif

using namespace Stats;
using namespace std::chrono;

/*
 * Calculate the frequency of the CPU based on timing a tight loop that we expect to
 * take one iteration per cycle.
 *
 * ITERS is the base number of iterations to use: the calibration routine is actually
 * run twice, once with ITERS iterations and once with 2*ITERS, and a delta is used to
 * remove measurement overhead.
 */
template <size_t ITERS, typename CLOCK, size_t TRIES = 10, size_t WARMUP = 100>
double CalcCpuFreq() {
    static_assert((ITERS & 3) == 0, "iters must be divisible by 4 because we unroll some loops by 4");
    const char* mhz;
    if ((mhz = getenv("UARCH_BENCH_CLOCK_MHZ"))) {
        double ghz = std::stoi(mhz) / 1000.0;
        return ghz;
    }

    std::array<nanoseconds::rep, TRIES> results;

    for (size_t w = 0; w < WARMUP + 1; w++) {
        for (size_t r = 0; r < TRIES; r++) {
            auto t0 = CLOCK::nanos();
            CAL_FN(ITERS, nullptr);
            auto t1 = CLOCK::nanos();
            CAL_FN(ITERS * 2, nullptr);
            auto t2 = CLOCK::nanos();
            results[r] = (t2 - t1) - (t1 - t0);
        }
    }

    DescriptiveStats stats = get_stats(results.begin(), results.end());

    double ghz = ((double)ITERS / stats.getMedian());
    return ghz;
}

template <typename CLOCK>
double ClockTimerT<CLOCK>::getGHz() {
    static double ghz = CalcCpuFreq<10000,CLOCK,1000>();
    return ghz;
}

template <typename CLOCK>
void ClockTimerT<CLOCK>::init(Context &c) {
        c.out() << "Median CPU speed: " << std::fixed << std::setw(4) << std::setprecision(3)
        << getGHz() << " GHz" << std::endl;
}

// explicit instantiation for the default clock
template double DefaultClockTimer::getGHz();
template void DefaultClockTimer::init(Context& c);

// stuff for calculating clock overhead

template <size_t ITERS, typename CLOCK>
DescriptiveStats CalcClockRes() {
    std::array<nanoseconds::rep, ITERS> results;

    for (int r = 0; r < 3; r++) {
        for (size_t i = 0; i < ITERS; i++) {
            auto t0 = CLOCK::nanos();
            auto t1 = CLOCK::nanos();
            results[i] = t1 - t0;
        }
    }

    return get_stats(results.begin(), results.end());
}

volatile int64_t sink;

template <size_t ITERS, typename CLOCK>
DescriptiveStats CalcClockCost() {
    std::array<double, ITERS> results;
    using timer = DefaultClockTimer;

    for (int r = 0; r < 3; r++) {
        for (size_t i = 0; i < ITERS; i++) {
            int64_t sum = 0;
            int64_t before = timer::now();
            for (int j = 0; j < 1000; j++) {
                sum += CLOCK::nanos();
            }
            results[i] = (timer::now() - before) / 1000.0;
            sink = sum;
        }
    }

    return get_stats(results.begin(), results.end());
}

template <typename CLOCK>
void printOneClock(std::ostream& out, const char* name) {
    out << setw(48) << name << setw(28) << CalcClockRes<100,CLOCK>().getString4(5,1);
    out << setw(30) << CalcClockCost<100,CLOCK>().getString4(5,1) << endl;
}

struct DumbClock {
    static int64_t nanos() { return 0; }
};

void printClockOverheads(std::ostream& out) {
    out << "----- Clock Stats --------\n";
    out << "                                                      Resolution (ns)               Runtime (ns)" << endl;
    out << "                           Name                        min/  med/  avg/  max         min/  med/  avg/  max" << endl;
#define PRINT_CLOCK(clock) printOneClock< clock >(out, #clock);
    PRINT_CLOCK(StdClockAdapt<system_clock>);
    PRINT_CLOCK(StdClockAdapt<steady_clock>);
    PRINT_CLOCK(StdClockAdapt<high_resolution_clock>);

    PRINT_CLOCK(GettimeAdapter<CLOCK_REALTIME>);
    PRINT_CLOCK(GettimeAdapter<CLOCK_REALTIME_COARSE>);
    PRINT_CLOCK(GettimeAdapter<CLOCK_MONOTONIC>);
    PRINT_CLOCK(GettimeAdapter<CLOCK_MONOTONIC_COARSE>);
    PRINT_CLOCK(GettimeAdapter<CLOCK_MONOTONIC_RAW>);
    PRINT_CLOCK(GettimeAdapter<CLOCK_PROCESS_CPUTIME_ID>);
    PRINT_CLOCK(GettimeAdapter<CLOCK_THREAD_CPUTIME_ID>);
#ifdef CLOCK_BOOTTIME
    PRINT_CLOCK(GettimeAdapter<CLOCK_BOOTTIME>);
#endif

    PRINT_CLOCK(DumbClock);

    out << endl;
}

<commit_msg>add logging for Mhz<commit_after>/*
 * timers.cpp
 *
 * Implementation for some generic timers defined mostly in timers.h.
 */

#include <chrono>
#include <iostream>

#include "timers.hpp"
#include "stats.hpp"

using namespace std;

/* execute a 1-cycle loop 'iters' times */
#if UARCH_BENCH_PORTABLE
bench2_f portable_add_chain;
#define CAL_FN portable_add_chain
#else
extern "C" bench2_f add_calibration_x86;
#define CAL_FN add_calibration_x86
#endif

using namespace Stats;
using namespace std::chrono;

/*
 * Calculate the frequency of the CPU based on timing a tight loop that we expect to
 * take one iteration per cycle.
 *
 * ITERS is the base number of iterations to use: the calibration routine is actually
 * run twice, once with ITERS iterations and once with 2*ITERS, and a delta is used to
 * remove measurement overhead.
 */
template <size_t ITERS, typename CLOCK, size_t TRIES = 10, size_t WARMUP = 100>
double CalcCpuFreq() {
    static_assert((ITERS & 3) == 0, "iters must be divisible by 4 because we unroll some loops by 4");
    const char* mhz;
    if ((mhz = getenv("UARCH_BENCH_CLOCK_MHZ"))) {
        double ghz = std::stoi(mhz) / 1000.0;
        fprintf(stderr, "Frequency set to %6.3f GHz using UARCH_BENCH_CLOCK_MHZ\n");
        return ghz;
    }

    std::array<nanoseconds::rep, TRIES> results;

    for (size_t w = 0; w < WARMUP + 1; w++) {
        for (size_t r = 0; r < TRIES; r++) {
            auto t0 = CLOCK::nanos();
            CAL_FN(ITERS, nullptr);
            auto t1 = CLOCK::nanos();
            CAL_FN(ITERS * 2, nullptr);
            auto t2 = CLOCK::nanos();
            results[r] = (t2 - t1) - (t1 - t0);
        }
    }

    DescriptiveStats stats = get_stats(results.begin(), results.end());

    double ghz = ((double)ITERS / stats.getMedian());
    return ghz;
}

template <typename CLOCK>
double ClockTimerT<CLOCK>::getGHz() {
    static double ghz = CalcCpuFreq<10000,CLOCK,1000>();
    return ghz;
}

template <typename CLOCK>
void ClockTimerT<CLOCK>::init(Context &c) {
        c.out() << "Median CPU speed: " << std::fixed << std::setw(4) << std::setprecision(3)
        << getGHz() << " GHz" << std::endl;
}

// explicit instantiation for the default clock
template double DefaultClockTimer::getGHz();
template void DefaultClockTimer::init(Context& c);

// stuff for calculating clock overhead

template <size_t ITERS, typename CLOCK>
DescriptiveStats CalcClockRes() {
    std::array<nanoseconds::rep, ITERS> results;

    for (int r = 0; r < 3; r++) {
        for (size_t i = 0; i < ITERS; i++) {
            auto t0 = CLOCK::nanos();
            auto t1 = CLOCK::nanos();
            results[i] = t1 - t0;
        }
    }

    return get_stats(results.begin(), results.end());
}

volatile int64_t sink;

template <size_t ITERS, typename CLOCK>
DescriptiveStats CalcClockCost() {
    std::array<double, ITERS> results;
    using timer = DefaultClockTimer;

    for (int r = 0; r < 3; r++) {
        for (size_t i = 0; i < ITERS; i++) {
            int64_t sum = 0;
            int64_t before = timer::now();
            for (int j = 0; j < 1000; j++) {
                sum += CLOCK::nanos();
            }
            results[i] = (timer::now() - before) / 1000.0;
            sink = sum;
        }
    }

    return get_stats(results.begin(), results.end());
}

template <typename CLOCK>
void printOneClock(std::ostream& out, const char* name) {
    out << setw(48) << name << setw(28) << CalcClockRes<100,CLOCK>().getString4(5,1);
    out << setw(30) << CalcClockCost<100,CLOCK>().getString4(5,1) << endl;
}

struct DumbClock {
    static int64_t nanos() { return 0; }
};

void printClockOverheads(std::ostream& out) {
    out << "----- Clock Stats --------\n";
    out << "                                                      Resolution (ns)               Runtime (ns)" << endl;
    out << "                           Name                        min/  med/  avg/  max         min/  med/  avg/  max" << endl;
#define PRINT_CLOCK(clock) printOneClock< clock >(out, #clock);
    PRINT_CLOCK(StdClockAdapt<system_clock>);
    PRINT_CLOCK(StdClockAdapt<steady_clock>);
    PRINT_CLOCK(StdClockAdapt<high_resolution_clock>);

    PRINT_CLOCK(GettimeAdapter<CLOCK_REALTIME>);
    PRINT_CLOCK(GettimeAdapter<CLOCK_REALTIME_COARSE>);
    PRINT_CLOCK(GettimeAdapter<CLOCK_MONOTONIC>);
    PRINT_CLOCK(GettimeAdapter<CLOCK_MONOTONIC_COARSE>);
    PRINT_CLOCK(GettimeAdapter<CLOCK_MONOTONIC_RAW>);
    PRINT_CLOCK(GettimeAdapter<CLOCK_PROCESS_CPUTIME_ID>);
    PRINT_CLOCK(GettimeAdapter<CLOCK_THREAD_CPUTIME_ID>);
#ifdef CLOCK_BOOTTIME
    PRINT_CLOCK(GettimeAdapter<CLOCK_BOOTTIME>);
#endif

    PRINT_CLOCK(DumbClock);

    out << endl;
}

<|endoftext|>


<commit_before>#include "sound.hpp"
#include "oddlib/audio/SequencePlayer.h"
#include "core/audiobuffer.hpp"
#include "logger.hpp"
#include "resourcemapper.hpp"
#include "audioconverter.hpp"
#include "alive_version.h"

SoundCache::SoundCache(OSBaseFileSystem& fs)
    : mFs(fs)
{

}

void SoundCache::Sync()
{
    mSoundDataCache.clear();

    bool ok = false;
    std::string versionFile = "{CacheDir}/CacheVersion.txt";
    if (mFs.FileExists(versionFile))
    {
        if (mFs.Open(versionFile)->LoadAllToString() == ALIVE_VERSION)
        {
            ok = true;
        }
    }

    if (!ok)
    {
        DeleteAll();
    }
}

void SoundCache::DeleteAll()
{
    TRACE_ENTRYEXIT;

    // Delete *.wav from {CacheDir}/disk
    std::string dirName = mFs.ExpandPath("{CacheDir}");
    const auto wavFiles = mFs.EnumerateFiles(dirName, "*.wav");
    for (const auto& wavFile : wavFiles)
    {
        mFs.DeleteFile(dirName + "/" + wavFile);
    }

    // Remove from memory
    mSoundDataCache.clear();

    // Update cache version marker to current
    const std::string fileName = mFs.ExpandPath("{CacheDir}/CacheVersion.txt");
    Oddlib::FileStream versionFile(fileName, Oddlib::IStream::ReadMode::ReadWrite);
    versionFile.Write(std::string(ALIVE_VERSION));
}

bool SoundCache::ExistsInMemoryCache(const std::string& name) const
{
    return mSoundDataCache.find(name) != std::end(mSoundDataCache);
}

class WavSound : public ISound
{
public:
    WavSound(const std::string& name, std::shared_ptr<std::vector<u8>>& data)
        : mName(name), mData(data)
    {
        memcpy(&mHeader.mData, data->data(), sizeof(mHeader.mData));
        mOffsetInBytes = sizeof(mHeader.mData);
    }

    virtual void Load() override { }
    virtual void DebugUi() override {}

    virtual void Play(f32* stream, u32 len) override
    {
        u32 kLenInBytes = len * sizeof(f32);

        // Handle the case where the audio call back wants N data but we only have N-X left
        if (mOffsetInBytes + kLenInBytes > mData->size())
        {
            kLenInBytes = static_cast<u32>(mData->size()) - mOffsetInBytes;
        }

        const f32* src = reinterpret_cast<const f32*>(mData->data() + mOffsetInBytes);
        for (u32 i = 0; i < kLenInBytes/sizeof(f32); i++)
        {
            stream[i] += src[i];
        }
        mOffsetInBytes += kLenInBytes;
    }

    virtual bool AtEnd() const override
    {
        return mOffsetInBytes >= mData->size();
    }

    virtual void Restart() override
    {
        mOffsetInBytes = sizeof(mHeader.mData);
    }

    virtual void Update() override { }
    virtual const std::string& Name() const override { return mName; }

private:
    size_t mOffsetInBytes = 0;
    std::string mName;
    std::shared_ptr<std::vector<u8>> mData;
    WavHeader mHeader;
};

std::unique_ptr<ISound> SoundCache::GetCached(const std::string& name)
{
    auto it = mSoundDataCache.find(name);
    if (it != std::end(mSoundDataCache))
    {
        return std::make_unique<WavSound>(name, it->second);
    }
    return nullptr;
}

void SoundCache::AddToMemoryAndDiskCache(ISound& sound)
{
    const std::string fileName = mFs.ExpandPath("{CacheDir}/" + sound.Name() + ".wav");

    // TODO: mod files that are already wav shouldn't be converted
    sound.Load();
    AudioConverter::Convert<WavEncoder>(sound, fileName.c_str());

    auto stream = mFs.Open(fileName);
    mSoundDataCache[sound.Name()] = std::make_shared<std::vector<u8>>(Oddlib::IStream::ReadAll(*stream));
}

bool SoundCache::AddToMemoryCacheFromDiskCache(const std::string& name)
{
    std::string fileName = mFs.ExpandPath("{CacheDir}/" + name + ".wav");
    if (mFs.FileExists(fileName))
    {
        auto stream = mFs.Open(fileName);
        mSoundDataCache[name] = std::make_shared<std::vector<u8>>(Oddlib::IStream::ReadAll(*stream));
        return true;
    }
    return false;
}

void SoundCache::RemoveFromMemoryCache(const std::string& name)
{
    auto it = mSoundDataCache.find(name);
    if (it != std::end(mSoundDataCache))
    {
        mSoundDataCache.erase(it);
    }
}

// ====================================================================

void Sound::PlaySoundScript(const char* soundName)
{
    auto ret = PlaySound(soundName, nullptr, true, true, true);
    if (ret)
    {
        std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
        mSoundPlayers.push_back(std::move(ret));
    }
}
std::unique_ptr<ISound> Sound::PlaySound(const char* soundName, const char* explicitSoundBankName, bool useMusicRec, bool useSfxRec, bool useCache)
{
    if (useCache)
    {
        std::unique_ptr<ISound> pSound = mCache.GetCached(soundName);
        if (pSound)
        {
            LOG_INFO("Play sound cached: " << soundName);
        }
        else
        {
            LOG_ERROR("Cached sound not found: " << soundName);
        }
        return pSound;
    }
    else
    {
        std::unique_ptr<ISound> pSound = mLocator.LocateSound(soundName, explicitSoundBankName, useMusicRec, useSfxRec);
        if (pSound)
        {
            LOG_INFO("Play sound: " << soundName);
            pSound->Load();
        }
        else
        {
            LOG_ERROR("Sound: " << soundName << " not found");
        }
        return pSound;
    }
}

void Sound::SetTheme(const char* themeName)
{
    mAmbiance = nullptr;
    mMusicTrack = nullptr;
    const MusicTheme* newTheme = mLocator.LocateSoundTheme(themeName);

    // Remove current musics from in memory cache
    if (mActiveTheme)
    {
        CacheActiveTheme(false);
    }

    mActiveTheme = newTheme;

    // Add new musics to in memory cache
    if (mActiveTheme)
    {
        CacheActiveTheme(true);
    }
    else
    {
        LOG_ERROR("Music theme " << themeName << " was not found");
    }
}

void Sound::CacheMemoryResidentSounds()
{
    TRACE_ENTRYEXIT;

    // initial one time sync
    mCache.Sync();

    const std::vector<SoundResource>& resources = mLocator.GetSoundResources();
    for (const SoundResource& resource : resources)
    {
        if (resource.mIsCacheResident)
        {
            CacheSound(resource.mResourceName);
        }
    }
}

void Sound::CacheActiveTheme(bool add)
{
    for (auto& entry : mActiveTheme->mEntries)
    {
        for (auto& e : entry.second)
        {
            if (add)
            {
                CacheSound(e.mMusicName);
            }
            else
            {
                mCache.RemoveFromMemoryCache(e.mMusicName);
            }
        }
    }
}

void Sound::CacheSound(const std::string& name)
{
    if (mCache.ExistsInMemoryCache(name))
    {
        // Already in memory
        return;
    }

    if (mCache.AddToMemoryCacheFromDiskCache(name))
    {
        // Already on disk and now added to in memory cache
        return;
    }

    std::unique_ptr<ISound> pSound = mLocator.LocateSound(name.c_str(), nullptr, true, true);
    if (pSound)
    {
        // Write into disk cache and then load from disk cache into memory cache
        mCache.AddToMemoryAndDiskCache(*pSound);
    }
}

void Sound::HandleEvent(const char* eventName)
{
    // TODO: Need quarter beat transition in some cases
    EnsureAmbiance();

    if (strcmp(eventName, "AMBIANCE") == 0)
    {
        mMusicTrack = nullptr;
        return;
    }

    auto ret = PlayThemeEntry(eventName);
    if (ret)
    {
        mMusicTrack = std::move(ret);
    }
}

std::unique_ptr<ISound> Sound::PlayThemeEntry(const char* entryName)
{
    if (mActiveTheme)
    {
        mActiveThemeEntry.SetMusicThemeEntry(mActiveTheme->FindEntry(entryName));

        const MusicThemeEntry* entry = mActiveThemeEntry.Entry();
        if (entry)
        {
            return PlaySound(entry->mMusicName.c_str(), nullptr, true, true, true);
        }
    }
    return nullptr;
}

void Sound::EnsureAmbiance()
{
    std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
    if (!mAmbiance)
    {
        mAmbiance = PlayThemeEntry("AMBIANCE");
    }
}

// Audio thread context
bool Sound::Play(f32* stream, u32 len)
{
    std::lock_guard<std::mutex> lock(mSoundPlayersMutex);

    if (mAmbiance)
    {
        mAmbiance->Play(stream, len);
    }

    if (mMusicTrack)
    {
        mMusicTrack->Play(stream, len);
    }

    for (auto& player : mSoundPlayers)
    {
        player->Play(stream, len);
    }
    return false;
}

/*static*/ void Sound::RegisterScriptBindings()
{
    Sqrat::Class<Sound, Sqrat::NoConstructor<Sound>> c(Sqrat::DefaultVM::Get(), "Sound");
    c.Func("PlaySoundEffect", &Sound::PlaySoundScript);
    c.Func("SetTheme", &Sound::SetTheme);
    Sqrat::RootTable().Bind("Sound", c);
}

Sound::Sound(IAudioController& audioController, ResourceLocator& locator, OSBaseFileSystem& fs)
    : mAudioController(audioController), mLocator(locator), mCache(fs), mScriptInstance("gSound", this)
{
    mAudioController.AddPlayer(this);
}

Sound::~Sound()
{
    // Ensure the audio call back stops at this point else will be
    // calling back into freed objects
    SDL_AudioQuit();

    mAudioController.RemovePlayer(this);
}

void Sound::Update()
{
    if (Debugging().mBrowserUi.soundBrowserOpen)
    {
        {
            std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
            if (!mSoundPlayers.empty())
            {
                mSoundPlayers[0]->DebugUi();
            }

            if (ImGui::Begin("Active SEQs"))
            {
                if (mAmbiance)
                {
                   ImGui::Text("Ambiance: %s", mAmbiance->Name().c_str());
                }

                if (mMusicTrack)
                {
                    ImGui::Text("Music: %s", mMusicTrack->Name().c_str());
                }

                int i = 0;
                for (auto& player : mSoundPlayers)
                {
                    i++;
                    if (ImGui::Button((std::to_string(i) + player->Name()).c_str()))
                    {
                        // TODO: Kill whatever this SEQ is
                    }
                }
            }
            ImGui::End();
        }

        SoundBrowserUi();
    }

    std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
    for (auto it = mSoundPlayers.begin(); it != mSoundPlayers.end();)
    {
        if ((*it)->AtEnd())
        {
            it = mSoundPlayers.erase(it);
        }
        else
        {
            it++;
        }
    }

    if (mAmbiance)
    {
        mAmbiance->Update();
        if (mAmbiance->AtEnd())
        {
            mAmbiance->Restart();
        }
    }

    if (mMusicTrack)
    {
        mMusicTrack->Update();
        if (mMusicTrack->AtEnd())
        {
            if (mActiveThemeEntry.ToNextEntry())
            {
                mMusicTrack = PlaySound(mActiveThemeEntry.Entry()->mMusicName.c_str(), nullptr, true, true, true);
            }
            else
            {
                mMusicTrack = nullptr;
            }
        }
    }

    for (auto& player : mSoundPlayers)
    {
        player->Update();
    }
}

static const char* kMusicEvents[] =
{
    "AMBIANCE",
    "BASE_LINE",
    "CRITTER_ATTACK",
    "CRITTER_PATROL",
    "SLIG_ATTACK",
    "SLIG_PATROL",
    "SLIG_POSSESSED",
};

void Sound::SoundBrowserUi()
{
    ImGui::Begin("Sound list");

    static const SoundResource* selected = nullptr;

    // left
    ImGui::BeginChild("left pane", ImVec2(200, 0), true);
    {
        for (const SoundResource& soundInfo : mLocator.GetSoundResources())
        {
            if (ImGui::Selectable(soundInfo.mResourceName.c_str()))
            {
                selected = &soundInfo;
            }
        }
        ImGui::EndChild();
    }
    ImGui::SameLine();

    // right
    ImGui::BeginGroup();
    {
        ImGui::BeginChild("item view");
        {
            if (selected)
            {
                ImGui::TextWrapped("Resource name: %s", selected->mResourceName.c_str());
                ImGui::Separator();
                ImGui::TextWrapped("Comment: %s", selected->mComment.empty() ? "(none)" : selected->mComment.c_str());
                ImGui::Separator();
                ImGui::TextWrapped("Is memory resident: %s", selected->mIsCacheResident ? "true" : "false");
                ImGui::TextWrapped("Is cached: %s", mCache.ExistsInMemoryCache(selected->mResourceName) ? "true" : "false");

                static bool bUseCache = false;
                ImGui::Checkbox("Use cache", &bUseCache);

                const bool bHasMusic = !selected->mMusic.mSoundBanks.empty();
                const bool bHasSample = !selected->mSoundEffect.mSoundBanks.empty();
                if (bHasMusic)
                {
                    if (ImGui::CollapsingHeader("SEQs"))
                    {
                        for (const std::string& sb : selected->mMusic.mSoundBanks)
                        {
                            if (ImGui::Selectable(sb.c_str()))
                            {
                                auto player = PlaySound(selected->mResourceName.c_str(), sb.c_str(), true, false, bUseCache);
                                if (player)
                                {
                                    std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
                                    mSoundPlayers.push_back(std::move(player));
                                }
                            }
                        }
                    }
                }

                if (bHasSample)
                {
                    if (ImGui::CollapsingHeader("Samples"))
                    {
                        for (const SoundEffectResourceLocation& sbLoc : selected->mSoundEffect.mSoundBanks)
                        {
                            for (const std::string& sb : sbLoc.mSoundBanks)
                            {
                                if (ImGui::Selectable(sb.c_str()))
                                {
                                    auto player = PlaySound(selected->mResourceName.c_str(), sb.c_str(), false, true, bUseCache);
                                    if (player)
                                    {
                                        std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
                                        mSoundPlayers.push_back(std::move(player));
                                    }
                                }
                            }
                        }
                    }
                }

                if (ImGui::Button("Play (cached/scripted)"))
                {
                    PlaySoundScript(selected->mResourceName.c_str());
                }
            }
            else
            {
                ImGui::TextWrapped("Click an item to display its info");
            }
        }
        ImGui::EndChild();
    }
    ImGui::EndGroup();

    ImGui::End();

    ImGui::Begin("Sound themes");

    for (const MusicTheme& theme : mLocator.mResMapper.mSoundResources.mThemes)
    {
        if (ImGui::RadioButton(theme.mName.c_str(), mActiveTheme && theme.mName == mActiveTheme->mName))
        {
            SetTheme(theme.mName.c_str());
            HandleEvent("BASE_LINE");
        }
    }

    for (const char* eventName : kMusicEvents)
    {
        if (ImGui::Button(eventName))
        {
            HandleEvent(eventName);
        }
    }

    ImGui::End();

}

void Sound::Render(int /*w*/, int /*h*/)
{

}
<commit_msg>fix debug build again<commit_after>#include "sound.hpp"
#include "oddlib/audio/SequencePlayer.h"
#include "core/audiobuffer.hpp"
#include "logger.hpp"
#include "resourcemapper.hpp"
#include "audioconverter.hpp"
#include "alive_version.h"

SoundCache::SoundCache(OSBaseFileSystem& fs)
    : mFs(fs)
{

}

void SoundCache::Sync()
{
    mSoundDataCache.clear();

    bool ok = false;
    std::string versionFile = "{CacheDir}/CacheVersion.txt";
    if (mFs.FileExists(versionFile))
    {
        if (mFs.Open(versionFile)->LoadAllToString() == ALIVE_VERSION)
        {
            ok = true;
        }
    }

    if (!ok)
    {
        DeleteAll();
    }
}

void SoundCache::DeleteAll()
{
    TRACE_ENTRYEXIT;

    // Delete *.wav from {CacheDir}/disk
    std::string dirName = mFs.ExpandPath("{CacheDir}");
    const auto wavFiles = mFs.EnumerateFiles(dirName, "*.wav");
    for (const auto& wavFile : wavFiles)
    {
        mFs.DeleteFile(dirName + "/" + wavFile);
    }

    // Remove from memory
    mSoundDataCache.clear();

    // Update cache version marker to current
    const std::string fileName = mFs.ExpandPath("{CacheDir}/CacheVersion.txt");
    Oddlib::FileStream versionFile(fileName, Oddlib::IStream::ReadMode::ReadWrite);
    versionFile.Write(std::string(ALIVE_VERSION));
}

bool SoundCache::ExistsInMemoryCache(const std::string& name) const
{
    return mSoundDataCache.find(name) != std::end(mSoundDataCache);
}

class WavSound : public ISound
{
public:
    WavSound(const std::string& name, std::shared_ptr<std::vector<u8>>& data)
        : mName(name), mData(data)
    {
        memcpy(&mHeader.mData, data->data(), sizeof(mHeader.mData));
        mOffsetInBytes = sizeof(mHeader.mData);
    }

    virtual void Load() override { }
    virtual void DebugUi() override {}

    virtual void Play(f32* stream, u32 len) override
    {
        size_t kLenInBytes = len * sizeof(f32);

        // Handle the case where the audio call back wants N data but we only have N-X left
        if (mOffsetInBytes + kLenInBytes > mData->size())
        {
            kLenInBytes = mData->size() - mOffsetInBytes;
        }

        const f32* src = reinterpret_cast<const f32*>(mData->data() + mOffsetInBytes);
        for (auto i = 0u; i < kLenInBytes/sizeof(f32); i++)
        {
            stream[i] += src[i];
        }
        mOffsetInBytes += kLenInBytes;
    }

    virtual bool AtEnd() const override
    {
        return mOffsetInBytes >= mData->size();
    }

    virtual void Restart() override
    {
        mOffsetInBytes = sizeof(mHeader.mData);
    }

    virtual void Update() override { }
    virtual const std::string& Name() const override { return mName; }

private:
    size_t mOffsetInBytes = 0;
    std::string mName;
    std::shared_ptr<std::vector<u8>> mData;
    WavHeader mHeader;
};

std::unique_ptr<ISound> SoundCache::GetCached(const std::string& name)
{
    auto it = mSoundDataCache.find(name);
    if (it != std::end(mSoundDataCache))
    {
        return std::make_unique<WavSound>(name, it->second);
    }
    return nullptr;
}

void SoundCache::AddToMemoryAndDiskCache(ISound& sound)
{
    const std::string fileName = mFs.ExpandPath("{CacheDir}/" + sound.Name() + ".wav");

    // TODO: mod files that are already wav shouldn't be converted
    sound.Load();
    AudioConverter::Convert<WavEncoder>(sound, fileName.c_str());

    auto stream = mFs.Open(fileName);
    mSoundDataCache[sound.Name()] = std::make_shared<std::vector<u8>>(Oddlib::IStream::ReadAll(*stream));
}

bool SoundCache::AddToMemoryCacheFromDiskCache(const std::string& name)
{
    std::string fileName = mFs.ExpandPath("{CacheDir}/" + name + ".wav");
    if (mFs.FileExists(fileName))
    {
        auto stream = mFs.Open(fileName);
        mSoundDataCache[name] = std::make_shared<std::vector<u8>>(Oddlib::IStream::ReadAll(*stream));
        return true;
    }
    return false;
}

void SoundCache::RemoveFromMemoryCache(const std::string& name)
{
    auto it = mSoundDataCache.find(name);
    if (it != std::end(mSoundDataCache))
    {
        mSoundDataCache.erase(it);
    }
}

// ====================================================================

void Sound::PlaySoundScript(const char* soundName)
{
    auto ret = PlaySound(soundName, nullptr, true, true, true);
    if (ret)
    {
        std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
        mSoundPlayers.push_back(std::move(ret));
    }
}
std::unique_ptr<ISound> Sound::PlaySound(const char* soundName, const char* explicitSoundBankName, bool useMusicRec, bool useSfxRec, bool useCache)
{
    if (useCache)
    {
        std::unique_ptr<ISound> pSound = mCache.GetCached(soundName);
        if (pSound)
        {
            LOG_INFO("Play sound cached: " << soundName);
        }
        else
        {
            LOG_ERROR("Cached sound not found: " << soundName);
        }
        return pSound;
    }
    else
    {
        std::unique_ptr<ISound> pSound = mLocator.LocateSound(soundName, explicitSoundBankName, useMusicRec, useSfxRec);
        if (pSound)
        {
            LOG_INFO("Play sound: " << soundName);
            pSound->Load();
        }
        else
        {
            LOG_ERROR("Sound: " << soundName << " not found");
        }
        return pSound;
    }
}

void Sound::SetTheme(const char* themeName)
{
    mAmbiance = nullptr;
    mMusicTrack = nullptr;
    const MusicTheme* newTheme = mLocator.LocateSoundTheme(themeName);

    // Remove current musics from in memory cache
    if (mActiveTheme)
    {
        CacheActiveTheme(false);
    }

    mActiveTheme = newTheme;

    // Add new musics to in memory cache
    if (mActiveTheme)
    {
        CacheActiveTheme(true);
    }
    else
    {
        LOG_ERROR("Music theme " << themeName << " was not found");
    }
}

void Sound::CacheMemoryResidentSounds()
{
    TRACE_ENTRYEXIT;

    // initial one time sync
    mCache.Sync();

    const std::vector<SoundResource>& resources = mLocator.GetSoundResources();
    for (const SoundResource& resource : resources)
    {
        if (resource.mIsCacheResident)
        {
            CacheSound(resource.mResourceName);
        }
    }
}

void Sound::CacheActiveTheme(bool add)
{
    for (auto& entry : mActiveTheme->mEntries)
    {
        for (auto& e : entry.second)
        {
            if (add)
            {
                CacheSound(e.mMusicName);
            }
            else
            {
                mCache.RemoveFromMemoryCache(e.mMusicName);
            }
        }
    }
}

void Sound::CacheSound(const std::string& name)
{
    if (mCache.ExistsInMemoryCache(name))
    {
        // Already in memory
        return;
    }

    if (mCache.AddToMemoryCacheFromDiskCache(name))
    {
        // Already on disk and now added to in memory cache
        return;
    }

    std::unique_ptr<ISound> pSound = mLocator.LocateSound(name.c_str(), nullptr, true, true);
    if (pSound)
    {
        // Write into disk cache and then load from disk cache into memory cache
        mCache.AddToMemoryAndDiskCache(*pSound);
    }
}

void Sound::HandleEvent(const char* eventName)
{
    // TODO: Need quarter beat transition in some cases
    EnsureAmbiance();

    if (strcmp(eventName, "AMBIANCE") == 0)
    {
        mMusicTrack = nullptr;
        return;
    }

    auto ret = PlayThemeEntry(eventName);
    if (ret)
    {
        mMusicTrack = std::move(ret);
    }
}

std::unique_ptr<ISound> Sound::PlayThemeEntry(const char* entryName)
{
    if (mActiveTheme)
    {
        mActiveThemeEntry.SetMusicThemeEntry(mActiveTheme->FindEntry(entryName));

        const MusicThemeEntry* entry = mActiveThemeEntry.Entry();
        if (entry)
        {
            return PlaySound(entry->mMusicName.c_str(), nullptr, true, true, true);
        }
    }
    return nullptr;
}

void Sound::EnsureAmbiance()
{
    std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
    if (!mAmbiance)
    {
        mAmbiance = PlayThemeEntry("AMBIANCE");
    }
}

// Audio thread context
bool Sound::Play(f32* stream, u32 len)
{
    std::lock_guard<std::mutex> lock(mSoundPlayersMutex);

    if (mAmbiance)
    {
        mAmbiance->Play(stream, len);
    }

    if (mMusicTrack)
    {
        mMusicTrack->Play(stream, len);
    }

    for (auto& player : mSoundPlayers)
    {
        player->Play(stream, len);
    }
    return false;
}

/*static*/ void Sound::RegisterScriptBindings()
{
    Sqrat::Class<Sound, Sqrat::NoConstructor<Sound>> c(Sqrat::DefaultVM::Get(), "Sound");
    c.Func("PlaySoundEffect", &Sound::PlaySoundScript);
    c.Func("SetTheme", &Sound::SetTheme);
    Sqrat::RootTable().Bind("Sound", c);
}

Sound::Sound(IAudioController& audioController, ResourceLocator& locator, OSBaseFileSystem& fs)
    : mAudioController(audioController), mLocator(locator), mCache(fs), mScriptInstance("gSound", this)
{
    mAudioController.AddPlayer(this);
}

Sound::~Sound()
{
    // Ensure the audio call back stops at this point else will be
    // calling back into freed objects
    SDL_AudioQuit();

    mAudioController.RemovePlayer(this);
}

void Sound::Update()
{
    if (Debugging().mBrowserUi.soundBrowserOpen)
    {
        {
            std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
            if (!mSoundPlayers.empty())
            {
                mSoundPlayers[0]->DebugUi();
            }

            if (ImGui::Begin("Active SEQs"))
            {
                if (mAmbiance)
                {
                   ImGui::Text("Ambiance: %s", mAmbiance->Name().c_str());
                }

                if (mMusicTrack)
                {
                    ImGui::Text("Music: %s", mMusicTrack->Name().c_str());
                }

                int i = 0;
                for (auto& player : mSoundPlayers)
                {
                    i++;
                    if (ImGui::Button((std::to_string(i) + player->Name()).c_str()))
                    {
                        // TODO: Kill whatever this SEQ is
                    }
                }
            }
            ImGui::End();
        }

        SoundBrowserUi();
    }

    std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
    for (auto it = mSoundPlayers.begin(); it != mSoundPlayers.end();)
    {
        if ((*it)->AtEnd())
        {
            it = mSoundPlayers.erase(it);
        }
        else
        {
            it++;
        }
    }

    if (mAmbiance)
    {
        mAmbiance->Update();
        if (mAmbiance->AtEnd())
        {
            mAmbiance->Restart();
        }
    }

    if (mMusicTrack)
    {
        mMusicTrack->Update();
        if (mMusicTrack->AtEnd())
        {
            if (mActiveThemeEntry.ToNextEntry())
            {
                mMusicTrack = PlaySound(mActiveThemeEntry.Entry()->mMusicName.c_str(), nullptr, true, true, true);
            }
            else
            {
                mMusicTrack = nullptr;
            }
        }
    }

    for (auto& player : mSoundPlayers)
    {
        player->Update();
    }
}

static const char* kMusicEvents[] =
{
    "AMBIANCE",
    "BASE_LINE",
    "CRITTER_ATTACK",
    "CRITTER_PATROL",
    "SLIG_ATTACK",
    "SLIG_PATROL",
    "SLIG_POSSESSED",
};

void Sound::SoundBrowserUi()
{
    ImGui::Begin("Sound list");

    static const SoundResource* selected = nullptr;

    // left
    ImGui::BeginChild("left pane", ImVec2(200, 0), true);
    {
        for (const SoundResource& soundInfo : mLocator.GetSoundResources())
        {
            if (ImGui::Selectable(soundInfo.mResourceName.c_str()))
            {
                selected = &soundInfo;
            }
        }
        ImGui::EndChild();
    }
    ImGui::SameLine();

    // right
    ImGui::BeginGroup();
    {
        ImGui::BeginChild("item view");
        {
            if (selected)
            {
                ImGui::TextWrapped("Resource name: %s", selected->mResourceName.c_str());
                ImGui::Separator();
                ImGui::TextWrapped("Comment: %s", selected->mComment.empty() ? "(none)" : selected->mComment.c_str());
                ImGui::Separator();
                ImGui::TextWrapped("Is memory resident: %s", selected->mIsCacheResident ? "true" : "false");
                ImGui::TextWrapped("Is cached: %s", mCache.ExistsInMemoryCache(selected->mResourceName) ? "true" : "false");

                static bool bUseCache = false;
                ImGui::Checkbox("Use cache", &bUseCache);

                const bool bHasMusic = !selected->mMusic.mSoundBanks.empty();
                const bool bHasSample = !selected->mSoundEffect.mSoundBanks.empty();
                if (bHasMusic)
                {
                    if (ImGui::CollapsingHeader("SEQs"))
                    {
                        for (const std::string& sb : selected->mMusic.mSoundBanks)
                        {
                            if (ImGui::Selectable(sb.c_str()))
                            {
                                auto player = PlaySound(selected->mResourceName.c_str(), sb.c_str(), true, false, bUseCache);
                                if (player)
                                {
                                    std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
                                    mSoundPlayers.push_back(std::move(player));
                                }
                            }
                        }
                    }
                }

                if (bHasSample)
                {
                    if (ImGui::CollapsingHeader("Samples"))
                    {
                        for (const SoundEffectResourceLocation& sbLoc : selected->mSoundEffect.mSoundBanks)
                        {
                            for (const std::string& sb : sbLoc.mSoundBanks)
                            {
                                if (ImGui::Selectable(sb.c_str()))
                                {
                                    auto player = PlaySound(selected->mResourceName.c_str(), sb.c_str(), false, true, bUseCache);
                                    if (player)
                                    {
                                        std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
                                        mSoundPlayers.push_back(std::move(player));
                                    }
                                }
                            }
                        }
                    }
                }

                if (ImGui::Button("Play (cached/scripted)"))
                {
                    PlaySoundScript(selected->mResourceName.c_str());
                }
            }
            else
            {
                ImGui::TextWrapped("Click an item to display its info");
            }
        }
        ImGui::EndChild();
    }
    ImGui::EndGroup();

    ImGui::End();

    ImGui::Begin("Sound themes");

    for (const MusicTheme& theme : mLocator.mResMapper.mSoundResources.mThemes)
    {
        if (ImGui::RadioButton(theme.mName.c_str(), mActiveTheme && theme.mName == mActiveTheme->mName))
        {
            SetTheme(theme.mName.c_str());
            HandleEvent("BASE_LINE");
        }
    }

    for (const char* eventName : kMusicEvents)
    {
        if (ImGui::Button(eventName))
        {
            HandleEvent(eventName);
        }
    }

    ImGui::End();

}

void Sound::Render(int /*w*/, int /*h*/)
{

}
<|endoftext|>


<commit_before>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
#if !defined(XALAN_DECIMALTOROMAN_HEADER_GUARD)
#define XALAN_DECIMALTOROMAN_HEADER_GUARD

// Base include file.  Must be first.
#include "XSLTDefinitions.hpp"

#include <XalanDOM/XalanDOMString.hpp>

/**
 * Structure to help in converting integers to roman numerals
 */
struct XALAN_XSLT_EXPORT DecimalToRoman
{
	enum { eMaxLetter = 2 };

	long			m_postValue;

	XalanDOMChar	m_postLetter[eMaxLetter + 1];

	long			m_preValue;

	XalanDOMChar	m_preLetter[eMaxLetter + 1];
};

#endif	// XALAN_DECIMALTOROMAN_HEADER_GUARD
<commit_msg>Change types to unsigned and use a typedef.<commit_after>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
#if !defined(XALAN_DECIMALTOROMAN_HEADER_GUARD)
#define XALAN_DECIMALTOROMAN_HEADER_GUARD

// Base include file.  Must be first.
#include "XSLTDefinitions.hpp"

#include <XalanDOM/XalanDOMString.hpp>

/**
 * Structure to help in converting integrals to roman numerals
 */
struct XALAN_XSLT_EXPORT DecimalToRoman
{
	enum { eMaxLetter = 2 };

	typedef unsigned long	ValueType;

	ValueType		m_postValue;

	XalanDOMChar	m_postLetter[eMaxLetter + 1];

	ValueType		m_preValue;

	XalanDOMChar	m_preLetter[eMaxLetter + 1];
};

#endif	// XALAN_DECIMALTOROMAN_HEADER_GUARD
<|endoftext|>


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

#include "validation.h"
#include "net_processing.h"
#include "netmessagemaker.h"
#include "darksend.h"
#include "spork.h"

#include <boost/lexical_cast.hpp>

class CSporkMessage;
class CSporkManager;

CSporkManager sporkManager;

std::map<uint256, CSporkMessage> mapSporks;

void CSporkManager::ProcessSpork(CNode* pfrom, std::string& strCommand, CDataStream& vRecv)
{
    if(fLiteMode) return; // disable all Zcoin specific functionality

    if (strCommand == NetMsgType::SPORK) {

        CDataStream vMsg(vRecv);
        CSporkMessage spork;
        vRecv >> spork;

        uint256 hash = spork.GetHash();

        std::string strLogMsg;
        {
            LOCK(cs_main);
            pfrom->setAskFor.erase(hash);
            if(!chainActive.Tip()) return;
            strLogMsg = strprintf("SPORK -- hash: %s id: %d value: %10d bestHeight: %d peer=%d", hash.ToString(), spork.nSporkID, spork.nValue, chainActive.Height(), pfrom->id);
        }

        if(mapSporksActive.count(spork.nSporkID)) {
            if (mapSporksActive[spork.nSporkID].nTimeSigned >= spork.nTimeSigned) {
                LogPrint("spork", "%s seen\n", strLogMsg);
                return;
            } else {
                LogPrintf("%s updated\n", strLogMsg);
            }
        } else {
            LogPrintf("%s new\n", strLogMsg);
        }

        if(!spork.CheckSignature()) {
            LogPrintf("CSporkManager::ProcessSpork -- invalid signature\n");
            Misbehaving(pfrom->GetId(), 100);
            return;
        }

        mapSporks[hash] = spork;
        mapSporksActive[spork.nSporkID] = spork;
        //spork.Relay();

        //does a task if needed
        ExecuteSpork(spork.nSporkID, spork.nValue);

    } else if (strCommand == NetMsgType::GETSPORKS) {

        std::map<int, CSporkMessage>::iterator it = mapSporksActive.begin();

        while(it != mapSporksActive.end()) {
            g_connman->PushMessage(pfrom, CNetMsgMaker(PROTOCOL_VERSION).Make(NetMsgType::SPORK, it->second));
            it++;
        }
    }

}

void CSporkManager::ExecuteSpork(int nSporkID, int nValue)
{
}

bool CSporkManager::UpdateSpork(int nSporkID, int64_t nValue)
{
    CSporkMessage spork = CSporkMessage(nSporkID, nValue, GetTime());

    mapSporks[spork.GetHash()] = spork;
    mapSporksActive[nSporkID] = spork;
    return true;
}

// grab the spork, otherwise say it's off
bool CSporkManager::IsSporkActive(int nSporkID)
{
    int64_t r = -1;

    if(mapSporksActive.count(nSporkID)){
        r = mapSporksActive[nSporkID].nValue;
    } else {
        switch (nSporkID) {
            case SPORK_2_INSTANTSEND_ENABLED:               r = SPORK_2_INSTANTSEND_ENABLED_DEFAULT; break;
            case SPORK_3_INSTANTSEND_BLOCK_FILTERING:       r = SPORK_3_INSTANTSEND_BLOCK_FILTERING_DEFAULT; break;
            case SPORK_5_INSTANTSEND_MAX_VALUE:             r = SPORK_5_INSTANTSEND_MAX_VALUE_DEFAULT; break;
            case SPORK_8_ZNODE_PAYMENT_ENFORCEMENT:    r = SPORK_8_ZNODE_PAYMENT_ENFORCEMENT_DEFAULT; break;
            case SPORK_9_SUPERBLOCKS_ENABLED:               r = SPORK_9_SUPERBLOCKS_ENABLED_DEFAULT; break;
            case SPORK_10_ZNODE_PAY_UPDATED_NODES:     r = SPORK_10_ZNODE_PAY_UPDATED_NODES_DEFAULT; break;
            case SPORK_12_RECONSIDER_BLOCKS:                r = SPORK_12_RECONSIDER_BLOCKS_DEFAULT; break;
            case SPORK_13_OLD_SUPERBLOCK_FLAG:              r = SPORK_13_OLD_SUPERBLOCK_FLAG_DEFAULT; break;
            case SPORK_14_REQUIRE_SENTINEL_FLAG:            r = SPORK_14_REQUIRE_SENTINEL_FLAG_DEFAULT; break;
            default:
                LogPrint("spork", "CSporkManager::IsSporkActive -- Unknown Spork ID %d\n", nSporkID);
                r = 4070908800ULL; // 2099-1-1 i.e. off by default
                break;
        }
    }

    return r < GetTime();
}

// grab the value of the spork on the network, or the default
int64_t CSporkManager::GetSporkValue(int nSporkID)
{
    if (mapSporksActive.count(nSporkID))
        return mapSporksActive[nSporkID].nValue;

    switch (nSporkID) {
        case SPORK_2_INSTANTSEND_ENABLED:               return SPORK_2_INSTANTSEND_ENABLED_DEFAULT;
        case SPORK_3_INSTANTSEND_BLOCK_FILTERING:       return SPORK_3_INSTANTSEND_BLOCK_FILTERING_DEFAULT;
        case SPORK_5_INSTANTSEND_MAX_VALUE:             return SPORK_5_INSTANTSEND_MAX_VALUE_DEFAULT;
        case SPORK_8_ZNODE_PAYMENT_ENFORCEMENT:    return SPORK_8_ZNODE_PAYMENT_ENFORCEMENT_DEFAULT;
        case SPORK_9_SUPERBLOCKS_ENABLED:               return SPORK_9_SUPERBLOCKS_ENABLED_DEFAULT;
        case SPORK_10_ZNODE_PAY_UPDATED_NODES:     return SPORK_10_ZNODE_PAY_UPDATED_NODES_DEFAULT;
        case SPORK_12_RECONSIDER_BLOCKS:                return SPORK_12_RECONSIDER_BLOCKS_DEFAULT;
        case SPORK_13_OLD_SUPERBLOCK_FLAG:              return SPORK_13_OLD_SUPERBLOCK_FLAG_DEFAULT;
        case SPORK_14_REQUIRE_SENTINEL_FLAG:            return SPORK_14_REQUIRE_SENTINEL_FLAG_DEFAULT;
        default:
            LogPrint("spork", "CSporkManager::GetSporkValue -- Unknown Spork ID %d\n", nSporkID);
            return -1;
    }

}

int CSporkManager::GetSporkIDByName(std::string strName)
{
    if (strName == "SPORK_2_INSTANTSEND_ENABLED")               return SPORK_2_INSTANTSEND_ENABLED;
    if (strName == "SPORK_3_INSTANTSEND_BLOCK_FILTERING")       return SPORK_3_INSTANTSEND_BLOCK_FILTERING;
    if (strName == "SPORK_5_INSTANTSEND_MAX_VALUE")             return SPORK_5_INSTANTSEND_MAX_VALUE;
    if (strName == "SPORK_8_ZNODE_PAYMENT_ENFORCEMENT")    return SPORK_8_ZNODE_PAYMENT_ENFORCEMENT;
    if (strName == "SPORK_9_SUPERBLOCKS_ENABLED")               return SPORK_9_SUPERBLOCKS_ENABLED;
    if (strName == "SPORK_10_ZNODE_PAY_UPDATED_NODES")     return SPORK_10_ZNODE_PAY_UPDATED_NODES;
    if (strName == "SPORK_12_RECONSIDER_BLOCKS")                return SPORK_12_RECONSIDER_BLOCKS;
    if (strName == "SPORK_13_OLD_SUPERBLOCK_FLAG")              return SPORK_13_OLD_SUPERBLOCK_FLAG;
    if (strName == "SPORK_14_REQUIRE_SENTINEL_FLAG")            return SPORK_14_REQUIRE_SENTINEL_FLAG;

    LogPrint("spork", "CSporkManager::GetSporkIDByName -- Unknown Spork name '%s'\n", strName);
    return -1;
}

std::string CSporkManager::GetSporkNameByID(int nSporkID)
{
    switch (nSporkID) {
        case SPORK_2_INSTANTSEND_ENABLED:               return "SPORK_2_INSTANTSEND_ENABLED";
        case SPORK_3_INSTANTSEND_BLOCK_FILTERING:       return "SPORK_3_INSTANTSEND_BLOCK_FILTERING";
        case SPORK_5_INSTANTSEND_MAX_VALUE:             return "SPORK_5_INSTANTSEND_MAX_VALUE";
        case SPORK_8_ZNODE_PAYMENT_ENFORCEMENT:    return "SPORK_8_ZNODE_PAYMENT_ENFORCEMENT";
        case SPORK_9_SUPERBLOCKS_ENABLED:               return "SPORK_9_SUPERBLOCKS_ENABLED";
        case SPORK_10_ZNODE_PAY_UPDATED_NODES:     return "SPORK_10_ZNODE_PAY_UPDATED_NODES";
        case SPORK_12_RECONSIDER_BLOCKS:                return "SPORK_12_RECONSIDER_BLOCKS";
        case SPORK_13_OLD_SUPERBLOCK_FLAG:              return "SPORK_13_OLD_SUPERBLOCK_FLAG";
        case SPORK_14_REQUIRE_SENTINEL_FLAG:            return "SPORK_14_REQUIRE_SENTINEL_FLAG";
        default:
            LogPrint("spork", "CSporkManager::GetSporkNameByID -- Unknown Spork ID %d\n", nSporkID);
            return "Unknown";
    }
}

bool CSporkManager::SetPrivKey(std::string strPrivKey)
{
    return true;
    CSporkMessage spork;

    spork.Sign(strPrivKey);

    if(spork.CheckSignature()){
        // Test signing successful, proceed
        LogPrintf("CSporkManager::SetPrivKey -- Successfully initialized as spork signer\n");
        strMasterPrivKey = strPrivKey;
        return true;
    } else {
        return false;
    }
}

bool CSporkMessage::Sign(std::string strSignKey)
{
    CKey key;
    CPubKey pubkey;
    std::string strError = "";
    std::string strMessage = boost::lexical_cast<std::string>(nSporkID) + boost::lexical_cast<std::string>(nValue) + boost::lexical_cast<std::string>(nTimeSigned);

    if(!darkSendSigner.GetKeysFromSecret(strSignKey, key, pubkey)) {
        LogPrintf("CSporkMessage::Sign -- GetKeysFromSecret() failed, invalid spork key %s\n", strSignKey);
        return false;
    }

    if(!darkSendSigner.SignMessage(strMessage, vchSig, key)) {
        LogPrintf("CSporkMessage::Sign -- SignMessage() failed\n");
        return false;
    }

    if(!darkSendSigner.VerifyMessage(pubkey, vchSig, strMessage, strError)) {
        LogPrintf("CSporkMessage::Sign -- VerifyMessage() failed, error: %s\n", strError);
        return false;
    }

    return true;
}

bool CSporkMessage::CheckSignature()
{
    //note: need to investigate why this is failing
    std::string strError = "";
    std::string strMessage = boost::lexical_cast<std::string>(nSporkID) + boost::lexical_cast<std::string>(nValue) + boost::lexical_cast<std::string>(nTimeSigned);
    CPubKey pubkey(ParseHex(Params().SporkPubKey()));

    if(!darkSendSigner.VerifyMessage(pubkey, vchSig, strMessage, strError)) {
        LogPrintf("CSporkMessage::CheckSignature -- VerifyMessage() failed, error: %s\n", strError);
        return false;
    }

    return true;
}

void CSporkMessage::Relay()
{
    // don't do anything here
    /*
    CInv inv(MSG_SPORK, GetHash());
    g_connman->RelayInv(inv);
    */
}
<commit_msg>Spork signing reinstated to stop banning by 0.13.* peers<commit_after>// Copyright (c) 2014-2017 The Dash Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "validation.h"
#include "net_processing.h"
#include "netmessagemaker.h"
#include "darksend.h"
#include "spork.h"

#include <boost/lexical_cast.hpp>

class CSporkMessage;
class CSporkManager;

CSporkManager sporkManager;

std::map<uint256, CSporkMessage> mapSporks;

void CSporkManager::ProcessSpork(CNode* pfrom, std::string& strCommand, CDataStream& vRecv)
{
    if(fLiteMode) return; // disable all Zcoin specific functionality

    if (strCommand == NetMsgType::SPORK) {

        CDataStream vMsg(vRecv);
        CSporkMessage spork;
        vRecv >> spork;

        uint256 hash = spork.GetHash();

        std::string strLogMsg;
        {
            LOCK(cs_main);
            pfrom->setAskFor.erase(hash);
            if(!chainActive.Tip()) return;
            strLogMsg = strprintf("SPORK -- hash: %s id: %d value: %10d bestHeight: %d peer=%d", hash.ToString(), spork.nSporkID, spork.nValue, chainActive.Height(), pfrom->id);
        }

        if(mapSporksActive.count(spork.nSporkID)) {
            if (mapSporksActive[spork.nSporkID].nTimeSigned >= spork.nTimeSigned) {
                LogPrint("spork", "%s seen\n", strLogMsg);
                return;
            } else {
                LogPrintf("%s updated\n", strLogMsg);
            }
        } else {
            LogPrintf("%s new\n", strLogMsg);
        }

        if(!spork.CheckSignature()) {
            LogPrintf("CSporkManager::ProcessSpork -- invalid signature\n");
            Misbehaving(pfrom->GetId(), 100);
            return;
        }

        mapSporks[hash] = spork;
        mapSporksActive[spork.nSporkID] = spork;
        //spork.Relay();

        //does a task if needed
        ExecuteSpork(spork.nSporkID, spork.nValue);

    } else if (strCommand == NetMsgType::GETSPORKS) {

        std::map<int, CSporkMessage>::iterator it = mapSporksActive.begin();

        while(it != mapSporksActive.end()) {
            g_connman->PushMessage(pfrom, CNetMsgMaker(PROTOCOL_VERSION).Make(NetMsgType::SPORK, it->second));
            it++;
        }
    }

}

void CSporkManager::ExecuteSpork(int nSporkID, int nValue)
{
}

bool CSporkManager::UpdateSpork(int nSporkID, int64_t nValue)
{
    CSporkMessage spork = CSporkMessage(nSporkID, nValue, GetTime());

    if(spork.Sign(strMasterPrivKey)) {
        //spork.Relay();
        mapSporks[spork.GetHash()] = spork;
        mapSporksActive[nSporkID] = spork;
        return true;
    }

    return true;
}

// grab the spork, otherwise say it's off
bool CSporkManager::IsSporkActive(int nSporkID)
{
    int64_t r = -1;

    if(mapSporksActive.count(nSporkID)){
        r = mapSporksActive[nSporkID].nValue;
    } else {
        switch (nSporkID) {
            case SPORK_2_INSTANTSEND_ENABLED:               r = SPORK_2_INSTANTSEND_ENABLED_DEFAULT; break;
            case SPORK_3_INSTANTSEND_BLOCK_FILTERING:       r = SPORK_3_INSTANTSEND_BLOCK_FILTERING_DEFAULT; break;
            case SPORK_5_INSTANTSEND_MAX_VALUE:             r = SPORK_5_INSTANTSEND_MAX_VALUE_DEFAULT; break;
            case SPORK_8_ZNODE_PAYMENT_ENFORCEMENT:    r = SPORK_8_ZNODE_PAYMENT_ENFORCEMENT_DEFAULT; break;
            case SPORK_9_SUPERBLOCKS_ENABLED:               r = SPORK_9_SUPERBLOCKS_ENABLED_DEFAULT; break;
            case SPORK_10_ZNODE_PAY_UPDATED_NODES:     r = SPORK_10_ZNODE_PAY_UPDATED_NODES_DEFAULT; break;
            case SPORK_12_RECONSIDER_BLOCKS:                r = SPORK_12_RECONSIDER_BLOCKS_DEFAULT; break;
            case SPORK_13_OLD_SUPERBLOCK_FLAG:              r = SPORK_13_OLD_SUPERBLOCK_FLAG_DEFAULT; break;
            case SPORK_14_REQUIRE_SENTINEL_FLAG:            r = SPORK_14_REQUIRE_SENTINEL_FLAG_DEFAULT; break;
            default:
                LogPrint("spork", "CSporkManager::IsSporkActive -- Unknown Spork ID %d\n", nSporkID);
                r = 4070908800ULL; // 2099-1-1 i.e. off by default
                break;
        }
    }

    return r < GetTime();
}

// grab the value of the spork on the network, or the default
int64_t CSporkManager::GetSporkValue(int nSporkID)
{
    if (mapSporksActive.count(nSporkID))
        return mapSporksActive[nSporkID].nValue;

    switch (nSporkID) {
        case SPORK_2_INSTANTSEND_ENABLED:               return SPORK_2_INSTANTSEND_ENABLED_DEFAULT;
        case SPORK_3_INSTANTSEND_BLOCK_FILTERING:       return SPORK_3_INSTANTSEND_BLOCK_FILTERING_DEFAULT;
        case SPORK_5_INSTANTSEND_MAX_VALUE:             return SPORK_5_INSTANTSEND_MAX_VALUE_DEFAULT;
        case SPORK_8_ZNODE_PAYMENT_ENFORCEMENT:    return SPORK_8_ZNODE_PAYMENT_ENFORCEMENT_DEFAULT;
        case SPORK_9_SUPERBLOCKS_ENABLED:               return SPORK_9_SUPERBLOCKS_ENABLED_DEFAULT;
        case SPORK_10_ZNODE_PAY_UPDATED_NODES:     return SPORK_10_ZNODE_PAY_UPDATED_NODES_DEFAULT;
        case SPORK_12_RECONSIDER_BLOCKS:                return SPORK_12_RECONSIDER_BLOCKS_DEFAULT;
        case SPORK_13_OLD_SUPERBLOCK_FLAG:              return SPORK_13_OLD_SUPERBLOCK_FLAG_DEFAULT;
        case SPORK_14_REQUIRE_SENTINEL_FLAG:            return SPORK_14_REQUIRE_SENTINEL_FLAG_DEFAULT;
        default:
            LogPrint("spork", "CSporkManager::GetSporkValue -- Unknown Spork ID %d\n", nSporkID);
            return -1;
    }

}

int CSporkManager::GetSporkIDByName(std::string strName)
{
    if (strName == "SPORK_2_INSTANTSEND_ENABLED")               return SPORK_2_INSTANTSEND_ENABLED;
    if (strName == "SPORK_3_INSTANTSEND_BLOCK_FILTERING")       return SPORK_3_INSTANTSEND_BLOCK_FILTERING;
    if (strName == "SPORK_5_INSTANTSEND_MAX_VALUE")             return SPORK_5_INSTANTSEND_MAX_VALUE;
    if (strName == "SPORK_8_ZNODE_PAYMENT_ENFORCEMENT")    return SPORK_8_ZNODE_PAYMENT_ENFORCEMENT;
    if (strName == "SPORK_9_SUPERBLOCKS_ENABLED")               return SPORK_9_SUPERBLOCKS_ENABLED;
    if (strName == "SPORK_10_ZNODE_PAY_UPDATED_NODES")     return SPORK_10_ZNODE_PAY_UPDATED_NODES;
    if (strName == "SPORK_12_RECONSIDER_BLOCKS")                return SPORK_12_RECONSIDER_BLOCKS;
    if (strName == "SPORK_13_OLD_SUPERBLOCK_FLAG")              return SPORK_13_OLD_SUPERBLOCK_FLAG;
    if (strName == "SPORK_14_REQUIRE_SENTINEL_FLAG")            return SPORK_14_REQUIRE_SENTINEL_FLAG;

    LogPrint("spork", "CSporkManager::GetSporkIDByName -- Unknown Spork name '%s'\n", strName);
    return -1;
}

std::string CSporkManager::GetSporkNameByID(int nSporkID)
{
    switch (nSporkID) {
        case SPORK_2_INSTANTSEND_ENABLED:               return "SPORK_2_INSTANTSEND_ENABLED";
        case SPORK_3_INSTANTSEND_BLOCK_FILTERING:       return "SPORK_3_INSTANTSEND_BLOCK_FILTERING";
        case SPORK_5_INSTANTSEND_MAX_VALUE:             return "SPORK_5_INSTANTSEND_MAX_VALUE";
        case SPORK_8_ZNODE_PAYMENT_ENFORCEMENT:    return "SPORK_8_ZNODE_PAYMENT_ENFORCEMENT";
        case SPORK_9_SUPERBLOCKS_ENABLED:               return "SPORK_9_SUPERBLOCKS_ENABLED";
        case SPORK_10_ZNODE_PAY_UPDATED_NODES:     return "SPORK_10_ZNODE_PAY_UPDATED_NODES";
        case SPORK_12_RECONSIDER_BLOCKS:                return "SPORK_12_RECONSIDER_BLOCKS";
        case SPORK_13_OLD_SUPERBLOCK_FLAG:              return "SPORK_13_OLD_SUPERBLOCK_FLAG";
        case SPORK_14_REQUIRE_SENTINEL_FLAG:            return "SPORK_14_REQUIRE_SENTINEL_FLAG";
        default:
            LogPrint("spork", "CSporkManager::GetSporkNameByID -- Unknown Spork ID %d\n", nSporkID);
            return "Unknown";
    }
}

bool CSporkManager::SetPrivKey(std::string strPrivKey)
{
    return true;
    CSporkMessage spork;

    spork.Sign(strPrivKey);

    if(spork.CheckSignature()){
        // Test signing successful, proceed
        LogPrintf("CSporkManager::SetPrivKey -- Successfully initialized as spork signer\n");
        strMasterPrivKey = strPrivKey;
        return true;
    } else {
        return false;
    }
}

bool CSporkMessage::Sign(std::string strSignKey)
{
    CKey key;
    CPubKey pubkey;
    std::string strError = "";
    std::string strMessage = boost::lexical_cast<std::string>(nSporkID) + boost::lexical_cast<std::string>(nValue) + boost::lexical_cast<std::string>(nTimeSigned);

    if(!darkSendSigner.GetKeysFromSecret(strSignKey, key, pubkey)) {
        LogPrintf("CSporkMessage::Sign -- GetKeysFromSecret() failed, invalid spork key %s\n", strSignKey);
        return false;
    }

    if(!darkSendSigner.SignMessage(strMessage, vchSig, key)) {
        LogPrintf("CSporkMessage::Sign -- SignMessage() failed\n");
        return false;
    }

    if(!darkSendSigner.VerifyMessage(pubkey, vchSig, strMessage, strError)) {
        LogPrintf("CSporkMessage::Sign -- VerifyMessage() failed, error: %s\n", strError);
        return false;
    }

    return true;
}

bool CSporkMessage::CheckSignature()
{
    //note: need to investigate why this is failing
    std::string strError = "";
    std::string strMessage = boost::lexical_cast<std::string>(nSporkID) + boost::lexical_cast<std::string>(nValue) + boost::lexical_cast<std::string>(nTimeSigned);
    CPubKey pubkey(ParseHex(Params().SporkPubKey()));

    if(!darkSendSigner.VerifyMessage(pubkey, vchSig, strMessage, strError)) {
        LogPrintf("CSporkMessage::CheckSignature -- VerifyMessage() failed, error: %s\n", strError);
        return false;
    }

    return true;
}

void CSporkMessage::Relay()
{
    CInv inv(MSG_SPORK, GetHash());
    g_connman->RelayInv(inv);
}
<|endoftext|>


<commit_before>#include <unordered_map>
#include <string>
#include <Rcpp.h>
using namespace Rcpp;
using namespace std;

template<int RTYPE> Vector<RTYPE> cpp_stack_impl(List array_list) {
    auto dimnames = vector<vector<string>>(); // dim: names along
    auto axmap = vector<unordered_map<string, int>>(); // dim: element name->index
    auto a2r = vector<vector<vector<int>>>(array_list.size()); // array > dim > element

    // create lookup tables for all present dimension names
    for (int ai=0; ai<Rf_xlength(array_list); ai++) { // array index
        auto a = as<Vector<RTYPE>>(array_list[ai]);
        auto dn = as<List>(a.attr("dimnames"));
        auto da = as<vector<int>>(a.attr("dim"));

        a2r[ai] = vector<vector<int>>(da.size());
        if (dimnames.size() < da.size()) {
            dimnames.resize(da.size());
            axmap.resize(da.size());
        }

        for (int d=0; d<da.size(); d++) { // dimension in array
            auto dni = as<vector<string>>(dn[d]);

            for (int e=0; e<da[d]; e++) { // element in dimension
                if (axmap[d].count(dni[e]) == 0) {
                    cout << "array " << ai << " dim " << d << ": " << dni[e] << " -> " << axmap[d].size() << "\n";
                    axmap[d].emplace(dni[e], axmap[d].size());
                    dimnames[d].push_back(dni[e]);
                }
                a2r[ai][d].push_back(axmap[d][dni[e]]);
            }
        }
    }

    for (int ai=0; ai<a2r.size(); ai++)
        for (int di=0; di<a2r[ai].size(); di++) {
            cout << "*** array " << ai << " dim " << di << ": ";
            copy(a2r[ai][di].begin(), a2r[ai][di].end(), ostream_iterator<int>(cout, " "));
            cout << "\n";
        }

    // create result array with attributes
    auto rdim = IntegerVector(dimnames.size());
    auto rdnames = List(dimnames.size());
    for (int i=0; i<dimnames.size(); i++) {
        rdim[i] = dimnames[i].size();
        rdnames[i] = CharacterVector(dimnames[i].begin(), dimnames[i].end());
    }
    auto n = accumulate(rdim.begin(), rdim.end(), 1, multiplies<int>());
    auto result = Vector<RTYPE>(n);
    result.attr("dim") = rdim;
    result.attr("dimnames") = rdnames;

    // fill the result array
    int maxdim = rdim.size() - 1;
    for (int ai=0; ai<Rf_xlength(array_list); ai++) { // each original array
        auto a = as<Vector<RTYPE>>(array_list[ai]);
        auto it = vector<vector<int>::iterator>(a2r[ai].size()); // each result dim
        for (int d=0; d<it.size(); d++)
            it[d] = a2r[ai][d].begin();

        int dim_offset;
        bool new_offset = true;
        for (int aidx=0; aidx<a.size(); aidx++) { // element in original array
            if (new_offset) { // dimension jump
                dim_offset = 0;
                for (int d=0; d<maxdim; d++)
                    dim_offset += rdim[d] * *it[d+1];
                new_offset = false;
            }

            cout << "result[" << *it[0] + dim_offset << "] = a[" << ai << "][" << aidx << "]\n";
            result[*it[0] + dim_offset] = a[aidx];

            it[0]++;
            for (int d=0; d<maxdim; d++) { // check if we're jumping dimensions
                if (it[d] != a2r[ai][d].end()) // dim+1 jump only if dim jump
                    break;

                new_offset = true;
                it[d] = a2r[ai][d].begin();
                it[d+1]++;
            }
        };
    }

    return result;
}

// [[Rcpp::export]]
SEXP cpp_stack(List array_list) {
    auto max_type = NILSXP;
    for (int ai=0; ai<array_list.size(); ai++) {
        int cur_type = TYPEOF(array_list[ai]);
        if (cur_type < LGLSXP || cur_type > STRSXP)
            stop("Invalid type: %d %s\n", cur_type, type2name(array_list[ai]));
        if (cur_type > max_type)
            max_type = cur_type;
    }

    switch(max_type) {
        case LGLSXP: return cpp_stack_impl<LGLSXP>(array_list);
        case INTSXP: return cpp_stack_impl<INTSXP>(array_list);
        case REALSXP: return cpp_stack_impl<REALSXP>(array_list);
        case CPLXSXP: return cpp_stack_impl<CPLXSXP>(array_list);
        case STRSXP: return cpp_stack_impl<STRSXP>(array_list);
        default: return R_NilValue; // this should not happen
    }
}
<commit_msg>comment out debug<commit_after>#include <unordered_map>
#include <string>
#include <Rcpp.h>
using namespace Rcpp;
using namespace std;

template<int RTYPE> Vector<RTYPE> cpp_stack_impl(List array_list) {
    auto dimnames = vector<vector<string>>(); // dim: names along
    auto axmap = vector<unordered_map<string, int>>(); // dim: element name->index
    auto a2r = vector<vector<vector<int>>>(array_list.size()); // array > dim > element

    // create lookup tables for all present dimension names
    for (int ai=0; ai<Rf_xlength(array_list); ai++) { // array index
        auto a = as<Vector<RTYPE>>(array_list[ai]);
        auto dn = as<List>(a.attr("dimnames"));
        auto da = as<vector<int>>(a.attr("dim"));

        a2r[ai] = vector<vector<int>>(da.size());
        if (dimnames.size() < da.size()) {
            dimnames.resize(da.size());
            axmap.resize(da.size());
        }

        for (int d=0; d<da.size(); d++) { // dimension in array
            auto dni = as<vector<string>>(dn[d]);

            for (int e=0; e<da[d]; e++) { // element in dimension
                if (axmap[d].count(dni[e]) == 0) {
//                    cout << "array " << ai << " dim " << d << ": " << dni[e] << " -> " << axmap[d].size() << "\n";
                    axmap[d].emplace(dni[e], axmap[d].size());
                    dimnames[d].push_back(dni[e]);
                }
                a2r[ai][d].push_back(axmap[d][dni[e]]);
            }
        }
    }

/*    for (int ai=0; ai<a2r.size(); ai++)
        for (int di=0; di<a2r[ai].size(); di++) {
            cout << "*** array " << ai << " dim " << di << ": ";
            copy(a2r[ai][di].begin(), a2r[ai][di].end(), ostream_iterator<int>(cout, " "));
            cout << "\n";
        }
*/
    // create result array with attributes
    auto rdim = IntegerVector(dimnames.size());
    auto rdnames = List(dimnames.size());
    for (int i=0; i<dimnames.size(); i++) {
        rdim[i] = dimnames[i].size();
        rdnames[i] = CharacterVector(dimnames[i].begin(), dimnames[i].end());
    }
    auto n = accumulate(rdim.begin(), rdim.end(), 1, multiplies<int>());
    auto result = Vector<RTYPE>(n);
    result.attr("dim") = rdim;
    result.attr("dimnames") = rdnames;

    // fill the result array
    int maxdim = rdim.size() - 1;
    for (int ai=0; ai<Rf_xlength(array_list); ai++) { // each original array
        auto a = as<Vector<RTYPE>>(array_list[ai]);
        auto it = vector<vector<int>::iterator>(a2r[ai].size()); // each result dim
        for (int d=0; d<it.size(); d++)
            it[d] = a2r[ai][d].begin();

        int dim_offset;
        bool new_offset = true;
        for (int aidx=0; aidx<a.size(); aidx++) { // element in original array
            if (new_offset) { // dimension jump
                dim_offset = 0;
                for (int d=0; d<maxdim; d++)
                    dim_offset += rdim[d] * *it[d+1];
                new_offset = false;
            }

//            cout << "result[" << *it[0] + dim_offset << "] = a[" << ai << "][" << aidx << "]\n";
            result[*it[0] + dim_offset] = a[aidx];

            it[0]++;
            for (int d=0; d<maxdim; d++) { // check if we're jumping dimensions
                if (it[d] != a2r[ai][d].end()) // dim+1 jump only if dim jump
                    break;

                new_offset = true;
                it[d] = a2r[ai][d].begin();
                it[d+1]++;
            }
        };
    }

    return result;
}

// [[Rcpp::export]]
SEXP cpp_stack(List array_list) {
    auto max_type = NILSXP;
    for (int ai=0; ai<array_list.size(); ai++) {
        int cur_type = TYPEOF(array_list[ai]);
        if (cur_type < LGLSXP || cur_type > STRSXP)
            stop("Invalid type: %d %s\n", cur_type, type2name(array_list[ai]));
        if (cur_type > max_type)
            max_type = cur_type;
    }

    switch(max_type) {
        case LGLSXP: return cpp_stack_impl<LGLSXP>(array_list);
        case INTSXP: return cpp_stack_impl<INTSXP>(array_list);
        case REALSXP: return cpp_stack_impl<REALSXP>(array_list);
        case CPLXSXP: return cpp_stack_impl<CPLXSXP>(array_list);
        case STRSXP: return cpp_stack_impl<STRSXP>(array_list);
        default: return R_NilValue; // this should not happen
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2018  Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
 *
 * This library 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 2.1 of the License, or (at your option) version 3, or any
 * later version accepted by the membership of KDE e.V. (or its
 * successor approved by the membership of KDE e.V.), which shall
 * act as a proxy defined in Section 6 of version 3 of the license.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "AcbfReferences.h"
#include <QTimer>
#include <QXmlStreamReader>

#include <acbf_debug.h>

using namespace AdvancedComicBookFormat;

class References::Private {
public:
    Private(References* qq)
        : q(qq)
    {}
    References* q;
    QMultiHash<QString, Reference*> referencesById;
    QObjectList references;

    void addReference(Reference* reference, bool emitListChangeSignal = true) {
        referencesById.insert(reference->id(), reference);
        references << reference;
        QObject::connect(reference, &Reference::languageChanged, q, &References::referencesChanged);
        QObject::connect(reference, &Reference::paragraphsChanged, q, &References::referencesChanged);
        QObject::connect(reference, &Reference::idChanged, q, [this, reference](){
            QMutableHashIterator<QString, Reference*> iterator(referencesById);
            while(iterator.findNext(reference)) {
                iterator.remove();
            }
            referencesById.insert(reference->id(), reference);
            Q_EMIT q->referencesChanged();
        });
        QObject::connect(reference, &QObject::destroyed, q, [this, reference](){
            referencesById.remove(referencesById.key(reference));
            references.removeAll(reference);
            Q_EMIT q->referencesChanged();
        });
        Q_EMIT q->referenceAdded(reference);
        if (emitListChangeSignal) {
            Q_EMIT q->referencesChanged();
        }
    }
};

References::References(Document* parent)
    : QObject(parent)
    , d(new Private(this))
{
    static const int typeId = qRegisterMetaType<References*>("References*");
    Q_UNUSED(typeId);
}

References::~References() = default;

void References::toXml(QXmlStreamWriter* writer) {
    writer->writeStartElement(QStringLiteral("references"));

    for(QObject* reference : d->references) {
        qobject_cast<Reference*>(reference)->toXml(writer);
    }
    writer->writeEndElement();
}

bool References::fromXml(QXmlStreamReader *xmlReader)
{
    qDeleteAll(d->references);
    while(xmlReader->readNextStartElement())
    {
        if(xmlReader->name() == QStringLiteral("reference"))
        {
            Reference* newReference = new Reference(this);
            if(!newReference->fromXml(xmlReader)) {
                return false;
            }
            d->addReference(newReference, false);
        }
        else
        {
            qCWarning(ACBF_LOG) << Q_FUNC_INFO << "currently unsupported subsection:" << xmlReader->name();
            xmlReader->skipCurrentElement();
        }
    }

    if (xmlReader->hasError()) {
        qCWarning(ACBF_LOG) << Q_FUNC_INFO << "Failed to read ACBF XML document at token" << xmlReader->name() << "(" << xmlReader->lineNumber() << ":" << xmlReader->columnNumber() << ") The reported error was:" << xmlReader->errorString();
    }

    qCDebug(ACBF_LOG) << Q_FUNC_INFO << "Created reference section with" << d->references.count() << "references";
    Q_EMIT referencesChanged();

    return !xmlReader->hasError();
}

Reference* References::reference(const QString& id) const
{
    return d->referencesById.value(id);
}

Reference* References::addReference(const QString& id, const QStringList& paragraphs, const QString& language)
{
    Reference* ref = new Reference(this);
    ref->setId(id);
    ref->setParagraphs(paragraphs);
    ref->setLanguage(language);
    d->addReference(ref);
    return ref;
}

QStringList References::referenceIds() const
{
    return d->referencesById.keys();
}

QObjectList References::references() const
{
    return d->references;
}

int References::referenceIndex(Reference* reference) const
{
    return d->references.indexOf(reference);
}

void References::swapReferences(QObject* swapThis, QObject* withThis)
{
    int first = d->references.indexOf(swapThis);
    int second = d->references.indexOf(withThis);
    swapReferencesByIndex(first, second);
}

void References::swapReferencesByIndex(int swapThis, int withThis)
{
    QObject* first = d->references[swapThis];
    QObject* second = d->references[withThis];
    d->references[swapThis] = second;
    d->references[withThis] = first;
    Q_EMIT referencesChanged();
    QTimer::singleShot(100, this, &References::referencesChanged);
}
<commit_msg>Swap reference more simply, and emit changes for the child properties<commit_after>/*
 * Copyright 2018  Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
 *
 * This library 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 2.1 of the License, or (at your option) version 3, or any
 * later version accepted by the membership of KDE e.V. (or its
 * successor approved by the membership of KDE e.V.), which shall
 * act as a proxy defined in Section 6 of version 3 of the license.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "AcbfReferences.h"
#include <QTimer>
#include <QXmlStreamReader>

#include <acbf_debug.h>

using namespace AdvancedComicBookFormat;

class References::Private {
public:
    Private(References* qq)
        : q(qq)
    {}
    References* q;
    QMultiHash<QString, Reference*> referencesById;
    QObjectList references;

    void addReference(Reference* reference, bool emitListChangeSignal = true) {
        referencesById.insert(reference->id(), reference);
        references << reference;
        QObject::connect(reference, &Reference::languageChanged, q, &References::referencesChanged);
        QObject::connect(reference, &Reference::paragraphsChanged, q, &References::referencesChanged);
        QObject::connect(reference, &Reference::idChanged, q, [this, reference](){
            QMutableHashIterator<QString, Reference*> iterator(referencesById);
            while(iterator.findNext(reference)) {
                iterator.remove();
            }
            referencesById.insert(reference->id(), reference);
            Q_EMIT q->referencesChanged();
        });
        QObject::connect(reference, &QObject::destroyed, q, [this, reference](){
            referencesById.remove(referencesById.key(reference));
            references.removeAll(reference);
            Q_EMIT q->referencesChanged();
        });
        Q_EMIT q->referenceAdded(reference);
        if (emitListChangeSignal) {
            Q_EMIT q->referencesChanged();
        }
    }
};

References::References(Document* parent)
    : QObject(parent)
    , d(new Private(this))
{
    static const int typeId = qRegisterMetaType<References*>("References*");
    Q_UNUSED(typeId);
}

References::~References() = default;

void References::toXml(QXmlStreamWriter* writer) {
    writer->writeStartElement(QStringLiteral("references"));

    for(QObject* reference : d->references) {
        qobject_cast<Reference*>(reference)->toXml(writer);
    }
    writer->writeEndElement();
}

bool References::fromXml(QXmlStreamReader *xmlReader)
{
    qDeleteAll(d->references);
    while(xmlReader->readNextStartElement())
    {
        if(xmlReader->name() == QStringLiteral("reference"))
        {
            Reference* newReference = new Reference(this);
            if(!newReference->fromXml(xmlReader)) {
                return false;
            }
            d->addReference(newReference, false);
        }
        else
        {
            qCWarning(ACBF_LOG) << Q_FUNC_INFO << "currently unsupported subsection:" << xmlReader->name();
            xmlReader->skipCurrentElement();
        }
    }

    if (xmlReader->hasError()) {
        qCWarning(ACBF_LOG) << Q_FUNC_INFO << "Failed to read ACBF XML document at token" << xmlReader->name() << "(" << xmlReader->lineNumber() << ":" << xmlReader->columnNumber() << ") The reported error was:" << xmlReader->errorString();
    }

    qCDebug(ACBF_LOG) << Q_FUNC_INFO << "Created reference section with" << d->references.count() << "references";
    Q_EMIT referencesChanged();

    return !xmlReader->hasError();
}

Reference* References::reference(const QString& id) const
{
    return d->referencesById.value(id);
}

Reference* References::addReference(const QString& id, const QStringList& paragraphs, const QString& language)
{
    Reference* ref = new Reference(this);
    ref->setId(id);
    ref->setParagraphs(paragraphs);
    ref->setLanguage(language);
    d->addReference(ref);
    return ref;
}

QStringList References::referenceIds() const
{
    return d->referencesById.keys();
}

QObjectList References::references() const
{
    return d->references;
}

int References::referenceIndex(Reference* reference) const
{
    return d->references.indexOf(reference);
}

void References::swapReferences(QObject* swapThis, QObject* withThis)
{
    int first = d->references.indexOf(swapThis);
    int second = d->references.indexOf(withThis);
    swapReferencesByIndex(first, second);
}

void References::swapReferencesByIndex(int swapThis, int withThis)
{
    if (swapThis > -1 && swapThis < d->references.count() && withThis > -1 && withThis < d->references.count()) {
        d->references.swapItemsAt(swapThis, withThis);
        InternalReferenceObject* first = qobject_cast<InternalReferenceObject*>(d->references[swapThis]);
        InternalReferenceObject* second = qobject_cast<InternalReferenceObject*>(d->references[withThis]);
        Q_EMIT first->propertyDataChanged();
        Q_EMIT second->propertyDataChanged();
        Q_EMIT referencesChanged();
    } else {
        qCWarning(ACBF_LOG) << "There was an attempt to swap two references, and at least one of them was outside the bounds of the current list of references:" << swapThis << withThis;
    }
}
<|endoftext|>


<commit_before>#pragma once
/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/

#ifndef _QITYPE_SIGNAL_HPP_
#define _QITYPE_SIGNAL_HPP_

#include <qi/atomic.hpp>
#include <qi/eventloop.hpp>
#include <qitype/signature.hpp>
#include <qitype/functiontype.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>

namespace qi {

  class ManageablePrivate;
  class SignalSubscriber;
  /** User classes can inherit from Manageable to benefit from  Event loop
  *  management
  */
  class QITYPE_API Manageable
  {
  public:
    Manageable();
    ~Manageable();
    Manageable(const Manageable& b);
    void operator = (const Manageable& b);

    EventLoop* eventLoop() const;
    void moveToEventLoop(EventLoop* eventLoop);

    ManageablePrivate* _p;
  };

  class GenericObject;
  typedef boost::shared_ptr<GenericObject> ObjectPtr;
  typedef boost::weak_ptr<GenericObject> ObjectWeakPtr;
  class SignalBasePrivate;

  class QITYPE_API SignalBase
  {
  public:
    explicit SignalBase(const std::string& signature);
    SignalBase();
    ~SignalBase();
    SignalBase(const SignalBase& b);
    SignalBase& operator = (const SignalBase& b);
    virtual std::string signature() const;

    typedef unsigned int Link;

    template<typename FUNCTION_TYPE>
    Link connect(FUNCTION_TYPE f, EventLoop* ctx = getDefaultObjectEventLoop());

    Link connect(qi::ObjectPtr target, unsigned int slot);
    Link connect(GenericFunction callback, EventLoop* ctx = getDefaultObjectEventLoop());
    Link connect(const SignalSubscriber& s);

    bool disconnectAll();

    /** Disconnect a SignalHandler. The associated callback will not be called
     * anymore as soon as this function returns, but might be called in an
     * other thread while this function runs.
     */
    bool disconnect(const Link& link);

    void trigger(const GenericFunctionParameters& params);
    void operator()(
      qi::AutoGenericValue p1 = qi::AutoGenericValue(),
      qi::AutoGenericValue p2 = qi::AutoGenericValue(),
      qi::AutoGenericValue p3 = qi::AutoGenericValue(),
      qi::AutoGenericValue p4 = qi::AutoGenericValue(),
      qi::AutoGenericValue p5 = qi::AutoGenericValue(),
      qi::AutoGenericValue p6 = qi::AutoGenericValue(),
      qi::AutoGenericValue p7 = qi::AutoGenericValue(),
      qi::AutoGenericValue p8 = qi::AutoGenericValue());

    std::vector<SignalSubscriber> subscribers();
    static const SignalBase::Link invalidLink;
  public:
    boost::shared_ptr<SignalBasePrivate> _p;
  };

  template<typename FUNCTION_TYPE>
  inline SignalBase::Link SignalBase::connect(FUNCTION_TYPE  callback, EventLoop* ctx)
  {
    return connect(makeGenericFunction(callback), ctx);
  }

  template<typename T>
  class Signal: public SignalBase, public boost::function<T>
  {
  public:
    Signal();
    Signal(const Signal<T>& b);
    Signal<T>& operator = (const Signal<T>& b);
    virtual std::string signature() const;
    using boost::function<T>::operator();
    inline SignalBase::Link connect(boost::function<T> f, EventLoop* ctx=getDefaultObjectEventLoop())
    {
      return SignalBase::connect(f, ctx);
    }
    inline SignalBase::Link connect(GenericFunction f, EventLoop* ctx=getDefaultObjectEventLoop())
    {
      return SignalBase::connect(f, ctx);
    }
    /// Auto-disconnects on target destruction
    inline SignalBase::Link connect(qi::ObjectPtr target, unsigned int slot)
    {
      return SignalBase::connect(target, slot);
    }
    /// IF O is a shared_ptr, will auto-disconnect if object is destroyed
    template<typename O, typename MF>
    inline SignalBase::Link connect(O* target, MF method, EventLoop* ctx=getDefaultObjectEventLoop());
    template<typename O, typename MF>
    inline SignalBase::Link connect(boost::shared_ptr<O> target, MF method, EventLoop* ctx=getDefaultObjectEventLoop());
  };

  namespace detail
  {
    /// Interface for a weak-lock recursive mechanism: if lock fail, unregister callback
    class WeakLock
    {
    public:
      virtual ~WeakLock(){}
      virtual bool tryLock() = 0;
      virtual void unlock() = 0;
      virtual WeakLock* clone() = 0;
    };
  }

 /** Event subscriber info.
  *
  * Only one of handler or target must be set.
  */
 class QITYPE_API SignalSubscriber
 : public boost::enable_shared_from_this<SignalSubscriber>
 {
 public:
   SignalSubscriber()
     : weakLock(0), target(0), method(0), enabled(true)
   {}

   SignalSubscriber(GenericFunction func, EventLoop* ctx = getDefaultObjectEventLoop(), detail::WeakLock* lock = 0);

   SignalSubscriber(qi::ObjectPtr target, unsigned int method);

   template<typename O, typename MF>
   SignalSubscriber(O* ptr, MF function, EventLoop* ctx = getDefaultObjectEventLoop());

   template<typename O, typename MF>
   SignalSubscriber(boost::shared_ptr<O> ptr, MF function, EventLoop* ctx = getDefaultObjectEventLoop());

   SignalSubscriber(const SignalSubscriber& b);

   ~SignalSubscriber();

   void operator = (const SignalSubscriber& b);

   void call(const GenericFunctionParameters& args);

   //wait till all threads are inactive except the current thread.
   void waitForInactive();

   void addActive(bool acquireLock, boost::thread::id tid = boost::this_thread::get_id());
   void removeActive(bool acquireLock, boost::thread::id tid = boost::this_thread::get_id());
 public:
   // Source information
   SignalBase*        source;
   /// Uid that can be passed to GenericObject::disconnect()
   SignalBase::Link  linkId;

   // Target information, kept here to be able to introspect a Subscriber
   //   Mode 1: Direct functor call
   GenericFunction      handler;
   detail::WeakLock*    weakLock; // try to acquire weakLocker, disconnect if cant
   boost::function<EventLoop*(void)> eventLoopGetter;

   //  Mode 2: metaCall
   ObjectWeakPtr*       target;
   unsigned int         method;

   boost::mutex                 mutex;
   // Fields below are protected by lock

   // If enabled is set to false while lock is acquired,
   // No more callback will trigger (activeThreads will se no push-back)
   bool                         enabled;
   // Number of calls in progress.
   // Each entry there is a subscriber call that can no longuer be aborted
   std::vector<boost::thread::id> activeThreads; // order not preserved
 };
 typedef boost::shared_ptr<SignalSubscriber> SignalSubscriberPtr;
}

#include <qitype/details/signal.hxx>

QI_NO_TYPE(qi::SignalBase)

#endif  // _QITYPE_SIGNAL_HPP_
<commit_msg>Signal: Add missing include for enable_shared_from_this.<commit_after>#pragma once
/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/

#ifndef _QITYPE_SIGNAL_HPP_
#define _QITYPE_SIGNAL_HPP_

#include <qi/atomic.hpp>
#include <qi/eventloop.hpp>
#include <qitype/signature.hpp>
#include <qitype/functiontype.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/enable_shared_from_this.hpp>

namespace qi {

  class ManageablePrivate;
  class SignalSubscriber;
  /** User classes can inherit from Manageable to benefit from  Event loop
  *  management
  */
  class QITYPE_API Manageable
  {
  public:
    Manageable();
    ~Manageable();
    Manageable(const Manageable& b);
    void operator = (const Manageable& b);

    EventLoop* eventLoop() const;
    void moveToEventLoop(EventLoop* eventLoop);

    ManageablePrivate* _p;
  };

  class GenericObject;
  typedef boost::shared_ptr<GenericObject> ObjectPtr;
  typedef boost::weak_ptr<GenericObject> ObjectWeakPtr;
  class SignalBasePrivate;

  class QITYPE_API SignalBase
  {
  public:
    explicit SignalBase(const std::string& signature);
    SignalBase();
    ~SignalBase();
    SignalBase(const SignalBase& b);
    SignalBase& operator = (const SignalBase& b);
    virtual std::string signature() const;

    typedef unsigned int Link;

    template<typename FUNCTION_TYPE>
    Link connect(FUNCTION_TYPE f, EventLoop* ctx = getDefaultObjectEventLoop());

    Link connect(qi::ObjectPtr target, unsigned int slot);
    Link connect(GenericFunction callback, EventLoop* ctx = getDefaultObjectEventLoop());
    Link connect(const SignalSubscriber& s);

    bool disconnectAll();

    /** Disconnect a SignalHandler. The associated callback will not be called
     * anymore as soon as this function returns, but might be called in an
     * other thread while this function runs.
     */
    bool disconnect(const Link& link);

    void trigger(const GenericFunctionParameters& params);
    void operator()(
      qi::AutoGenericValue p1 = qi::AutoGenericValue(),
      qi::AutoGenericValue p2 = qi::AutoGenericValue(),
      qi::AutoGenericValue p3 = qi::AutoGenericValue(),
      qi::AutoGenericValue p4 = qi::AutoGenericValue(),
      qi::AutoGenericValue p5 = qi::AutoGenericValue(),
      qi::AutoGenericValue p6 = qi::AutoGenericValue(),
      qi::AutoGenericValue p7 = qi::AutoGenericValue(),
      qi::AutoGenericValue p8 = qi::AutoGenericValue());

    std::vector<SignalSubscriber> subscribers();
    static const SignalBase::Link invalidLink;
  public:
    boost::shared_ptr<SignalBasePrivate> _p;
  };

  template<typename FUNCTION_TYPE>
  inline SignalBase::Link SignalBase::connect(FUNCTION_TYPE  callback, EventLoop* ctx)
  {
    return connect(makeGenericFunction(callback), ctx);
  }

  template<typename T>
  class Signal: public SignalBase, public boost::function<T>
  {
  public:
    Signal();
    Signal(const Signal<T>& b);
    Signal<T>& operator = (const Signal<T>& b);
    virtual std::string signature() const;
    using boost::function<T>::operator();
    inline SignalBase::Link connect(boost::function<T> f, EventLoop* ctx=getDefaultObjectEventLoop())
    {
      return SignalBase::connect(f, ctx);
    }
    inline SignalBase::Link connect(GenericFunction f, EventLoop* ctx=getDefaultObjectEventLoop())
    {
      return SignalBase::connect(f, ctx);
    }
    /// Auto-disconnects on target destruction
    inline SignalBase::Link connect(qi::ObjectPtr target, unsigned int slot)
    {
      return SignalBase::connect(target, slot);
    }
    /// IF O is a shared_ptr, will auto-disconnect if object is destroyed
    template<typename O, typename MF>
    inline SignalBase::Link connect(O* target, MF method, EventLoop* ctx=getDefaultObjectEventLoop());
    template<typename O, typename MF>
    inline SignalBase::Link connect(boost::shared_ptr<O> target, MF method, EventLoop* ctx=getDefaultObjectEventLoop());
  };

  namespace detail
  {
    /// Interface for a weak-lock recursive mechanism: if lock fail, unregister callback
    class WeakLock
    {
    public:
      virtual ~WeakLock(){}
      virtual bool tryLock() = 0;
      virtual void unlock() = 0;
      virtual WeakLock* clone() = 0;
    };
  }

 /** Event subscriber info.
  *
  * Only one of handler or target must be set.
  */
 class QITYPE_API SignalSubscriber
 : public boost::enable_shared_from_this<SignalSubscriber>
 {
 public:
   SignalSubscriber()
     : weakLock(0), target(0), method(0), enabled(true)
   {}

   SignalSubscriber(GenericFunction func, EventLoop* ctx = getDefaultObjectEventLoop(), detail::WeakLock* lock = 0);

   SignalSubscriber(qi::ObjectPtr target, unsigned int method);

   template<typename O, typename MF>
   SignalSubscriber(O* ptr, MF function, EventLoop* ctx = getDefaultObjectEventLoop());

   template<typename O, typename MF>
   SignalSubscriber(boost::shared_ptr<O> ptr, MF function, EventLoop* ctx = getDefaultObjectEventLoop());

   SignalSubscriber(const SignalSubscriber& b);

   ~SignalSubscriber();

   void operator = (const SignalSubscriber& b);

   void call(const GenericFunctionParameters& args);

   //wait till all threads are inactive except the current thread.
   void waitForInactive();

   void addActive(bool acquireLock, boost::thread::id tid = boost::this_thread::get_id());
   void removeActive(bool acquireLock, boost::thread::id tid = boost::this_thread::get_id());
 public:
   // Source information
   SignalBase*        source;
   /// Uid that can be passed to GenericObject::disconnect()
   SignalBase::Link  linkId;

   // Target information, kept here to be able to introspect a Subscriber
   //   Mode 1: Direct functor call
   GenericFunction      handler;
   detail::WeakLock*    weakLock; // try to acquire weakLocker, disconnect if cant
   boost::function<EventLoop*(void)> eventLoopGetter;

   //  Mode 2: metaCall
   ObjectWeakPtr*       target;
   unsigned int         method;

   boost::mutex                 mutex;
   // Fields below are protected by lock

   // If enabled is set to false while lock is acquired,
   // No more callback will trigger (activeThreads will se no push-back)
   bool                         enabled;
   // Number of calls in progress.
   // Each entry there is a subscriber call that can no longuer be aborted
   std::vector<boost::thread::id> activeThreads; // order not preserved
 };
 typedef boost::shared_ptr<SignalSubscriber> SignalSubscriberPtr;
}

#include <qitype/details/signal.hxx>

QI_NO_TYPE(qi::SignalBase)

#endif  // _QITYPE_SIGNAL_HPP_
<|endoftext|>