<commit_before>
#include "mpris.h"
#include "dbus.h"
#include <map>
#include <vector>
#include <functional>
#include <core/sdk/IEnvironment.h>

extern "C" {
    #include <unistd.h>
}

std::string thumbnailPath;
thread_local char localBuffer[4096];
static MPRISRemote remote;

static const std::map<MPRISProperty, const std::vector<const char*>> MPRISPropertyNames =
    {{MPRISProperty::Volume, {"Volume", NULL}},
     {MPRISProperty::PlaybackStatus, {"PlaybackStatus", NULL}},
     {MPRISProperty::LoopStatus, {"LoopStatus", NULL}},
     {MPRISProperty::Shuffle, {"Shuffle", NULL}},
     {MPRISProperty::Metadata, {"Metadata", NULL}}};

static std::string GetMetadataString(ITrack* track, const char* key)
{
    track->GetString(key, localBuffer, sizeof(localBuffer));
    return std::string(localBuffer);
}

static std::string GetThumbnailPath(ITrack* track)
{
    int64_t thumbnailId = track->GetInt64(track::ThumbnailId);
    return thumbnailPath + std::to_string(thumbnailId) + ".jpg";
}

static class MPRISPlugin : public IPlugin {
    public:
        MPRISPlugin() { }
        void Release() { }
        const char* Name() { return "MPRIS interface"; }
        const char* Version() { return "0.1.0"; }
        const char* Author() { return "brunosmmm"; }
        const char* Guid() { return "457df67f-f489-415f-975e-282f470b1c10"; }
        bool Configurable() { return false; }
        void Configure() { }
        void Reload() { }
        int SdkVersion() { return musik::core::sdk::SdkVersion; }
} plugin;

extern "C" void SetEnvironment(IEnvironment* environment) {
    if (environment) {
        environment->GetPath(PathLibrary, localBuffer, sizeof(localBuffer));
        thumbnailPath = std::string(localBuffer) + "/thumbs/";
    }
}

extern "C" IPlugin* GetPlugin() {
    return &plugin;
}

extern "C" IPlaybackRemote* GetPlaybackRemote() {
    return &remote;
}

bool MPRISRemote::MPRISInit() {
    int ret = 0;
    std::string requested_name;

    if (this->mpris_initialized) {
        return true;
    }

    ret = sd_bus_default_user(&(this->bus));
    if (ret < 0) {
        MPRISDeinit();
        return false;
    }

    // add DBUS entries
    ret = sd_bus_add_object_vtable(this->bus, NULL, "/org/mpris/MediaPlayer2",
                                   "org.mpris.MediaPlayer2", musikcube_mp_table, this);
    if (ret < 0) {
        MPRISDeinit();
        return false;
    }
    ret = sd_bus_add_object_vtable(this->bus, NULL, "/org/mpris/MediaPlayer2",
                                   "org.mpris.MediaPlayer2.Player", musikcube_mpp_table, this);
    if (ret < 0) {
        MPRISDeinit();
        return false;
    }

    requested_name = std::string("org.mpris.MediaPlayer2.musikcube.instance") + std::to_string(getpid());
    ret = sd_bus_request_name(this->bus, requested_name.c_str(), 0);
    if (ret < 0) {
        MPRISDeinit();
        return false;
    }

    // start loop
    thread.reset(new std::thread(std::bind(&MPRISRemote::MPRISLoop, this)));

    return true;
}

void MPRISRemote::MPRISDeinit() {
    sd_bus_close(this->bus);
    sd_bus_unref(this->bus);
    bus = NULL;
    stop_processing = true;
    if (thread) {
        thread->join();
        thread.reset();
    }
}

void MPRISRemote::MPRISEmitChange(MPRISProperty prop) {
    if (bus) {
        char** strv = (char**)(MPRISPropertyNames.at(prop).data());
        std::unique_lock<decltype(sd_mutex)> lock(sd_mutex);
        sd_bus_emit_properties_changed_strv(bus, "/org/mpris/MediaPlayer2",
                                            "org.mpris.MediaPlayer2.Player",
                                            strv);
        sd_bus_flush(bus);
    }
};

void MPRISRemote::MPRISEmitSeek(double curpos) {
    if (bus) {
        int64_t position = (int64_t)(curpos*1000*1000);
        std::unique_lock<decltype(sd_mutex)> lock(sd_mutex);
        sd_bus_emit_signal(bus, "/org/mpris/MediaPlayer2",
                           "org.mpris.MediaPlayer2.Player",
                           "Seeked", "x", position);
    }
};

void MPRISRemote::MPRISLoop() {
    while (!stop_processing) {
        if (bus && sd_bus_get_current_slot(bus)) {
            if (sd_bus_process(bus, NULL) > 0) {
                continue;
            }
            if (sd_bus_wait(bus, 500*1000) < 0) {
                break;
            }
        }
    }
}

void MPRISRemote::OnTrackChanged(ITrack* track) {
    if (playback) {
        MPRISEmitChange(MPRISProperty::Metadata);
        MPRISEmitSeek(playback->GetPosition());
    }
}

void MPRISRemote::OnPlaybackStateChanged(PlaybackState state) {
    if (playback) {
        MPRISEmitChange(MPRISProperty::PlaybackStatus);
    }
}

void MPRISRemote::OnVolumeChanged(double volume) {
    if (playback) {
        MPRISEmitChange(MPRISProperty::Volume);
    }
}

void MPRISRemote::OnPlaybackTimeChanged(double time) {
    if (playback) {
        MPRISEmitChange(MPRISProperty::Metadata);
        MPRISEmitSeek(time);
    }
}

void MPRISRemote::OnModeChanged(RepeatMode repeatMode, bool shuffled) {
    if (playback) {
        MPRISEmitChange(MPRISProperty::LoopStatus);
        MPRISEmitChange(MPRISProperty::Shuffle);
    }
}

struct MPRISMetadataValues MPRISRemote::MPRISGetMetadata() {
    struct MPRISMetadataValues metadata;
    if (playback) {
        auto curTrack = playback->GetPlayingTrack();
        if (curTrack) {
            metadata.artist = GetMetadataString(curTrack, track::Artist);
            metadata.title = GetMetadataString(curTrack, track::Title);
            metadata.albumArtist = GetMetadataString(curTrack, track::AlbumArtist);
            metadata.genre = GetMetadataString(curTrack, track::Genre);
            // TODO implement track ID properly using track index in playlist if possible
            metadata.trackid = std::string("/1");
            metadata.album = GetMetadataString(curTrack, track::Album);
            metadata.discNumber = curTrack->GetInt32(track::DiscNum);
            metadata.trackNumber = curTrack->GetInt32(track::TrackNum);
            metadata.length = curTrack->GetInt64(track::Duration)*1000*1000;
            metadata.albumArt = GetThumbnailPath(curTrack);
            metadata.available = true;
        }
    }
    return metadata;
}

void MPRISRemote::SetPlaybackService(IPlaybackService* playback) {
    std::unique_lock<decltype(sd_mutex)> lock(sd_mutex);
    this->playback = playback;
    mpris_initialized = MPRISInit();
}

void MPRISRemote::MPRISNext() {
    if (playback) {
        playback->Next();
    }
}

void MPRISRemote::MPRISPrev() {
    if (playback) {
        playback->Previous();
    }
}

void MPRISRemote::MPRISPause() {
    if (playback) {
        auto state = playback->GetPlaybackState();
        if (state == PlaybackState::PlaybackPlaying) {
            playback->PauseOrResume();
        }
    }
}

void MPRISRemote::MPRISPlayPause() {
    if (playback) {
        playback->PauseOrResume();
    }
}

void MPRISRemote::MPRISStop() {
    if (playback) {
        playback->Stop();
    }
}

void MPRISRemote::MPRISPlay() {
    if (playback) {
        auto state = playback->GetPlaybackState();
        if (state != PlaybackState::PlaybackPlaying) {
            playback->PauseOrResume();
        }
    }
}

void MPRISRemote::MPRISSeek(int64_t position, bool relative) {
    double _pos = ((double)position)/(1000*1000);
    if (playback) {
      if (!relative) {
        playback->SetPosition(_pos);
      }
      else {
        double curPos = playback->GetPosition();
        playback->SetPosition(curPos+_pos);
      }
    }
}

const char* MPRISRemote::MPRISGetPlaybackStatus() {
    if (playback) {
        auto state = playback->GetPlaybackState();
        switch (state) {
        case PlaybackState::PlaybackPlaying:
            return "Playing";
        case PlaybackState::PlaybackPaused:
            return "Paused";
        case PlaybackState::PlaybackPrepared:
        case PlaybackState::PlaybackStopped:
        default:
            break;
        }
    }
    return "Stopped";
}

const char* MPRISRemote::MPRISGetLoopStatus() {
    if (playback) {
        auto state = playback->GetRepeatMode();
        switch (state) {
        case RepeatMode::RepeatTrack:
            return "Track";
        case RepeatMode::RepeatList:
            return "Playlist";
        case RepeatMode::RepeatNone:
        default:
            break;
        }
    }
    return "None";
}

void MPRISRemote::MPRISSetLoopStatus(const char* state) {
    if (playback) {
        if (!strcmp(state, "None")) {
            playback->SetRepeatMode(RepeatMode::RepeatNone);
        }
        else if (!strcmp(state, "Playlist")) {
            playback->SetRepeatMode(RepeatMode::RepeatList);
        }
        else if (!strcmp(state, "Track")) {
            playback->SetRepeatMode(RepeatMode::RepeatTrack);
        }
    }
}

uint64_t MPRISRemote::MPRISGetPosition() {
    if (playback) {
        return (uint64_t)(playback->GetPosition()*1000*1000);
    }
    return 0;
}

unsigned int MPRISRemote::MPRISGetShuffleStatus() {
    if (playback) {
        return playback->IsShuffled() ? 1: 0;
    }
    return 0;
}

void MPRISRemote::MPRISSetShuffleStatus(unsigned int state) {
    if (playback)
        {
            unsigned int isShuffled = playback->IsShuffled() ? 1: 0;
            if ((state & 0x1) ^ isShuffled) {
                playback->ToggleShuffle();
            }
        }
}

double MPRISRemote::MPRISGetVolume() {
    if (playback) {
        return playback->GetVolume();
    }
    return 0.0;
}

void MPRISRemote::MPRISSetVolume(double vol) {
    if (playback) {
        playback->SetVolume(vol);
    }
}

