text:
/**
 * @file persistence.cpp
 *
 * This file contains file based persistence related functions.
 */

#include "zurbank/persistence.h"

#include "zurbank/dex.h"
#include "zurbank/log.h"
#include "zurbank/mdex.h"
#include "zurbank/zurbank.h"
#include "zurbank/rules.h"
#include "zurbank/sp.h"
#include "zurbank/tally.h"
#include "zurbank/utilszurcoin.h"

#include "chain.h"
#include "main.h"
#include "tinyformat.h"
#include "uint256.h"
#include "util.h"

#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>

#include <openssl/sha.h>

#include <stdint.h>

#include <fstream>
#include <set>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

using namespace mastercore;

//! Number of "Dev Zus" of the last processed block (needed to save global state)
extern int64_t exodus_prev;

//! Path for file based persistence
extern boost::filesystem::path pathStateFiles;

enum FILETYPES {
  FILETYPE_BALANCES = 0,
  FILETYPE_OFFERS,
  FILETYPE_ACCEPTS,
  FILETYPE_GLOBALS,
  FILETYPE_CROWDSALES,
  FILETYPE_MDEXORDERS,
  NUM_FILETYPES
};

static char const * const statePrefix[NUM_FILETYPES] = {
    "balances",
    "offers",
    "accepts",
    "globals",
    "crowdsales",
    "mdexorders",
};

static bool is_state_prefix(std::string const &str)
{
    for (int i = 0; i < NUM_FILETYPES; ++i) {
        if (boost::equals(str, statePrefix[i])) {
            return true;
        }
    }

    return false;
}

static int write_msc_balances(std::ofstream& file, SHA256_CTX* shaCtx)
{
    std::unordered_map<std::string, CMPTally>::iterator iter;
    for (iter = mp_tally_map.begin(); iter != mp_tally_map.end(); ++iter) {
        bool emptyWallet = true;

        std::string lineOut = (*iter).first;
        lineOut.append("=");
        CMPTally& curAddr = (*iter).second;
        curAddr.init();
        uint32_t propertyId = 0;
        while (0 != (propertyId = curAddr.next())) {
            int64_t balance = (*iter).second.getMoney(propertyId, BALANCE);
            int64_t sellReserved = (*iter).second.getMoney(propertyId, SELLOFFER_RESERVE);
            int64_t acceptReserved = (*iter).second.getMoney(propertyId, ACCEPT_RESERVE);
            int64_t metadexReserved = (*iter).second.getMoney(propertyId, METADEX_RESERVE);

            // we don't allow 0 balances to read in, so if we don't write them
            // it makes things match up better between persisted state and processed state
            if (0 == balance && 0 == sellReserved && 0 == acceptReserved && 0 == metadexReserved) {
                continue;
            }

            emptyWallet = false;

            lineOut.append(strprintf("%d:%d,%d,%d,%d;",
                    propertyId,
                    balance,
                    sellReserved,
                    acceptReserved,
                    metadexReserved));
        }

        if (false == emptyWallet) {
            // add the line to the hash
            SHA256_Update(shaCtx, lineOut.c_str(), lineOut.length());

            // write the line
            file << lineOut << std::endl;
        }
    }

    return 0;
}

static int write_mp_offers(std::ofstream& file, SHA256_CTX* shaCtx)
{
    OfferMap::const_iterator iter;
    for (iter = my_offers.begin(); iter != my_offers.end(); ++iter) {
        // decompose the key for address
        std::vector<std::string> vstr;
        boost::split(vstr, iter->first, boost::is_any_of("-"), boost::token_compress_on);
        const CMPOffer& offer = iter->second;
        offer.saveOffer(file, shaCtx, vstr[0]);
    }

    return 0;
}

static int write_mp_accepts(std::ofstream& file, SHA256_CTX* shaCtx)
{
    AcceptMap::const_iterator iter;
    for (iter = my_accepts.begin(); iter != my_accepts.end(); ++iter) {
        // decompose the key for address
        std::vector<std::string> vstr;
        boost::split(vstr, iter->first, boost::is_any_of("-+"), boost::token_compress_on);
        const CMPAccept& accept = iter->second;
        accept.saveAccept(file, shaCtx, vstr[0], vstr[1]);
    }

    return 0;
}

static int write_globals_state(std::ofstream& file, SHA256_CTX* shaCtx)
{
    uint32_t nextSPID = pDbSpInfo->peekNextSPID(OMNI_PROPERTY_MSC);
    uint32_t nextTestSPID = pDbSpInfo->peekNextSPID(OMNI_PROPERTY_TMSC);
    std::string lineOut = strprintf("%d,%d,%d",
            exodus_prev,
            nextSPID,
            nextTestSPID);

    // add the line to the hash
    SHA256_Update(shaCtx, lineOut.c_str(), lineOut.length());

    // write the line
    file << lineOut << std::endl;

    return 0;
}

static int write_mp_crowdsales(std::ofstream& file, SHA256_CTX* shaCtx)
{
    for (CrowdMap::const_iterator it = my_crowds.begin(); it != my_crowds.end(); ++it) {
        // decompose the key for address
        const CMPCrowd& crowd = it->second;
        crowd.saveCrowdSale(file, shaCtx, it->first);
    }

    return 0;
}

static int write_mp_metadex(std::ofstream &file, SHA256_CTX* shaCtx)
{
    for (md_PropertiesMap::iterator my_it = metadex.begin(); my_it != metadex.end(); ++my_it) {
        md_PricesMap& prices = my_it->second;
        for (md_PricesMap::iterator it = prices.begin(); it != prices.end(); ++it) {
            md_Set& indexes = (it->second);
            for (md_Set::iterator it = indexes.begin(); it != indexes.end(); ++it) {
                const CMPMetaDEx& meta = *it;
                meta.saveOffer(file, shaCtx);
            }
        }
    }

    return 0;
}

static int input_msc_balances_string(const std::string& s)
{
    // "address=propertybalancedata"
    std::vector<std::string> addrData;
    boost::split(addrData, s, boost::is_any_of("="), boost::token_compress_on);
    if (addrData.size() != 2) return -1;

    std::string strAddress = addrData[0];

    // split the tuples of properties
    std::vector<std::string> vProperties;
    boost::split(vProperties, addrData[1], boost::is_any_of(";"), boost::token_compress_on);

    std::vector<std::string>::const_iterator iter;
    for (iter = vProperties.begin(); iter != vProperties.end(); ++iter) {
        if ((*iter).empty()) {
            continue;
        }

        // "propertyid:balancedata"
        std::vector<std::string> curProperty;
        boost::split(curProperty, *iter, boost::is_any_of(":"), boost::token_compress_on);
        if (curProperty.size() != 2) return -1;

        // "balance,sellreserved,acceptreserved,metadexreserved"
        std::vector<std::string> curBalance;
        boost::split(curBalance, curProperty[1], boost::is_any_of(","), boost::token_compress_on);
        if (curBalance.size() != 4) return -1;

        uint32_t propertyId = boost::lexical_cast<uint32_t>(curProperty[0]);

        int64_t balance = boost::lexical_cast<int64_t>(curBalance[0]);
        int64_t sellReserved = boost::lexical_cast<int64_t>(curBalance[1]);
        int64_t acceptReserved = boost::lexical_cast<int64_t>(curBalance[2]);
        int64_t metadexReserved = boost::lexical_cast<int64_t>(curBalance[3]);

        if (balance) update_tally_map(strAddress, propertyId, balance, BALANCE);
        if (sellReserved) update_tally_map(strAddress, propertyId, sellReserved, SELLOFFER_RESERVE);
        if (acceptReserved) update_tally_map(strAddress, propertyId, acceptReserved, ACCEPT_RESERVE);
        if (metadexReserved) update_tally_map(strAddress, propertyId, metadexReserved, METADEX_RESERVE);
    }

    return 0;
}

// seller-address, offer_block, amount, property, desired ZUR , property_desired, fee, blocktimelimit
// 13z1JFtDMGTYQvtMq5gs4LmCztK3rmEZga,299076,76375000,1,6415500,0,10000,6
static int input_mp_offers_string(const std::string& s)
{
    std::vector<std::string> vstr;
    boost::split(vstr, s, boost::is_any_of(" ,="), boost::token_compress_on);

    if (9 != vstr.size()) return -1;

    int i = 0;

    std::string sellerAddr = vstr[i++];
    int offerBlock = boost::lexical_cast<int>(vstr[i++]);
    int64_t amountOriginal = boost::lexical_cast<int64_t>(vstr[i++]);
    uint32_t prop = boost::lexical_cast<uint32_t>(vstr[i++]);
    int64_t btcDesired = boost::lexical_cast<int64_t>(vstr[i++]);
    uint32_t prop_desired = boost::lexical_cast<uint32_t>(vstr[i++]);
    int64_t minFee = boost::lexical_cast<int64_t>(vstr[i++]);
    uint8_t blocktimelimit = boost::lexical_cast<unsigned int>(vstr[i++]); // lexical_cast can't handle char!
    uint256 txid = uint256S(vstr[i++]);

    // TODO: should this be here? There are usually no sanity checks..
    if (OMNI_PROPERTY_ZUR != prop_desired) return -1;

    const std::string combo = STR_SELLOFFER_ADDR_PROP_COMBO(sellerAddr, prop);
    CMPOffer newOffer(offerBlock, amountOriginal, prop, btcDesired, minFee, blocktimelimit, txid);

    if (!my_offers.insert(std::make_pair(combo, newOffer)).second) return -1;

    return 0;
}

// seller-address, property, buyer-address, amount, fee, block
// 13z1JFtDMGTYQvtMq5gs4LmCztK3rmEZga,1, 148EFCFXbk2LrUhEHDfs9y3A5dJ4tttKVd,100000,11000,299126
// 13z1JFtDMGTYQvtMq5gs4LmCztK3rmEZga,1,1Md8GwMtWpiobRnjRabMT98EW6Jh4rEUNy,50000000,11000,299132
static int input_mp_accepts_string(const std::string& s)
{
    int nBlock;
    unsigned char blocktimelimit;
    std::vector<std::string> vstr;
    boost::split(vstr, s, boost::is_any_of(" ,="), boost::token_compress_on);
    int64_t amountRemaining, amountOriginal, offerOriginal, btcDesired;
    unsigned int prop;
    std::string sellerAddr, buyerAddr, txidStr;
    int i = 0;

    if (10 != vstr.size()) return -1;

    sellerAddr = vstr[i++];
    prop = boost::lexical_cast<unsigned int>(vstr[i++]);
    buyerAddr = vstr[i++];
    nBlock = atoi(vstr[i++]);
    amountRemaining = boost::lexical_cast<int64_t>(vstr[i++]);
    amountOriginal = boost::lexical_cast<uint64_t>(vstr[i++]);
    blocktimelimit = atoi(vstr[i++]);
    offerOriginal = boost::lexical_cast<int64_t>(vstr[i++]);
    btcDesired = boost::lexical_cast<int64_t>(vstr[i++]);
    txidStr = vstr[i++];

    const std::string combo = STR_ACCEPT_ADDR_PROP_ADDR_COMBO(sellerAddr, buyerAddr, prop);
    CMPAccept newAccept(amountOriginal, amountRemaining, nBlock, blocktimelimit, prop, offerOriginal, btcDesired, uint256S(txidStr));
    if (my_accepts.insert(std::make_pair(combo, newAccept)).second) {
        return 0;
    } else {
        return -1;
    }
}

// exodus_prev
static int input_globals_state_string(const std::string& s)
{
    int64_t exodusPrev;
    uint32_t nextSPID, nextTestSPID;
    std::vector<std::string> vstr;
    boost::split(vstr, s, boost::is_any_of(" ,="), boost::token_compress_on);
    if (3 != vstr.size()) return -1;

    int i = 0;
    exodusPrev = boost::lexical_cast<int64_t>(vstr[i++]);
    nextSPID = boost::lexical_cast<uint32_t>(vstr[i++]);
    nextTestSPID = boost::lexical_cast<uint32_t>(vstr[i++]);

    exodus_prev = exodusPrev;
    pDbSpInfo->init(nextSPID, nextTestSPID);
    return 0;
}

