<commit_before>#include <iostream>
#include <ipc_msgque.hh>
#include <string.h>
#include <stdlib.h>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include <imque/queue.hh>

const int CHILD_NUM = 200;
const int LOOP_COUNT = 400;

void sigsegv_handler(int sig) {
  std::cerr << "#" << getpid() << ":" << sig << std::endl;
  exit(1);
}

void gen_random_string(std::string& s, std::size_t size) {
  const char cs[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  s.resize(size);
  for(int i=0; i < size; i++) {
    s[i] = cs[rand() % strlen(cs)];
  }
}

void writer_start(msgque_t& que) {
  std::cout << "# child(writer): " << getpid() << std::endl;
  srand(time(NULL)+getpid());

  for(int i=0; i < LOOP_COUNT; i++) {
    unsigned size = (rand() % 128) + 3;
    std::string s;
    gen_random_string(s, size);

    if(que.push(s.c_str(), s.size()) == false) {
      std::cout << "@ [" << getpid() << "] queue is full" << std::endl;
      usleep(rand() % 300);
    } else {
      std::cout << "@ [" << getpid() << "] write: " << s << std::endl;
    }
    usleep(rand() % 400);
  }
  std::cout << "# exit: " << getpid() << std::endl;
}

void reader_start(msgque_t& que) {
  std::cout << "# child(reader): " << getpid() << std::endl;
  srand(time(NULL));
  usleep(10);

  for(int i=0; i < LOOP_COUNT*3.5; i++) {
    std::string s;

    if(que.pop(s) == false) {
      std::cout << "@ [" << getpid() << "] queue is empty" << std::endl;
      usleep(rand() % 200);
    } else {
      std::cout << "@ [" << getpid() << "] read: " << s << std::endl;
    }
  }
  std::cout << "# exit: " << getpid() << std::endl;
}

int main(int argc, char** argv) {
  msgque_t que(1024*32, 1024*1024*4);
  que.init();
  signal(SIGSEGV, sigsegv_handler);

  if(! que) {
    std::cerr << "[ERROR] message queue initialization failed" << std::endl;
    return 1;
  }

  for(int i=0; i < CHILD_NUM; i++) {
    if(fork() == 0) {
      if(i % 3 == 0) {
        reader_start(que);
      } else {
        writer_start(que);
      }
      return 0;
    }
  }

  waitid(P_ALL, 0, NULL, WEXITED);

  return 0;
}
<commit_msg>Queueクラスがある程度動くようになった<commit_after>#include <iostream>
#include <ipc_msgque.hh>
#include <imque/queue.hh>
#include <string.h>
#include <stdlib.h>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include <imque/queue.hh>

const int CHILD_NUM = 200;
const int LOOP_COUNT = 400;

void sigsegv_handler(int sig) {
  std::cerr << "#" << getpid() << ":" << sig << std::endl;
  exit(1);
}

void gen_random_string(std::string& s, std::size_t size) {
  const char cs[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  s.resize(size);
  for(int i=0; i < size; i++) {
    s[i] = cs[rand() % strlen(cs)];
  }
}

void writer_start(msgque_t& que) {
  std::cout << "# child(writer): " << getpid() << std::endl;
  srand(time(NULL)+getpid());

  for(int i=0; i < LOOP_COUNT; i++) {
    unsigned size = (rand() % 128) + 3;
    std::string s;
    gen_random_string(s, size);

    if(que.push(s.c_str(), s.size()) == false) {
      std::cout << "@ [" << getpid() << "] queue is full" << std::endl;
      usleep(rand() % 300);
    } else {
      std::cout << "@ [" << getpid() << "] write: " << s << std::endl;
    }
    usleep(rand() % 400);
  }
  std::cout << "# exit: " << getpid() << std::endl;
}

void reader_start(msgque_t& que) {
  std::cout << "# child(reader): " << getpid() << std::endl;
  srand(time(NULL));
  usleep(10);

  for(int i=0; i < LOOP_COUNT*3.5; i++) {
    std::string s;

    if(que.pop(s) == false) {
      std::cout << "@ [" << getpid() << "] queue is empty" << std::endl;
      usleep(rand() % 200);
    } else {
      std::cout << "@ [" << getpid() << "] read: " << s << std::endl;
    }
  }
  std::cout << "# exit: " << getpid() << std::endl;
}

int main(int argc, char** argv) {
  imque::Queue que2(1024, 1024*1024);
  assert(que2);
  que2.init();

  int val = 111;
  que2.enq(&val, sizeof(int));
  std::cout << "# enq" << std::endl;

  size_t size;
  int* pval = (int*)que2.deq(size);
  std::cout << "# deq" << std::endl;
  std::cout << "# " << *pval << ", " << size << std::endl;

  return 0;

  msgque_t que(1024*32, 1024*1024*4);
  que.init();
  signal(SIGSEGV, sigsegv_handler);

  if(! que) {
    std::cerr << "[ERROR] message queue initialization failed" << std::endl;
    return 1;
  }

  for(int i=0; i < CHILD_NUM; i++) {
    if(fork() == 0) {
      if(i % 3 == 0) {
        reader_start(que);
      } else {
        writer_start(que);
      }
      return 0;
    }
  }

  waitid(P_ALL, 0, NULL, WEXITED);

  return 0;
}
<|endoftext|>


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

#include "blockencodings.h"
#include "chainparams.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
#include "globals.h"
#include "hash.h"
#include "random.h"
#include "streams.h"
#include "txmempool.h"
#include "util.h"
#include "validation.h"

#include <unordered_map>

CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock &block)
    : nonce(GetRand(std::numeric_limits<uint64_t>::max())),
      shorttxids(block.vtx.size() - 1), prefilledtxn(1), header(block) {
    FillShortTxIDSelector();
    // TODO: Use our mempool prior to block acceptance to predictively fill more
    // than just the coinbase.
    prefilledtxn[0] = {0, block.vtx[0]};
    for (size_t i = 1; i < block.vtx.size(); i++) {
        const CTransaction &tx = *block.vtx[i];
        shorttxids[i - 1] = GetShortID(tx.GetHash());
    }
}

void CBlockHeaderAndShortTxIDs::FillShortTxIDSelector() const {
    CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
    stream << header << nonce;
    CSHA256 hasher;
    hasher.Write((unsigned char *)&(*stream.begin()),
                 stream.end() - stream.begin());
    uint256 shorttxidhash;
    hasher.Finalize(shorttxidhash.begin());
    shorttxidk0 = shorttxidhash.GetUint64(0);
    shorttxidk1 = shorttxidhash.GetUint64(1);
}

uint64_t CBlockHeaderAndShortTxIDs::GetShortID(const uint256 &txhash) const {
    static_assert(SHORTTXIDS_LENGTH == 6,
                  "shorttxids calculation assumes 6-byte shorttxids");
    return SipHashUint256(shorttxidk0, shorttxidk1, txhash) & 0xffffffffffffL;
}

ReadStatus PartiallyDownloadedBlock::InitData(
    const CBlockHeaderAndShortTxIDs &cmpctblock,
    const std::vector<std::pair<uint256, CTransactionRef>> &extra_txn) {
    if (cmpctblock.header.IsNull() ||
        (cmpctblock.shorttxids.empty() && cmpctblock.prefilledtxn.empty()))
        return READ_STATUS_INVALID;
    if (cmpctblock.shorttxids.size() + cmpctblock.prefilledtxn.size() >
        nMaxBlockSize / MIN_TRANSACTION_SIZE)
        return READ_STATUS_INVALID;

    assert(header.IsNull() && txn_available.empty());
    header = cmpctblock.header;
    txn_available.resize(cmpctblock.BlockTxCount());

    int32_t lastprefilledindex = -1;
    for (size_t i = 0; i < cmpctblock.prefilledtxn.size(); i++) {
        if (cmpctblock.prefilledtxn[i].tx->IsNull()) return READ_STATUS_INVALID;

        lastprefilledindex += cmpctblock.prefilledtxn[i].index +
                              1; // index is a uint16_t, so can't overflow here
        if (lastprefilledindex > std::numeric_limits<uint16_t>::max())
            return READ_STATUS_INVALID;
        if ((uint32_t)lastprefilledindex > cmpctblock.shorttxids.size() + i) {
            // If we are inserting a tx at an index greater than our full list
            // of shorttxids
            // plus the number of prefilled txn we've inserted, then we have txn
            // for which we
            // have neither a prefilled txn or a shorttxid!
            return READ_STATUS_INVALID;
        }
        txn_available[lastprefilledindex] = cmpctblock.prefilledtxn[i].tx;
    }
    prefilled_count = cmpctblock.prefilledtxn.size();

    // Calculate map of txids -> positions and check mempool to see what we have
    // (or don't). Because well-formed cmpctblock messages will have a
    // (relatively) uniform distribution of short IDs, any highly-uneven
    // distribution of elements can be safely treated as a READ_STATUS_FAILED.
    std::unordered_map<uint64_t, uint16_t> shorttxids(
        cmpctblock.shorttxids.size());
    uint16_t index_offset = 0;
    for (size_t i = 0; i < cmpctblock.shorttxids.size(); i++) {
        while (txn_available[i + index_offset])
            index_offset++;
        shorttxids[cmpctblock.shorttxids[i]] = i + index_offset;
        // To determine the chance that the number of entries in a bucket
        // exceeds N, we use the fact that the number of elements in a single
        // bucket is binomially distributed (with n = the number of shorttxids
        // S, and p = 1 / the number of buckets), that in the worst case the
        // number of buckets is equal to S (due to std::unordered_map having a
        // default load factor of 1.0), and that the chance for any bucket to
        // exceed N elements is at most buckets * (the chance that any given
        // bucket is above N elements). Thus: P(max_elements_per_bucket > N) <=
        // S * (1 - cdf(binomial(n=S,p=1/S), N)). If we assume blocks of up to
        // 16000, allowing 12 elements per bucket should only fail once per ~1
        // million block transfers (per peer and connection).
        if (shorttxids.bucket_size(
                shorttxids.bucket(cmpctblock.shorttxids[i])) > 12)
            return READ_STATUS_FAILED;
    }
    // TODO: in the shortid-collision case, we should instead request both
    // transactions which collided. Falling back to full-block-request here is
    // overkill.
    if (shorttxids.size() != cmpctblock.shorttxids.size()) {
        // Short ID collision
        return READ_STATUS_FAILED;
    }

    std::vector<bool> have_txn(txn_available.size());
    {
        LOCK(pool->cs);
        const std::vector<std::pair<uint256, CTxMemPool::txiter>> &vTxHashes =
            pool->vTxHashes;
        for (size_t i = 0; i < vTxHashes.size(); i++) {
            uint64_t shortid = cmpctblock.GetShortID(vTxHashes[i].first);
            std::unordered_map<uint64_t, uint16_t>::iterator idit =
                shorttxids.find(shortid);
            if (idit != shorttxids.end()) {
                if (!have_txn[idit->second]) {
                    txn_available[idit->second] =
                        vTxHashes[i].second->GetSharedTx();
                    have_txn[idit->second] = true;
                    mempool_count++;
                } else {
                    // If we find two mempool txn that match the short id, just
                    // request it. This should be rare enough that the extra
                    // bandwidth doesn't matter, but eating a round-trip due to
                    // FillBlock failure would be annoying.
                    if (txn_available[idit->second]) {
                        txn_available[idit->second].reset();
                        mempool_count--;
                    }
                }
            }
            // Though ideally we'd continue scanning for the
            // two-txn-match-shortid case, the performance win of an early exit
            // here is too good to pass up and worth the extra risk.
            if (mempool_count == shorttxids.size()) break;
        }
    }

    for (size_t i = 0; i < extra_txn.size(); i++) {
        uint64_t shortid = cmpctblock.GetShortID(extra_txn[i].first);
        std::unordered_map<uint64_t, uint16_t>::iterator idit =
            shorttxids.find(shortid);
        if (idit != shorttxids.end()) {
            if (!have_txn[idit->second]) {
                txn_available[idit->second] = extra_txn[i].second;
                have_txn[idit->second] = true;
                mempool_count++;
                extra_count++;
            } else {
                // If we find two mempool/extra txn that match the short id,
                // just request it. This should be rare enough that the extra
                // bandwidth doesn't matter, but eating a round-trip due to
                // FillBlock failure would be annoying. Note that we dont want
                // duplication between extra_txn and mempool to trigger this
                // case, so we compare hashes first.
                if (txn_available[idit->second] &&
                    txn_available[idit->second]->GetHash() !=
                        extra_txn[i].second->GetHash()) {
                    txn_available[idit->second].reset();
                    mempool_count--;
                    extra_count--;
                }
            }
        }

        // Though ideally we'd continue scanning for the two-txn-match-shortid
        // case, the performance win of an early exit here is too good to pass
        // up and worth the extra risk.
        if (mempool_count == shorttxids.size()) break;
    }

    LogPrint("cmpctblock", "Initialized PartiallyDownloadedBlock for block %s "
                           "using a cmpctblock of size %lu\n",
             cmpctblock.header.GetHash().ToString(),
             GetSerializeSize(cmpctblock, SER_NETWORK, PROTOCOL_VERSION));

    return READ_STATUS_OK;
}

bool PartiallyDownloadedBlock::IsTxAvailable(size_t index) const {
    assert(!header.IsNull());
    assert(index < txn_available.size());
    return txn_available[index] ? true : false;
}

ReadStatus PartiallyDownloadedBlock::FillBlock(
    CBlock &block, const std::vector<CTransactionRef> &vtx_missing) {
    assert(!header.IsNull());
    uint256 hash = header.GetHash();
    block = header;
    block.vtx.resize(txn_available.size());

    size_t tx_missing_offset = 0;
    for (size_t i = 0; i < txn_available.size(); i++) {
        if (!txn_available[i]) {
            if (vtx_missing.size() <= tx_missing_offset)
                return READ_STATUS_INVALID;
            block.vtx[i] = vtx_missing[tx_missing_offset++];
        } else
            block.vtx[i] = std::move(txn_available[i]);
    }

    // Make sure we can't call FillBlock again.
    header.SetNull();
    txn_available.clear();

    if (vtx_missing.size() != tx_missing_offset) return READ_STATUS_INVALID;

    CValidationState state;
    if (!CheckBlock(block, state, Params().GetConsensus())) {
        // TODO: We really want to just check merkle tree manually here, but
        // that is expensive, and CheckBlock caches a block's "checked-status"
        // (in the CBlock?). CBlock should be able to check its own merkle root
        // and cache that check.
        if (state.CorruptionPossible()) {
            // Possible Short ID collision.
            return READ_STATUS_FAILED;
        }
        return READ_STATUS_CHECKBLOCK_FAILED;
    }

    LogPrint("cmpctblock", "Successfully reconstructed block %s with %lu txn "
                           "prefilled, %lu txn from mempool (incl at least %lu "
                           "from extra pool) and %lu txn requested\n",
             hash.ToString(), prefilled_count, mempool_count, extra_count,
             vtx_missing.size());
    if (vtx_missing.size() < 5) {
        for (const auto &tx : vtx_missing)
            LogPrint("cmpctblock", "Reconstructed block %s required tx %s\n",
                     hash.ToString(), tx->GetId().ToString());
    }

    return READ_STATUS_OK;
}
<commit_msg>Relayout some comment in blockencoding.cpp<commit_after>// Copyright (c) 2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "blockencodings.h"
#include "chainparams.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
#include "globals.h"
#include "hash.h"
#include "random.h"
#include "streams.h"
#include "txmempool.h"
#include "util.h"
#include "validation.h"

#include <unordered_map>

CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock &block)
    : nonce(GetRand(std::numeric_limits<uint64_t>::max())),
      shorttxids(block.vtx.size() - 1), prefilledtxn(1), header(block) {
    FillShortTxIDSelector();
    // TODO: Use our mempool prior to block acceptance to predictively fill more
    // than just the coinbase.
    prefilledtxn[0] = {0, block.vtx[0]};
    for (size_t i = 1; i < block.vtx.size(); i++) {
        const CTransaction &tx = *block.vtx[i];
        shorttxids[i - 1] = GetShortID(tx.GetHash());
    }
}