MPRISMetadataValues::MPRISMetadataValues() {
    trackid = "";
    length = 0;
    artist = "";
    title = "";
    album = "";
    albumArtist = "";
    genre = "";
    comment = "";
    trackNumber = 0;
    discNumber = 0;
    available = false;
}
<commit_msg>Revert mpris change that seems to be causing more harm than good.<commit_after>
#include "mpris.h"
#include "dbus.h"
#include <map>
#include <vector>
#include <functional>
#include <core/sdk/IEnvironment.h>

extern "C" {
    #include <unistd.h>
}

std::string thumbnailPath;
thread_local char localBuffer[4096];
static MPRISRemote remote;

static const std::map<MPRISProperty, const std::vector<const char*>> MPRISPropertyNames =
    {{MPRISProperty::Volume, {"Volume", NULL}},
     {MPRISProperty::PlaybackStatus, {"PlaybackStatus", NULL}},
     {MPRISProperty::LoopStatus, {"LoopStatus", NULL}},
     {MPRISProperty::Shuffle, {"Shuffle", NULL}},
     {MPRISProperty::Metadata, {"Metadata", NULL}}};

static std::string GetMetadataString(ITrack* track, const char* key)
{
    track->GetString(key, localBuffer, sizeof(localBuffer));
    return std::string(localBuffer);
}

static std::string GetThumbnailPath(ITrack* track)
{
    int64_t thumbnailId = track->GetInt64(track::ThumbnailId);
    return thumbnailPath + std::to_string(thumbnailId) + ".jpg";
}

static class MPRISPlugin : public IPlugin {
    public:
        MPRISPlugin() { }
        void Release() { }
        const char* Name() { return "MPRIS interface"; }
        const char* Version() { return "0.1.0"; }
        const char* Author() { return "brunosmmm"; }
        const char* Guid() { return "457df67f-f489-415f-975e-282f470b1c10"; }
        bool Configurable() { return false; }
        void Configure() { }
        void Reload() { }
        int SdkVersion() { return musik::core::sdk::SdkVersion; }
} plugin;

extern "C" void SetEnvironment(IEnvironment* environment) {
    if (environment) {
        environment->GetPath(PathLibrary, localBuffer, sizeof(localBuffer));
        thumbnailPath = std::string(localBuffer) + "/thumbs/";
    }
}

extern "C" IPlugin* GetPlugin() {
    return &plugin;
}

extern "C" IPlaybackRemote* GetPlaybackRemote() {
    return &remote;
}

bool MPRISRemote::MPRISInit() {
    int ret = 0;
    std::string requested_name;

    if (this->mpris_initialized) {
        return true;
    }

    ret = sd_bus_default_user(&(this->bus));
    if (ret < 0) {
        MPRISDeinit();
        return false;
    }

    // add DBUS entries
    ret = sd_bus_add_object_vtable(this->bus, NULL, "/org/mpris/MediaPlayer2",
                                   "org.mpris.MediaPlayer2", musikcube_mp_table, this);
    if (ret < 0) {
        MPRISDeinit();
        return false;
    }
    ret = sd_bus_add_object_vtable(this->bus, NULL, "/org/mpris/MediaPlayer2",
                                   "org.mpris.MediaPlayer2.Player", musikcube_mpp_table, this);
    if (ret < 0) {
        MPRISDeinit();
        return false;
    }

    requested_name = std::string("org.mpris.MediaPlayer2.musikcube.instance") + std::to_string(getpid());
    ret = sd_bus_request_name(this->bus, requested_name.c_str(), 0);
    if (ret < 0) {
        MPRISDeinit();
        return false;
    }

    // start loop
    thread.reset(new std::thread(std::bind(&MPRISRemote::MPRISLoop, this)));

    return true;
}

void MPRISRemote::MPRISDeinit() {
    sd_bus_close(this->bus);
    sd_bus_unref(this->bus);
    bus = NULL;
    stop_processing = true;
    if (thread) {
        thread->join();
        thread.reset();
    }
}

void MPRISRemote::MPRISEmitChange(MPRISProperty prop) {
    if (bus) {
        char** strv = (char**)(MPRISPropertyNames.at(prop).data());
        std::unique_lock<decltype(sd_mutex)> lock(sd_mutex);
        sd_bus_emit_properties_changed_strv(bus, "/org/mpris/MediaPlayer2",
                                            "org.mpris.MediaPlayer2.Player",
                                            strv);
        sd_bus_flush(bus);
    }
};

void MPRISRemote::MPRISEmitSeek(double curpos) {
    if (bus) {
        int64_t position = (int64_t)(curpos*1000*1000);
        std::unique_lock<decltype(sd_mutex)> lock(sd_mutex);
        sd_bus_emit_signal(bus, "/org/mpris/MediaPlayer2",
                           "org.mpris.MediaPlayer2.Player",
                           "Seeked", "x", position);
    }
};

void MPRISRemote::MPRISLoop() {
    while (!stop_processing) {
        if (bus) {
            if (sd_bus_process(bus, NULL) > 0) {
                continue;
            }
            if (sd_bus_wait(bus, 500*1000) < 0) {
                break;
            }
        }
    }
}

void MPRISRemote::OnTrackChanged(ITrack* track) {
    if (playback) {
        MPRISEmitChange(MPRISProperty::Metadata);
        MPRISEmitSeek(playback->GetPosition());
    }
}

void MPRISRemote::OnPlaybackStateChanged(PlaybackState state) {
    if (playback) {
        MPRISEmitChange(MPRISProperty::PlaybackStatus);
    }
}

void MPRISRemote::OnVolumeChanged(double volume) {
    if (playback) {
        MPRISEmitChange(MPRISProperty::Volume);
    }
}

void MPRISRemote::OnPlaybackTimeChanged(double time) {
    if (playback) {
        MPRISEmitChange(MPRISProperty::Metadata);
        MPRISEmitSeek(time);
    }
}

void MPRISRemote::OnModeChanged(RepeatMode repeatMode, bool shuffled) {
    if (playback) {
        MPRISEmitChange(MPRISProperty::LoopStatus);
        MPRISEmitChange(MPRISProperty::Shuffle);
    }
}

struct MPRISMetadataValues MPRISRemote::MPRISGetMetadata() {
    struct MPRISMetadataValues metadata;
    if (playback) {
        auto curTrack = playback->GetPlayingTrack();
        if (curTrack) {
            metadata.artist = GetMetadataString(curTrack, track::Artist);
            metadata.title = GetMetadataString(curTrack, track::Title);
            metadata.albumArtist = GetMetadataString(curTrack, track::AlbumArtist);
            metadata.genre = GetMetadataString(curTrack, track::Genre);
            // TODO implement track ID properly using track index in playlist if possible
            metadata.trackid = std::string("/1");
            metadata.album = GetMetadataString(curTrack, track::Album);
            metadata.discNumber = curTrack->GetInt32(track::DiscNum);
            metadata.trackNumber = curTrack->GetInt32(track::TrackNum);
            metadata.length = curTrack->GetInt64(track::Duration)*1000*1000;
            metadata.albumArt = GetThumbnailPath(curTrack);
            metadata.available = true;
        }
    }
    return metadata;
}

void MPRISRemote::SetPlaybackService(IPlaybackService* playback) {
    std::unique_lock<decltype(sd_mutex)> lock(sd_mutex);
    this->playback = playback;
    mpris_initialized = MPRISInit();
}

void MPRISRemote::MPRISNext() {
    if (playback) {
        playback->Next();
    }
}

void MPRISRemote::MPRISPrev() {
    if (playback) {
        playback->Previous();
    }
}

void MPRISRemote::MPRISPause() {
    if (playback) {
        auto state = playback->GetPlaybackState();
        if (state == PlaybackState::PlaybackPlaying) {
            playback->PauseOrResume();
        }
    }
}

void MPRISRemote::MPRISPlayPause() {
    if (playback) {
        playback->PauseOrResume();
    }
}

void MPRISRemote::MPRISStop() {
    if (playback) {
        playback->Stop();
    }
}

void MPRISRemote::MPRISPlay() {
    if (playback) {
        auto state = playback->GetPlaybackState();
        if (state != PlaybackState::PlaybackPlaying) {
            playback->PauseOrResume();
        }
    }
}

void MPRISRemote::MPRISSeek(int64_t position, bool relative) {
    double _pos = ((double)position)/(1000*1000);
    if (playback) {
      if (!relative) {
        playback->SetPosition(_pos);
      }
      else {
        double curPos = playback->GetPosition();
        playback->SetPosition(curPos+_pos);
      }
    }
}

const char* MPRISRemote::MPRISGetPlaybackStatus() {
    if (playback) {
        auto state = playback->GetPlaybackState();
        switch (state) {
        case PlaybackState::PlaybackPlaying:
            return "Playing";
        case PlaybackState::PlaybackPaused:
            return "Paused";
        case PlaybackState::PlaybackPrepared:
        case PlaybackState::PlaybackStopped:
        default:
            break;
        }
    }
    return "Stopped";
}

const char* MPRISRemote::MPRISGetLoopStatus() {
    if (playback) {
        auto state = playback->GetRepeatMode();
        switch (state) {
        case RepeatMode::RepeatTrack:
            return "Track";
        case RepeatMode::RepeatList:
            return "Playlist";
        case RepeatMode::RepeatNone:
        default:
            break;
        }
    }
    return "None";
}

void MPRISRemote::MPRISSetLoopStatus(const char* state) {
    if (playback) {
        if (!strcmp(state, "None")) {
            playback->SetRepeatMode(RepeatMode::RepeatNone);
        }
        else if (!strcmp(state, "Playlist")) {
            playback->SetRepeatMode(RepeatMode::RepeatList);
        }
        else if (!strcmp(state, "Track")) {
            playback->SetRepeatMode(RepeatMode::RepeatTrack);
        }
    }
}

uint64_t MPRISRemote::MPRISGetPosition() {
    if (playback) {
        return (uint64_t)(playback->GetPosition()*1000*1000);
    }
    return 0;
}

unsigned int MPRISRemote::MPRISGetShuffleStatus() {
    if (playback) {
        return playback->IsShuffled() ? 1: 0;
    }
    return 0;
}

void MPRISRemote::MPRISSetShuffleStatus(unsigned int state) {
    if (playback)
        {
            unsigned int isShuffled = playback->IsShuffled() ? 1: 0;
            if ((state & 0x1) ^ isShuffled) {
                playback->ToggleShuffle();
            }
        }
}

double MPRISRemote::MPRISGetVolume() {
    if (playback) {
        return playback->GetVolume();
    }
    return 0.0;
}