// addr,propertyId,nValue,property_desired,deadline,early_bird,percentage,txid
static int input_mp_crowdsale_string(const std::string& s)
{
    std::vector<std::string> vstr;
    boost::split(vstr, s, boost::is_any_of(" ,"), boost::token_compress_on);

    if (9 > vstr.size()) return -1;

    unsigned int i = 0;

    std::string sellerAddr = vstr[i++];
    uint32_t propertyId = boost::lexical_cast<uint32_t>(vstr[i++]);
    int64_t nValue = boost::lexical_cast<int64_t>(vstr[i++]);
    uint32_t property_desired = boost::lexical_cast<uint32_t>(vstr[i++]);
    int64_t deadline = boost::lexical_cast<int64_t>(vstr[i++]);
    uint8_t early_bird = boost::lexical_cast<unsigned int>(vstr[i++]); // lexical_cast can't handle char!
    uint8_t percentage = boost::lexical_cast<unsigned int>(vstr[i++]); // lexical_cast can't handle char!
    int64_t u_created = boost::lexical_cast<int64_t>(vstr[i++]);
    int64_t i_created = boost::lexical_cast<int64_t>(vstr[i++]);

    CMPCrowd newCrowdsale(propertyId, nValue, property_desired, deadline, early_bird, percentage, u_created, i_created);

    // load the remaining as database pairs
    while (i < vstr.size()) {
        std::vector<std::string> entryData;
        boost::split(entryData, vstr[i++], boost::is_any_of("="), boost::token_compress_on);
        if (2 != entryData.size()) return -1;

        std::vector<std::string> valueData;
        boost::split(valueData, entryData[1], boost::is_any_of(";"), boost::token_compress_on);

        std::vector<int64_t> vals;
        for (std::vector<std::string>::const_iterator it = valueData.begin(); it != valueData.end(); ++it) {
            vals.push_back(boost::lexical_cast<int64_t>(*it));
        }

        uint256 txHash = uint256S(entryData[0]);
        newCrowdsale.insertDatabase(txHash, vals);
    }

    if (!my_crowds.insert(std::make_pair(sellerAddr, newCrowdsale)).second) {
        return -1;
    }

    return 0;
}

// address, block, amount for sale, property, amount desired, property desired, subaction, idx, txid, amount remaining
static int input_mp_mdexorder_string(const std::string& s)
{
    std::vector<std::string> vstr;
    boost::split(vstr, s, boost::is_any_of(" ,="), boost::token_compress_on);

    if (10 != vstr.size()) return -1;

    int i = 0;

    std::string addr = vstr[i++];
    int block = boost::lexical_cast<int>(vstr[i++]);
    int64_t amount_forsale = boost::lexical_cast<int64_t>(vstr[i++]);
    uint32_t property = boost::lexical_cast<uint32_t>(vstr[i++]);
    int64_t amount_desired = boost::lexical_cast<int64_t>(vstr[i++]);
    uint32_t desired_property = boost::lexical_cast<uint32_t>(vstr[i++]);
    uint8_t subaction = boost::lexical_cast<unsigned int>(vstr[i++]); // lexical_cast can't handle char!
    unsigned int idx = boost::lexical_cast<unsigned int>(vstr[i++]);
    uint256 txid = uint256S(vstr[i++]);
    int64_t amount_remaining = boost::lexical_cast<int64_t>(vstr[i++]);

    CMPMetaDEx mdexObj(addr, block, property, amount_forsale, desired_property,
            amount_desired, txid, idx, subaction, amount_remaining);

    if (!MetaDEx_INSERT(mdexObj)) return -1;

    return 0;
}

static int write_state_file(const CBlockIndex* pBlockIndex, int what)
{
    boost::filesystem::path path = pathStateFiles / strprintf("%s-%s.dat", statePrefix[what], pBlockIndex->GetBlockHash().ToString());
    const std::string strFile = path.string();

    std::ofstream file;
    file.open(strFile.c_str());

    SHA256_CTX shaCtx;
    SHA256_Init(&shaCtx);

    int result = 0;

    switch (what) {
        case FILETYPE_BALANCES:
            result = write_msc_balances(file, &shaCtx);
            break;

        case FILETYPE_OFFERS:
            result = write_mp_offers(file, &shaCtx);
            break;

        case FILETYPE_ACCEPTS:
            result = write_mp_accepts(file, &shaCtx);
            break;

        case FILETYPE_GLOBALS:
            result = write_globals_state(file, &shaCtx);
            break;

        case FILETYPE_CROWDSALES:
            result = write_mp_crowdsales(file, &shaCtx);
            break;

        case FILETYPE_MDEXORDERS:
            result = write_mp_metadex(file, &shaCtx);
            break;
    }

    // generate and wite the double hash of all the contents written
    uint256 hash1;
    SHA256_Final((unsigned char*) &hash1, &shaCtx);
    uint256 hash2;
    SHA256((unsigned char*) &hash1, sizeof (hash1), (unsigned char*) &hash2);
    file << "!" << hash2.ToString() << std::endl;

    file.flush();
    file.close();
    return result;
}

static void prune_state_files(const CBlockIndex* topIndex)
{
    // build a set of blockHashes for which we have any state files
    std::set<uint256> statefulBlockHashes;

    boost::filesystem::directory_iterator dIter(pathStateFiles);
    boost::filesystem::directory_iterator endIter;
    for (; dIter != endIter; ++dIter) {
        std::string fName = dIter->path().empty() ? "<invalid>" : (*--dIter->path().end()).string();
        if (false == boost::filesystem::is_regular_file(dIter->status())) {
            // skip funny business
            PrintToLog("Non-regular file found in persistence directory : %s\n", fName);
            continue;
        }

        std::vector<std::string> vstr;
        boost::split(vstr, fName, boost::is_any_of("-."), boost::token_compress_on);
        if (vstr.size() == 3 &&
                is_state_prefix(vstr[0]) &&
                boost::equals(vstr[2], "dat")) {
            uint256 blockHash;
            blockHash.SetHex(vstr[1]);
            statefulBlockHashes.insert(blockHash);
        } else {
            PrintToLog("None state file found in persistence directory : %s\n", fName);
        }
    }

    // for each blockHash in the set, determine the distance from the given block
    std::set<uint256>::const_iterator iter;
    for (iter = statefulBlockHashes.begin(); iter != statefulBlockHashes.end(); ++iter) {
        // look up the CBlockIndex for height info
        CBlockIndex const *curIndex = GetBlockIndex(*iter);

        // if we have nothing int the index, or this block is too old..
        if (NULL == curIndex || (((topIndex->nHeight - curIndex->nHeight) > MAX_STATE_HISTORY)
                && (curIndex->nHeight % STORE_EVERY_N_BLOCK != 0))) {
            if (msc_debug_persistence) {
                if (curIndex) {
                    PrintToLog("State from Block:%s is no longer need, removing files (age-from-tip: %d)\n", (*iter).ToString(), topIndex->nHeight - curIndex->nHeight);
                } else {
                    PrintToLog("State from Block:%s is no longer need, removing files (not in index)\n", (*iter).ToString());
                }
            }

            // destroy the associated files!
            std::string strBlockHash = iter->ToString();
            for (int i = 0; i < NUM_FILETYPES; ++i) {
                boost::filesystem::path path = pathStateFiles / strprintf("%s-%s.dat", statePrefix[i], strBlockHash);
                boost::filesystem::remove(path);
            }
        }
    }
}

/**
 * Indicates whether persistence is enabled and the state is stored.
 */
bool IsPersistenceEnabled(int blockHeight) {
    // if too far away from the top -- do not write
    if (GetHeight() > (blockHeight + MAX_STATE_HISTORY)
            && (blockHeight % STORE_EVERY_N_BLOCK != 0)) {
        return false;
    }

    return true;
}

/**
 * Stores the in-memory state in files.
 */
int PersistInMemoryState(const CBlockIndex* pBlockIndex)
{
    // write the new state as of the given block
    write_state_file(pBlockIndex, FILETYPE_BALANCES);
    write_state_file(pBlockIndex, FILETYPE_OFFERS);
    write_state_file(pBlockIndex, FILETYPE_ACCEPTS);
    write_state_file(pBlockIndex, FILETYPE_GLOBALS);
    write_state_file(pBlockIndex, FILETYPE_CROWDSALES);
    write_state_file(pBlockIndex, FILETYPE_MDEXORDERS);

    // clean-up the directory
    prune_state_files(pBlockIndex);

    pDbSpInfo->setWatermark(pBlockIndex->GetBlockHash());

    return 0;
}

/**
 * Loads and retrieves state from a file.
 */
int RestoreInMemoryState(const std::string& filename, int what, bool verifyHash)
{
    int lines = 0;
    int (*inputLineFunc)(const std::string&) = NULL;

    SHA256_CTX shaCtx;
    SHA256_Init(&shaCtx);

    switch (what) {
        case FILETYPE_BALANCES:
            mp_tally_map.clear();
            inputLineFunc = input_msc_balances_string;
            break;

        case FILETYPE_OFFERS:
            my_offers.clear();
            inputLineFunc = input_mp_offers_string;
            break;

        case FILETYPE_ACCEPTS:
            my_accepts.clear();
            inputLineFunc = input_mp_accepts_string;
            break;

        case FILETYPE_GLOBALS:
            inputLineFunc = input_globals_state_string;
            break;

        case FILETYPE_CROWDSALES:
            my_crowds.clear();
            inputLineFunc = input_mp_crowdsale_string;
            break;

        case FILETYPE_MDEXORDERS:
            // FIXME
            // memory leak ... gotta unallocate inner layers first....
            // TODO
            // ...
            metadex.clear();
            inputLineFunc = input_mp_mdexorder_string;
            break;

        default:
            return -1;
    }

    if (msc_debug_persistence) {
        LogPrintf("Loading %s ... \n", filename);
        PrintToLog("%s(%s), line %d, file: %s\n", __FUNCTION__, filename, __LINE__, __FILE__);
    }

    std::ifstream file;
    file.open(filename.c_str());
    if (!file.is_open()) {
        if (msc_debug_persistence) LogPrintf("%s(%s): file not found, line %d, file: %s\n", __FUNCTION__, filename, __LINE__, __FILE__);
        return -1;
    }

    int res = 0;

    std::string fileHash;
    while (file.good()) {
        std::string line;
        std::getline(file, line);
        if (line.empty() || line[0] == '#') continue;

        // remove \r if the file came from Windows
        line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());

        // record and skip hashes in the file
        if (line[0] == '!') {
            fileHash = line.substr(1);
            continue;
        }

        // update hash?
        if (verifyHash) {
            SHA256_Update(&shaCtx, line.c_str(), line.length());
        }

        if (inputLineFunc) {
            if (inputLineFunc(line) < 0) {
                res = -1;
                break;
            }
        }

        ++lines;
    }

    file.close();

    if (verifyHash && res == 0) {
        // generate and wite the double hash of all the contents written
        uint256 hash1;
        SHA256_Final((unsigned char*) &hash1, &shaCtx);
        uint256 hash2;
        SHA256((unsigned char*) &hash1, sizeof (hash1), (unsigned char*) &hash2);

        if (false == boost::iequals(hash2.ToString(), fileHash)) {
            PrintToLog("File %s loaded, but failed hash validation!\n", filename);
            res = -1;
        }
    }

    PrintToLog("%s(%s), loaded lines= %d, res= %d\n", __FUNCTION__, filename, lines, res);
    LogPrintf("%s(): file: %s , loaded lines= %d, res= %d\n", __FUNCTION__, filename, lines, res);

    return res;
}

/**
 * Loads and restores the latest state. Returns -1 if reparse is required.
 */