void CBlockHeaderAndShortTxIDs::FillShortTxIDSelector() const {
    CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
    stream << header << nonce;
    CSHA256 hasher;
    hasher.Write((unsigned char *)&(*stream.begin()),
                 stream.end() - stream.begin());
    uint256 shorttxidhash;
    hasher.Finalize(shorttxidhash.begin());
    shorttxidk0 = shorttxidhash.GetUint64(0);
    shorttxidk1 = shorttxidhash.GetUint64(1);
}

uint64_t CBlockHeaderAndShortTxIDs::GetShortID(const uint256 &txhash) const {
    static_assert(SHORTTXIDS_LENGTH == 6,
                  "shorttxids calculation assumes 6-byte shorttxids");
    return SipHashUint256(shorttxidk0, shorttxidk1, txhash) & 0xffffffffffffL;
}

ReadStatus PartiallyDownloadedBlock::InitData(
    const CBlockHeaderAndShortTxIDs &cmpctblock,
    const std::vector<std::pair<uint256, CTransactionRef>> &extra_txn) {
    if (cmpctblock.header.IsNull() ||
        (cmpctblock.shorttxids.empty() && cmpctblock.prefilledtxn.empty()))
        return READ_STATUS_INVALID;
    if (cmpctblock.shorttxids.size() + cmpctblock.prefilledtxn.size() >
        nMaxBlockSize / MIN_TRANSACTION_SIZE)
        return READ_STATUS_INVALID;

    assert(header.IsNull() && txn_available.empty());
    header = cmpctblock.header;
    txn_available.resize(cmpctblock.BlockTxCount());

    int32_t lastprefilledindex = -1;
    for (size_t i = 0; i < cmpctblock.prefilledtxn.size(); i++) {
        if (cmpctblock.prefilledtxn[i].tx->IsNull()) return READ_STATUS_INVALID;

        // index is a uint16_t, so can't overflow here.
        lastprefilledindex += cmpctblock.prefilledtxn[i].index + 1;
        if (lastprefilledindex > std::numeric_limits<uint16_t>::max())
            return READ_STATUS_INVALID;
        if ((uint32_t)lastprefilledindex > cmpctblock.shorttxids.size() + i) {
            // If we are inserting a tx at an index greater than our full list
            // of shorttxids plus the number of prefilled txn we've inserted,
            // then we have txn for which we have neither a prefilled txn or a
            // shorttxid!
            return READ_STATUS_INVALID;
        }
        txn_available[lastprefilledindex] = cmpctblock.prefilledtxn[i].tx;
    }
    prefilled_count = cmpctblock.prefilledtxn.size();

    // Calculate map of txids -> positions and check mempool to see what we have
    // (or don't). Because well-formed cmpctblock messages will have a
    // (relatively) uniform distribution of short IDs, any highly-uneven
    // distribution of elements can be safely treated as a READ_STATUS_FAILED.
    std::unordered_map<uint64_t, uint16_t> shorttxids(
        cmpctblock.shorttxids.size());
    uint16_t index_offset = 0;
    for (size_t i = 0; i < cmpctblock.shorttxids.size(); i++) {
        while (txn_available[i + index_offset])
            index_offset++;
        shorttxids[cmpctblock.shorttxids[i]] = i + index_offset;
        // To determine the chance that the number of entries in a bucket
        // exceeds N, we use the fact that the number of elements in a single
        // bucket is binomially distributed (with n = the number of shorttxids
        // S, and p = 1 / the number of buckets), that in the worst case the
        // number of buckets is equal to S (due to std::unordered_map having a
        // default load factor of 1.0), and that the chance for any bucket to
        // exceed N elements is at most buckets * (the chance that any given
        // bucket is above N elements). Thus: P(max_elements_per_bucket > N) <=
        // S * (1 - cdf(binomial(n=S,p=1/S), N)). If we assume blocks of up to
        // 16000, allowing 12 elements per bucket should only fail once per ~1
        // million block transfers (per peer and connection).
        if (shorttxids.bucket_size(
                shorttxids.bucket(cmpctblock.shorttxids[i])) > 12)
            return READ_STATUS_FAILED;
    }
    // TODO: in the shortid-collision case, we should instead request both
    // transactions which collided. Falling back to full-block-request here is
    // overkill.
    if (shorttxids.size() != cmpctblock.shorttxids.size()) {
        // Short ID collision
        return READ_STATUS_FAILED;
    }

    std::vector<bool> have_txn(txn_available.size());
    {
        LOCK(pool->cs);
        const std::vector<std::pair<uint256, CTxMemPool::txiter>> &vTxHashes =
            pool->vTxHashes;
        for (size_t i = 0; i < vTxHashes.size(); i++) {
            uint64_t shortid = cmpctblock.GetShortID(vTxHashes[i].first);
            std::unordered_map<uint64_t, uint16_t>::iterator idit =
                shorttxids.find(shortid);
            if (idit != shorttxids.end()) {
                if (!have_txn[idit->second]) {
                    txn_available[idit->second] =
                        vTxHashes[i].second->GetSharedTx();
                    have_txn[idit->second] = true;
                    mempool_count++;
                } else {
                    // If we find two mempool txn that match the short id, just
                    // request it. This should be rare enough that the extra
                    // bandwidth doesn't matter, but eating a round-trip due to
                    // FillBlock failure would be annoying.
                    if (txn_available[idit->second]) {
                        txn_available[idit->second].reset();
                        mempool_count--;
                    }
                }
            }
            // Though ideally we'd continue scanning for the
            // two-txn-match-shortid case, the performance win of an early exit
            // here is too good to pass up and worth the extra risk.
            if (mempool_count == shorttxids.size()) break;
        }
    }

    for (size_t i = 0; i < extra_txn.size(); i++) {
        uint64_t shortid = cmpctblock.GetShortID(extra_txn[i].first);
        std::unordered_map<uint64_t, uint16_t>::iterator idit =
            shorttxids.find(shortid);
        if (idit != shorttxids.end()) {
            if (!have_txn[idit->second]) {
                txn_available[idit->second] = extra_txn[i].second;
                have_txn[idit->second] = true;
                mempool_count++;
                extra_count++;
            } else {
                // If we find two mempool/extra txn that match the short id,
                // just request it. This should be rare enough that the extra
                // bandwidth doesn't matter, but eating a round-trip due to
                // FillBlock failure would be annoying. Note that we dont want
                // duplication between extra_txn and mempool to trigger this
                // case, so we compare hashes first.
                if (txn_available[idit->second] &&
                    txn_available[idit->second]->GetHash() !=
                        extra_txn[i].second->GetHash()) {
                    txn_available[idit->second].reset();
                    mempool_count--;
                    extra_count--;
                }
            }
        }

        // Though ideally we'd continue scanning for the two-txn-match-shortid
        // case, the performance win of an early exit here is too good to pass
        // up and worth the extra risk.
        if (mempool_count == shorttxids.size()) break;
    }

    LogPrint("cmpctblock", "Initialized PartiallyDownloadedBlock for block %s "
                           "using a cmpctblock of size %lu\n",
             cmpctblock.header.GetHash().ToString(),
             GetSerializeSize(cmpctblock, SER_NETWORK, PROTOCOL_VERSION));

    return READ_STATUS_OK;
}