void MPRISRemote::MPRISSetVolume(double vol) {
    if (playback) {
        playback->SetVolume(vol);
    }
}

MPRISMetadataValues::MPRISMetadataValues() {
    trackid = "";
    length = 0;
    artist = "";
    title = "";
    album = "";
    albumArtist = "";
    genre = "";
    comment = "";
    trackNumber = 0;
    discNumber = 0;
    available = false;
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "main.h"
#include "db.h"
#include "txdb.h"
#include "init.h"
#include "miner.h"
#include "bitcoinrpc.h"

using namespace json_spirit;
using namespace std;

Value getsubsidy(const Array& params, bool fHelp)
{
    if (fHelp || params.size() > 1)
        throw runtime_error(
            "getsubsidy [nTarget]\n"
            "Returns proof-of-work subsidy value for the specified value of target.");

    return (uint64_t)GetProofOfWorkReward(0);
}

Value getmininginfo(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 0)
        throw runtime_error(
            "getmininginfo\n"
            "Returns an object containing mining-related information.");

    uint64_t nMinWeight = 0, nMaxWeight = 0, nWeight = 0;
    pwalletMain->GetStakeWeight(*pwalletMain, nMinWeight, nMaxWeight, nWeight);

    Object obj, diff, weight;
    obj.push_back(Pair("blocks",        (int)nBestHeight));
    obj.push_back(Pair("currentblocksize",(uint64_t)nLastBlockSize));
    obj.push_back(Pair("currentblocktx",(uint64_t)nLastBlockTx));

    diff.push_back(Pair("proof-of-work",        GetDifficulty()));
    diff.push_back(Pair("proof-of-stake",       GetDifficulty(GetLastBlockIndex(pindexBest, true))));
    diff.push_back(Pair("search-interval",      (int)nLastCoinStakeSearchInterval));
    obj.push_back(Pair("difficulty",    diff));

    obj.push_back(Pair("blockvalue",    (uint64_t)GetProofOfWorkReward(0)));
    obj.push_back(Pair("netmhashps",     GetPoWMHashPS()));
    obj.push_back(Pair("netstakeweight", GetPoSKernelPS()));
    obj.push_back(Pair("errors",        GetWarnings("statusbar")));
    obj.push_back(Pair("pooledtx",      (uint64_t)mempool.size()));

    weight.push_back(Pair("minimum",    (uint64_t)nMinWeight));
    weight.push_back(Pair("maximum",    (uint64_t)nMaxWeight));
    weight.push_back(Pair("combined",  (uint64_t)nWeight));
    obj.push_back(Pair("stakeweight", weight));

    obj.push_back(Pair("stakeinterest",    (uint64_t)COIN_YEAR_REWARD));
    obj.push_back(Pair("testnet",       fTestNet));
    return obj;
}

Value getstakinginfo(const Array& params, bool fHelp)
{
    if (fHelp || params.size() != 0)
        throw runtime_error(
            "getstakinginfo\n"
            "Returns an object containing staking-related information.");

    uint64_t nMinWeight = 0, nMaxWeight = 0, nWeight = 0;
    pwalletMain->GetStakeWeight(*pwalletMain, nMinWeight, nMaxWeight, nWeight);

    uint64_t nNetworkWeight = GetPoSKernelPS();
    bool staking = nLastCoinStakeSearchInterval && nWeight;
    int nExpectedTime = staking ? (nTargetSpacing * nNetworkWeight / nWeight) : -1;

    Object obj;

    obj.push_back(Pair("enabled", GetBoolArg("-staking", true)));
    obj.push_back(Pair("staking", staking));
    obj.push_back(Pair("errors", GetWarnings("statusbar")));

    obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize));
    obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx));
    obj.push_back(Pair("pooledtx", (uint64_t)mempool.size()));

    obj.push_back(Pair("difficulty", GetDifficulty(GetLastBlockIndex(pindexBest, true))));
    obj.push_back(Pair("search-interval", (int)nLastCoinStakeSearchInterval));

    obj.push_back(Pair("weight", (uint64_t)nWeight));
    obj.push_back(Pair("netstakeweight", (uint64_t)nNetworkWeight));

    obj.push_back(Pair("expectedtime", nExpectedTime));

    return obj;
}

Value getworkex(const Array& params, bool fHelp)
{
    if (fHelp || params.size() > 2)
        throw runtime_error(
            "getworkex [data, coinbase]\n"
            "If [data, coinbase] is not specified, returns extended work data.\n"
        );

    if (vNodes.empty())
        throw JSONRPCError(-9, "DFSCoin is not connected!");

    if (IsInitialBlockDownload())
        throw JSONRPCError(-10, "DFSCoin is downloading blocks...");

    if (pindexBest->nHeight >= LAST_POW_BLOCK)
        throw JSONRPCError(RPC_MISC_ERROR, "No more PoW blocks");

    typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
    static mapNewBlock_t mapNewBlock;
    static vector<CBlock*> vNewBlock;
    static CReserveKey reservekey(pwalletMain);

    if (params.size() == 0)
    {
        // Update block
        static unsigned int nTransactionsUpdatedLast;
        static CBlockIndex* pindexPrev;
        static int64_t nStart;
        static CBlock* pblock;
        if (pindexPrev != pindexBest ||
            (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
        {
            if (pindexPrev != pindexBest)
            {
                // Deallocate old blocks since they're obsolete now
                mapNewBlock.clear();
                BOOST_FOREACH(CBlock* pblock, vNewBlock)
                    delete pblock;
                vNewBlock.clear();
            }
            nTransactionsUpdatedLast = nTransactionsUpdated;
            pindexPrev = pindexBest;
            nStart = GetTime();

            // Create new block
            pblock = CreateNewBlock(pwalletMain);
            if (!pblock)
                throw JSONRPCError(-7, "Out of memory");
            vNewBlock.push_back(pblock);
        }

        // Update nTime
        pblock->nTime = max(pindexPrev->GetPastTimeLimit()+1, GetAdjustedTime());
        pblock->nNonce = 0;

        // Update nExtraNonce
        static unsigned int nExtraNonce = 0;
        IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);

        // Save
        mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);

        // Prebuild hash buffers
        char pmidstate[32];
        char pdata[128];
        char phash1[64];
        FormatHashBuffers(pblock, pmidstate, pdata, phash1);

        uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();

        CTransaction coinbaseTx = pblock->vtx[0];
        std::vector<uint256> merkle = pblock->GetMerkleBranch(0);

        Object result;
        result.push_back(Pair("data",     HexStr(BEGIN(pdata), END(pdata))));
        result.push_back(Pair("target",   HexStr(BEGIN(hashTarget), END(hashTarget))));

        CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
        ssTx << coinbaseTx;
        result.push_back(Pair("coinbase", HexStr(ssTx.begin(), ssTx.end())));

        Array merkle_arr;

        BOOST_FOREACH(uint256 merkleh, merkle) {
            merkle_arr.push_back(HexStr(BEGIN(merkleh), END(merkleh)));
        }

        result.push_back(Pair("merkle", merkle_arr));

        return result;
    }
    else
    {
        // Parse parameters
        vector<unsigned char> vchData = ParseHex(params[0].get_str());
        vector<unsigned char> coinbase;

        if(params.size() == 2)
            coinbase = ParseHex(params[1].get_str());

        if (vchData.size() != 128)
            throw JSONRPCError(-8, "Invalid parameter");

        CBlock* pdata = (CBlock*)&vchData[0];

        // Byte reverse
        for (int i = 0; i < 128/4; i++)
            ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);

        // Get saved block
        if (!mapNewBlock.count(pdata->hashMerkleRoot))
            return false;
        CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;

        pblock->nTime = pdata->nTime;
        pblock->nNonce = pdata->nNonce;

        if(coinbase.size() == 0)
            pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
        else
            CDataStream(coinbase, SER_NETWORK, PROTOCOL_VERSION) >> pblock->vtx[0]; // FIXME - HACK!

        pblock->hashMerkleRoot = pblock->BuildMerkleTree();

        return CheckWork(pblock, *pwalletMain, reservekey);
    }
}

Value getwork(const Array& params, bool fHelp)
{
    if (fHelp || params.size() > 1)
        throw runtime_error(
            "getwork [data]\n"
            "If [data] is not specified, returns formatted hash data to work on:\n"
            "  \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
            "  \"data\" : block data\n"
            "  \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
            "  \"target\" : little endian hash target\n"
            "If [data] is specified, tries to solve the block and returns true if it was successful.");

    if (vNodes.empty())
        throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "DFSCoin is not connected!");

    if (IsInitialBlockDownload())
        throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "DFSCoin is downloading blocks...");

    if (pindexBest->nHeight >= LAST_POW_BLOCK)
        throw JSONRPCError(RPC_MISC_ERROR, "No more PoW blocks");

    typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
    static mapNewBlock_t mapNewBlock;    // FIXME: thread safety
    static vector<CBlock*> vNewBlock;
    static CReserveKey reservekey(pwalletMain);

    if (params.size() == 0)
    {
        // Update block
        static unsigned int nTransactionsUpdatedLast;
        static CBlockIndex* pindexPrev;
        static int64_t nStart;
        static CBlock* pblock;
        if (pindexPrev != pindexBest ||
            (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
        {
            if (pindexPrev != pindexBest)
            {
                // Deallocate old blocks since they're obsolete now
                mapNewBlock.clear();
                BOOST_FOREACH(CBlock* pblock, vNewBlock)
                    delete pblock;
                vNewBlock.clear();
            }

            // Clear pindexPrev so future getworks make a new block, despite any failures from here on
            pindexPrev = NULL;

            // Store the pindexBest used before CreateNewBlock, to avoid races
            nTransactionsUpdatedLast = nTransactionsUpdated;
            CBlockIndex* pindexPrevNew = pindexBest;
            nStart = GetTime();

            // Create new block
            pblock = CreateNewBlock(pwalletMain);
            if (!pblock)
                throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
            vNewBlock.push_back(pblock);

            // Need to update only after we know CreateNewBlock succeeded
            pindexPrev = pindexPrevNew;
        }

        // Update nTime
        pblock->UpdateTime(pindexPrev);
        pblock->nNonce = 0;

        // Update nExtraNonce
        static unsigned int nExtraNonce = 0;
        IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);

        // Save
        mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);

        // Pre-build hash buffers
        char pmidstate[32];
        char pdata[128];
        char phash1[64];
        FormatHashBuffers(pblock, pmidstate, pdata, phash1);

        uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();

        Object result;
        result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
        result.push_back(Pair("data",     HexStr(BEGIN(pdata), END(pdata))));
        result.push_back(Pair("hash1",    HexStr(BEGIN(phash1), END(phash1)))); // deprecated
        result.push_back(Pair("target",   HexStr(BEGIN(hashTarget), END(hashTarget))));
        return result;
    }
    else
    {
        // Parse parameters
        vector<unsigned char> vchData = ParseHex(params[0].get_str());
        if (vchData.size() != 128)
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
        CBlock* pdata = (CBlock*)&vchData[0];

        // Byte reverse
        for (int i = 0; i < 128/4; i++)
            ((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);

        // Get saved block
        if (!mapNewBlock.count(pdata->hashMerkleRoot))
            return false;
        CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;

        pblock->nTime = pdata->nTime;
        pblock->nNonce = pdata->nNonce;
        pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
        pblock->hashMerkleRoot = pblock->BuildMerkleTree();

        return CheckWork(pblock, *pwalletMain, reservekey);
    }
}