int LoadMostRelevantInMemoryState()
{
    PrintToLog("Trying to load most relevant state into memory..\n");
    int res = -1;
    // check the SP database and roll it back to its latest valid state
    // according to the active chain
    uint256 spWatermark;
    if (!pDbSpInfo->getWatermark(spWatermark)) {
        // trigger a full reparse, if the SP database has no watermark
        PrintToLog("Failed to load historical state: SP database has no watermark\n");
        return -1;
    }

    CBlockIndex const *spBlockIndex = GetBlockIndex(spWatermark);
    if (NULL == spBlockIndex) {
        // trigger a full reparse, if the watermark isn't a real block
        PrintToLog("Failed to load historical state: watermark isn't a real block\n");
        return -1;
    }

    while (NULL != spBlockIndex && false == chainActive.Contains(spBlockIndex)) {
        int remainingSPs = pDbSpInfo->popBlock(spBlockIndex->GetBlockHash());
        if (remainingSPs < 0) {
            // trigger a full reparse, if the levelDB cannot roll back
            PrintToLog("Failed to load historical state: no valid state found after rolling back SP database\n");
            return -1;
        } /*else if (remainingSPs == 0) {
      // potential optimization here?
    }*/
        spBlockIndex = spBlockIndex->pprev;
        if (spBlockIndex != NULL) {
            pDbSpInfo->setWatermark(spBlockIndex->GetBlockHash());
        }
    }

    // prepare a set of available files by block hash pruning any that are
    // not in the active chain
    std::set<uint256> persistedBlocks;
    boost::filesystem::directory_iterator dIter(pathStateFiles);
    boost::filesystem::directory_iterator endIter;
    for (; dIter != endIter; ++dIter) {
        if (false == boost::filesystem::is_regular_file(dIter->status()) || dIter->path().empty()) {
            // skip funny business
            continue;
        }

        std::string fName = (*--dIter->path().end()).string();
        std::vector<std::string> vstr;
        boost::split(vstr, fName, boost::is_any_of("-."), boost::token_compress_on);
        if (vstr.size() == 3 &&
                boost::equals(vstr[2], "dat")) {
            uint256 blockHash;
            blockHash.SetHex(vstr[1]);
            CBlockIndex *pBlockIndex = GetBlockIndex(blockHash);
            if (pBlockIndex == NULL || false == chainActive.Contains(pBlockIndex)) {
                continue;
            }

            // this is a valid block in the active chain, store it
            persistedBlocks.insert(blockHash);
        }
    }

    // using the SP's watermark after its fixed-up as the tip
    // walk backwards until we find a valid and full set of persisted state files
    // for each block we discard, roll back the SP database
    CBlockIndex const *curTip = spBlockIndex;
    int abortRollBackBlock = 9999999;
    if (curTip != NULL) {
        abortRollBackBlock = ConsensusParams().GENESIS_BLOCK - 1;
    }
    while (NULL != curTip && persistedBlocks.size() > 0 && curTip->nHeight > abortRollBackBlock ) {
        if (persistedBlocks.find(curTip->GetBlockHash()) != persistedBlocks.end()) {
            int success = -1;
            for (int i = 0; i < NUM_FILETYPES; ++i) {
                boost::filesystem::path path = pathStateFiles / strprintf("%s-%s.dat", statePrefix[i], curTip->GetBlockHash().ToString());
                const std::string strFile = path.string();
                success = RestoreInMemoryState(strFile, i, true);
                if (success < 0) {
                    PrintToConsole("Found a state inconsistency at block height %d. "
                            "Reverting up to %d blocks.. this may take a few minutes.\n",
                            curTip->nHeight, (curTip->nHeight - abortRollBackBlock - 1));
                    break;
                }
            }

            if (success >= 0) {
                res = curTip->nHeight;
                break;
            }

            // remove this from the persistedBlock Set
            persistedBlocks.erase(spBlockIndex->GetBlockHash());
        }

        // go to the previous block
        if (pDbSpInfo->popBlock(curTip->GetBlockHash()) <= 0) {
            // trigger a full reparse, if the levelDB cannot roll back
            PrintToLog("Failed to load historical state: no valid state found after rolling back SP database (2)\n");
            return -1;
        }
        curTip = curTip->pprev;
        spBlockIndex = curTip;
        if (curTip != NULL) {
            pDbSpInfo->setWatermark(curTip->GetBlockHash());
        }
    }

    if (persistedBlocks.size() == 0) {
        // trigger a reparse if we exhausted the persistence files without success
        PrintToLog("Failed to load historical state: no valid state found after exhausting persistence files\n");
        return -1;
    }

    // return the height of the block we settled at
    return res;
}

ext: cpp


text:
#include <boost/ut.hpp>
#include <libembeddedhal/motor/mock.hpp>

namespace embed {
boost::ut::suite motor_mock_test = []() {
  using namespace boost::ut;

  // Setup
  embed::mock::motor mock;
  constexpr auto expected1 = percent::from_ratio(1, 2);
  constexpr auto expected2 = percent::from_ratio(1, 4);
  mock.spy_power.trigger_error_on_call(3);

  // Exercise + Verify
  expect(bool{ mock.power(expected1) });
  expect(expected1 == std::get<0>(mock.spy_power.call_history().at(0)));

  expect(bool{ mock.power(expected2) });
  expect(expected2 == std::get<0>(mock.spy_power.call_history().at(1)));

  expect(!mock.power(expected2));
  expect(expected2 == std::get<0>(mock.spy_power.call_history().at(2)));
};
}  // namespace embed

max_stars_repo_name: MaliaLabor/libembeddedhal

max_issues_repo_name: MaliaLabor/libembeddedhal

max_forks_repo_name: MaliaLabor/libembeddedhal


text:
//
//  bindings.hpp
//  android-libbitcoin
//
//  Created by Dan Sexton on 5/17/18.
//  Copyright © 2018 Coin Ninja, LLC. All rights reserved.
//

#ifndef bindings_hpp
#define bindings_hpp

#include <string>
#include <boost/regex.hpp>
#include <iostream>
#include <sstream>
#include <string.h>
#include <iostream>

#include <android/log.h>
#include <jni.h>

#include "libbitcoin.hpp"
#include "hd_wallet.hpp"
#include "misc.hpp"
#include "usable_address.hpp"
#include "Base58Check.hpp"
#include "cipher_key_vendor.hpp"
#include "cipher_keys.hpp"
#include "encryption_cipher_keys.hpp"

extern "C"
{
    JNIEXPORT void JNICALL
    Java_com_coinninja_bindings_Libbitcoin_initWords(JNIEnv *env, jobject instance,
                                                    jobjectArray seedWordList,
                                                    jboolean testnet);

    JNIEXPORT void JNICALL
    Java_com_coinninja_bindings_Libbitcoin_init(JNIEnv *env, jobject instance, jboolean isTestNet);

    JNIEXPORT jobjectArray JNICALL
    Java_com_coinninja_bindings_Libbitcoin_getSeedWords(JNIEnv *env, jobject instance);

    JNIEXPORT jstring JNICALL
    Java_com_coinninja_bindings_Libbitcoin_getExternalChangeAddress(JNIEnv *env, jobject instance, jint index);

    JNIEXPORT jstring JNICALL
    Java_com_coinninja_bindings_Libbitcoin_getInternalChangeAddress(JNIEnv *env, jobject instance, jint index);

    JNIEXPORT jboolean JNICALL
    Java_com_coinninja_bindings_Libbitcoin_isTestnet(JNIEnv *env, jobject instance);

    int *convertJints(JNIEnv *env, jintArray array);

    JNIEXPORT jstring JNICALL
    Java_com_coinninja_bindings_Libbitcoin_getCoinNinjaVerificationKey(JNIEnv *env, jobject instance);

    JNIEXPORT jstring JNICALL
    Java_com_coinninja_bindings_Libbitcoin_sign(JNIEnv *env, jobject instance, jstring message_);

    JNIEXPORT jstring JNICALL
    Java_com_coinninja_bindings_Libbitcoin_encodeTransaction(JNIEnv *env, jobject instance);

    JNIEXPORT void JNICALL
    Java_com_coinninja_bindings_Libbitcoin_populateTransaction(JNIEnv *env, jobject instance,
                                                            jstring paymentAddress_, jlong amount,
                                                            jlong feeAmount, jlong changeAmount,
                                                            jintArray changePath_);

    JNIEXPORT void JNICALL
    Java_com_coinninja_bindings_Libbitcoin_signInput(JNIEnv *env, jobject instance, jint inputIndex,
                                                    jstring txId_, jlong amount,
                                                    jintArray derivationPath_);

    JNIEXPORT jstring JNICALL
    Java_com_coinninja_bindings_Libbitcoin_broadcast(JNIEnv *env, jobject instance);

    JNIEXPORT void JNICALL
    Java_com_coinninja_bindings_Libbitcoin_populateUtxo(JNIEnv *env, jobject instance, jstring txId_,
                                                        jlong amount, jint index, jboolean replaceable);
    JNIEXPORT jboolean JNICALL
    Java_com_coinninja_bindings_Libbitcoin_isBase58CheckEncoded(JNIEnv *env, jobject instance, jstring address_);

    JNIEXPORT jobject JNICALL
    Java_com_coinninja_bindings_Libbitcoin_getKeys(JNIEnv *env, jobject instance, jbyteArray publicKey);

    JNIEXPORT jobject JNICALL
    Java_com_coinninja_bindings_Libbitcoin_decryptionKeys(JNIEnv *env, jobject instance, jintArray path_, jbyteArray publicKey);

    JNIEXPORT jbyteArray JNICALL
    Java_com_coinninja_bindings_Libbitcoin_getUncompressedPublicKey(JNIEnv *env, jobject instance,
                                                            jintArray path_);
}

#endif /* bindings_hpp */

ext: hpp

max_stars_repo_name: coinninjadev/android-libbitcoin

max_issues_repo_name: coinninjadev/android-libbitcoin

max_forks_repo_name: coinninjadev/android-libbitcoin


text:
//---------------------------------------------------------------------------//
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//
// See http://kylelutz.github.com/compute for more information.
//---------------------------------------------------------------------------//

#ifndef BOOST_COMPUTE_CONTAINER_DETAIL_SCALAR_HPP
#define BOOST_COMPUTE_CONTAINER_DETAIL_SCALAR_HPP

#include <boost/compute/buffer.hpp>
#include <boost/compute/detail/read_write_single_value.hpp>

namespace boost {
namespace compute {
namespace detail {

// scalar<T> provides a trivial "container" that stores a
// single value in a memory buffer on a compute device
template<class T>
class scalar
{
public:
    typedef T value_type;

    scalar(const context &context)
        : m_buffer(context, sizeof(T))
    {
    }

    ~scalar()
    {
    }

    T read(command_queue &queue) const
    {
        return read_single_value<T>(m_buffer, 0, queue);
    }

    void write(const T &value, command_queue &queue)
    {
        write_single_value<T>(value, m_buffer, 0, queue);
    }

    const buffer& get_buffer() const
    {
        return m_buffer;
    }

private:
    buffer m_buffer;
};

} // end detail namespace
} // end compute namespace
} // end boost namespace

#endif // BOOST_COMPUTE_CONTAINER_DETAIL_SCALAR_HPP

max_stars_repo_name: bastiankoe/compute

max_issues_repo_name: bastiankoe/compute

max_forks_repo_name: bastiankoe/compute


text:
#include <deip/protocol/config.hpp>
#include <deip/chain/database/database.hpp>
#include <deip/chain/operation_notification.hpp>
#include <deip/chain/services/dbs_research.hpp>
#include <deip/chain/services/dbs_review.hpp>
#include <deip/chain/services/dbs_review_vote.hpp>
#include <deip/chain/services/dbs_discipline.hpp>
#include <deip/chain/services/dbs_expert_token.hpp>
#include <deip/chain/services/dbs_expertise_contribution.hpp>
#include <deip/eci_history/eci_history_api.hpp>
#include <deip/eci_history/eci_history_plugin.hpp>
#include <deip/eci_history/research_eci_history_object.hpp>
#include <deip/eci_history/research_content_eci_history_object.hpp>
#include <deip/eci_history/account_eci_history_object.hpp>
#include <deip/eci_history/discipline_eci_history_object.hpp>

#include <fc/smart_ref_impl.hpp>
#include <fc/thread/thread.hpp>

#include <boost/algorithm/string.hpp>

namespace deip {
namespace eci_history {

using namespace deip::protocol;
using namespace deip::chain;

namespace detail {

class eci_history_plugin_impl
{
public:
    eci_history_plugin_impl(eci_history_plugin& _plugin)
        : _self(_plugin)
    {
    }

    virtual ~eci_history_plugin_impl()
    {
    }

    deip::chain::database& database()
    {
        return _self.database();
    }

    void pre_operation(const operation_notification& op_obj);
    void post_operation(const operation_notification& op_obj);

    eci_history_plugin& _self;
};

struct post_operation_visitor
{
    eci_history_plugin& _plugin;

    post_operation_visitor(eci_history_plugin& plugin)
        : _plugin(plugin)
    {
    }

    typedef void result_type;

    template <typename T> void operator()(const T&) const
    {
    }