bool PartiallyDownloadedBlock::IsTxAvailable(size_t index) const {
    assert(!header.IsNull());
    assert(index < txn_available.size());
    return txn_available[index] ? true : false;
}

ReadStatus PartiallyDownloadedBlock::FillBlock(
    CBlock &block, const std::vector<CTransactionRef> &vtx_missing) {
    assert(!header.IsNull());
    uint256 hash = header.GetHash();
    block = header;
    block.vtx.resize(txn_available.size());

    size_t tx_missing_offset = 0;
    for (size_t i = 0; i < txn_available.size(); i++) {
        if (!txn_available[i]) {
            if (vtx_missing.size() <= tx_missing_offset)
                return READ_STATUS_INVALID;
            block.vtx[i] = vtx_missing[tx_missing_offset++];
        } else
            block.vtx[i] = std::move(txn_available[i]);
    }

    // Make sure we can't call FillBlock again.
    header.SetNull();
    txn_available.clear();

    if (vtx_missing.size() != tx_missing_offset) return READ_STATUS_INVALID;

    CValidationState state;
    if (!CheckBlock(block, state, Params().GetConsensus())) {
        // TODO: We really want to just check merkle tree manually here, but
        // that is expensive, and CheckBlock caches a block's "checked-status"
        // (in the CBlock?). CBlock should be able to check its own merkle root
        // and cache that check.
        if (state.CorruptionPossible()) {
            // Possible Short ID collision.
            return READ_STATUS_FAILED;
        }
        return READ_STATUS_CHECKBLOCK_FAILED;
    }

    LogPrint("cmpctblock", "Successfully reconstructed block %s with %lu txn "
                           "prefilled, %lu txn from mempool (incl at least %lu "
                           "from extra pool) and %lu txn requested\n",
             hash.ToString(), prefilled_count, mempool_count, extra_count,
             vtx_missing.size());
    if (vtx_missing.size() < 5) {
        for (const auto &tx : vtx_missing)
            LogPrint("cmpctblock", "Reconstructed block %s required tx %s\n",
                     hash.ToString(), tx->GetId().ToString());
    }

    return READ_STATUS_OK;
}
<|endoftext|>


<commit_before>#include <array>
#include <iterator>
#include <vector>
#include <functional>
#include <algorithm>
#include <numeric>

#include "builtin.hh"
#include "util.hh"
#include "number.hh"
#include "procedure.hh"
#include "lisp_ptr.hh"
#include "eval.hh"
#include "builtin_util.hh"
#include "printer.hh"

using namespace std;
using namespace Procedure;

namespace {

template<typename Fun>
inline void number_pred(Fun&& fun){
  auto arg = pick_args_1();
  auto num = arg.get<Number*>();
  if(!num){
    VM.return_value = Lisp_ptr{false};
    return;
  }

  VM.return_value = Lisp_ptr{fun(num)};
}

void complexp(){
  number_pred([](Number* n){
      return n->type() >= Number::Type::integer;
    });
}

void realp(){
  number_pred([](Number* n){
      return n->type() >= Number::Type::integer
        && n->type() <= Number::Type::real;
    });
}

void rationalp(){
  number_pred([](Number* n){
      return n->type() >= Number::Type::integer
        && n->type() < Number::Type::real;
    });
}

void integerp(){
  number_pred([](Number* n){
      return n->type() == Number::Type::integer;
    });
}

void exactp(){
  number_pred([](Number* n){
      return n->type() == Number::Type::integer;
    });
}

void inexactp(){
  number_pred([](Number* n){
      return n->type() == Number::Type::complex
        || n->type() == Number::Type::real;
    });
}

void number_type_check_failed(const char* func_name, Lisp_ptr p){
  fprintf(zs::err, "native func: %s: arg is not number! (%s)\n",
          func_name, stringify(p.tag()));
  VM.return_value = {};
}

struct complex_found{
  static constexpr const char* msg = "native func: number compare: complex cannot be ordinated\n";
  bool operator()(const Number::complex_type&, const Number::complex_type&) const{
    fprintf(zs::err, msg);
    return false;
  }
};

template<template <typename> class Fun,
         class ComplexComparator = complex_found>
struct number_comparator {
  Lisp_ptr non_number_found;

  number_comparator() : non_number_found(){}

  inline bool operator()(const Lisp_ptr& p1, const Lisp_ptr& p2){
    auto n1 = p1.get<Number*>();
    if(!n1 || n1->type() < Number::Type::integer){
      non_number_found = p1;
      return true;
    }

    auto n2 = p2.get<Number*>();
    if(!n2 || n2->type() < Number::Type::integer){
      non_number_found = p2;
      return true;
    }

    if(n1->type() == Number::Type::integer && n2->type() == Number::Type::integer){
      static const Fun<Number::integer_type> fun;
      return fun(n1->get<Number::integer_type>(), n2->get<Number::integer_type>());
    }else if(n1->type() <= Number::Type::real && n2->type() <= Number::Type::real){
      static const Fun<Number::real_type> fun;
      return fun(n1->coerce<Number::real_type>(), n2->coerce<Number::real_type>());
    }else{
      static const ComplexComparator fun;
      return fun(n1->coerce<Number::complex_type>(), n2->coerce<Number::complex_type>());
    }
  }