Value getblocktemplate(const Array& params, bool fHelp)
{
    if (fHelp || params.size() > 1)
        throw runtime_error(
            "getblocktemplate [params]\n"
            "Returns data needed to construct a block to work on:\n"
            "  \"version\" : block version\n"
            "  \"previousblockhash\" : hash of current highest block\n"
            "  \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
            "  \"coinbaseaux\" : data that should be included in coinbase\n"
            "  \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
            "  \"target\" : hash target\n"
            "  \"mintime\" : minimum timestamp appropriate for next block\n"
            "  \"curtime\" : current timestamp\n"
            "  \"mutable\" : list of ways the block template may be changed\n"
            "  \"noncerange\" : range of valid nonces\n"
            "  \"sigoplimit\" : limit of sigops in blocks\n"
            "  \"sizelimit\" : limit of block size\n"
            "  \"bits\" : compressed target of next block\n"
            "  \"height\" : height of the next block\n"
            "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.");

    std::string strMode = "template";
    if (params.size() > 0)
    {
        const Object& oparam = params[0].get_obj();
        const Value& modeval = find_value(oparam, "mode");
        if (modeval.type() == str_type)
            strMode = modeval.get_str();
        else if (modeval.type() == null_type)
        {
            /* Do nothing */
        }
        else
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
    }

    if (strMode != "template")
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");

    if (vNodes.empty())
        throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "DFSCoin is not connected!");

    if (IsInitialBlockDownload())
        throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "DFSCoin is downloading blocks...");

    if (pindexBest->nHeight >= LAST_POW_BLOCK)
        throw JSONRPCError(RPC_MISC_ERROR, "No more PoW blocks");

    static CReserveKey reservekey(pwalletMain);

    // Update block
    static unsigned int nTransactionsUpdatedLast;
    static CBlockIndex* pindexPrev;
    static int64_t nStart;
    static CBlock* pblock;
    if (pindexPrev != pindexBest ||
        (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
    {
        // Clear pindexPrev so future calls make a new block, despite any failures from here on
        pindexPrev = NULL;

        // Store the pindexBest used before CreateNewBlock, to avoid races
        nTransactionsUpdatedLast = nTransactionsUpdated;
        CBlockIndex* pindexPrevNew = pindexBest;
        nStart = GetTime();

        // Create new block
        if(pblock)
        {
            delete pblock;
            pblock = NULL;
        }
        pblock = CreateNewBlock(pwalletMain);
        if (!pblock)
            throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");

        // Need to update only after we know CreateNewBlock succeeded
        pindexPrev = pindexPrevNew;
    }

    // Update nTime
    pblock->UpdateTime(pindexPrev);
    pblock->nNonce = 0;

    Array transactions;
    map<uint256, int64_t> setTxIndex;
    int i = 0;
    CTxDB txdb("r");
    BOOST_FOREACH (CTransaction& tx, pblock->vtx)
    {
        uint256 txHash = tx.GetHash();
        setTxIndex[txHash] = i++;

        if (tx.IsCoinBase() || tx.IsCoinStake())
            continue;

        Object entry;

        CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
        ssTx << tx;
        entry.push_back(Pair("data", HexStr(ssTx.begin(), ssTx.end())));

        entry.push_back(Pair("hash", txHash.GetHex()));

        MapPrevTx mapInputs;
        map<uint256, CTxIndex> mapUnused;
        bool fInvalid = false;
        if (tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid))
        {
            entry.push_back(Pair("fee", (int64_t)(tx.GetValueIn(mapInputs) - tx.GetValueOut())));

            Array deps;
            BOOST_FOREACH (MapPrevTx::value_type& inp, mapInputs)
            {
                if (setTxIndex.count(inp.first))
                    deps.push_back(setTxIndex[inp.first]);
            }
            entry.push_back(Pair("depends", deps));

            int64_t nSigOps = tx.GetLegacySigOpCount();
            nSigOps += tx.GetP2SHSigOpCount(mapInputs);
            entry.push_back(Pair("sigops", nSigOps));
        }

        transactions.push_back(entry);
    }

    Object aux;
    aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())));

    uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();

    static Array aMutable;
    if (aMutable.empty())
    {
        aMutable.push_back("time");
        aMutable.push_back("transactions");
        aMutable.push_back("prevblock");
    }

    Object result;
    result.push_back(Pair("version", pblock->nVersion));
    result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
    result.push_back(Pair("transactions", transactions));
    result.push_back(Pair("coinbaseaux", aux));
    result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
    result.push_back(Pair("target", hashTarget.GetHex()));
    result.push_back(Pair("mintime", (int64_t)pindexPrev->GetPastTimeLimit()+1));
    result.push_back(Pair("mutable", aMutable));
    result.push_back(Pair("noncerange", "00000000ffffffff"));
    result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS));
    result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE));
    result.push_back(Pair("curtime", (int64_t)pblock->nTime));
    result.push_back(Pair("bits", strprintf("%08x", pblock->nBits)));
    result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1)));

    return result;
}

Value submitblock(const Array& params, bool fHelp)
{
    if (fHelp || params.size() < 1 || params.size() > 2)
        throw runtime_error(
            "submitblock <hex data> [optional-params-obj]\n"
            "[optional-params-obj] parameter is currently ignored.\n"
            "Attempts to submit new block to network.\n"
            "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.");

    vector<unsigned char> blockData(ParseHex(params[0].get_str()));
    CDataStream ssBlock(blockData, SER_NETWORK, PROTOCOL_VERSION);
    CBlock block;
    try {
        ssBlock >> block;
    }
    catch (std::exception &e) {
        throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
    }

    bool fAccepted = ProcessBlock(NULL, &block);
    if (!fAccepted)
        return "rejected";

    return Value::null;
}

<commit_msg>Delete rpcmining.cpp<commit_after><|endoftext|>


<commit_before>#include "memc/Compiler.hpp"