    void operator()(const research_content_eci_history_operation& op) const
    {
        const auto& disciplines_service = _plugin.database().obtain_service<chain::dbs_discipline>();

        const auto& discipline = disciplines_service.get_discipline(op.discipline_id);

        _plugin.database().create<research_content_eci_history_object>([&](research_content_eci_history_object& hist_o) {
            hist_o.research_content_id = op.research_content_id;
            hist_o.discipline_id = op.discipline_id;
            hist_o.discipline_external_id = discipline.external_id;
            hist_o.eci = op.diff.current();
            hist_o.delta = op.diff.diff();
            hist_o.contribution_type = op.diff.contribution_type;
            hist_o.contribution_id = op.diff.contribution_id;
            hist_o.timestamp = op.diff.timestamp;

            for (const auto& criteria : op.diff.assessment_criterias)
            {
                hist_o.assessment_criterias.insert(std::make_pair(criteria.first, criteria.second));
            }
        });
    }

    void operator()(const research_eci_history_operation& op) const
    {
        const auto& disciplines_service = _plugin.database().obtain_service<chain::dbs_discipline>();

        const auto& discipline = disciplines_service.get_discipline(op.discipline_id);

        _plugin.database().create<research_eci_history_object>([&](research_eci_history_object& hist_o) {
            hist_o.research_id = op.research_id;
            hist_o.discipline_id = op.discipline_id;
            hist_o.discipline_external_id = discipline.external_id;
            hist_o.eci = op.diff.current();
            hist_o.delta = op.diff.diff();
            hist_o.contribution_type = op.diff.contribution_type;
            hist_o.contribution_id = op.diff.contribution_id;
            hist_o.timestamp = op.diff.timestamp;

            for (const auto& criteria : op.diff.assessment_criterias)
            {
                hist_o.assessment_criterias.insert(std::make_pair(criteria.first, criteria.second));
            }
        });
    }

    void operator()(const account_eci_history_operation& op) const
    {
        const auto& research_service = _plugin.database().obtain_service<chain::dbs_research>();
        const auto& reviews_service = _plugin.database().obtain_service<chain::dbs_review>();
        const auto& review_votes_service = _plugin.database().obtain_service<chain::dbs_review_vote>();
        const auto& disciplines_service = _plugin.database().obtain_service<chain::dbs_discipline>();

        const uint16_t event_contribution_type = op.diff.contribution_type;
        const uint16_t event_contribution_id = op.diff.contribution_id;

        uint16_t contribution_type = event_contribution_type;
        int64_t contribution_id = event_contribution_id;

        const auto event_type = static_cast<expertise_contribution_type>(event_contribution_type);
        const auto recipient_type = static_cast<reward_recipient_type>(op.recipient_type);

        switch (recipient_type)
        {
            case reward_recipient_type::author:
            {
                switch (event_type)
                {
                    case expertise_contribution_type::publication:
                    {
                        break; // rewarded account is author of research content
                    }

                    case expertise_contribution_type::review: // rewarded account is author of research content
                    {
                        const auto& review = reviews_service.get_review(review_id_type(event_contribution_id));

                        contribution_type = static_cast<uint16_t>(expertise_contribution_type::publication);
                        contribution_id = review.research_content_id._id;

                        break;
                    }

                    case expertise_contribution_type::review_support: // rewarded account is author of research content
                    {
                        const auto& review_vote = review_votes_service.get_review_vote(review_vote_id_type(event_contribution_id));
                        const auto& review = reviews_service.get_review(review_vote.review_id);

                        contribution_type = static_cast<uint16_t>(expertise_contribution_type::publication);
                        contribution_id = review.research_content_id._id;

                        break;
                    }

                    default:
                    {
                        break;
                    }
                }

                break;
            }
            case reward_recipient_type::reviewer:
            {
                switch (event_type)
                {
                    case expertise_contribution_type::review:
                    {
                        const auto& review = reviews_service.get_review(review_id_type(event_contribution_id));

                        if (review.author == op.account) // rewarded account is review author
                        {
                            break;
                        }

                        const auto& research_content_reviews = reviews_service.get_reviews_by_research_content(review.research_content_id);
                        const auto& review_itr = std::find_if(research_content_reviews.begin(), research_content_reviews.end(),
                          [&](const review_object& rw) { return rw.author == op.account && rw.id != review.id; });

                        if (review_itr != research_content_reviews.end()) // rewarded account is author of another review for the same research content
                        {
                            const review_object& research_content_review = *review_itr;
                            contribution_type = static_cast<uint16_t>(expertise_contribution_type::review);
                            contribution_id = research_content_review.id._id;

                            break;
                        }

                        break;
                    }

                    case expertise_contribution_type::review_support:
                    {
                        const auto& review_vote = review_votes_service.get_review_vote(review_vote_id_type(event_contribution_id));
                        const auto& review = reviews_service.get_review(review_vote.review_id);

                        if (review.author == op.account) // rewarded account is author of review that has been supported
                        {
                            contribution_type = static_cast<uint16_t>(expertise_contribution_type::review);
                            contribution_id = review.id._id;

                            break;
                        }

                        const auto& research_content_reviews = reviews_service.get_reviews_by_research_content(review.research_content_id);
                        const auto& review_itr = std::find_if(research_content_reviews.begin(), research_content_reviews.end(),
                          [&](const review_object& rw) { return rw.author == op.account && rw.id != review.id; });

                        if (review_itr != research_content_reviews.end()) // rewarded account is author of another review for the same research content
                        {
                            const review_object& research_content_review = *review_itr;
                            contribution_type = static_cast<uint16_t>(expertise_contribution_type::review);
                            contribution_id = research_content_review.id._id;

                            break;
                        }

                        break;
                    }

                    default:
                    {
                        break;
                    }
                }

                break;
            }

            case reward_recipient_type::review_supporter:
            {
                switch (event_type)
                {
                    case expertise_contribution_type::review:
                    {
                        const auto& review = reviews_service.get_review(review_id_type(event_contribution_id));
                        const auto& research_content_reviews_votes = review_votes_service.get_review_votes_by_researh_content(review.research_content_id);

                        const auto& review_vote_itr = std::find_if(research_content_reviews_votes.begin(), research_content_reviews_votes.end(),
                            [&](const review_vote_object& vote) { return vote.voter == op.account; });

                        if (review_vote_itr != research_content_reviews_votes.end()) // rewarded account is review supporter of any review within the same research content
                        {
                            const review_vote_object& research_content_review_vote = *review_vote_itr;
                            contribution_type = static_cast<uint16_t>(expertise_contribution_type::review_support);
                            contribution_id = research_content_review_vote.id._id; // bug, as votes can be left for multiple reviews within the same research content

                            break;
                        }

                        break;
                    }

                    case expertise_contribution_type::review_support:
                    {
                        break; // rewarded account is review supporter
                    }

                    default:
                    {
                        break;
                    }
                }
            }
            default:
            {
                break;
            }
        }

        // const auto& researches = research_service.get_researches_by_member(op.account);
        const auto& discipline = disciplines_service.get_discipline(op.discipline_id);

        _plugin.database().create<account_eci_history_object>([&](account_eci_history_object& hist_o) {
            hist_o.account = op.account;
            hist_o.discipline_id = op.discipline_id;
            hist_o.discipline_external_id = discipline.external_id;
            hist_o.eci = op.diff.current();
            hist_o.delta = op.diff.diff();
            hist_o.contribution_type = contribution_type;
            hist_o.contribution_id = contribution_id;
            hist_o.event_contribution_type = event_contribution_type;
            hist_o.event_contribution_id = event_contribution_id;

            hist_o.timestamp = op.diff.timestamp;

            for (const auto& criteria : op.diff.assessment_criterias)
            {
                hist_o.assessment_criterias.insert(std::make_pair(criteria.first, criteria.second));
            }

            // for (const research_object& research : researches)
            // {
            //     hist_o.researches.insert(research.external_id);
            // }
        });
    }

    void operator()(const disciplines_eci_history_operation& op) const
    {
        const auto& disciplines_service = _plugin.database().obtain_service<chain::dbs_discipline>();
        const auto& expertise_contribution_service = _plugin.database().obtain_service<chain::dbs_expertise_contribution>();

        const auto& disciplines = disciplines_service.lookup_disciplines(discipline_id_type(1), DEIP_API_BULK_FETCH_LIMIT);

        std::map<discipline_eci_history_id_type, share_type> disciplines_stats;
        share_type total_expertise = share_type(0);

        for (const discipline_object& discipline : disciplines)
        {
            const auto& expertise_contributions = expertise_contribution_service.get_expertise_contributions_by_discipline(discipline.id);
            share_type total_eci = share_type(0);
            flat_map<uint16_t, assessment_criteria_value> total_assessment_criterias;

            for (const expertise_contribution_object& exp_contribution : expertise_contributions)
            {
                total_eci += exp_contribution.eci;
                for (const auto& criteria : exp_contribution.assessment_criterias)
                {
                    if (total_assessment_criterias.find(criteria.first) == total_assessment_criterias.end())
                    {
                        total_assessment_criterias.insert(std::make_pair(criteria.first, criteria.second));
                    }
                    else
                    {
                        total_assessment_criterias.at(criteria.first) += criteria.second;
                    }
                }
            }

            const auto& hist = _plugin.database().create<discipline_eci_history_object>([&](discipline_eci_history_object& hist_o) {
                hist_o.discipline_id = discipline.id;
                hist_o.discipline_external_id = discipline.external_id;
                hist_o.eci = total_eci;
                hist_o.timestamp = op.timestamp;

                if (op.contributions.find(hist_o.discipline_id._id) != op.contributions.end())
                {
                    const auto& contributions = op.contributions.at(hist_o.discipline_id._id);
                    for (auto& contribution : contributions)
                    {
                        hist_o.contributions.push_back(contribution);
                    }
                }

                for (const auto& criteria : total_assessment_criterias)
                {
                    hist_o.assessment_criterias.insert(std::make_pair(criteria.first, criteria.second));
                }
            });

            disciplines_stats.insert(std::make_pair(hist.id, total_eci));
            total_expertise += total_eci;
        }

        for (const auto& stat : disciplines_stats)
        {
            const auto& expertise = stat.second;
            const auto& percentage = total_expertise.value != 0
                ? share_type(std::round( (((double(expertise.value) / double(total_expertise.value)) * double(100)) * DEIP_1_PERCENT) ))
                : share_type(0);

            const auto& hist = _plugin.database().get<discipline_eci_history_object, by_id>(stat.first);

            _plugin.database().modify(hist, [&](discipline_eci_history_object& hist_o) {
                hist_o.percentage = percent(percentage);
                hist_o.total_eci = total_expertise;
            });
        }
    }
};

void eci_history_plugin_impl::pre_operation(const operation_notification& note)
{
}

void eci_history_plugin_impl::post_operation(const operation_notification& note)
{
    note.op.visit(post_operation_visitor(_self));
}

} // end namespace detail

eci_history_plugin::eci_history_plugin(application* app)
    : plugin(app)
    , my(new detail::eci_history_plugin_impl(*this))
{
    ilog("Loading ECI history plugin");
}

eci_history_plugin::~eci_history_plugin()
{
}

std::string eci_history_plugin::plugin_name() const
{
    return ECI_HISTORY_PLUGIN_NAME;
}

void eci_history_plugin::plugin_set_program_options(boost::program_options::options_description& cli, boost::program_options::options_description& cfg)
{

}

void eci_history_plugin::plugin_initialize(const boost::program_options::variables_map& options)
{
    ilog("Intializing ECI history plugin");

    chain::database& db = database();

    db.add_plugin_index<account_eci_history_index>();
    db.add_plugin_index<research_eci_history_index>();
    db.add_plugin_index<research_content_eci_history_index>();
    db.add_plugin_index<discipline_eci_history_index>();

    db.pre_apply_operation.connect([&](const operation_notification& note) { my->pre_operation(note); });
    db.post_apply_operation.connect([&](const operation_notification& note) { my->post_operation(note); });
}

void eci_history_plugin::plugin_startup()
{
    ilog("eci_history plugin: plugin_startup() begin");
    app().register_api_factory<eci_history_api>("eci_history_api");
    ilog("eci_history plugin: plugin_startup() end");
}

} // namespace eci_history
} // namespace deip

DEIP_DEFINE_PLUGIN(eci_history, deip::eci_history::eci_history_plugin)

max_stars_repo_name: DEIPworld/deip-chain

max_issues_repo_name: DEIPworld/deip-chain

max_forks_repo_name: DEIPworld/deip-chain


text:
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <iostream>

using namespace std;
using namespace boost::filesystem;