  struct for_is_sorted : public number_comparator {
    inline bool operator()(const Lisp_ptr& p1, const Lisp_ptr& p2){
      return !number_comparator::operator()(p2, p1);
    }
  };
};

template<typename Fun>
inline void number_compare(const char* name, Fun&& fun){
  std::vector<Lisp_ptr> args;
  stack_to_vector(VM.stack, args);

  auto ret = std::is_sorted(begin(args), end(args), fun);
  if(fun.non_number_found){
    number_type_check_failed(name, fun.non_number_found);
    return;
  }

  VM.return_value = Lisp_ptr{ret};
}

void number_equal(){
  number_compare("=",
                 number_comparator<std::equal_to,
                                   std::equal_to<Number::complex_type> >::for_is_sorted());
}

void number_less(){
  number_compare("<",
                 number_comparator<std::less>::for_is_sorted());
}

void number_greater(){
  number_compare(">",
                 number_comparator<std::greater>::for_is_sorted());
}

void number_less_eq(){
  number_compare("<=",
                 number_comparator<std::less_equal>::for_is_sorted());
}

void number_greater_eq(){
  number_compare(">=",
                 number_comparator<std::greater_equal>::for_is_sorted());
}

void zerop(){
  number_pred([](Number* num) -> bool {
      switch(num->type()){
      case Number::Type::complex: {
        auto c = num->get<Number::complex_type>();
        return (c.real() == 0 && c.imag() == 0);
      }
      case Number::Type::real:
        return num->get<Number::real_type>() == 0;
      case Number::Type::integer:
        return num->get<Number::integer_type>() == 0;
      case Number::Type::uninitialized:
      default:
        UNEXP_DEFAULT();
      }
    });
}

template<template <typename> class Fun>
struct pos_neg_pred{
  inline bool operator()(Number* num){
    static constexpr Fun<Number::integer_type> fun;

    switch(num->type()){
    case Number::Type::complex:
      return false;
    case Number::Type::real:
      return fun(num->get<Number::real_type>(), 0);
    case Number::Type::integer:
      return fun(num->get<Number::integer_type>(), 0);
    case Number::Type::uninitialized:
    default:
      UNEXP_DEFAULT();
    }
  }
};

void positivep(){
  number_pred(pos_neg_pred<std::greater>());
}

void negativep(){
  number_pred(pos_neg_pred<std::less>());
}

template<template <typename> class Fun>
struct even_odd_pred{
  inline bool operator()(Number* num){
    static constexpr Fun<Number::integer_type> fun;

    switch(num->type()){
    case Number::Type::complex:
    case Number::Type::real:
      return false;
    case Number::Type::integer:
      return fun(num->get<Number::integer_type>() % 2, 0);
    case Number::Type::uninitialized:
    default:
      UNEXP_DEFAULT();
    }
  }
};

void oddp(){
  number_pred(even_odd_pred<std::not_equal_to>());
}

void evenp(){
  number_pred(even_odd_pred<std::equal_to>());
}

template<class Fun>
inline
void number_accumulate(const char* name, Number&& init, Fun&& fun){
  std::vector<Lisp_ptr> args;
  stack_to_vector(VM.stack, args);

  for(auto i = begin(args), e = end(args);
      i != e; ++i){
    auto n = i->get<Number*>();
    if(!n){
      number_type_check_failed(name, *i);
      return;
    }

    if(!fun(init, *n)){
      VM.return_value = {};
      return;
    }
  }

  VM.return_value = {new Number(init)};
}

void number_max(){
  number_accumulate("max", Number(),
                    [](Number& n1, const Number& n2) -> bool {
                      if(n1.type() == Number::Type::uninitialized){
                        n1 = n2;
                        return true;
                      }

                      if(n2.type() == Number::Type::uninitialized){
                        return true;
                      }

                      if(n1.type() == Number::Type::integer && n2.type() == Number::Type::integer){
                        if(n1.get<Number::integer_type>() < n2.get<Number::integer_type>())
                          n1 = n2;
                        return true;
                      }

                      if(n1.type() <= Number::Type::real && n2.type() <= Number::Type::real){
                        if(n1.coerce<Number::real_type>() < n2.coerce<Number::real_type>())
                          n1 = Number{n2.coerce<Number::real_type>()};
                        if(n1.type() != Number::Type::real)
                          n1 = Number{n1.coerce<Number::real_type>()};
                        return true;
                      }

                      fprintf(zs::err, complex_found::msg);
                      return false;
                    });
}

void number_min(){
  number_accumulate("min", Number(),
                    [](Number& n1, const Number& n2) -> bool {
                      if(n1.type() == Number::Type::uninitialized){
                        n1 = n2;
                        return true;
                      }

                      if(n2.type() == Number::Type::uninitialized){
                        return true;
                      }

                      if(n1.type() == Number::Type::integer && n2.type() == Number::Type::integer){
                        if(n1.get<Number::integer_type>() > n2.get<Number::integer_type>())
                          n1 = n2;
                        return true;
                      }

                      if(n1.type() <= Number::Type::real && n2.type() <= Number::Type::real){
                        if(n1.coerce<Number::real_type>() > n2.coerce<Number::real_type>())
                          n1 = Number{n2.coerce<Number::real_type>()};
                        if(n1.type() != Number::Type::real)
                          n1 = Number{n1.coerce<Number::real_type>()};
                        return true;
                      }

                      fprintf(zs::err, complex_found::msg);
                      return false;
                    });
}

void number_plus(){
  number_accumulate("+", Number(0l),
                    [](Number& n1, const Number& n2) -> bool {
                      if(n1.type() == Number::Type::uninitialized){
                        n1 = n2;
                        return true;
                      }

                      if(n2.type() == Number::Type::uninitialized){
                        return true;
                      }

                      // n1 type <= n2 type
                      if(n1.type() == Number::Type::integer && n2.type() == Number::Type::integer){
                        n1.get<Number::integer_type>() += n2.get<Number::integer_type>();
                        return true;
                      }

                      if(n1.type() == Number::Type::real && n2.type() <= Number::Type::real){
                        n1.get<Number::real_type>() += n2.coerce<Number::real_type>();
                        return true;
                      }

                      if(n1.type() == Number::Type::complex && n2.type() <= Number::Type::complex){
                        n1.get<Number::complex_type>() += n2.coerce<Number::complex_type>();
                        return true;
                      }

                      // n1 type > n2 type
                      if(n1.type() < Number::Type::real && n2.type() == Number::Type::real){
                        n1 = Number{n1.coerce<Number::real_type>() + n2.get<Number::real_type>()};
                        return true;
                      }

                      if(n1.type() < Number::Type::complex && n2.type() == Number::Type::complex){
                        n1 = Number{n1.coerce<Number::complex_type>() + n2.get<Number::complex_type>()};
                        return true;
                      }

                      // ???
                      fprintf(zs::err, "native func: +: failed at numeric conversion!\n");
                      return false;
                    });
}

constexpr struct Entry {
  const char* name;
  const NProcedure func;

  constexpr Entry(const char* n, const NProcedure& f)
    : name(n), func(f){}
} builtin_numeric[] = {
  {"complex?", {
      complexp,
      Calling::function, {1, false}}},
  {"real?", {
      realp,
      Calling::function, {1, false}}},
  {"rational?", {
      rationalp,
      Calling::function, {1, false}}},
  {"integer?", {
      integerp,
      Calling::function, {1, false}}},

  {"exact?", {
      exactp,
      Calling::function, {1, false}}},
  {"inexact?", {
      inexactp,
      Calling::function, {1, false}}},

  {"=", {
      number_equal,
      Calling::function, {2, true}}},
  {"<", {
      number_less,
      Calling::function, {2, true}}},
  {">", {
      number_greater,
      Calling::function, {2, true}}},
  {"<=", {
      number_less_eq,
      Calling::function, {2, true}}},
  {">=", {
      number_greater_eq,
      Calling::function, {2, true}}},

  {"zero?", {
      zerop,
      Calling::function, {1, false}}},
  {"positive?", {
      positivep,
      Calling::function, {1, false}}},
  {"negative?", {
      negativep,
      Calling::function, {1, false}}},
  {"odd?", {
      oddp,
      Calling::function, {1, false}}},
  {"even?", {
      evenp,
      Calling::function, {1, false}}},

  {"max", {
      number_max,
      Calling::function, {2, true}}},
  {"min", {
      number_min,
      Calling::function, {2, true}}},

  {"+", {
      number_plus,
      Calling::function, {0, true}}}
};

} //namespace

void install_builtin_numeric(){
  for(auto& e : builtin_numeric){
    VM.set(intern(VM.symtable, e.name), {&e.func});
  }
}
<commit_msg>rewrite number comparator<commit_after>#include <array>
#include <iterator>
#include <vector>
#include <functional>
#include <algorithm>
#include <numeric>

#include "builtin.hh"
#include "util.hh"
#include "number.hh"
#include "procedure.hh"
#include "lisp_ptr.hh"
#include "eval.hh"
#include "builtin_util.hh"
#include "printer.hh"

using namespace std;
using namespace Procedure;