namespace memc {

Compiler::Compiler (opt::Options* opts)
{
   mem::log::Formatter* formatter = NULL;

   // TODO: Should use a factory or something here
   if (opts->getStr("--log-formatter") == "test-friendly")
   {
      formatter = new mem::log::TestFriendlyFormatter();
   }
   else
   {
      mem::log::ConsoleFormatter* cons_formatter = new mem::log::ConsoleFormatter();

      if (opts->getStrEnum("--color") == "yes")
      {
         cons_formatter->setColorsEnabled(true);
      }
      else
      {
         cons_formatter->setColorsEnabled(false);
      }

      formatter = cons_formatter;
   }

   _logger = new mem::log::ConsoleLogger();
   _logger->setFormatter(formatter);

   _parser = new langmem::Parse();
   _parser->setLogger(_logger);
   _parser->setSymbolTable(&symbols);

   _opts = opts;

   addDecorator(new mem::decorator::External());
   addDecorator(new mem::decorator::Overriding());
   addDecorator(new mem::decorator::Require());
   addDecorator(new mem::decorator::Virtual());

   addMacro(new mem::ast::macro::PtrMacros());

   // Setup AST visitors
   addAstVisitor(new mem::ast::visitor::CoherenceChecker());
   addAstVisitor(new mem::ast::visitor::UseAlias());
   addAstVisitor(new mem::ast::visitor::Prechecker());
   addAstVisitor(new mem::ast::visitor::FindClasses());
   addAstVisitor(new mem::ast::visitor::TopTypesChecker());
   addAstVisitor(new mem::ast::visitor::Decorate(_decorators));
   addAstVisitor(new mem::ast::visitor::Ctor());
   addAstVisitor(new mem::ast::visitor::BlockTypesChecker());
   addAstVisitor(new mem::ast::visitor::CoherenceChecker());
   addAstVisitor(new mem::ast::visitor::CheckValidity());
   addAstVisitor(new mem::ast::visitor::FindEntryPoint());
   addAstVisitor(new mem::ast::visitor::Stats());

   mem::st::util::setupBool(this->symbols, this->symbols.gCoreTypes());
   mem::st::util::setupBugType(this->symbols, this->symbols.gCoreTypes());
   mem::st::util::setupInts(this->symbols, this->symbols.gCoreTypes());
   mem::st::util::setupVoid(this->symbols, this->symbols.gCoreTypes());
}

Compiler::~Compiler ()
{
   delete _logger;
   delete _parser;

   mem::decorator::DecoratorMap::iterator i;

   for (i = _decorators.begin(); i != _decorators.end(); ++i)
   {
      delete i->second;
   }

   // Delete AST visitors
   for (size_t i = 0; i < ast_visitors.size(); ++i)
   {
      delete ast_visitors[i];
   }

   // Delete ST visitors
   for (size_t i = 0; i < st_visitors.size(); ++i)
   {
      delete st_visitors[i];
   }
}

void
Compiler::addDecorator (mem::decorator::Decorator* dec)
{
   DEBUG_REQUIRE (dec != NULL);

   if (_decorators.find(dec->Name()) == _decorators.end())
   {
      _decorators[dec->Name()] = dec;
   }
}

void
Compiler::addMacro (mem::ast::macro::Macro* macro)
{
   DEBUG_REQUIRE (macro != NULL);

   mem::st::Macro* macro_sym = macro->getSymbol();
   symbols.System()->addChild(macro_sym);
   delete macro;
}

void
Compiler::dumpAst ()
{
   if (_opts->isSet("--dump-ast-xml"))
   {
      std::string dump_path = _opts->getStr("--dump-ast-xml");

      std::ofstream dump_file(dump_path.c_str());

      mem::ast::visitor::XmlDumper dumper;
      dumper.Out(&dump_file);
      dumper.setup();
      dumper.visit(&ast);
      dumper.tearDown();

      dump_file.close();
      _logger->info("AST dumped to %s (XML)", dump_path.c_str());
   }
}

void
Compiler::dumpSt ()
{
   if (_opts->isSet("--dump-st-xml"))
   {
      std::string dump_path = _opts->getStr("--dump-st-xml");
      // Open dump file
      // TODO We should have a default output file in case the user did not
      // give one
      std::ofstream st_dump_file(dump_path.c_str());

      mem::st::visitor::XmlDumper dumper;
      dumper._out = &st_dump_file;
      dumper.setup();
      dumper.visitPreorder(symbols.Root());

      st_dump_file.close();
      _logger->info("SymbolTable dumped to %s (XML)", dump_path.c_str());
   }
}

void
Compiler::emitCode ()
{
   if (_opts->hasArguments())
   {
#ifdef HAVE_LLVM
      std::string llvm_ir_path = "./mem.bc";
      std::string bin_path = "./mem.out";

      if (_opts->isSet("--emit-llvm-bc"))
      {
         llvm_ir_path = _opts->getStr("--emit-llvm-bc");
      }

      if (_opts->isSet("--output"))
      {
         bin_path = _opts->getStr("--output");
      }

      codegen::llvm_::Codegen cg;
      cg.setSymbolTable(&symbols);
      cg.gen(&ast);

      // Dump LLVM bytecode
      std::ofstream bc_file;
      bc_file.open(llvm_ir_path.c_str());
      bc_file << cg.getLlvmByteCode();
      bc_file.close();

      _logger->info("LLVM ByteCode dumped to %s", llvm_ir_path.c_str());

      // Generate native code
      std::string out_s = llvm_ir_path  + ".s";
      std::string out_as = llvm_ir_path + ".as";

      tool::CommandChain cc (_tools);
      cc.run(Toolbox::LLVM_COMPILER, "-o=" + out_s + " " + llvm_ir_path)
         ->then(Toolbox::GCC, out_s + " -o " + bin_path);

      if (cc.Status() == 0)
      {
         _logger->debug("Binary generated as %s", bin_path.c_str());
      }
      else
      {
         _logger->fatalError("Couldn't generate binary as %s",
            bin_path.c_str());
      }
#endif // HAVE_LLVM
   }
}

void
Compiler::parse (std::string file_path)
{
   _logger->debug("[%s] parsing...", file_path.c_str());

   std::string ns_name = mem::Util::getNamespaceNameFromPath(file_path);
   std::vector<std::string> ns_parts = mem::Util::split(ns_name, '.');
   mem::st::Namespace* file_sym = mem::st::util::createNamespace(symbols.Home(), ns_parts);
   assert(file_sym != NULL);

   std::vector<std::string> paths_tried;

   mem::fs::File* file = fm.tryOpenFile(file_path, paths_tried);

   if (file != NULL)
   {
      mem::ast::node::File* file_node = NULL;
      gTOKENIZER.reset();
      gTOKENIZER.setInputFile(file->Path());
      file_node = _parser->parse (file);
      file_node->setBoundSymbol(file_sym);
      file_node->setId(ns_name);
      file_node->setIncludePath(file->IncludePath());
      file_node->setPath(file_path);
      ast.addChild(file_node);

      mem::ast::visitor::FindUse find_use;
      _logger->debug("Searching for use statements", "");
      find_use.visit_preorder(file_node);
      for (size_t i = 0; i < find_use._uses.size(); ++i)
      {
         if (ns_name == find_use._uses[i])
         {
            // FIXME This thing is probably failing, should use a pointer.
            mem::log::Message warn(mem::log::WARNING);
            warn.formatMessage(
               "File {path:%s} is trying to include itself: include ignored.",
               file_path.c_str()
            );
            _logger->log(&warn);
         }
         else
         {
            std::string rel_file_path (find_use._uses[i]);
            mem::Util::namespace_to_path(rel_file_path);
            rel_file_path += ".mem";
            this->_parse_queue.push(rel_file_path);
         }
      }
   }
   else
   {
      std::string description = "We tried :\n";
      std::vector<std::string>::size_type i;
      for (i=0; i<paths_tried.size(); ++i)
      {
         description.append("* {path:" + paths_tried[i] + "}\n");
      }

      mem::log::Message* msg = new mem::log::Error();
      msg->formatMessage("Couldn't open file {path:%s}.", file_path.c_str());
      msg->setSecondaryText(description);
      _logger->log(msg);
   }
}

void
Compiler::printBuildSummary ()
{
   std::ostringstream sec_text;

   if (_logger->FatalErrorCount() > 0)
   {
      sec_text << "Fatal errors: ";
      sec_text << _logger->FatalErrorCount();
      sec_text << "\n";
   }

   if (_logger->ErrorCount() > 0)
   {
      sec_text << "Errors: ";
      sec_text << _logger->ErrorCount();
      sec_text << "\n";
   }

   if (_logger->WarningCount() > 0)
   {
      sec_text << "Warnings: ";
      sec_text << _logger->WarningCount();
      sec_text << "\n";
   }

   if (isBuildSuccessful())
   {
      _logger->info("Build SUCCESSFUL", "");
   }
   else
   {
      mem::log::Message* err = new mem::log::FatalError();
      err->setPrimaryText("Build FAILED");
      err->setSecondaryText(sec_text.str());
      _logger->log(err);
   }
}

void
Compiler::printUsage (std::ostream& out)
{
   out << "USAGE: ";
#ifdef PACKAGE_NAME
   out << PACKAGE_NAME;
#else
   out << "?";
#endif
   out << " [OPTIONS] <input>\n\n";
   out << "OPTIONS:\n";

   _opts->dump(out);

   /*
   std::map<std::string, opt::CliOption*>::iterator i;
   for (i = _opts->_cli_options.begin(); i != _opts->_cli_options.end(); ++i)
   {
      if (i->second != NULL)
      {
         out << " --";
         out << i->second->_cli_name;
         out << " : \n    ";
         out << i->second->_description;
         out << "\n";
      }
   }
   */
}

void
Compiler::processParseQueue ()
{
   while (!_parse_queue.empty())
   {
      parse(_parse_queue.front());
      _parse_queue.pop();
   }
}

void
Compiler::run ()
{
   std::string formatter_id = _opts->getStr("--log-formatter");

   if (formatter_id == "xml")
   {
      _logger->setFormatter(new mem::log::XmlFormatter());
   }
   _logger->begin();

   fm.appendPath(".");

   // Need to set this before parsing command line arguments because it can
   // raise warnings
#ifdef NDEBUG
   _logger->setLevel(mem::log::INFO);
#else
   _logger->setLevel(mem::log::DEBUG);
#endif

   if (_opts->isSet("--log-level"))
   {
      _logger->setLevel(_opts->getInt("--log-level"));
   }

   if (_opts->isSet("--version"))
   {
      std::cout << PACKAGE_NAME " version " PACKAGE_VERSION;
      std::cout << " (" __DATE__ " " __TIME__ ")";
      IF_DEBUG
      {
         std::cout << " [DEBUG]";
      }
      std::cout << "\n\n";
      std::cout << "       Build date: " << __DATE__ << "\n";
      std::cout << "       Build time: " << __TIME__ << "\n";
      std::cout << "         Compiler: " << COMPILER_NAME << "\n";
      std::cout << "   Compiler flags: " << COMPILER_FLAGS << "\n";
      std::cout << "          Version: " << PACKAGE_VERSION << "\n";
      std::cout << "             Yacc: " << YACC_EXE << "\n";
   }
   else if (_opts->isSet("--help"))
   {
      printUsage(std::cout);
   }
   else if (_opts->hasArguments())
   {
      _parse_queue.push(_opts->getArgument(0));

      processParseQueue();

      runAstVisitors();
      //runStVisitors();

      dumpAst();
      dumpSt();

      if (isBuildSuccessful()) emitCode();

      printBuildSummary();
   }

   _logger->finish();

}

void
Compiler::runAstVisitors ()
{
   for (size_t i=0;
      i < ast_visitors.size() && _logger->FatalErrorCount() == 0 && _logger->ErrorCount()==0; ++i)
   {
      _logger->debug("[%s] running...", ast_visitors[i]->_name.c_str());
      ast_visitors[i]->SymbolTable(&symbols);
      ast_visitors[i]->Logger(_logger);
      ast_visitors[i]->setup();
      ast_visitors[i]->visit_preorder(&ast);
      ast_visitors[i]->tearDown();
   }
}

void
Compiler::runStVisitors ()
{
   for (size_t i = 0; i < st_visitors.size(); ++i)
   {
      _logger->debug("[%s] running...", st_visitors[i]->gNameCstr());
      st_visitors[i]->setup();
      st_visitors[i]->visitPreorder(symbols.Root());
      st_visitors[i]->tearDown();
   }
}

}
<commit_msg>Add id to `build failed` log message<commit_after>#include "memc/Compiler.hpp"