int main(int argc, char** argv)
{
    if (argc < 3) {
        fprintf(stderr, "USAGE: %s {sym} {rsrc}\n\n"
                        "  Creates {sym}.c from the contents of {rsrc}\n",
                argv[0]);
        return EXIT_FAILURE;
    }

    path dst{argv[1]};
    path src{argv[2]};

    string sym = src.filename().string();
    replace(sym.begin(), sym.end(), '.', '_');
    replace(sym.begin(), sym.end(), '-', '_');

    create_directories(dst.parent_path());

    boost::filesystem::ofstream ofs{dst};

    boost::filesystem::ifstream ifs{src};

    ofs << "#include <stdlib.h>" << endl;
    ofs << "const char _resource_" << sym << "[] = {" << endl;

    size_t lineCount = 0;
    while (!ifs.eof())
    {
        char c;
        ifs.get(c);
        ofs << "0x" << hex << (c&0xff) << ", ";
        if (++lineCount == 10) {
            ofs << endl;
            lineCount = 0;
        }
    }

    ofs << "};" << endl;
    ofs << "const size_t _resource_" << sym << "_len = sizeof(_resource_" << sym << ");";

    return EXIT_SUCCESS;
}

max_stars_repo_name: darrenmothersele/embed-resource

max_issues_repo_name: darrenmothersele/embed-resource

max_forks_repo_name: darrenmothersele/embed-resource


text:
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <utility>
#include <numeric>
#include <string>
#include <fstream>
#include <map>
#include <sstream>
#include <limits>
#include <bitset>
#include <cmath>
#include <unordered_map>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>

class MemoryGame
{
    public:
        MemoryGame(std::vector<long long> start_num) : starting_numbers(start_num) {}
        std::vector<long long> starting_numbers;
        long long play(const long long end_turn)
        {
            // key - number, value - vector of indices
            std::map<long long, std::vector<long long>> map;
            long long last_spoken;
            long long turn = 1LL;
            std::transform(starting_numbers.begin(), starting_numbers.end(), std::inserter(map, map.end()),
            [&](const long long num)
            {
                std::vector<long long> indices {turn};
                last_spoken = num;
                turn++;
                return std::pair<long long, std::vector<long long>>(num, indices);
            });
            while(turn <= end_turn)
            {
                // if size == 1 speak 0 because number was spoken only once, otherwise speak difference between turns
                last_spoken = (map[last_spoken].size() == 1) ? 0 : map[last_spoken][1] - map[last_spoken][0];
                // save current turn in map for spoken number
                if(map[last_spoken].size() == 2)
                {
                    map[last_spoken][0] = map[last_spoken][1]; // shift to first
                    map[last_spoken].pop_back(); // remove second
                }
                map[last_spoken].push_back(turn);
                turn++;
            }
            return last_spoken;
        }
};

void part1(MemoryGame game)
{
    std::cout << "======\nPart 1\n======\n";
    constexpr long long end = 2020LL;
    std::cout << end << "th number spoken = " << game.play(end) << '\n';
}

void part2(MemoryGame game)
{
    std::cout << "======\nPart 2\n======\n";
    constexpr long long end = 30000000LL;
    std::cout << end <<  "th number spoken = " << game.play(end) << '\n';
}

std::vector<long long> get_input(const std::string file_name)
{
    std::ifstream file(file_name);
    std::string line;
    std::vector<std::string> line_elements;
    std::vector<long long> all_elements;
    if(file.is_open())
    {
        while (std::getline(file, line))
        {
            boost::split(line_elements, line, boost::is_any_of(","), boost::token_compress_on);
            std::transform(line_elements.begin(), line_elements.end(), std::back_inserter(all_elements),
               [](const std::string& str) { return std::stoll(str); });
        }
    }
    file.close();
    return all_elements;
}

int main()
{
    const std::string file_name = "/home/daria/Documents/AoC2020/input/day15.txt";
    std::vector<long long> puzzle_input = get_input(file_name);
    MemoryGame game(puzzle_input);
    part1(game);
    std::cout << '\n';
    part2(game);
    std::cout << '\n';
}


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

#ifndef INCLUDED_UHD_USRP_DBOARD_MANAGER_HPP
#define INCLUDED_UHD_USRP_DBOARD_MANAGER_HPP

#include <uhd/config.hpp>
#include <uhd/property_tree.hpp>
#include <uhd/usrp/dboard_base.hpp>
#include <uhd/usrp/dboard_id.hpp>
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
#include <string>
#include <vector>

namespace uhd{ namespace usrp{

/*!
 * A daughter board subdev dboard_manager class.
 * Create subdev instances for each subdev on a dboard.
 * Provide wax::obj access to the subdevs inside.
 */
class UHD_API dboard_manager : boost::noncopyable{
public:
    typedef boost::shared_ptr<dboard_manager> sptr;

    //dboard constructor (each dboard should have a ::make with this signature)
    typedef dboard_base::sptr(*dboard_ctor_t)(dboard_base::ctor_args_t);

    /*!
     * Register a rx or tx dboard into the system.
     * For single subdevice boards, omit subdev_names.
     * \param dboard_id the dboard id (rx or tx)
     * \param dboard_ctor the dboard constructor function pointer
     * \param name the canonical name for the dboard represented
     * \param subdev_names the names of the subdevs on this dboard
     */
    static void register_dboard(
        const dboard_id_t &dboard_id,
        dboard_ctor_t dboard_ctor,
        const std::string &name,
        const std::vector<std::string> &subdev_names = std::vector<std::string>(1, "0")
    );

    /*!
     * Register an xcvr dboard into the system.
     * For single subdevice boards, omit subdev_names.
     * \param rx_dboard_id the rx unit dboard id
     * \param tx_dboard_id the tx unit dboard id
     * \param dboard_ctor the dboard constructor function pointer
     * \param name the canonical name for the dboard represented
     * \param subdev_names the names of the subdevs on this dboard
     */
    static void register_dboard(
        const dboard_id_t &rx_dboard_id,
        const dboard_id_t &tx_dboard_id,
        dboard_ctor_t dboard_ctor,
        const std::string &name,
        const std::vector<std::string> &subdev_names = std::vector<std::string>(1, "0")
    );

    /*!
     * Make a new dboard manager.
     * \param rx_dboard_id the id of the rx dboard
     * \param tx_dboard_id the id of the tx dboard
     * \param gdboard_id the id of the grand-dboard
     * \param iface the custom dboard interface
     * \param subtree the subtree to load with props
     * \return an sptr to the new dboard manager
     */
    static sptr make(
        dboard_id_t rx_dboard_id,
        dboard_id_t tx_dboard_id,
        dboard_id_t gdboard_id,
        dboard_iface::sptr iface,
        property_tree::sptr subtree
    );
};

}} //namespace

#endif /* INCLUDED_UHD_USRP_DBOARD_MANAGER_HPP */

max_stars_repo_name: cityscapesc/specobs

max_issues_repo_name: cityscapesc/specobs

max_forks_repo_name: cityscapesc/specobs


text:
// Boost.Range library
//
//  Copyright Thorsten Ottosen 2003-2004. Use, modification and
//  distribution is subject to the Boost Software License, Version
//  1.0. (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//
// For more information, see http://www.boost.org/libs/range/
//

#ifndef BOOST_RANGE_DETAIL_VALUE_TYPE_HPP
#define BOOST_RANGE_DETAIL_VALUE_TYPE_HPP

#include <boost/range/detail/common.hpp>
#include <boost/range/detail/remove_extent.hpp>
#include <boost/iterator/iterator_traits.hpp>

//////////////////////////////////////////////////////////////////////////////
// missing partial specialization  workaround.
//////////////////////////////////////////////////////////////////////////////

namespace network_boost
{
    namespace range_detail
    {
        template< typename T >
        struct range_value_type_;

        template<>
        struct range_value_type_<std_container_>
        {
            template< typename C >
            struct pts
            {
                typedef BOOST_RANGE_DEDUCED_TYPENAME C::value_type type;
            };
        };

        template<>
        struct range_value_type_<std_pair_>
        {
            template< typename P >
            struct pts
            {
                typedef BOOST_RANGE_DEDUCED_TYPENAME network_boost::iterator_value< BOOST_RANGE_DEDUCED_TYPENAME P::first_type >::type type;
            };
        };

        template<>
        struct range_value_type_<array_>
        {
            template< typename T >
            struct pts
            {
                typedef BOOST_DEDUCED_TYPENAME remove_extent<T>::type type;
            };
        };

    }

    template< typename C >
    class range_value
    {
        typedef BOOST_DEDUCED_TYPENAME range_detail::range<C>::type c_type;
    public:
        typedef BOOST_DEDUCED_TYPENAME range_detail::range_value_type_<c_type>::BOOST_NESTED_TEMPLATE pts<C>::type type;
    };

}

#endif

max_stars_repo_name: Liastre/uri

max_issues_repo_name: Liastre/uri

max_forks_repo_name: Liastre/uri


text:
#include "Service.h"

#include "Manager.h"

#include "PCSC/Events.h"
#include "PCSC/MediaStatus.h"
#include "PCSC/ProtocolTypes.h"
#include "PCSC/ReaderState.h"

#include "XFS/Logger.h"
#include "XFS/Memory.h"

#include <cassert>
// Для std::memcpy
#include <cstring>
#include <sstream>
#include <string>
// Для работы с текущим временем, для получения времени дедлайна.
#include <boost/chrono/chrono.hpp>

class Hex {
    const char* mBegin;
    const char* mEnd;
private:
    friend inline XFS::Logger& operator<<(XFS::Logger& os, const Hex& h) {
        os << std::hex << std::setfill('0');
        for (const char* it = h.mBegin; it < h.mEnd; ++it) {
            unsigned int value = *it & 0xFF;
            os << std::setw(2) << value << ' ';
        }
        return os;
    }
public:
    Hex(LPBYTE begin, ULONG count) : mBegin((const char*)begin), mEnd((const char*)begin + count) {}
};