namespace {

template<typename Fun>
inline void number_pred(Fun&& fun){
  auto arg = pick_args_1();
  auto num = arg.get<Number*>();
  if(!num){
    VM.return_value = Lisp_ptr{false};
    return;
  }

  VM.return_value = Lisp_ptr{fun(num)};
}

void complexp(){
  number_pred([](Number* n){
      return n->type() >= Number::Type::integer;
    });
}

void realp(){
  number_pred([](Number* n){
      return n->type() >= Number::Type::integer
        && n->type() <= Number::Type::real;
    });
}

void rationalp(){
  number_pred([](Number* n){
      return n->type() >= Number::Type::integer
        && n->type() < Number::Type::real;
    });
}

void integerp(){
  number_pred([](Number* n){
      return n->type() == Number::Type::integer;
    });
}

void exactp(){
  number_pred([](Number* n){
      return n->type() == Number::Type::integer;
    });
}

void inexactp(){
  number_pred([](Number* n){
      return n->type() == Number::Type::complex
        || n->type() == Number::Type::real;
    });
}

void number_type_check_failed(const char* func_name, Lisp_ptr p){
  fprintf(zs::err, "native func: %s: arg is not number! (%s)\n",
          func_name, stringify(p.tag()));
  VM.return_value = {};
}

struct complex_found{
  static constexpr const char* msg = "native func: number compare: complex cannot be ordinated\n";
  bool operator()(const Number::complex_type&, const Number::complex_type&) const{
    fprintf(zs::err, msg);
    return false;
  }
};

template<template <typename> class Fun,
         class ComplexComparator = complex_found>
struct number_comparator {
  inline bool operator()(const Number* n1, const Number* n2){
    if(n1->type() == Number::Type::integer && n2->type() == Number::Type::integer){
      static const Fun<Number::integer_type> fun;
      return fun(n1->get<Number::integer_type>(), n2->get<Number::integer_type>());
    }else if(n1->type() <= Number::Type::real && n2->type() <= Number::Type::real){
      static const Fun<Number::real_type> fun;
      return fun(n1->coerce<Number::real_type>(), n2->coerce<Number::real_type>());
    }else{
      static const ComplexComparator fun;
      return fun(n1->coerce<Number::complex_type>(), n2->coerce<Number::complex_type>());
    }
  }
};

template<typename Fun>
inline void number_compare(const char* name, Fun&& fun){
  std::vector<Lisp_ptr> args;
  stack_to_vector(VM.stack, args);

  auto i1 = begin(args);
  const auto e = end(args);

  auto n1 = i1->get<Number*>();
  if(!n1 || n1->type() < Number::Type::integer){
    number_type_check_failed(name, *i1);
    return;
  }

  for(auto i2 = next(i1); i2 != e; i1 = i2, ++i2){
    auto n2 = i2->get<Number*>();
    if(!n2 || n2->type() < Number::Type::integer){
      number_type_check_failed(name, *i2);
      return;
    }

    if(!fun(n1, n2)){
      VM.return_value = Lisp_ptr{false};
      return;
    }

    n1 = n2;
  }

  VM.return_value = Lisp_ptr{true};
}

void number_equal(){
  number_compare("=",
                 number_comparator<std::equal_to,
                                   std::equal_to<Number::complex_type> >());
}

void number_less(){
  number_compare("<",
                 number_comparator<std::less>());
}

void number_greater(){
  number_compare(">",
                 number_comparator<std::greater>());
}

void number_less_eq(){
  number_compare("<=",
                 number_comparator<std::less_equal>());
}

void number_greater_eq(){
  number_compare(">=",
                 number_comparator<std::greater_equal>());
}

void zerop(){
  number_pred([](Number* num) -> bool {
      switch(num->type()){
      case Number::Type::complex: {
        auto c = num->get<Number::complex_type>();
        return (c.real() == 0 && c.imag() == 0);
      }
      case Number::Type::real:
        return num->get<Number::real_type>() == 0;
      case Number::Type::integer:
        return num->get<Number::integer_type>() == 0;
      case Number::Type::uninitialized:
      default:
        UNEXP_DEFAULT();
      }
    });
}

template<template <typename> class Fun>
struct pos_neg_pred{
  inline bool operator()(Number* num){
    static constexpr Fun<Number::integer_type> fun;

    switch(num->type()){
    case Number::Type::complex:
      return false;
    case Number::Type::real:
      return fun(num->get<Number::real_type>(), 0);
    case Number::Type::integer:
      return fun(num->get<Number::integer_type>(), 0);
    case Number::Type::uninitialized:
    default:
      UNEXP_DEFAULT();
    }
  }
};

void positivep(){
  number_pred(pos_neg_pred<std::greater>());
}

void negativep(){
  number_pred(pos_neg_pred<std::less>());
}

template<template <typename> class Fun>
struct even_odd_pred{
  inline bool operator()(Number* num){
    static constexpr Fun<Number::integer_type> fun;

    switch(num->type()){
    case Number::Type::complex:
    case Number::Type::real:
      return false;
    case Number::Type::integer:
      return fun(num->get<Number::integer_type>() % 2, 0);
    case Number::Type::uninitialized:
    default:
      UNEXP_DEFAULT();
    }
  }
};

void oddp(){
  number_pred(even_odd_pred<std::not_equal_to>());
}

void evenp(){
  number_pred(even_odd_pred<std::equal_to>());
}

template<class Fun>
inline
void number_accumulate(const char* name, Number&& init, Fun&& fun){
  std::vector<Lisp_ptr> args;
  stack_to_vector(VM.stack, args);

  for(auto i = begin(args), e = end(args);
      i != e; ++i){
    auto n = i->get<Number*>();
    if(!n){
      number_type_check_failed(name, *i);
      return;
    }

    if(!fun(init, *n)){
      VM.return_value = {};
      return;
    }
  }

  VM.return_value = {new Number(init)};
}

void number_max(){
  number_accumulate("max", Number(),
                    [](Number& n1, const Number& n2) -> bool {
                      if(n1.type() == Number::Type::uninitialized){
                        n1 = n2;
                        return true;
                      }

                      if(n2.type() == Number::Type::uninitialized){
                        return true;
                      }

                      if(n1.type() == Number::Type::integer && n2.type() == Number::Type::integer){
                        if(n1.get<Number::integer_type>() < n2.get<Number::integer_type>())
                          n1 = n2;
                        return true;
                      }

                      if(n1.type() <= Number::Type::real && n2.type() <= Number::Type::real){
                        if(n1.coerce<Number::real_type>() < n2.coerce<Number::real_type>())
                          n1 = Number{n2.coerce<Number::real_type>()};
                        if(n1.type() != Number::Type::real)
                          n1 = Number{n1.coerce<Number::real_type>()};
                        return true;
                      }

                      fprintf(zs::err, complex_found::msg);
                      return false;
                    });
}

void number_min(){
  number_accumulate("min", Number(),
                    [](Number& n1, const Number& n2) -> bool {
                      if(n1.type() == Number::Type::uninitialized){
                        n1 = n2;
                        return true;
                      }

                      if(n2.type() == Number::Type::uninitialized){
                        return true;
                      }

                      if(n1.type() == Number::Type::integer && n2.type() == Number::Type::integer){
                        if(n1.get<Number::integer_type>() > n2.get<Number::integer_type>())
                          n1 = n2;
                        return true;
                      }

                      if(n1.type() <= Number::Type::real && n2.type() <= Number::Type::real){
                        if(n1.coerce<Number::real_type>() > n2.coerce<Number::real_type>())
                          n1 = Number{n2.coerce<Number::real_type>()};
                        if(n1.type() != Number::Type::real)
                          n1 = Number{n1.coerce<Number::real_type>()};
                        return true;
                      }

                      fprintf(zs::err, complex_found::msg);
                      return false;
                    });
}

void number_plus(){
  number_accumulate("+", Number(0l),
                    [](Number& n1, const Number& n2) -> bool {
                      if(n1.type() == Number::Type::uninitialized){
                        n1 = n2;
                        return true;
                      }

                      if(n2.type() == Number::Type::uninitialized){
                        return true;
                      }

                      // n1 type <= n2 type
                      if(n1.type() == Number::Type::integer && n2.type() == Number::Type::integer){
                        n1.get<Number::integer_type>() += n2.get<Number::integer_type>();
                        return true;
                      }

                      if(n1.type() == Number::Type::real && n2.type() <= Number::Type::real){
                        n1.get<Number::real_type>() += n2.coerce<Number::real_type>();
                        return true;
                      }

                      if(n1.type() == Number::Type::complex && n2.type() <= Number::Type::complex){
                        n1.get<Number::complex_type>() += n2.coerce<Number::complex_type>();
                        return true;
                      }

                      // n1 type > n2 type
                      if(n1.type() < Number::Type::real && n2.type() == Number::Type::real){
                        n1 = Number{n1.coerce<Number::real_type>() + n2.get<Number::real_type>()};
                        return true;
                      }

                      if(n1.type() < Number::Type::complex && n2.type() == Number::Type::complex){
                        n1 = Number{n1.coerce<Number::complex_type>() + n2.get<Number::complex_type>()};
                        return true;
                      }

                      // ???
                      fprintf(zs::err, "native func: +: failed at numeric conversion!\n");
                      return false;
                    });
}

constexpr struct Entry {
  const char* name;
  const NProcedure func;

  constexpr Entry(const char* n, const NProcedure& f)
    : name(n), func(f){}
} builtin_numeric[] = {
  {"complex?", {
      complexp,
      Calling::function, {1, false}}},
  {"real?", {
      realp,
      Calling::function, {1, false}}},
  {"rational?", {
      rationalp,
      Calling::function, {1, false}}},
  {"integer?", {
      integerp,
      Calling::function, {1, false}}},

  {"exact?", {
      exactp,
      Calling::function, {1, false}}},
  {"inexact?", {
      inexactp,
      Calling::function, {1, false}}},

  {"=", {
      number_equal,
      Calling::function, {2, true}}},
  {"<", {
      number_less,
      Calling::function, {2, true}}},
  {">", {
      number_greater,
      Calling::function, {2, true}}},
  {"<=", {
      number_less_eq,
      Calling::function, {2, true}}},
  {">=", {
      number_greater_eq,
      Calling::function, {2, true}}},

  {"zero?", {
      zerop,
      Calling::function, {1, false}}},
  {"positive?", {
      positivep,
      Calling::function, {1, false}}},
  {"negative?", {
      negativep,
      Calling::function, {1, false}}},
  {"odd?", {
      oddp,
      Calling::function, {1, false}}},
  {"even?", {
      evenp,
      Calling::function, {1, false}}},

  {"max", {
      number_max,
      Calling::function, {2, true}}},
  {"min", {
      number_min,
      Calling::function, {2, true}}},

  {"+", {
      number_plus,
      Calling::function, {0, true}}}
};

} //namespace

void install_builtin_numeric(){
  for(auto& e : builtin_numeric){
    VM.set(intern(VM.symtable, e.name), {&e.func});
  }
}
<|endoftext|>


<commit_before>/*
 Copyright (c) 2010, The Barbarian Group
 All rights reserved.

 Redistribution and use in source and binary forms, with or without modification, are permitted provided that
 the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this list of conditions and
	the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
	the following disclaimer in the documentation and/or other materials provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
*/

#include "cinder/ip/Trim.h"