namespace memc {

Compiler::Compiler (opt::Options* opts)
{
   mem::log::Formatter* formatter = NULL;

   // TODO: Should use a factory or something here
   if (opts->getStr("--log-formatter") == "test-friendly")
   {
      formatter = new mem::log::TestFriendlyFormatter();
   }
   else
   {
      mem::log::ConsoleFormatter* cons_formatter = new mem::log::ConsoleFormatter();

      if (opts->getStrEnum("--color") == "yes")
      {
         cons_formatter->setColorsEnabled(true);
      }
      else
      {
         cons_formatter->setColorsEnabled(false);
      }

      formatter = cons_formatter;
   }

   _logger = new mem::log::ConsoleLogger();
   _logger->setFormatter(formatter);

   _parser = new langmem::Parse();
   _parser->setLogger(_logger);
   _parser->setSymbolTable(&symbols);

   _opts = opts;

   addDecorator(new mem::decorator::External());
   addDecorator(new mem::decorator::Overriding());
   addDecorator(new mem::decorator::Require());
   addDecorator(new mem::decorator::Virtual());

   addMacro(new mem::ast::macro::PtrMacros());

   // Setup AST visitors
   addAstVisitor(new mem::ast::visitor::CoherenceChecker());
   addAstVisitor(new mem::ast::visitor::UseAlias());
   addAstVisitor(new mem::ast::visitor::Prechecker());
   addAstVisitor(new mem::ast::visitor::FindClasses());
   addAstVisitor(new mem::ast::visitor::TopTypesChecker());
   addAstVisitor(new mem::ast::visitor::Decorate(_decorators));
   addAstVisitor(new mem::ast::visitor::Ctor());
   addAstVisitor(new mem::ast::visitor::BlockTypesChecker());
   addAstVisitor(new mem::ast::visitor::CoherenceChecker());
   addAstVisitor(new mem::ast::visitor::CheckValidity());
   addAstVisitor(new mem::ast::visitor::FindEntryPoint());
   addAstVisitor(new mem::ast::visitor::Stats());

   mem::st::util::setupBool(this->symbols, this->symbols.gCoreTypes());
   mem::st::util::setupBugType(this->symbols, this->symbols.gCoreTypes());
   mem::st::util::setupInts(this->symbols, this->symbols.gCoreTypes());
   mem::st::util::setupVoid(this->symbols, this->symbols.gCoreTypes());
}

Compiler::~Compiler ()
{
   delete _logger;
   delete _parser;

   mem::decorator::DecoratorMap::iterator i;

   for (i = _decorators.begin(); i != _decorators.end(); ++i)
   {
      delete i->second;
   }

   // Delete AST visitors
   for (size_t i = 0; i < ast_visitors.size(); ++i)
   {
      delete ast_visitors[i];
   }

   // Delete ST visitors
   for (size_t i = 0; i < st_visitors.size(); ++i)
   {
      delete st_visitors[i];
   }
}

void
Compiler::addDecorator (mem::decorator::Decorator* dec)
{
   DEBUG_REQUIRE (dec != NULL);

   if (_decorators.find(dec->Name()) == _decorators.end())
   {
      _decorators[dec->Name()] = dec;
   }
}

void
Compiler::addMacro (mem::ast::macro::Macro* macro)
{
   DEBUG_REQUIRE (macro != NULL);

   mem::st::Macro* macro_sym = macro->getSymbol();
   symbols.System()->addChild(macro_sym);
   delete macro;
}

void
Compiler::dumpAst ()
{
   if (_opts->isSet("--dump-ast-xml"))
   {
      std::string dump_path = _opts->getStr("--dump-ast-xml");

      std::ofstream dump_file(dump_path.c_str());

      mem::ast::visitor::XmlDumper dumper;
      dumper.Out(&dump_file);
      dumper.setup();
      dumper.visit(&ast);
      dumper.tearDown();

      dump_file.close();
      _logger->info("AST dumped to %s (XML)", dump_path.c_str());
   }
}

void
Compiler::dumpSt ()
{
   if (_opts->isSet("--dump-st-xml"))
   {
      std::string dump_path = _opts->getStr("--dump-st-xml");
      // Open dump file
      // TODO We should have a default output file in case the user did not
      // give one
      std::ofstream st_dump_file(dump_path.c_str());

      mem::st::visitor::XmlDumper dumper;
      dumper._out = &st_dump_file;
      dumper.setup();
      dumper.visitPreorder(symbols.Root());

      st_dump_file.close();
      _logger->info("SymbolTable dumped to %s (XML)", dump_path.c_str());
   }
}

void
Compiler::emitCode ()
{
   if (_opts->hasArguments())
   {
#ifdef HAVE_LLVM
      std::string llvm_ir_path = "./mem.bc";
      std::string bin_path = "./mem.out";

      if (_opts->isSet("--emit-llvm-bc"))
      {
         llvm_ir_path = _opts->getStr("--emit-llvm-bc");
      }

      if (_opts->isSet("--output"))
      {
         bin_path = _opts->getStr("--output");
      }

      codegen::llvm_::Codegen cg;
      cg.setSymbolTable(&symbols);
      cg.gen(&ast);

      // Dump LLVM bytecode
      std::ofstream bc_file;
      bc_file.open(llvm_ir_path.c_str());
      bc_file << cg.getLlvmByteCode();
      bc_file.close();

      _logger->info("LLVM ByteCode dumped to %s", llvm_ir_path.c_str());

      // Generate native code
      std::string out_s = llvm_ir_path  + ".s";
      std::string out_as = llvm_ir_path + ".as";

      tool::CommandChain cc (_tools);
      cc.run(Toolbox::LLVM_COMPILER, "-o=" + out_s + " " + llvm_ir_path)
         ->then(Toolbox::GCC, out_s + " -o " + bin_path);

      if (cc.Status() == 0)
      {
         _logger->debug("Binary generated as %s", bin_path.c_str());
      }
      else
      {
         _logger->fatalError("Couldn't generate binary as %s",
            bin_path.c_str());
      }
#endif // HAVE_LLVM
   }
}

void
Compiler::parse (std::string file_path)
{
   _logger->debug("[%s] parsing...", file_path.c_str());

   std::string ns_name = mem::Util::getNamespaceNameFromPath(file_path);
   std::vector<std::string> ns_parts = mem::Util::split(ns_name, '.');
   mem::st::Namespace* file_sym = mem::st::util::createNamespace(symbols.Home(), ns_parts);
   assert(file_sym != NULL);

   std::vector<std::string> paths_tried;

   mem::fs::File* file = fm.tryOpenFile(file_path, paths_tried);

   if (file != NULL)
   {
      mem::ast::node::File* file_node = NULL;
      gTOKENIZER.reset();
      gTOKENIZER.setInputFile(file->Path());
      file_node = _parser->parse (file);
      file_node->setBoundSymbol(file_sym);
      file_node->setId(ns_name);
      file_node->setIncludePath(file->IncludePath());
      file_node->setPath(file_path);
      ast.addChild(file_node);

      mem::ast::visitor::FindUse find_use;
      _logger->debug("Searching for use statements", "");
      find_use.visit_preorder(file_node);
      for (size_t i = 0; i < find_use._uses.size(); ++i)
      {
         if (ns_name == find_use._uses[i])
         {
            // FIXME This thing is probably failing, should use a pointer.
            mem::log::Message warn(mem::log::WARNING);
            warn.formatMessage(
               "File {path:%s} is trying to include itself: include ignored.",
               file_path.c_str()
            );
            _logger->log(&warn);
         }
         else
         {
            std::string rel_file_path (find_use._uses[i]);
            mem::Util::namespace_to_path(rel_file_path);
            rel_file_path += ".mem";
            this->_parse_queue.push(rel_file_path);
         }
      }
   }
   else
   {
      std::string description = "We tried :\n";
      std::vector<std::string>::size_type i;
      for (i=0; i<paths_tried.size(); ++i)
      {
         description.append("* {path:" + paths_tried[i] + "}\n");
      }

      mem::log::Message* msg = new mem::log::Error();
      msg->formatMessage("Couldn't open file {path:%s}.", file_path.c_str());
      msg->setSecondaryText(description);
      _logger->log(msg);
   }
}

void
Compiler::printBuildSummary ()
{
   std::ostringstream sec_text;

   if (_logger->FatalErrorCount() > 0)
   {
      sec_text << "Fatal errors: ";
      sec_text << _logger->FatalErrorCount();
      sec_text << "\n";
   }

   if (_logger->ErrorCount() > 0)
   {
      sec_text << "Errors: ";
      sec_text << _logger->ErrorCount();
      sec_text << "\n";
   }

   if (_logger->WarningCount() > 0)
   {
      sec_text << "Warnings: ";
      sec_text << _logger->WarningCount();
      sec_text << "\n";
   }

   if (isBuildSuccessful())
   {
      _logger->info("Build SUCCESSFUL", "");
   }
   else
   {
      mem::log::Message* err = new mem::log::FatalError();
      err->setPrimaryText("Build FAILED");
      err->setId("build-failed");
      err->setSecondaryText(sec_text.str());
      _logger->log(err);
   }
}

void
Compiler::printUsage (std::ostream& out)
{
   out << "USAGE: ";
#ifdef PACKAGE_NAME
   out << PACKAGE_NAME;
#else
   out << "?";
#endif
   out << " [OPTIONS] <input>\n\n";
   out << "OPTIONS:\n";

   _opts->dump(out);

   /*
   std::map<std::string, opt::CliOption*>::iterator i;
   for (i = _opts->_cli_options.begin(); i != _opts->_cli_options.end(); ++i)
   {
      if (i->second != NULL)
      {
         out << " --";
         out << i->second->_cli_name;
         out << " : \n    ";
         out << i->second->_description;
         out << "\n";
      }
   }
   */
}

void
Compiler::processParseQueue ()
{
   while (!_parse_queue.empty())
   {
      parse(_parse_queue.front());
      _parse_queue.pop();
   }
}

void
Compiler::run ()
{
   std::string formatter_id = _opts->getStr("--log-formatter");

   if (formatter_id == "xml")
   {
      _logger->setFormatter(new mem::log::XmlFormatter());
   }
   _logger->begin();

   fm.appendPath(".");

   // Need to set this before parsing command line arguments because it can
   // raise warnings
#ifdef NDEBUG
   _logger->setLevel(mem::log::INFO);
#else
   _logger->setLevel(mem::log::DEBUG);
#endif

   if (_opts->isSet("--log-level"))
   {
      _logger->setLevel(_opts->getInt("--log-level"));
   }

   if (_opts->isSet("--version"))
   {
      std::cout << PACKAGE_NAME " version " PACKAGE_VERSION;
      std::cout << " (" __DATE__ " " __TIME__ ")";
      IF_DEBUG
      {
         std::cout << " [DEBUG]";
      }
      std::cout << "\n\n";
      std::cout << "       Build date: " << __DATE__ << "\n";
      std::cout << "       Build time: " << __TIME__ << "\n";
      std::cout << "         Compiler: " << COMPILER_NAME << "\n";
      std::cout << "   Compiler flags: " << COMPILER_FLAGS << "\n";
      std::cout << "          Version: " << PACKAGE_VERSION << "\n";
      std::cout << "             Yacc: " << YACC_EXE << "\n";
   }
   else if (_opts->isSet("--help"))
   {
      printUsage(std::cout);
   }
   else if (_opts->hasArguments())
   {
      _parse_queue.push(_opts->getArgument(0));

      processParseQueue();

      runAstVisitors();
      //runStVisitors();

      dumpAst();
      dumpSt();

      if (isBuildSuccessful()) emitCode();

      printBuildSummary();
   }

   _logger->finish();

}

void
Compiler::runAstVisitors ()
{
   for (size_t i=0;
      i < ast_visitors.size() && _logger->FatalErrorCount() == 0 && _logger->ErrorCount()==0; ++i)
   {
      _logger->debug("[%s] running...", ast_visitors[i]->_name.c_str());
      ast_visitors[i]->SymbolTable(&symbols);
      ast_visitors[i]->Logger(_logger);
      ast_visitors[i]->setup();
      ast_visitors[i]->visit_preorder(&ast);
      ast_visitors[i]->tearDown();
   }
}

void
Compiler::runStVisitors ()
{
   for (size_t i = 0; i < st_visitors.size(); ++i)
   {
      _logger->debug("[%s] running...", st_visitors[i]->gNameCstr());
      st_visitors[i]->setup();
      st_visitors[i]->visitPreorder(symbols.Root());
      st_visitors[i]->tearDown();
   }
}

}
<|endoftext|>