class CardReadTask : public Task {
    /// Данные, которые должны быть прочитаны.
    XFS::ReadFlags mFlags;
public:
    CardReadTask(bc::steady_clock::time_point deadline, Service& service,
                HWND hWnd, REQUESTID ReqID, XFS::ReadFlags flags
    ) : Task(deadline, service, hWnd, ReqID), mFlags(flags) {
        XFS::Logger() << "Service " << service.handle() << ": Listen reader(s), read flags: " << flags;
    }
    virtual bool match(const SCARD_READERSTATE& state, bool deviceChange) const {
        // Если изменения нас не интересуют, выходим.
        if (!mService.match(state, deviceChange)) {
            return false;
        }
        DWORD added = (state.dwCurrentState ^ state.dwEventState) & state.dwEventState;
        // Если в указанном бите есть изменения и он был установлен, генерируем событие вставки карты.
        if (added & SCARD_STATE_PRESENT) {
            {XFS::Logger() << "Service " << mService.handle() << ": Card inserted to reader '" << state.szReader << "', read flags: " << mFlags; }

            WFSIDCCARDDATA** result = mService.wrap(translate(state), mFlags);
            // Уведомляем поставщика задачи, что она выполнена.
            XFS::Result(ReqID, serviceHandle(), WFS_SUCCESS).attach(result).send(hWnd, WFS_EXECUTE_COMPLETE);
            // Задача обработана, можно удалять из списка.
            return true;
        }
        return false;
    }
private:
    WFSIDCCARDDATA* translate(const SCARD_READERSTATE& state) const {
        //TODO: Возможно, необходимо выделять память через WFSAllocateMore
        WFSIDCCARDDATA* data = XFS::alloc<WFSIDCCARDDATA>();
        // data->lpbData содержит ATR (Answer To Reset), прочитанный с чипа
        data->wDataSource = WFS_IDC_CHIP;
        data->wStatus = WFS_IDC_DATAOK;
        data->ulDataLength = state.cbAtr;
        data->lpbData = XFS::allocArr<BYTE>(state.cbAtr);
        std::memcpy(data->lpbData, state.rgbAtr, state.cbAtr);
        {XFS::Logger() << "Service " << mService.handle() << ": ATR=" << Hex(data->lpbData, data->ulDataLength);}
        return data;
    }
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Service::Service(Manager& pcsc, HSERVICE hService, const Settings& settings)
    : pcsc(pcsc)
    , hService(hService)
    , hCard(0)
    , mActiveProtocol(0)
    , mBindedReaderName(settings.readerName)
    , mSettings(settings)
    , mInited(false)
{
}
Service::~Service() {
    if (hCard != 0) {
        close();
    }
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PCSC::Status Service::open(const char* readerName) {
    assert(hCard == 0 && "Must open only one card at one service");
    PCSC::Status st = SCardConnect(pcsc.context(), readerName,
        mSettings.exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED,
        // У нас нет предпочитаемого протокола, работаем с тем, что дают
        SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
        // Получаем хендл карты и выбранный протокол.
        &hCard, (DWORD*)&mActiveProtocol
    );
    {
        XFS::Logger()
            << "SCardConnect(hContext=" << pcsc.context()
            << ", szReader=" << readerName << ", ..., hCard=&" << hCard
            << ", dwActiveProtocol=&" << mActiveProtocol << ") = " << st;
    }
    if (st) {
        // Если открытие совершилось корректно, то запоминаем имя текущего считывателя.
        mBindedReaderName = readerName;
        XFS::Logger() << "Service " << handle() << " binded to reader '" << mBindedReaderName << "'";
    }
    return st;
}
PCSC::Status Service::close() {
    assert(hCard != 0 && "Attempt disconnect from non-connected card");
    // При закрытии соединения ничего не делаем с карточкой, оставляем ее в считывателе.
    PCSC::Status st = SCardDisconnect(hCard, SCARD_LEAVE_CARD);
    {XFS::Logger() << "SCardDisconnect(hCard=" << hCard << ", SCARD_LEAVE_CARD) = " << st; }
    hCard = 0;
    // Сбрасываем привязку на привязку из настроек. Таким образом, если в настойках
    // не указано конкретного считывателя, то прявязка будет пустая и сервис привяжется
    // к первому считывателю, в котором он обнаружит карточку. Если же конкретный считыватель
    // будет указан, то сервис будет игнорировать все события, кроме как от этого считывателя.
    mBindedReaderName = mSettings.readerName;
    return st;
}

PCSC::Status Service::lock() {
    PCSC::Status st = SCardBeginTransaction(hCard);
    {XFS::Logger() << "SCardBeginTransaction(hCard=" << hCard << ") = " << st; }

    return st;
}
PCSC::Status Service::unlock() {
    // Заканчиваем транзакцию, ничего не делаем с картой.
    PCSC::Status st = SCardEndTransaction(hCard, SCARD_LEAVE_CARD);
    {XFS::Logger() << "SCardEndTransaction(hCard=" << hCard << ", SCARD_LEAVE_CARD) = " << st; }

    return st;
}
bool Service::match(const SCARD_READERSTATE& state, bool deviceChange) {
    // Изменения в количестве считывателей нас не интересуют.
    if (deviceChange) {
        return false;
    }
    // Один сервис может наблюдать только за одним считывателем за раз.
    // В зависимости от настроек, это может быть как один и тот же считыватель,
    // так и тот считыватель, в который первым была вставлена карточка. Поэтому,
    // если в данный момент мы уже работаем с какой-то карточкой, то игнорируем все
    // уведомления от остальных считывателей.
    if (!mBindedReaderName.empty() && mBindedReaderName != state.szReader) {
        return false;
    }
    return true;
}

void Service::notify(const SCARD_READERSTATE& state, bool deviceChange) {
    // Если изменения нас не интересуют, выходим.
    if (!match(state, deviceChange)) {
        return;
    }
    DWORD added = (state.dwCurrentState ^ state.dwEventState) & state.dwEventState;
    // В том случае, если сервис только что был добавлен, то реально изменений в
    // считывателях, скорее всего, не будет. Однако нам требуется как-то узнать текущее
    // состояние считывателей и разослать заинтересованным уведомления, поэтому на первый
    // раз мы исследуем новое состояние, а не изменения.
    //
    // Хотя стоит заметить, что с уведомлением подписчиков будут гонки. Прерывание ожидания
    // изменений, которое приводи к вызову этой функции, выполняется в момент добавления
    // сервиса, когда вызывающая сторона еще не могла зарегистрировать в нем каких-либо
    // слушателей. Тем не менее, из-за многопоточной природы она может успеть это сделать,
    // до того, как проснется поток ожидания изменений и сможет рапортовать о них через эту
    // функцию.
    DWORD forCheck = mInited ? added : state.dwEventState;
    mInited = true;
    {XFS::Logger() << "Service::notify: reader=" << state.szReader << ", state=" << PCSC::ReaderState(state.dwEventState) << ", added=" << PCSC::ReaderState(added); }
    /*if (forCheck & SCARD_STATE_) {
        EventNotifier::notify(WFS_SYSTEM_EVENT, PCSC::DeviceDetected(*this, state));
    }*/
    if (forCheck & SCARD_STATE_EMPTY) {
        EventNotifier::notify(WFS_SERVICE_EVENT, PCSC::CardRemoved(*this));
        if (hCard != 0) {
            close();
        }
    }
    if (forCheck & SCARD_STATE_PRESENT) {
        open(state.szReader);
        EventNotifier::notify(WFS_EXECUTE_EVENT, PCSC::CardInserted(*this));
    }
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
std::pair<WFSIDCSTATUS*, PCSC::Status> Service::getStatus() {
    // Состояние считывателя.
    PCSC::MediaStatus state;
    DWORD nameLen = 0;
    DWORD atrLen = 0;
    // Если карточки не будет в считывателе, то вернется ошибка и в ответ мы дадим WFS_IDC_MEDIANOTPRESENT
    PCSC::Status st = SCARD_S_SUCCESS;
    if (hCard != 0) {
        st = SCardStatus(hCard,
            // Имя получать не будем, тем не менее длину получить требуется, NULL недопустим.
            NULL, &nameLen,
            // Небольшой хак допустим, у нас прозрачная обертка, ничего лишнего.
            (DWORD*)&state, (DWORD*)&mActiveProtocol,
            // ATR получать не будем, тем не менее длину получить требуется, NULL недопустим.
            NULL, &atrLen
        );
        {XFS::Logger() << "SCardStatus(hCard=" << hCard << ", ..., state=&" << state << ", dwActiveProtocol=&" << mActiveProtocol << ", ...) = " << st; }
    }
    bool hasCard = hCard != 0 && st;
    //TODO: Возможно, необходимо выделять память через WFSAllocateMore
    WFSIDCSTATUS* lpStatus = XFS::alloc<WFSIDCSTATUS>();
    // Набор флагов, определяющих состояние устройства. Наше устройство всегда на связи,
    // т.к. в противном случае при открытии сессии с PC/SC драйвером будет ошибка.
    lpStatus->fwDevice = WFS_IDC_DEVONLINE;
    lpStatus->fwMedia = hasCard ? state.translateMedia() : WFS_IDC_MEDIANOTPRESENT;
    // У считывателей нет корзины для захваченных карт.
    lpStatus->fwRetainBin = WFS_IDC_RETAINNOTSUPP;
    // Модуль безопасноси отсутствует
    lpStatus->fwSecurity = WFS_IDC_SECNOTSUPP;
    // Количество возвращенных карт. Так как карта вставляется и вытаскивается руками,
    // то данный параметр не имеет смысла.
    //TODO Хотя, может быть, можно будет его отслеживать как количество вытащенных карт.
    lpStatus->usCards = 0;
    lpStatus->fwChipPower = hasCard ? state.translateChipPower() : WFS_IDC_CHIPNOCARD;
    //TODO: Добавить lpszExtra со всеми параметрами, полученными от PC/SC.
    return std::make_pair(lpStatus, st);
}
std::pair<WFSIDCCAPS*, PCSC::Status> Service::getCaps() const {
    //TODO: Возможно, необходимо выделять память через WFSAllocateMore
    WFSIDCCAPS* lpCaps = XFS::alloc<WFSIDCCAPS>();

    // Получаем поддерживаемые картой протоколы.
    PCSC::ProtocolTypes types;
    DWORD len = sizeof(DWORD);
    PCSC::Status st = SCARD_S_SUCCESS;
    if (hCard != 0) {
        st = SCardGetAttrib(hCard, SCARD_ATTR_PROTOCOL_TYPES, (BYTE*)&types, &len);
        {XFS::Logger() << "SCardGetAttrib(hCard=" << hCard << ", attr=SCARD_ATTR_PROTOCOL_TYPES, types=&" << types << "...) = " << st; }
    }
    bool hasCard = hCard != 0 && st;
    // Устройство является считывателем карт.
    lpCaps->wClass = WFS_SERVICE_CLASS_IDC;
    // Карта вставляется рукой и может быть вытащена в любой момент.
    lpCaps->fwType = WFS_IDC_TYPEDIP;
    // Устройство содержит только считыватель карт.
    lpCaps->bCompound = FALSE;
    // Какие треки могут быть прочитаны -- никакие, только чип.
    // Так как Kalignite не желает работать, если считыватель не умеет читать хоть какой-то
    // трек, то сообщаем, что умеем читать самый востребованный, чтобы удовлетворить Kaliginte.
    lpCaps->fwReadTracks = mSettings.workarounds.track2.report ? WFS_IDC_TRACK2 : WFS_IDC_NOTSUPP;
    // Какие треки могут быть записаны -- никакие, только чип.
    lpCaps->fwWriteTracks = WFS_IDC_NOTSUPP;
    // Виды поддерживаемых устройством протоколов -- все возможные.
    lpCaps->fwChipProtocols = hasCard ? types.translate() : (WFS_IDC_CHIPT0 | WFS_IDC_CHIPT1);
    // Максимальное количество карт, которое устройство может захватить. Всегда 0, т.к. не захватывает.
    lpCaps->usCards = 0;
    // Тип модуля безопасности. Не поддерживается.
    lpCaps->fwSecType = WFS_IDC_SECNOTSUPP;
    // Данные два поля нужны только для моторизированных считывателей, мы не поддерживаем.
    lpCaps->fwPowerOnOption = WFS_IDC_NOACTION;
    lpCaps->fwPowerOffOption = WFS_IDC_NOACTION;
    // Возможность программирования Flux Sensor. На всякий случай скажем, что не умеем.
    //TODO: Что такое Flux Sensor?
    lpCaps->bFluxSensorProgrammable = FALSE;
    // Можно ли начать чтение/запись на карту, когда она уже была вытащена (при этом она затягивается
    // обратно). Так как у нас не моторизированный считыватель, то мы этого не умеем.
    lpCaps->bReadWriteAccessFollowingEject = FALSE;
    // Величина магнитной силы, которую необходипо приложить для размагничивания намагниченной
    // дорожки. Так как чтение/запись треков не поддерживается, то не поддерживаем.
    lpCaps->fwWriteMode = WFS_IDC_NOTSUPP;
    // Возможности считывателя по управлению питанием чипа.
    //TODO: Получить реальные возможности считывателя. Пока предполагаем, что все возможности есть.
    lpCaps->fwChipPower = WFS_IDC_CHIPPOWERCOLD | WFS_IDC_CHIPPOWERWARM | WFS_IDC_CHIPPOWEROFF;
    //TODO: Добавить lpszExtra со всеми параметрами, полученными от PC/SC.
    return std::make_pair(lpCaps, st);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Service::asyncRead(DWORD dwTimeOut, HWND hWnd, REQUESTID ReqID, XFS::ReadFlags forRead) {
    // В считывателе нет карты, начинаем ожидание, пока вставят. В противном случае
    // информацию можно послать сразу.
    if (hCard == 0) {
        namespace bc = boost::chrono;
        bc::steady_clock::time_point now = bc::steady_clock::now();
        pcsc.addTask(Task::Ptr(new CardReadTask(now + bc::milliseconds(dwTimeOut), *this, hWnd, ReqID, forRead)));
    } else {
        WFSIDCCARDDATA** result = wrap(readChip(), forRead);
        // Уведомляем поставщика задачи, что она выполнена.
        XFS::Result(ReqID, handle(), WFS_SUCCESS).attach(result).send(hWnd, WFS_EXECUTE_COMPLETE);
    }
}
std::pair<DWORD, BYTE*> Service::readATR() const {
    assert(hCard != 0 && "Service::readATR: Attempt read ATR when card not in the reader");

    {XFS::Logger() << "Read ATR (hCard=" << hCard << ')'; }

    std::pair<DWORD, BYTE*> result;

    // Получаем ATR (Answer To Reset). Сначала длину, потом сами данные.
    PCSC::Status st = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, NULL, &result.first);
    {XFS::Logger() << "SCardGetAttrib(hCard=" << hCard << ", SCARD_ATTR_ATR_STRING, ..., size=&" << result.first << ") = " << st; }
    //TODO: Возможно, необходимо выделять память через WFSAllocateMore
    result.second = XFS::allocArr<BYTE>(result.first);
    st = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, result.second, &result.first);
    {
        XFS::Logger l;
        l << "SCardGetAttrib(hCard=" << hCard << ", SCARD_ATTR_ATR_STRING, atr=&["
          << Hex(result.second, result.first) << "], size=&" << result.first << ") = " << st;
    }
    return result;
}
WFSIDCCARDDATA* Service::readChip() const {
    assert(hCard != 0 && "Service::readChip: Attempt read ATR when card not in the reader");

    {XFS::Logger() << "Read chip (hCard=" << hCard << ')'; }
    //TODO: Возможно, необходимо выделять память через WFSAllocateMore
    WFSIDCCARDDATA* data = XFS::alloc<WFSIDCCARDDATA>();
    // data->lpbData содержит ATR (Answer To Reset), прочитанный с чипа
    data->wDataSource = WFS_IDC_CHIP;
    //TODO: Статус прочитанных данных необходимо выставлять в соответствии со статусом,
    // который вернула SCardGetAttrib.
    data->wStatus = WFS_IDC_DATAOK;

    std::pair<DWORD, BYTE*> atr = readATR();
    data->ulDataLength = atr.first;
    data->lpbData = atr.second;
    return data;
}
WFSIDCCARDDATA* Service::readTrack2() const {
    assert(hCard != 0 && "Attempt read TRACK2 when card not in the reader");
    assert(mSettings.workarounds.track2.report == true && "Attempt read TRACK2 when setting Workarounds.Track2.Report is false");

    {XFS::Logger() << "Read track2 (hCard=" << hCard << ')'; }
    std::size_t size = mSettings.workarounds.track2.value.size();
    //TODO: Возможно, необходимо выделять память через WFSAllocateMore
    WFSIDCCARDDATA* data = XFS::alloc<WFSIDCCARDDATA>();
    data->wDataSource  = WFS_IDC_TRACK2;
    data->wStatus      = size != 0 ? WFS_IDC_DATAOK : WFS_IDC_DATAMISSING;
    if (size != 0) {
        data->ulDataLength = size;
        data->lpbData      = XFS::allocArr<BYTE>(size);
        std::memcpy(data->lpbData, mSettings.workarounds.track2.value.c_str(), size);
    }
    return data;
}

WFSIDCCARDDATA** Service::wrap(WFSIDCCARDDATA* iccData, XFS::ReadFlags forRead) const {
    assert((forRead.value() & WFS_IDC_CHIP) && "Service::wrap: Chip data not requested");
    // Данный вызов вернет заполненный нулями массив под два указателя на WFSIDCCARDDATA.
    // В поледнем элементе NULL -- признак конца массива.
    //TODO: Возможно, необходимо выделять память через WFSAllocateMore
    WFSIDCCARDDATA** result = XFS::allocArr<WFSIDCCARDDATA*>(forRead.size() + 1);

    std::size_t j = 0;
    for (std::size_t i = 0; i < XFS::ReadFlags::count; ++i) {
        XFS::ReadFlags::type flag = (1 << i);
        if (forRead.value() & flag) {
            {XFS::Logger() << "Read " << XFS::ReadFlags(flag); }
            if (flag == WFS_IDC_CHIP) {
                result[j] = iccData;
            } else
            // Kalignite требует, чтобы track2 мог читаться устройством, иначе он падает.
            // Поэтому, если такая информация запрошена и у нас в настройках сказано ее отдать,
            // то эмулируем ее наличие.
            if (flag == WFS_IDC_TRACK2 && mSettings.workarounds.track2.report) {
                result[j] = readTrack2();
            } else {
                //TODO: Возможно, необходимо выделять память через WFSAllocateMore
                WFSIDCCARDDATA* data = XFS::alloc<WFSIDCCARDDATA>();
                data->wDataSource = flag;
                data->wStatus = WFS_IDC_DATASRCNOTSUPP;
                result[j] = data;
            }
            ++j;
        }
    }
    return result;
}
std::pair<WFSIDCCHIPIO*, PCSC::Status> Service::transmit(const WFSIDCCHIPIO* input) const {
    assert(input != NULL && "Service::transmit: No input from XFS subsystem");
    assert(hCard != 0 && "Service::transmit: No card in reader");

    {
        XFS::Logger l;
        l << "Service::transmit(input): dwActiveProtocol=" << mActiveProtocol
             << ", protocol=" << input->wChipProtocol
             << ", len=" << input->ulChipDataLength
             << ", data=[" << Hex(input->lpbChipData, input->ulChipDataLength)
             << ']';
    }
    //TODO: Возможно, необходимо выделять память через WFSAllocateMore
    WFSIDCCHIPIO* result = XFS::alloc<WFSIDCCHIPIO>();
    result->wChipProtocol = input->wChipProtocol;

    std::size_t inputSize = input->ulChipDataLength;
    if (mSettings.workarounds.correctChipIO && input->wChipProtocol == WFS_IDC_CHIPT0) {
        // Команду получения результата Kalignite передает правильно, без ненужного довеска.
        // Эта комана состоит всего из 4 байт, т.е. даже не содержит поля со своей длиной.
        // Так как он в принципе формирует данную команду, непонятно, зачем же он для других
        // команд передает лишний байт.
        if (inputSize > sizeof(SCARD_T0_COMMAND)) {
            const SCARD_T0_COMMAND* cmd = (SCARD_T0_COMMAND*)input->lpbChipData;
            // bP3 содержит размер передаваемых чипу данных
            inputSize = sizeof(SCARD_T0_COMMAND) + cmd->bP3;
        }
    }
    // 2 байта на код ответа, остальное -- на сам ответ чипа.
    result->ulChipDataLength = 256 + 2;
    // TODO: Сколько памяти выделять под буфер? Для протокола T0 нужно минимум 2 под код ответа.
    result->lpbChipData = XFS::allocArr<BYTE>(result->ulChipDataLength);
    //TODO: Убедится в выравнивании! Необходимо выравнивание на двойное слово!
    SCARD_IO_REQUEST ioRq = {input->wChipProtocol, sizeof(SCARD_IO_REQUEST)};
    PCSC::Status st = SCardTransmit(hCard,
        &ioRq, input->lpbChipData, inputSize,
        NULL, result->lpbChipData, &result->ulChipDataLength
    );
    {XFS::Logger() << "SCardTransmit(hCard=" << hCard << ", ...) = " << st; }
    {
        XFS::Logger l;
        l << "Service::transmit(result): len=" << result->ulChipDataLength
          << ", data=[" << Hex(result->lpbChipData, result->ulChipDataLength)
         << ']';
    }

    return std::make_pair(result, st);
}
std::pair<WFSIDCCHIPPOWEROUT*, PCSC::Status> Service::reset(XFS::ResetAction action) const {
    assert(hCard != 0 && "Service::reset: No card in the reader");

    PCSC::Status st = SCardReconnect(
        hCard,
        mSettings.exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED,
        // Текущий активный протокол должен быть в числе запрошенных, иначе
        // функция вернет ошибку.
        mActiveProtocol.value() | SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
        action.translate(),
        (DWORD*)&mActiveProtocol
    );
    {XFS::Logger() << "SCardReconnect(hCard=" << hCard << ", ..., dwActiveProtocol=&" << mActiveProtocol << ") = " << st; }

    std::pair<DWORD, BYTE*> atr = readATR();
    WFSIDCCHIPPOWEROUT* result = XFS::alloc<WFSIDCCHIPPOWEROUT>();
    result->ulChipDataLength = atr.first;
    result->lpbChipData = atr.second;
    return std::make_pair(result, st);
}

max_stars_repo_name: Mingun/pcsc-cenxfs-bridge

max_issues_repo_name: Mingun/pcsc-cenxfs-bridge

max_forks_repo_name: Mingun/pcsc-cenxfs-bridge


text:
//          Copyright Oliver Kowalke 2017.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_CONTEXT_CONTINUATION_H
#define BOOST_CONTEXT_CONTINUATION_H

#include <boost/predef.h>
#if BOOST_OS_MACOS
#define _XOPEN_SOURCE 600
#endif

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

#include <boost/context/detail/config.hpp>

#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <memory>
#include <ostream>
#include <system_error>
#include <tuple>
#include <utility>

#include <boost/assert.hpp>
#include <boost/config.hpp>

#include <boost/context/detail/disable_overload.hpp>
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
#include <boost/context/detail/exchange.hpp>
#endif
#include <boost/context/detail/externc.hpp>
#if defined(BOOST_NO_CXX17_STD_INVOKE)
#include <boost/context/detail/invoke.hpp>
#endif
#include <boost/context/fixedsize_stack.hpp>
#include <boost/context/flags.hpp>
#include <boost/context/preallocated.hpp>
#if defined(BOOST_USE_SEGMENTED_STACKS)
#include <boost/context/segmented_stack.hpp>
#endif
#include <boost/context/stack_context.hpp>

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_PREFIX
#endif

namespace boost {
namespace context {
namespace detail {

// tampoline function
// entered if the execution context
// is resumed for the first time
template <typename Record> static void entry_func(void *data) noexcept {
  Record *record = static_cast<Record *>(data);
  BOOST_ASSERT(nullptr != record);
  // start execution of toplevel context-function
  record->run();
}

struct BOOST_CONTEXT_DECL activation_record {
  ucontext_t uctx{};
  stack_context sctx{};
  bool main_ctx{true};
  activation_record *from{nullptr};
  std::function<activation_record *(activation_record *&)> ontop{};
  bool terminated{false};
  bool force_unwind{false};
#if defined(BOOST_USE_ASAN)
  void *fake_stack{nullptr};
  void *stack_bottom{nullptr};
  std::size_t stack_size{0};
#endif

  static activation_record *&current() noexcept;

  // used for toplevel-context
  // (e.g. main context, thread-entry context)
  activation_record() {
    if (BOOST_UNLIKELY(0 != ::getcontext(&uctx))) {
      throw std::system_error(std::error_code(errno, std::system_category()),
                              "getcontext() failed");
    }
  }

  activation_record(stack_context sctx_) noexcept
      : sctx(sctx_), main_ctx(false) {}

  virtual ~activation_record() {}

  activation_record(activation_record const &) = delete;
  activation_record &operator=(activation_record const &) = delete;

  bool is_main_context() const noexcept { return main_ctx; }

  activation_record *resume() {
    from = current();
    // store `this` in static, thread local pointer
    // `this` will become the active (running) context
    current() = this;
#if defined(BOOST_USE_SEGMENTED_STACKS)
    // adjust segmented stack properties
    __splitstack_getcontext(from->sctx.segments_ctx);
    __splitstack_setcontext(sctx.segments_ctx);
#endif
#if defined(BOOST_USE_ASAN)
    if (terminated) {
      __sanitizer_start_switch_fiber(nullptr, stack_bottom, stack_size);
    } else {
      __sanitizer_start_switch_fiber(&from->fake_stack, stack_bottom,
                                     stack_size);
    }
#endif
    // context switch from parent context to `this`-context
    ::swapcontext(&from->uctx, &uctx);
#if defined(BOOST_USE_ASAN)
    __sanitizer_finish_switch_fiber(
        current()->fake_stack, (const void **)&current()->from->stack_bottom,
        &current()->from->stack_size);
#endif
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
    return exchange(current()->from, nullptr);
#else
    return std::exchange(current()->from, nullptr);
#endif
  }

  template <typename Ctx, typename Fn> activation_record *resume_with(Fn &&fn) {
    from = current();
    // store `this` in static, thread local pointer
    // `this` will become the active (running) context
    // returned by continuation::current()
    current() = this;
#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
    current()->ontop = std::bind(
        [](typename std::decay<Fn>::type &fn, activation_record *&ptr) {
          Ctx c{ptr};
          c = fn(std::move(c));
          if (!c) {
            ptr = nullptr;
          }
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
          return exchange(c.ptr_, nullptr);
#else
          return std::exchange(c.ptr_, nullptr);
#endif
        },
        std::forward<Fn>(fn), std::placeholders::_1);
#else
    current()->ontop = [fn = std::forward<Fn>(fn)](activation_record *&ptr) {
      Ctx c{ptr};
      c = fn(std::move(c));
      if (!c) {
        ptr = nullptr;
      }
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
      return exchange(c.ptr_, nullptr);
#else
      return std::exchange(c.ptr_, nullptr);
#endif
    };
#endif
#if defined(BOOST_USE_SEGMENTED_STACKS)
    // adjust segmented stack properties
    __splitstack_getcontext(from->sctx.segments_ctx);
    __splitstack_setcontext(sctx.segments_ctx);
#endif
#if defined(BOOST_USE_ASAN)
    __sanitizer_start_switch_fiber(&from->fake_stack, stack_bottom, stack_size);
#endif
    // context switch from parent context to `this`-context
    ::swapcontext(&from->uctx, &uctx);
#if defined(BOOST_USE_ASAN)
    __sanitizer_finish_switch_fiber(
        current()->fake_stack, (const void **)&current()->from->stack_bottom,
        &current()->from->stack_size);
#endif
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
    return exchange(current()->from, nullptr);
#else
    return std::exchange(current()->from, nullptr);
#endif
  }

  virtual void deallocate() noexcept {}
};

struct BOOST_CONTEXT_DECL activation_record_initializer {
  activation_record_initializer() noexcept;
  ~activation_record_initializer();
};

struct forced_unwind {
  activation_record *from{nullptr};
#ifndef BOOST_ASSERT_IS_VOID
  bool caught{false};
#endif

  forced_unwind(activation_record *from_) noexcept : from{from_} {}

#ifndef BOOST_ASSERT_IS_VOID
  ~forced_unwind() { BOOST_ASSERT(caught); }
#endif
};

template <typename Ctx, typename StackAlloc, typename Fn>
class capture_record : public activation_record {
private:
  typename std::decay<StackAlloc>::type salloc_;
  typename std::decay<Fn>::type fn_;

  static void destroy(capture_record *p) noexcept {
    typename std::decay<StackAlloc>::type salloc = std::move(p->salloc_);
    stack_context sctx = p->sctx;
    // deallocate activation record
    p->~capture_record();
    // destroy stack with stack allocator
    salloc.deallocate(sctx);
  }

public:
  capture_record(stack_context sctx, StackAlloc &&salloc, Fn &&fn) noexcept
      : activation_record{sctx}, salloc_{std::forward<StackAlloc>(salloc)},
        fn_(std::forward<Fn>(fn)) {}

  void deallocate() noexcept override final {
    BOOST_ASSERT(main_ctx || (!main_ctx && terminated));
    destroy(this);
  }

  void run() {
#if defined(BOOST_USE_ASAN)
    __sanitizer_finish_switch_fiber(
        fake_stack, (const void **)&from->stack_bottom, &from->stack_size);
#endif
    Ctx c{from};
    try {
      // invoke context-function
#if defined(BOOST_NO_CXX17_STD_INVOKE)
      c = boost::context::detail::invoke(fn_, std::move(c));
#else
      c = std::invoke(fn_, std::move(c));
#endif
    } catch (forced_unwind const &ex) {
      c = Ctx{ex.from};
#ifndef BOOST_ASSERT_IS_VOID
      const_cast<forced_unwind &>(ex).caught = true;
#endif
    }
    // this context has finished its task
    from = nullptr;
    ontop = nullptr;
    terminated = true;
    force_unwind = false;
    c.resume();
    BOOST_ASSERT_MSG(false, "continuation already terminated");
  }
};

template <typename Ctx, typename StackAlloc, typename Fn>
static activation_record *create_context1(StackAlloc &&salloc, Fn &&fn) {
  typedef capture_record<Ctx, StackAlloc, Fn> capture_t;

  auto sctx = salloc.allocate();
  // reserve space for control structure
  void *storage =
      reinterpret_cast<void *>((reinterpret_cast<uintptr_t>(sctx.sp) -
                                static_cast<uintptr_t>(sizeof(capture_t))) &
                               ~static_cast<uintptr_t>(0xff));
  // placment new for control structure on context stack
  capture_t *record = new (storage)
      capture_t{sctx, std::forward<StackAlloc>(salloc), std::forward<Fn>(fn)};
  // stack bottom
  void *stack_bottom = reinterpret_cast<void *>(
      reinterpret_cast<uintptr_t>(sctx.sp) - static_cast<uintptr_t>(sctx.size));
  // create user-context
  if (BOOST_UNLIKELY(0 != ::getcontext(&record->uctx))) {
    record->~capture_t();
    salloc.deallocate(sctx);
    throw std::system_error(std::error_code(errno, std::system_category()),
                            "getcontext() failed");
  }
  record->uctx.uc_stack.ss_sp = stack_bottom;
  // 64byte gap between control structure and stack top
  record->uctx.uc_stack.ss_size = reinterpret_cast<uintptr_t>(storage) -
                                  reinterpret_cast<uintptr_t>(stack_bottom) -
                                  static_cast<uintptr_t>(64);
  record->uctx.uc_link = nullptr;
  ::makecontext(&record->uctx, (void (*)()) & entry_func<capture_t>, 1, record);
#if defined(BOOST_USE_ASAN)
  record->stack_bottom = record->uctx.uc_stack.ss_sp;
  record->stack_size = record->uctx.uc_stack.ss_size;
#endif
  return record;
}

template <typename Ctx, typename StackAlloc, typename Fn>
static activation_record *create_context2(preallocated palloc,
                                          StackAlloc &&salloc, Fn &&fn) {
  typedef capture_record<Ctx, StackAlloc, Fn> capture_t;

  // reserve space for control structure
  void *storage =
      reinterpret_cast<void *>((reinterpret_cast<uintptr_t>(palloc.sp) -
                                static_cast<uintptr_t>(sizeof(capture_t))) &
                               ~static_cast<uintptr_t>(0xff));
  // placment new for control structure on context stack
  capture_t *record = new (storage) capture_t{
      palloc.sctx, std::forward<StackAlloc>(salloc), std::forward<Fn>(fn)};
  // stack bottom
  void *stack_bottom =
      reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(palloc.sctx.sp) -
                               static_cast<uintptr_t>(palloc.sctx.size));
  // create user-context
  if (BOOST_UNLIKELY(0 != ::getcontext(&record->uctx))) {
    record->~capture_t();
    salloc.deallocate(palloc.sctx);
    throw std::system_error(std::error_code(errno, std::system_category()),
                            "getcontext() failed");
  }
  record->uctx.uc_stack.ss_sp = stack_bottom;
  // 64byte gap between control structure and stack top
  record->uctx.uc_stack.ss_size = reinterpret_cast<uintptr_t>(storage) -
                                  reinterpret_cast<uintptr_t>(stack_bottom) -
                                  static_cast<uintptr_t>(64);
  record->uctx.uc_link = nullptr;
  ::makecontext(&record->uctx, (void (*)()) & entry_func<capture_t>, 1, record);
#if defined(BOOST_USE_ASAN)
  record->stack_bottom = record->uctx.uc_stack.ss_sp;
  record->stack_size = record->uctx.uc_stack.ss_size;
#endif
  return record;
}

} // namespace detail

class BOOST_CONTEXT_DECL continuation {
private:
  friend struct detail::activation_record;

  template <typename Ctx, typename StackAlloc, typename Fn>
  friend class detail::capture_record;

  template <typename Ctx, typename StackAlloc, typename Fn>
  friend detail::activation_record *detail::create_context1(StackAlloc &&,
                                                            Fn &&);

  template <typename Ctx, typename StackAlloc, typename Fn>
  friend detail::activation_record *
  detail::create_context2(preallocated, StackAlloc &&, Fn &&);

  template <typename StackAlloc, typename Fn>
  friend continuation callcc(std::allocator_arg_t, StackAlloc &&, Fn &&);

  template <typename StackAlloc, typename Fn>
  friend continuation callcc(std::allocator_arg_t, preallocated, StackAlloc &&,
                             Fn &&);

  detail::activation_record *ptr_{nullptr};

  continuation(detail::activation_record *ptr) noexcept : ptr_{ptr} {}

public:
  continuation() = default;

  ~continuation() {
    if (BOOST_UNLIKELY(nullptr != ptr_) && !ptr_->main_ctx) {
      if (BOOST_LIKELY(!ptr_->terminated)) {
        ptr_->force_unwind = true;
        ptr_->resume();
        BOOST_ASSERT(ptr_->terminated);
      }
      ptr_->deallocate();
    }
  }

  continuation(continuation const &) = delete;
  continuation &operator=(continuation const &) = delete;

  continuation(continuation &&other) noexcept { swap(other); }

  continuation &operator=(continuation &&other) noexcept {
    if (BOOST_LIKELY(this != &other)) {
      continuation tmp = std::move(other);
      swap(tmp);
    }
    return *this;
  }

  continuation resume() & { return std::move(*this).resume(); }

  continuation resume() && {
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
    detail::activation_record *ptr = detail::exchange(ptr_, nullptr)->resume();
#else
    detail::activation_record *ptr = std::exchange(ptr_, nullptr)->resume();
#endif
    if (BOOST_UNLIKELY(detail::activation_record::current()->force_unwind)) {
      throw detail::forced_unwind{ptr};
    } else if (BOOST_UNLIKELY(nullptr !=
                              detail::activation_record::current()->ontop)) {
      ptr = detail::activation_record::current()->ontop(ptr);
      detail::activation_record::current()->ontop = nullptr;
    }
    return {ptr};
  }

  template <typename Fn> continuation resume_with(Fn &&fn) & {
    return std::move(*this).resume_with(std::forward<Fn>(fn));
  }

  template <typename Fn> continuation resume_with(Fn &&fn) && {
#if defined(BOOST_NO_CXX14_STD_EXCHANGE)
    detail::activation_record *ptr =
        detail::exchange(ptr_, nullptr)
            ->resume_with<continuation>(std::forward<Fn>(fn));
#else
    detail::activation_record *ptr =
        std::exchange(ptr_, nullptr)
            ->resume_with<continuation>(std::forward<Fn>(fn));
#endif
    if (BOOST_UNLIKELY(detail::activation_record::current()->force_unwind)) {
      throw detail::forced_unwind{ptr};
    } else if (BOOST_UNLIKELY(nullptr !=
                              detail::activation_record::current()->ontop)) {
      ptr = detail::activation_record::current()->ontop(ptr);
      detail::activation_record::current()->ontop = nullptr;
    }
    return {ptr};
  }

  explicit operator bool() const noexcept {
    return nullptr != ptr_ && !ptr_->terminated;
  }

  bool operator!() const noexcept {
    return nullptr == ptr_ || ptr_->terminated;
  }

  bool operator<(continuation const &other) const noexcept {
    return ptr_ < other.ptr_;
  }

  template <typename charT, class traitsT>
  friend std::basic_ostream<charT, traitsT> &
  operator<<(std::basic_ostream<charT, traitsT> &os,
             continuation const &other) {
    if (nullptr != other.ptr_) {
      return os << other.ptr_;
    } else {
      return os << "{not-a-context}";
    }
  }

  void swap(continuation &other) noexcept { std::swap(ptr_, other.ptr_); }
};

template <typename Fn, typename = detail::disable_overload<continuation, Fn>>
continuation callcc(Fn &&fn) {
  return callcc(std::allocator_arg,
#if defined(BOOST_USE_SEGMENTED_STACKS)
                segmented_stack(),
#else
                fixedsize_stack(),
#endif
                std::forward<Fn>(fn));
}

template <typename StackAlloc, typename Fn>
continuation callcc(std::allocator_arg_t, StackAlloc &&salloc, Fn &&fn) {
  return continuation{
      detail::create_context1<continuation>(std::forward<StackAlloc>(salloc),
                                            std::forward<Fn>(fn))}
      .resume();
}

template <typename StackAlloc, typename Fn>
continuation callcc(std::allocator_arg_t, preallocated palloc,
                    StackAlloc &&salloc, Fn &&fn) {
  return continuation{
      detail::create_context2<continuation>(
          palloc, std::forward<StackAlloc>(salloc), std::forward<Fn>(fn))}
      .resume();
}

inline void swap(continuation &l, continuation &r) noexcept { l.swap(r); }

} // namespace context
} // namespace boost

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_SUFFIX
#endif

#endif // BOOST_CONTEXT_CONTINUATION_H

max_stars_repo_name: henrywarhurst/matrix

max_issues_repo_name: henrywarhurst/matrix

max_forks_repo_name: henrywarhurst/matrix