namespace cinder { namespace ip {

template<typename T>
bool transparentHorizontalScanline( const SurfaceT<T> &surface, int32_t row, int32_t x1, int32_t x2 )
{
	const T *dstPtr = surface.getDataAlpha( Vec2i( x1, row ) );
	uint8_t inc = surface.getPixelInc();
	for( int32_t x = x1; x < x2; ++x ) {
		if( *dstPtr ) return false;
		dstPtr += inc;
	}
	return true;
}

template<typename T>
bool transparentVerticalScanline( const SurfaceT<T> &surface, int32_t column, int32_t y1, int32_t y2 )
{
	const T *dstPtr = surface.getDataAlpha( Vec2i( column, y1 ) );
	int32_t rowBytes = surface.getRowBytes();
	for( int32_t y = y1; y < y2; ++y ) {
		if( *dstPtr ) return false;
		dstPtr += rowBytes;
	}
	return true;
}

template<typename T>
Area findNonTransparentArea( const SurfaceT<T> &surface, const Area &unclippedBounds )
{
	const Area bounds = unclippedBounds.getClipBy( surface.getBounds() );
	// if no alpha we'll fail over the to alpha-less fill
	if( ! surface.hasAlpha() ) {
		return surface.getBounds();
	}

	int32_t topLine, bottomLine;
	int32_t leftColumn, rightColumn;
	// find the top and bottom lines
	for( topLine = bounds.getY1(); topLine < bounds.getY2(); ++topLine ) {
		if( ! transparentHorizontalScanline( surface, topLine, bounds.getX1(), bounds.getX2() ) ) {
			break;
		}
	}
	for( bottomLine = bounds.getY2() - 1; bottomLine > topLine; --bottomLine ) {
		if( ! transparentHorizontalScanline( surface, bottomLine, bounds.getX1(), bounds.getX2() ) ) {
			break;
		}
	}

	// find the left and right columns
	for( leftColumn = bounds.getX1(); leftColumn < bounds.getX2(); ++leftColumn ) {
		if( ! transparentVerticalScanline( surface, leftColumn, topLine, bottomLine ) ) {
			break;
		}
	}
	for( rightColumn = bounds.getX2(); rightColumn > leftColumn; --rightColumn ) {
		if( ! transparentVerticalScanline( surface, rightColumn, topLine, bottomLine ) ) {
			break;
		}
	}

	return Area( leftColumn, topLine, rightColumn, bottomLine );
}

#define TRIM_PROTOTYPES(r,data,T)\
	template Area findNonTransparentArea( const SurfaceT<T> &surface, const Area &unclippedBounds );

BOOST_PP_SEQ_FOR_EACH( TRIM_PROTOTYPES, ~, CHANNEL_TYPES )

} } // namespace cinder::ip
<commit_msg>Fixing off-by-one on right/bottom for ip::findNonTransparentArea<commit_after>/*
 Copyright (c) 2010, The Barbarian Group
 All rights reserved.

 Redistribution and use in source and binary forms, with or without modification, are permitted provided that
 the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this list of conditions and
	the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
	the following disclaimer in the documentation and/or other materials provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
*/

#include "cinder/ip/Trim.h"

namespace cinder { namespace ip {

template<typename T>
bool transparentHorizontalScanline( const SurfaceT<T> &surface, int32_t row, int32_t x1, int32_t x2 )
{
	const T *dstPtr = surface.getDataAlpha( Vec2i( x1, row ) );
	uint8_t inc = surface.getPixelInc();
	for( int32_t x = x1; x < x2; ++x ) {
		if( *dstPtr ) return false;
		dstPtr += inc;
	}
	return true;
}

template<typename T>
bool transparentVerticalScanline( const SurfaceT<T> &surface, int32_t column, int32_t y1, int32_t y2 )
{
	const T *dstPtr = surface.getDataAlpha( Vec2i( column, y1 ) );
	int32_t rowBytes = surface.getRowBytes();
	for( int32_t y = y1; y < y2; ++y ) {
		if( *dstPtr ) return false;
		dstPtr += rowBytes;
	}
	return true;
}

template<typename T>
Area findNonTransparentArea( const SurfaceT<T> &surface, const Area &unclippedBounds )
{
	const Area bounds = unclippedBounds.getClipBy( surface.getBounds() );
	// if no alpha we'll fail over the to alpha-less fill
	if( ! surface.hasAlpha() ) {
		return surface.getBounds();
	}

	int32_t topLine, bottomLine;
	int32_t leftColumn, rightColumn;
	// find the top and bottom lines
	for( topLine = bounds.getY1(); topLine < bounds.getY2(); ++topLine ) {
		if( ! transparentHorizontalScanline( surface, topLine, bounds.getX1(), bounds.getX2() ) ) {
			break;
		}
	}
	for( bottomLine = bounds.getY2() - 1; bottomLine > topLine; --bottomLine ) {
		if( ! transparentHorizontalScanline( surface, bottomLine, bounds.getX1(), bounds.getX2() ) ) {
			break;
		}
	}

	// find the left and right columns
	for( leftColumn = bounds.getX1(); leftColumn < bounds.getX2(); ++leftColumn ) {
		if( ! transparentVerticalScanline( surface, leftColumn, topLine, bottomLine ) ) {
			break;
		}
	}
	for( rightColumn = bounds.getX2(); rightColumn > leftColumn; --rightColumn ) {
		if( ! transparentVerticalScanline( surface, rightColumn, topLine, bottomLine ) ) {
			break;
		}
	}

	// we add one to right and bottom because Area represents an inclusive range on top/left and exclusive range on bottom/right
	return Area( leftColumn, topLine, rightColumn + 1, bottomLine + 1 );
}

#define TRIM_PROTOTYPES(r,data,T)\
	template Area findNonTransparentArea( const SurfaceT<T> &surface, const Area &unclippedBounds );

BOOST_PP_SEQ_FOR_EACH( TRIM_PROTOTYPES, ~, CHANNEL_TYPES )

} } // namespace cinder::ip
<|endoftext|>


<commit_before>/*
* (C) 2014,2015 Jack Lloyd
*     2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include "cli.h"

#if defined(BOTAN_HAS_TLS) && defined(BOTAN_TARGET_OS_HAS_SOCKETS)

#include <botan/tls_client.h>
#include <botan/hex.h>

#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
#include <botan/tls_session_manager_sqlite.h>
#endif

#include <string>
#include <memory>

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>

#if !defined(MSG_NOSIGNAL)
  #define MSG_NOSIGNAL 0
#endif

#include "credentials.h"

namespace Botan_CLI {

class TLS_Client final : public Command, public Botan::TLS::Callbacks
   {
   public:
      TLS_Client() : Command("tls_client host --port=443 --print-certs --policy= "
                             "--tls1.0 --tls1.1 --tls1.2 "
                             "--session-db= --session-db-pass= --next-protocols= --type=tcp") {}

      void go() override
         {
         // TODO client cert auth

         std::unique_ptr<Botan::TLS::Session_Manager> session_mgr;

         const std::string sessions_db = get_arg("session-db");

         if(!sessions_db.empty())
            {
#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
            const std::string sessions_passphrase = get_arg("session-db-pass");
            session_mgr.reset(new Botan::TLS::Session_Manager_SQLite(sessions_passphrase, rng(), sessions_db));
#else
            error_output() << "Ignoring session DB file, sqlite not enabled\n";
#endif
            }

         if(!session_mgr)
            {
            session_mgr.reset(new Botan::TLS::Session_Manager_In_Memory(rng()));
            }

         std::string policy_file = get_arg("policy");

         std::unique_ptr<Botan::TLS::Policy> policy;

         if(policy_file.size() > 0)
            {
            std::ifstream policy_stream(policy_file);
            if(!policy_stream.good())
               {
               error_output() << "Failed reading policy file\n";
               return;
               }
            policy.reset(new Botan::TLS::Text_Policy(policy_stream));
            }

         if(!policy)
            {
            policy.reset(new Botan::TLS::Policy);
            }

         Basic_Credentials_Manager creds;

         const std::string host = get_arg("host");
         const uint16_t port = get_arg_sz("port");
         const std::string transport = get_arg("type");

         if(transport != "tcp" && transport != "udp")
            throw CLI_Usage_Error("Invalid transport type '" + transport + "' for TLS");

         const bool use_tcp = (transport == "tcp");

         const std::vector<std::string> protocols_to_offer = Botan::split_on("next-protocols", ',');

         m_sockfd = connect_to_host(host, port, use_tcp);

         using namespace std::placeholders;

         auto version = policy->latest_supported_version(!use_tcp);

         if(flag_set("tls1.0"))
            {
            version = Botan::TLS::Protocol_Version::TLS_V10;
            }
         else if(flag_set("tls1.1"))
            {
            version = Botan::TLS::Protocol_Version::TLS_V11;
            }

         Botan::TLS::Client client(*this,
                                   *session_mgr,
                                   creds,
                                   *policy,
                                   rng(),
                                   Botan::TLS::Server_Information(host, port),
                                   version,
                                   protocols_to_offer);

         bool first_active = true;

         while(!client.is_closed())
            {
            fd_set readfds;
            FD_ZERO(&readfds);
            FD_SET(m_sockfd, &readfds);

            if(client.is_active())
               {
               FD_SET(STDIN_FILENO, &readfds);
               if(first_active && !protocols_to_offer.empty())
                  {
                  std::string app = client.application_protocol();
                  if(app != "")
                     output() << "Server choose protocol: " << client.application_protocol() << "\n";
                  first_active = false;
                  }
               }

            struct timeval timeout = { 1, 0 };

            ::select(m_sockfd + 1, &readfds, nullptr, nullptr, &timeout);

            if(FD_ISSET(m_sockfd, &readfds))
               {
               uint8_t buf[4*1024] = { 0 };

               ssize_t got = ::read(m_sockfd, buf, sizeof(buf));

               if(got == 0)
                  {
                  output() << "EOF on socket\n";
                  break;
                  }
               else if(got == -1)
                  {
                  output() << "Socket error: " << errno << " " << strerror(errno) << "\n";
                  continue;
                  }

               client.received_data(buf, got);
               }

            if(FD_ISSET(STDIN_FILENO, &readfds))
               {
               uint8_t buf[1024] = { 0 };
               ssize_t got = read(STDIN_FILENO, buf, sizeof(buf));

               if(got == 0)
                  {
                  output() << "EOF on stdin\n";
                  client.close();
                  break;
                  }
               else if(got == -1)
                  {
                  output() << "Stdin error: " << errno << " " << strerror(errno) << "\n";
                  continue;
                  }

               if(got == 2 && buf[1] == '\n')
                  {
                  char cmd = buf[0];

                  if(cmd == 'R' || cmd == 'r')
                     {
                     output() << "Client initiated renegotiation\n";
                     client.renegotiate(cmd == 'R');
                     }
                  else if(cmd == 'Q')
                     {
                     output() << "Client initiated close\n";
                     client.close();
                     }
                  }
               else
                  client.send(buf, got);
               }

            if(client.timeout_check())
               {
               output() << "Timeout detected\n";
               }
            }

         ::close(m_sockfd);
         }

   private:
      int connect_to_host(const std::string& host, uint16_t port, bool tcp)
         {
         hostent* host_addr = ::gethostbyname(host.c_str());

         if(!host_addr)
            throw CLI_Error("gethostbyname failed for " + host);

         if(host_addr->h_addrtype != AF_INET) // FIXME
            throw CLI_Error(host + " has IPv6 address, not supported");

         int type = tcp ? SOCK_STREAM : SOCK_DGRAM;

         int fd = ::socket(PF_INET, type, 0);
         if(fd == -1)
            throw CLI_Error("Unable to acquire socket");

         sockaddr_in socket_info;
         ::memset(&socket_info, 0, sizeof(socket_info));
         socket_info.sin_family = AF_INET;
         socket_info.sin_port = htons(port);

         ::memcpy(&socket_info.sin_addr,
                  host_addr->h_addr,
                  host_addr->h_length);

         socket_info.sin_addr = *reinterpret_cast<struct in_addr*>(host_addr->h_addr); // FIXME

         if(::connect(fd, reinterpret_cast<sockaddr*>(&socket_info), sizeof(struct sockaddr)) != 0)
            {
            ::close(fd);
            throw CLI_Error("connect failed");
            }

         return fd;
         }

      bool tls_session_established(const Botan::TLS::Session& session) override
         {
         output() << "Handshake complete, " << session.version().to_string()
                   << " using " << session.ciphersuite().to_string() << "\n";

         if(!session.session_id().empty())
            output() << "Session ID " << Botan::hex_encode(session.session_id()) << "\n";

         if(!session.session_ticket().empty())
            output() << "Session ticket " << Botan::hex_encode(session.session_ticket()) << "\n";

         if(flag_set("print-certs"))
            {
            const std::vector<Botan::X509_Certificate>& certs = session.peer_certs();

            for(size_t i = 0; i != certs.size(); ++i)
               {
               output() << "Certificate " << i+1 << "/" << certs.size() << "\n";
               output() << certs[i].to_string();
               output() << certs[i].PEM_encode();
               }
            }

         return true;
         }

      static void dgram_socket_write(int sockfd, const uint8_t buf[], size_t length)
         {
         int r = send(sockfd, buf, length, MSG_NOSIGNAL);

         if(r == -1)
            throw CLI_Error("Socket write failed errno=" + std::to_string(errno));
         }

      void tls_emit_data(const uint8_t buf[], size_t length) override
         {
         size_t offset = 0;

         while(length)
            {
            ssize_t sent = ::send(m_sockfd, buf + offset, length, MSG_NOSIGNAL);

            if(sent == -1)
               {
               if(errno == EINTR)
                  sent = 0;
               else
                  throw CLI_Error("Socket write failed errno=" + std::to_string(errno));
               }

            offset += sent;
            length -= sent;
            }
         }

      void tls_alert(Botan::TLS::Alert alert) override
         {
         output() << "Alert: " << alert.type_string() << "\n";
         }

      void tls_record_received(uint64_t /*seq_no*/, const uint8_t buf[], size_t buf_size) override
         {
         for(size_t i = 0; i != buf_size; ++i)
            output() << buf[i];
         }

      private:
         int m_sockfd = -1;
   };