<commit_before>// Copyright (c) 2011, Cornell University
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of HyperDex nor the names of its contributors may be
//       used to endorse or promote products derived from this software without
//       specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

// Google Log
#include <glog/logging.h>

// HyperDex
#include <hyperdex/physical.h>

hyperdex :: physical :: physical(ev::loop_ref lr, const po6::net::ipaddr& ip, bool listen)
    : m_lr(lr)
    , m_wakeup(lr)
    , m_listen(ip.family(), SOCK_STREAM, IPPROTO_TCP)
    , m_listen_event(lr)
    , m_bindto()
    , m_lock()
    , m_channels()
    , m_location_map()
    , m_incoming()
{
    // Enable other threads to wake us from the event loop.
    m_wakeup.set<physical, &physical::refresh>(this);
    m_wakeup.start();

    if (listen)
    {
        // Enable other hosts to connect to us.
        m_listen.bind(po6::net::location(ip, 0));
        m_listen.listen(4);
        m_listen_event.set<physical, &physical::accept_connection>(this);
        m_listen_event.set(m_listen.get(), ev::READ);
        m_listen_event.start();
    }
    else
    {
        m_listen.close();
    }

    // Setup a channel which connects to ourself.  The purpose of this channel
    // is two-fold:  first, it gives a really cheap way of doing self-messaging
    // without the queue at the higher layer (at the expense of a bigger
    // performance hit); second, it gives us a way to get a port to which we
    // may bind repeatedly when establishing connections.
    //
    // TODO:  Make establishing this channel only serve the second purpose.
    if (listen)
    {
        std::tr1::shared_ptr<channel> chan;
        chan.reset(new channel(m_lr, this, po6::net::location(ip, 0), m_listen.getsockname()));
        m_location_map[chan->loc] = m_channels.size();
        m_channels.push_back(chan);
        m_bindto = chan->soc.getsockname();
    }
    else
    {
        m_bindto = po6::net::location(ip, 0);
    }
}

hyperdex :: physical :: ~physical()
{
}

void
hyperdex :: physical :: send(const po6::net::location& to,
                             const e::buffer& msg)
{
    // Get a reference to the channel for "to" with mtx held.
    m_lock.rdlock();
    std::map<po6::net::location, size_t>::iterator position;
    std::tr1::shared_ptr<channel> chan;

    if ((position = m_location_map.find(to)) == m_location_map.end())
    {
        m_lock.unlock();
        m_lock.wrlock();
        chan = create_connection(to);

        if (!chan)
        {
            m_lock.unlock();
            return;
        }
    }
    else
    {
        chan = m_channels[position->second];
    }

    chan->mtx.lock();
    m_lock.unlock();

    // Add msg to the outgoing buffer.
    bool notify = chan->outgoing.empty() && chan->outprogress.empty();
    chan->outgoing.push(msg);

    // Notify the event loop thread.
    if (notify)
    {
        m_wakeup.send();
    }

    chan->mtx.unlock();
}

bool
hyperdex :: physical :: recv(po6::net::location* from,
                             e::buffer* msg)
{
    message m;
    bool ret = m_incoming.pop(&m);
    *from = m.loc;
    *msg = m.buf;
    return ret;
}

void
hyperdex :: physical :: shutdown()
{
    m_incoming.shutdown();
}

// Invariant:  m_lock.wrlock must be held.
std::tr1::shared_ptr<hyperdex :: physical :: channel>
hyperdex :: physical :: create_connection(const po6::net::location& to)
{
    std::map<po6::net::location, size_t>::iterator position;

    if ((position = m_location_map.find(to)) != m_location_map.end())
    {
        return m_channels[position->second];
    }

    try
    {
        std::tr1::shared_ptr<channel> chan;
        chan.reset(new channel(m_lr, this, m_bindto, to));
        place_channel(chan);
        return chan;
    }
    catch (...)
    {
        return std::tr1::shared_ptr<channel>();
    }
}

// Invariant:  no locks may be held.
void
hyperdex :: physical :: remove_connection(channel* to_remove)
{
    m_lock.wrlock();
    po6::net::location loc = to_remove->loc;
    std::map<po6::net::location, size_t>::iterator iter = m_location_map.find(loc);

    if (iter == m_location_map.end())
    {
        LOG(FATAL) << "The physical layer's address map got out of sync with its open channels.";
    }

    size_t index = iter->second;
    m_location_map.erase(iter);
    // This lock/unlock pair is necessary to ensure the channel is no longer in
    // use.  As we know that we are the only one who can use m_location_map and
    // m_channels, the only other possibility is that a send is in-progress
    // (after the rdlock is released, but before the chan lock is released).
    // Once this succeeds, we know that no one else may possibly use this
    // channel.
    m_channels[index]->mtx.lock();
    m_channels[index]->mtx.unlock();
    m_channels[index] = std::tr1::shared_ptr<channel>();
    m_lock.unlock();
}

void
hyperdex :: physical :: refresh(ev::async&, int)
{
    m_lock.rdlock();

    for (std::map<po6::net::location, size_t>::iterator i = m_location_map.begin();
            i != m_location_map.end(); ++ i)
    {
        if (m_channels[i->second])
        {
            m_channels[i->second]->mtx.lock();
            int flags = ev::READ;

            // If there is data to write.
            if (m_channels[i->second]->outprogress.size() > 0 ||
                m_channels[i->second]->outgoing.size() > 0)
            {
                flags |= ev::WRITE;
            }

            m_channels[i->second]->io.set(flags);
            m_channels[i->second]->mtx.unlock();
        }
    }

    m_lock.unlock();
}

void
hyperdex :: physical :: accept_connection(ev::io&, int)
{
    std::tr1::shared_ptr<channel> chan;

    try
    {
        chan.reset(new channel(m_lr, this, &m_listen));
    }
    catch (po6::error& e)
    {
        return;
    }

    m_lock.wrlock();

    try
    {
        place_channel(chan);
    }
    catch (...)
    {
        m_lock.unlock();
        throw;
    }

    m_lock.unlock();
}

// Invariant:  m_lock.wrlock must be held.
void
hyperdex :: physical :: place_channel(std::tr1::shared_ptr<channel> chan)
{
    // Find an empty slot.
    for (size_t i = 0; i < m_channels.size(); ++i)
    {
        if (!m_channels[i])
        {
            m_channels[i] = chan;
            m_location_map[chan->loc] = i;
            return;
        }
    }

    // Resize to create new slots.
    m_location_map[chan->loc] = m_channels.size();
    m_channels.push_back(chan);
}

hyperdex :: physical :: channel :: channel(ev::loop_ref lr,
                                           physical* m,
                                           po6::net::socket* accept_from)
    : mtx()
    , soc()
    , loc()
    , io(lr)
    , inprogress()
    , outprogress()
    , outgoing()
    , manager(m)
{
    accept_from->accept(&soc);
    loc = soc.getpeername();
    io.set<channel, &channel::io_cb>(this);
    io.set(soc.get(), ev::READ);
    io.start();
}

hyperdex :: physical :: channel :: channel(ev::loop_ref lr,
                                           physical* m,
                                           const po6::net::location& from,
                                           const po6::net::location& to)
    : mtx()
    , soc(to.address.family(), SOCK_STREAM, IPPROTO_TCP)
    , loc(to)
    , io(lr)
    , inprogress()
    , outprogress()
    , outgoing()
    , manager(m)
{
    io.set<channel, &channel::io_cb>(this);
    io.set(soc.get(), ev::READ);
    io.start();

    soc.reuseaddr(true);
    soc.bind(from);
    soc.connect(to);
}

hyperdex :: physical :: channel :: ~channel()
{
    io.stop();
}

#define IO_BLOCKSIZE 65536

void
hyperdex :: physical :: channel :: io_cb(ev::io& w, int revents)
{
    if (revents & ev::READ)
    {
        read_cb(w);
    }

    if (revents & ev::WRITE)
    {
        write_cb(w);
    }
}

void
hyperdex :: physical :: channel :: read_cb(ev::io&)
{
    if (!mtx.trylock())
    {
        return;
    }

    char block[IO_BLOCKSIZE];

    try
    {
        size_t ret = soc.read(block, IO_BLOCKSIZE);
        inprogress += e::buffer(block, ret);

        if (ret == 0)
        {
            mtx.unlock();
            manager->remove_connection(this);
            return;
        }

        while (inprogress.size() >= sizeof(uint32_t))
        {
            e::unpacker up(inprogress);
            uint32_t message_size;
            up >> message_size;

            if (inprogress.size() < message_size + sizeof(uint32_t))
            {
                break;
            }

            e::buffer buf;
            up >> buf;
            message m;
            m.loc = loc;
            m.buf = buf;
            manager->m_incoming.push(m);
            inprogress.trim_prefix(message_size + sizeof(uint32_t));
        }
    }
    catch (po6::error& e)
    {
        if (e != EAGAIN && e != EINTR && e != EWOULDBLOCK)
        {
            LOG(ERROR) << "could not read from " << loc << "; closing";
            mtx.unlock();
            manager->remove_connection(this);
            return;
        }
    }

    mtx.unlock();
}

void
hyperdex :: physical :: channel :: write_cb(ev::io&)
{
    if (!mtx.trylock())
    {
        return;
    }

    if (outgoing.empty() && outprogress.empty())
    {
        io.set(ev::READ);
        mtx.unlock();
        // XXX this probably means we need to shut it down.
        return;
    }

    // Pop one message to flush to network.
    if (outprogress.empty())
    {
        uint32_t size = outgoing.front().size();
        outprogress.pack() << size << outgoing.front();
        outgoing.pop();
    }

    try
    {
        size_t ret = soc.write(outprogress.get(), outprogress.size());
        outprogress.trim_prefix(ret);
    }
    catch (po6::error& e)
    {
        if (e != EAGAIN && e != EINTR && e != EWOULDBLOCK)
        {
            LOG(ERROR) << "could not write to " << loc << "; closing";
            mtx.unlock();
            manager->remove_connection(this);
            return;
        }
    }

    mtx.unlock();
}