BOTAN_REGISTER_COMMAND("tls_client", TLS_Client);

}

#endif
<commit_msg>Fix TLS client next protocol handling<commit_after>/*
* (C) 2014,2015 Jack Lloyd
*     2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include "cli.h"

#if defined(BOTAN_HAS_TLS) && defined(BOTAN_TARGET_OS_HAS_SOCKETS)

#include <botan/tls_client.h>
#include <botan/hex.h>

#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
#include <botan/tls_session_manager_sqlite.h>
#endif

#include <string>
#include <memory>

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>

#if !defined(MSG_NOSIGNAL)
  #define MSG_NOSIGNAL 0
#endif

#include "credentials.h"

namespace Botan_CLI {

class TLS_Client final : public Command, public Botan::TLS::Callbacks
   {
   public:
      TLS_Client() : Command("tls_client host --port=443 --print-certs --policy= "
                             "--tls1.0 --tls1.1 --tls1.2 "
                             "--session-db= --session-db-pass= --next-protocols= --type=tcp") {}

      void go() override
         {
         // TODO client cert auth

         std::unique_ptr<Botan::TLS::Session_Manager> session_mgr;

         const std::string sessions_db = get_arg("session-db");

         if(!sessions_db.empty())
            {
#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
            const std::string sessions_passphrase = get_arg("session-db-pass");
            session_mgr.reset(new Botan::TLS::Session_Manager_SQLite(sessions_passphrase, rng(), sessions_db));
#else
            error_output() << "Ignoring session DB file, sqlite not enabled\n";
#endif
            }

         if(!session_mgr)
            {
            session_mgr.reset(new Botan::TLS::Session_Manager_In_Memory(rng()));
            }

         std::string policy_file = get_arg("policy");

         std::unique_ptr<Botan::TLS::Policy> policy;

         if(policy_file.size() > 0)
            {
            std::ifstream policy_stream(policy_file);
            if(!policy_stream.good())
               {
               error_output() << "Failed reading policy file\n";
               return;
               }
            policy.reset(new Botan::TLS::Text_Policy(policy_stream));
            }

         if(!policy)
            {
            policy.reset(new Botan::TLS::Policy);
            }

         Basic_Credentials_Manager creds;

         const std::string host = get_arg("host");
         const uint16_t port = get_arg_sz("port");
         const std::string transport = get_arg("type");
         const std::string next_protos = get_arg("next-protocols");

         if(transport != "tcp" && transport != "udp")
            throw CLI_Usage_Error("Invalid transport type '" + transport + "' for TLS");

         const bool use_tcp = (transport == "tcp");

         const std::vector<std::string> protocols_to_offer = Botan::split_on(next_protos, ',');

         m_sockfd = connect_to_host(host, port, use_tcp);

         using namespace std::placeholders;

         auto version = policy->latest_supported_version(!use_tcp);

         if(flag_set("tls1.0"))
            {
            version = Botan::TLS::Protocol_Version::TLS_V10;
            }
         else if(flag_set("tls1.1"))
            {
            version = Botan::TLS::Protocol_Version::TLS_V11;
            }

         Botan::TLS::Client client(*this,
                                   *session_mgr,
                                   creds,
                                   *policy,
                                   rng(),
                                   Botan::TLS::Server_Information(host, port),
                                   version,
                                   protocols_to_offer);

         bool first_active = true;

         while(!client.is_closed())
            {
            fd_set readfds;
            FD_ZERO(&readfds);
            FD_SET(m_sockfd, &readfds);

            if(client.is_active())
               {
               FD_SET(STDIN_FILENO, &readfds);
               if(first_active && !protocols_to_offer.empty())
                  {
                  std::string app = client.application_protocol();
                  if(app != "")
                     output() << "Server choose protocol: " << client.application_protocol() << "\n";
                  first_active = false;
                  }
               }

            struct timeval timeout = { 1, 0 };

            ::select(m_sockfd + 1, &readfds, nullptr, nullptr, &timeout);

            if(FD_ISSET(m_sockfd, &readfds))
               {
               uint8_t buf[4*1024] = { 0 };

               ssize_t got = ::read(m_sockfd, buf, sizeof(buf));

               if(got == 0)
                  {
                  output() << "EOF on socket\n";
                  break;
                  }
               else if(got == -1)
                  {
                  output() << "Socket error: " << errno << " " << strerror(errno) << "\n";
                  continue;
                  }

               client.received_data(buf, got);
               }

            if(FD_ISSET(STDIN_FILENO, &readfds))
               {
               uint8_t buf[1024] = { 0 };
               ssize_t got = read(STDIN_FILENO, buf, sizeof(buf));

               if(got == 0)
                  {
                  output() << "EOF on stdin\n";
                  client.close();
                  break;
                  }
               else if(got == -1)
                  {
                  output() << "Stdin error: " << errno << " " << strerror(errno) << "\n";
                  continue;
                  }

               if(got == 2 && buf[1] == '\n')
                  {
                  char cmd = buf[0];

                  if(cmd == 'R' || cmd == 'r')
                     {
                     output() << "Client initiated renegotiation\n";
                     client.renegotiate(cmd == 'R');
                     }
                  else if(cmd == 'Q')
                     {
                     output() << "Client initiated close\n";
                     client.close();
                     }
                  }
               else
                  client.send(buf, got);
               }

            if(client.timeout_check())
               {
               output() << "Timeout detected\n";
               }
            }

         ::close(m_sockfd);
         }

   private:
      int connect_to_host(const std::string& host, uint16_t port, bool tcp)
         {
         hostent* host_addr = ::gethostbyname(host.c_str());

         if(!host_addr)
            throw CLI_Error("gethostbyname failed for " + host);

         if(host_addr->h_addrtype != AF_INET) // FIXME
            throw CLI_Error(host + " has IPv6 address, not supported");

         int type = tcp ? SOCK_STREAM : SOCK_DGRAM;

         int fd = ::socket(PF_INET, type, 0);
         if(fd == -1)
            throw CLI_Error("Unable to acquire socket");

         sockaddr_in socket_info;
         ::memset(&socket_info, 0, sizeof(socket_info));
         socket_info.sin_family = AF_INET;
         socket_info.sin_port = htons(port);

         ::memcpy(&socket_info.sin_addr,
                  host_addr->h_addr,
                  host_addr->h_length);

         socket_info.sin_addr = *reinterpret_cast<struct in_addr*>(host_addr->h_addr); // FIXME

         if(::connect(fd, reinterpret_cast<sockaddr*>(&socket_info), sizeof(struct sockaddr)) != 0)
            {
            ::close(fd);
            throw CLI_Error("connect failed");
            }

         return fd;
         }

      bool tls_session_established(const Botan::TLS::Session& session) override
         {
         output() << "Handshake complete, " << session.version().to_string()
                   << " using " << session.ciphersuite().to_string() << "\n";

         if(!session.session_id().empty())
            output() << "Session ID " << Botan::hex_encode(session.session_id()) << "\n";

         if(!session.session_ticket().empty())
            output() << "Session ticket " << Botan::hex_encode(session.session_ticket()) << "\n";

         if(flag_set("print-certs"))
            {
            const std::vector<Botan::X509_Certificate>& certs = session.peer_certs();

            for(size_t i = 0; i != certs.size(); ++i)
               {
               output() << "Certificate " << i+1 << "/" << certs.size() << "\n";
               output() << certs[i].to_string();
               output() << certs[i].PEM_encode();
               }
            }

         return true;
         }

      static void dgram_socket_write(int sockfd, const uint8_t buf[], size_t length)
         {
         int r = send(sockfd, buf, length, MSG_NOSIGNAL);

         if(r == -1)
            throw CLI_Error("Socket write failed errno=" + std::to_string(errno));
         }

      void tls_emit_data(const uint8_t buf[], size_t length) override
         {
         size_t offset = 0;

         while(length)
            {
            ssize_t sent = ::send(m_sockfd, buf + offset, length, MSG_NOSIGNAL);

            if(sent == -1)
               {
               if(errno == EINTR)
                  sent = 0;
               else
                  throw CLI_Error("Socket write failed errno=" + std::to_string(errno));
               }

            offset += sent;
            length -= sent;
            }
         }

      void tls_alert(Botan::TLS::Alert alert) override
         {
         output() << "Alert: " << alert.type_string() << "\n";
         }

      void tls_record_received(uint64_t /*seq_no*/, const uint8_t buf[], size_t buf_size) override
         {
         for(size_t i = 0; i != buf_size; ++i)
            output() << buf[i];
         }

      private:
         int m_sockfd = -1;
   };