hyperdex :: physical :: message :: message()
    : loc()
    , buf()
{
}

hyperdex :: physical :: message :: ~message()
{
}
<commit_msg>Use the proper pack/unpack routines for buffer.<commit_after>// Copyright (c) 2011, Cornell University
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of HyperDex nor the names of its contributors may be
//       used to endorse or promote products derived from this software without
//       specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

// Google Log
#include <glog/logging.h>

// HyperDex
#include <hyperdex/physical.h>

hyperdex :: physical :: physical(ev::loop_ref lr, const po6::net::ipaddr& ip, bool listen)
    : m_lr(lr)
    , m_wakeup(lr)
    , m_listen(ip.family(), SOCK_STREAM, IPPROTO_TCP)
    , m_listen_event(lr)
    , m_bindto()
    , m_lock()
    , m_channels()
    , m_location_map()
    , m_incoming()
{
    // Enable other threads to wake us from the event loop.
    m_wakeup.set<physical, &physical::refresh>(this);
    m_wakeup.start();

    if (listen)
    {
        // Enable other hosts to connect to us.
        m_listen.bind(po6::net::location(ip, 0));
        m_listen.listen(4);
        m_listen_event.set<physical, &physical::accept_connection>(this);
        m_listen_event.set(m_listen.get(), ev::READ);
        m_listen_event.start();
    }
    else
    {
        m_listen.close();
    }

    // Setup a channel which connects to ourself.  The purpose of this channel
    // is two-fold:  first, it gives a really cheap way of doing self-messaging
    // without the queue at the higher layer (at the expense of a bigger
    // performance hit); second, it gives us a way to get a port to which we
    // may bind repeatedly when establishing connections.
    //
    // TODO:  Make establishing this channel only serve the second purpose.
    if (listen)
    {
        std::tr1::shared_ptr<channel> chan;
        chan.reset(new channel(m_lr, this, po6::net::location(ip, 0), m_listen.getsockname()));
        m_location_map[chan->loc] = m_channels.size();
        m_channels.push_back(chan);
        m_bindto = chan->soc.getsockname();
    }
    else
    {
        m_bindto = po6::net::location(ip, 0);
    }
}

hyperdex :: physical :: ~physical()
{
}

void
hyperdex :: physical :: send(const po6::net::location& to,
                             const e::buffer& msg)
{
    // Get a reference to the channel for "to" with mtx held.
    m_lock.rdlock();
    std::map<po6::net::location, size_t>::iterator position;
    std::tr1::shared_ptr<channel> chan;

    if ((position = m_location_map.find(to)) == m_location_map.end())
    {
        m_lock.unlock();
        m_lock.wrlock();
        chan = create_connection(to);

        if (!chan)
        {
            m_lock.unlock();
            return;
        }
    }
    else
    {
        chan = m_channels[position->second];
    }

    chan->mtx.lock();
    m_lock.unlock();

    // Add msg to the outgoing buffer.
    bool notify = chan->outgoing.empty() && chan->outprogress.empty();
    chan->outgoing.push(msg);

    // Notify the event loop thread.
    if (notify)
    {
        m_wakeup.send();
    }

    chan->mtx.unlock();
}

bool
hyperdex :: physical :: recv(po6::net::location* from,
                             e::buffer* msg)
{
    message m;
    bool ret = m_incoming.pop(&m);
    *from = m.loc;
    *msg = m.buf;
    return ret;
}

void
hyperdex :: physical :: shutdown()
{
    m_incoming.shutdown();
}

// Invariant:  m_lock.wrlock must be held.
std::tr1::shared_ptr<hyperdex :: physical :: channel>
hyperdex :: physical :: create_connection(const po6::net::location& to)
{
    std::map<po6::net::location, size_t>::iterator position;

    if ((position = m_location_map.find(to)) != m_location_map.end())
    {
        return m_channels[position->second];
    }

    try
    {
        std::tr1::shared_ptr<channel> chan;
        chan.reset(new channel(m_lr, this, m_bindto, to));
        place_channel(chan);
        return chan;
    }
    catch (...)
    {
        return std::tr1::shared_ptr<channel>();
    }
}

// Invariant:  no locks may be held.
void
hyperdex :: physical :: remove_connection(channel* to_remove)
{
    m_lock.wrlock();
    po6::net::location loc = to_remove->loc;
    std::map<po6::net::location, size_t>::iterator iter = m_location_map.find(loc);

    if (iter == m_location_map.end())
    {
        LOG(FATAL) << "The physical layer's address map got out of sync with its open channels.";
    }

    size_t index = iter->second;
    m_location_map.erase(iter);
    // This lock/unlock pair is necessary to ensure the channel is no longer in
    // use.  As we know that we are the only one who can use m_location_map and
    // m_channels, the only other possibility is that a send is in-progress
    // (after the rdlock is released, but before the chan lock is released).
    // Once this succeeds, we know that no one else may possibly use this
    // channel.
    m_channels[index]->mtx.lock();
    m_channels[index]->mtx.unlock();
    m_channels[index] = std::tr1::shared_ptr<channel>();
    m_lock.unlock();
}

void
hyperdex :: physical :: refresh(ev::async&, int)
{
    m_lock.rdlock();

    for (std::map<po6::net::location, size_t>::iterator i = m_location_map.begin();
            i != m_location_map.end(); ++ i)
    {
        if (m_channels[i->second])
        {
            m_channels[i->second]->mtx.lock();
            int flags = ev::READ;

            // If there is data to write.
            if (m_channels[i->second]->outprogress.size() > 0 ||
                m_channels[i->second]->outgoing.size() > 0)
            {
                flags |= ev::WRITE;
            }

            m_channels[i->second]->io.set(flags);
            m_channels[i->second]->mtx.unlock();
        }
    }

    m_lock.unlock();
}

void
hyperdex :: physical :: accept_connection(ev::io&, int)
{
    std::tr1::shared_ptr<channel> chan;

    try
    {
        chan.reset(new channel(m_lr, this, &m_listen));
    }
    catch (po6::error& e)
    {
        return;
    }

    m_lock.wrlock();

    try
    {
        place_channel(chan);
    }
    catch (...)
    {
        m_lock.unlock();
        throw;
    }

    m_lock.unlock();
}

// Invariant:  m_lock.wrlock must be held.
void
hyperdex :: physical :: place_channel(std::tr1::shared_ptr<channel> chan)
{
    // Find an empty slot.
    for (size_t i = 0; i < m_channels.size(); ++i)
    {
        if (!m_channels[i])
        {
            m_channels[i] = chan;
            m_location_map[chan->loc] = i;
            return;
        }
    }

    // Resize to create new slots.
    m_location_map[chan->loc] = m_channels.size();
    m_channels.push_back(chan);
}

hyperdex :: physical :: channel :: channel(ev::loop_ref lr,
                                           physical* m,
                                           po6::net::socket* accept_from)
    : mtx()
    , soc()
    , loc()
    , io(lr)
    , inprogress()
    , outprogress()
    , outgoing()
    , manager(m)
{
    accept_from->accept(&soc);
    loc = soc.getpeername();
    io.set<channel, &channel::io_cb>(this);
    io.set(soc.get(), ev::READ);
    io.start();
}

hyperdex :: physical :: channel :: channel(ev::loop_ref lr,
                                           physical* m,
                                           const po6::net::location& from,
                                           const po6::net::location& to)
    : mtx()
    , soc(to.address.family(), SOCK_STREAM, IPPROTO_TCP)
    , loc(to)
    , io(lr)
    , inprogress()
    , outprogress()
    , outgoing()
    , manager(m)
{
    io.set<channel, &channel::io_cb>(this);
    io.set(soc.get(), ev::READ);
    io.start();

    soc.reuseaddr(true);
    soc.bind(from);
    soc.connect(to);
}

hyperdex :: physical :: channel :: ~channel()
{
    io.stop();
}

#define IO_BLOCKSIZE 65536

void
hyperdex :: physical :: channel :: io_cb(ev::io& w, int revents)
{
    if (revents & ev::READ)
    {
        read_cb(w);
    }

    if (revents & ev::WRITE)
    {
        write_cb(w);
    }
}

void
hyperdex :: physical :: channel :: read_cb(ev::io&)
{
    if (!mtx.trylock())
    {
        return;
    }

    char block[IO_BLOCKSIZE];

    try
    {
        size_t ret = soc.read(block, IO_BLOCKSIZE);
        inprogress += e::buffer(block, ret);

        if (ret == 0)
        {
            mtx.unlock();
            manager->remove_connection(this);
            return;
        }

        while (inprogress.size() >= sizeof(uint32_t))
        {
            uint32_t message_size;
            inprogress.unpack() >> message_size;

            if (inprogress.size() < message_size + sizeof(uint32_t))
            {
                break;
            }

            message m;
            inprogress.unpack() >> m.buf; // Different unpacker.
            m.loc = loc;
            manager->m_incoming.push(m);
            inprogress.trim_prefix(message_size + sizeof(uint32_t));
        }
    }
    catch (po6::error& e)
    {
        if (e != EAGAIN && e != EINTR && e != EWOULDBLOCK)
        {
            LOG(ERROR) << "could not read from " << loc << "; closing";
            mtx.unlock();
            manager->remove_connection(this);
            return;
        }
    }

    mtx.unlock();
}

void
hyperdex :: physical :: channel :: write_cb(ev::io&)
{
    if (!mtx.trylock())
    {
        return;
    }

    if (outgoing.empty() && outprogress.empty())
    {
        io.set(ev::READ);
        io.start();
        mtx.unlock();
        return;
    }

    // Pop one message to flush to network.
    if (outprogress.empty())
    {
        outprogress.pack() << outgoing.front();
        outgoing.pop();
    }

    try
    {
        size_t ret = soc.write(outprogress.get(), outprogress.size());
        outprogress.trim_prefix(ret);
    }
    catch (po6::error& e)
    {
        if (e != EAGAIN && e != EINTR && e != EWOULDBLOCK)
        {
            LOG(ERROR) << "could not write to " << loc << "; closing";
            mtx.unlock();
            manager->remove_connection(this);
            return;
        }
    }

    mtx.unlock();
}

hyperdex :: physical :: message :: message()
    : loc()
    , buf()
{
}

hyperdex :: physical :: message :: ~message()
{
}
<|endoftext|>