BOTAN_REGISTER_COMMAND("tls_client", TLS_Client);

}

#endif
<|endoftext|>


<commit_before>#include "command_manager.hh"

#include "utils.hh"
#include "assert.hh"

#include <algorithm>

namespace Kakoune
{

void CommandManager::register_command(const std::string& command_name, Command command,
                                      const CommandCompleter& completer)
{
    m_commands[command_name] = CommandAndCompleter { command, completer };
}

void CommandManager::register_command(const std::vector<std::string>& command_names, Command command,
                                      const CommandCompleter& completer)
{
    for (auto command_name : command_names)
        register_command(command_name, command, completer);
}

typedef std::vector<std::pair<size_t, size_t>> TokenList;
static TokenList split(const std::string& line)
{
    TokenList result;

    size_t pos = 0;
    while (pos < line.length())
    {
        while(line[pos] == ' ' and pos != line.length())
            ++pos;

        char delimiter = ' ';
        if (line[pos] == '"' or line[pos] == '\'')
        {
            delimiter = line[pos];
            ++pos;
        }

        size_t token_start = pos;

        while ((line[pos] != delimiter or line[pos-1] == '\\') and
               pos != line.length())
            ++pos;

        result.push_back(std::make_pair(token_start, pos));

        ++pos;
    }
    return result;
}

struct command_not_found : runtime_error
{
    command_not_found(const std::string& command)
        : runtime_error(command + " : no such command") {}
};

void CommandManager::execute(const std::string& command_line,
                             const Context& context)
{
    TokenList tokens = split(command_line);
    if (tokens.empty())
        return;

    CommandParameters params;
    for (auto it = tokens.begin(); it != tokens.end(); ++it)
    {
        params.push_back(command_line.substr(it->first,
                                             it->second - it->first));
    }

    execute(params, context);
}

void CommandManager::execute(const CommandParameters& params,
                             const Context& context)
{
    if (params.empty())
        return;

    auto command_it = m_commands.find(params[0]);
    if (command_it == m_commands.end())
        throw command_not_found(params[0]);

    command_it->second.command(CommandParameters(params.begin() + 1, params.end()), context);
}

Completions CommandManager::complete(const std::string& command_line, size_t cursor_pos)
{
    TokenList tokens = split(command_line);

    size_t token_to_complete = tokens.size();
    for (size_t i = 0; i < tokens.size(); ++i)
    {
        if (tokens[i].first <= cursor_pos and tokens[i].second >= cursor_pos)
        {
            token_to_complete = i;
            break;
        }
    }

    if (token_to_complete == 0 or tokens.empty()) // command name completion
    {
        size_t cmd_start = tokens.empty() ? 0 : tokens[0].first;
        Completions result(cmd_start, cursor_pos);
        std::string prefix = command_line.substr(cmd_start,
                                                 cursor_pos - cmd_start);

        for (auto& command : m_commands)
        {
            if (command.first.substr(0, prefix.length()) == prefix)
                result.candidates.push_back(command.first);
        }

        return result;
    }

    assert(not tokens.empty());
    std::string command_name =
        command_line.substr(tokens[0].first,
                            tokens[0].second - tokens[0].first);

    auto command_it = m_commands.find(command_name);
    if (command_it == m_commands.end() or not command_it->second.completer)
        return Completions();

    CommandParameters params;
    for (auto it = tokens.begin() + 1; it != tokens.end(); ++it)
    {
        params.push_back(command_line.substr(it->first,
                                             it->second - it->first));
    }

    size_t start = token_to_complete < tokens.size() ?
                   tokens[token_to_complete].first : cursor_pos;
    Completions result(start , cursor_pos);
    size_t cursor_pos_in_token = cursor_pos - start;

    result.candidates = command_it->second.completer(params,
                                                     token_to_complete - 1,
                                                     cursor_pos_in_token);
    return result;
}

CandidateList PerArgumentCommandCompleter::operator()(const CommandParameters& params,
                                                      size_t token_to_complete,
                                                      size_t pos_in_token) const
{
    if (token_to_complete >= m_completers.size())
        return CandidateList();

    // it is possible to try to complete a new argument
    assert(token_to_complete <= params.size());

    const std::string& argument = token_to_complete < params.size() ?
                                  params[token_to_complete] : std::string();
    return m_completers[token_to_complete](argument, pos_in_token);
}

}
<commit_msg>CommandManager: support ';' as a command separator<commit_after>#include "command_manager.hh"

#include "utils.hh"
#include "assert.hh"

#include <algorithm>

namespace Kakoune
{

void CommandManager::register_command(const std::string& command_name, Command command,
                                      const CommandCompleter& completer)
{
    m_commands[command_name] = CommandAndCompleter { command, completer };
}

void CommandManager::register_command(const std::vector<std::string>& command_names, Command command,
                                      const CommandCompleter& completer)
{
    for (auto command_name : command_names)
        register_command(command_name, command, completer);
}

typedef std::vector<std::pair<size_t, size_t>> TokenList;
static TokenList split(const std::string& line)
{
    TokenList result;

    size_t pos = 0;
    while (pos < line.length())
    {
        while(line[pos] == ' ' and pos != line.length())
            ++pos;

        char delimiter = ' ';
        if (line[pos] == '"' or line[pos] == '\'')
        {
            delimiter = line[pos];
            ++pos;
        }

        size_t token_start = pos;

        while (((line[pos] != delimiter and line[pos] != ';') or
                line[pos-1] == '\\') and pos != line.length())
            ++pos;

        result.push_back(std::make_pair(token_start, pos));

        if (line[pos] == ';')
           result.push_back(std::make_pair(pos, pos+1));

        ++pos;
    }
    return result;
}

struct command_not_found : runtime_error
{
    command_not_found(const std::string& command)
        : runtime_error(command + " : no such command") {}
};

void CommandManager::execute(const std::string& command_line,
                             const Context& context)
{
    TokenList tokens = split(command_line);
    if (tokens.empty())
        return;

    CommandParameters params;
    for (auto it = tokens.begin(); it != tokens.end(); ++it)
    {
        params.push_back(command_line.substr(it->first,
                                             it->second - it->first));
    }

    execute(params, context);
}

void CommandManager::execute(const CommandParameters& params,
                             const Context& context)
{
    if (params.empty())
        return;

    auto begin = params.begin();
    auto end = begin;
    while (true)
    {
        while (end != params.end() and *end != ";")
            ++end;

        if (end != begin)
        {
            auto command_it = m_commands.find(*begin);
            if (command_it == m_commands.end())
                throw command_not_found(*begin);

            command_it->second.command(CommandParameters(begin + 1, end), context);
        }

        if (end == params.end())
            break;

        begin = end+1;
        end   = begin;
    }
}

Completions CommandManager::complete(const std::string& command_line, size_t cursor_pos)
{
    TokenList tokens = split(command_line);

    size_t token_to_complete = tokens.size();
    for (size_t i = 0; i < tokens.size(); ++i)
    {
        if (tokens[i].first <= cursor_pos and tokens[i].second >= cursor_pos)
        {
            token_to_complete = i;
            break;
        }
    }

    if (token_to_complete == 0 or tokens.empty()) // command name completion
    {
        size_t cmd_start = tokens.empty() ? 0 : tokens[0].first;
        Completions result(cmd_start, cursor_pos);
        std::string prefix = command_line.substr(cmd_start,
                                                 cursor_pos - cmd_start);

        for (auto& command : m_commands)
        {
            if (command.first.substr(0, prefix.length()) == prefix)
                result.candidates.push_back(command.first);
        }

        return result;
    }

    assert(not tokens.empty());
    std::string command_name =
        command_line.substr(tokens[0].first,
                            tokens[0].second - tokens[0].first);

    auto command_it = m_commands.find(command_name);
    if (command_it == m_commands.end() or not command_it->second.completer)
        return Completions();

    CommandParameters params;
    for (auto it = tokens.begin() + 1; it != tokens.end(); ++it)
    {
        params.push_back(command_line.substr(it->first,
                                             it->second - it->first));
    }

    size_t start = token_to_complete < tokens.size() ?
                   tokens[token_to_complete].first : cursor_pos;
    Completions result(start , cursor_pos);
    size_t cursor_pos_in_token = cursor_pos - start;

    result.candidates = command_it->second.completer(params,
                                                     token_to_complete - 1,
                                                     cursor_pos_in_token);
    return result;
}

CandidateList PerArgumentCommandCompleter::operator()(const CommandParameters& params,
                                                      size_t token_to_complete,
                                                      size_t pos_in_token) const
{
    if (token_to_complete >= m_completers.size())
        return CandidateList();

    // it is possible to try to complete a new argument
    assert(token_to_complete <= params.size());

    const std::string& argument = token_to_complete < params.size() ?
                                  params[token_to_complete] : std::string();
    return m_completers[token_to_complete](argument, pos_in_token);
}

}
<|endoftext|>