text:
/**
 * Copyright (c) 2019 Osaka University
 *
 * This software is released under the MIT License.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * @author Yoji Yamamoto
 *
 */
#include "worker.hpp"

#include <algorithm>
#include <chrono>
#include <functional>
#include <iostream>
#include <iterator>
#include <memory>
#include <sstream>
#include <thread>

#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/bind.hpp>
#include <vector>

#include <ndn-cxx/data.hpp>
#include <ndn-cxx/face.hpp>
#include <ndn-cxx/interest-filter.hpp>
#include <ndn-cxx/interest.hpp>
#include <ndn-cxx/name.hpp>
#include <ndn-cxx/util/time.hpp>

#include <json11.hpp>

#include "decode.hpp"
#include "encode.hpp"

using namespace ndn::literals::time_literals;

Worker::Worker(const std::string& cd_str, detector_ptr detector)
    : m_cd_string(cd_str),
      m_detector(detector),
      m_num_thread(1),
      m_num_queue(1),
      m_current_queue(0),
      m_io_service_pool(m_num_queue),
      m_worker_pool(),
      m_thread_pool(),
      m_timer_service(),
      m_id_generator(1)
{
  for(auto&& ios : m_io_service_pool) {
    m_worker_pool.emplace_back(ios);
  }
  for(unsigned int n = 0; n < m_num_thread; ++n) {
    m_thread_pool.emplace_back([this, n] { this->m_io_service_pool.at(n % m_num_queue).run(); });
  }

  m_timer_worker.reset(new boost::asio::io_service::work(m_timer_service));
}

Worker::~Worker()
{
  m_worker_pool.clear();
  for(auto& thread : m_thread_pool) {
    if(thread.joinable()) thread.join();
  }
}

void Worker::run()
{
  std::thread timer_thread([this] { this->m_timer_service.run(); });
  std::thread ndn_thread([this] {
    // INFO("Run NFD");
    std::cerr << "Run NFD" << std::endl;
    this->m_ndn_face.setInterestFilter(m_cd_string, std::bind(&Worker::onInterest, this, _1, _2),
                                       ndn::RegisterPrefixSuccessCallback(),
                                       std::bind(&Worker::onRegisterFailed, this, _1, _2));
    this->m_ndn_face.processEvents();
  });

  timer_thread.join();
  ndn_thread.join();
  return;
}

void Worker::onInterest(const ndn::InterestFilter& filter, const ndn::Interest& interest)
{
  std::stringstream ss;
  ss << "Receive Interest packet: " << interest;
  // DEBUG("%s", ss.str().c_str());
  std::cerr << ss.str().c_str() << std::endl;
  ss.clear();
  ss.str("");

  std::string edge_mode = "c";
  if(interest.hasApplicationParameters()) {
    std::string param(interest.getApplicationParameters().value(),
                      interest.getApplicationParameters().value() + interest.getApplicationParameters().value_size());
    std::cerr << "parameter  oK: " << param << std::endl;
    edge_mode = param;
  }

  std::vector<std::string> target_name(ExtractTargets(decodeURI(interest.getName().toUri())));
  std::string session_id(ExtractSessionID(decodeURI(interest.getName().toUri())));

  if(target_name.size() == 0) {
    std::cerr << "Target is not properly specified." << std::endl;
    return;
  }
  if(edge_mode == "e") {
    std::vector<std::string> detection_result;
    m_detector->detect(detection_result);

    std::cerr << "Specified target: [" << target_name[0] << "]" << std::endl;
    std::cerr << "List of detected objects: [" << std::endl;
    for(const std::string& line : detection_result) {
      std::cerr << line << std::endl;
    }
    std::cerr << "]" << std::endl;

    bool is_found = false;
    if(std::find(detection_result.begin(), detection_result.end(), target_name[0]) !=
       detection_result.end()) {
      std::cout << "------------------------------------" << std::endl;
      std::cout << "----------[Target Found!]-----------" << std::endl;
      std::cout << "------------------------------------" << std::endl;
      is_found = true;
    } else {
      std::cout << "------------------------------------" << std::endl;
      std::cout << "--------[Target Not Found!]---------" << std::endl;
      std::cout << "------------------------------------" << std::endl;
      is_found = false;
    }

    std::string m_location(m_cd_string);
    boost::algorithm::replace_all(m_location, "/", "");
    std::string result_str = Encoder::encode(m_location, "", target_name[0], is_found, session_id);
    std::vector<uint8_t> data_vector;
    std::copy(result_str.begin(), result_str.end(), std::back_inserter(data_vector));

    auto data = ndn::make_shared<ndn::Data>(interest.getName());
    data->setFreshnessPeriod(10_s);
    data->setContent(reinterpret_cast<const uint8_t*>(data_vector.data()), data_vector.size());

    m_key_chain.sign(*data);

    std::cerr << "Sending Data: " << *data << std::endl;

    m_ndn_face.put(*data);
  } else if(edge_mode == "c") {  // cloud mode--------------------------

    // if(m_store.empty()){
    if(!interest.getName()[-1].isSegment()) {
      cv::Mat raw = m_detector->returnMat();

      std::cout << raw.rows << " " << raw.cols << " " << raw.dims << " " << raw.channels()
                << std::endl;
      raw = raw.reshape(1, 1);

      std::vector<uint8_t> data_vector = raw;

      ndn::Name prefix = interest.getName();
      if(prefix.size() > 0 && prefix[-1].isVersion()) {
        m_prefix = prefix.getPrefix(-1);
        m_versionedPrefix = prefix;
      } else {
        m_prefix = prefix;
        m_versionedPrefix = ndn::Name(m_prefix).appendVersion();
      }

      m_store.clear();
      // populateStore(is);
      populateStore(data_vector);
      processSegmentInterest(interest);
    } else {
      processSegmentInterest(interest);
    }
  }
}

void Worker::onRegisterFailed(const ndn::Name& prefix, const std::string& reason)
{
  std::cerr << "ERROR: Failed to register prefix \"" << prefix << "\" in local hub's daemon ("
            << reason << ")" << std::endl;
  m_ndn_face_ptr->shutdown();
}

std::string Worker::ExtractSessionID(const std::string& interest_name)
{
  std::vector<std::string> token_list;
  boost::algorithm::split(token_list, interest_name, boost::is_any_of("/"));

  std::string sessionID(token_list.back());

  return sessionID;
}

std::vector<std::string> Worker::ExtractTargets(const std::string& interest_name)
{
  std::vector<std::string> token_list;
  boost::algorithm::split(token_list, interest_name, boost::is_any_of("/"));

  token_list.pop_back();
  std::string keyword(token_list.back());
  std::vector<std::string> key_list;
  boost::algorithm::split(key_list, keyword, boost::is_any_of(" "));

  std::string material(key_list.back());
  std::vector<std::string> material_list;
  boost::algorithm::split(material_list, material, boost::is_any_of(":"));

  std::string target(material_list.back());
  boost::algorithm::replace_all(target, "[", "");
  boost::algorithm::replace_all(target, "]", "");

  std::vector<std::string> target_list;
  boost::algorithm::split(target_list, target, boost::is_any_of(","));

  return target_list;
}

std::string Worker::decodeURI(const std::string& uri)
{
  std::string replaced_string(uri);

  boost::algorithm::replace_all(replaced_string, "%23", "#");
  boost::algorithm::replace_all(replaced_string, "%3A", ":");
  boost::algorithm::replace_all(replaced_string, "%2C", ",");
  boost::algorithm::replace_all(replaced_string, "%5B", "[");
  boost::algorithm::replace_all(replaced_string, "%5D", "]");
  boost::algorithm::replace_all(replaced_string, "%20", " ");
  boost::algorithm::replace_all(replaced_string, "%27", "'");
  boost::algorithm::replace_all(replaced_string, "%28", "(");
  boost::algorithm::replace_all(replaced_string, "%29", ")");
  boost::algorithm::replace_all(replaced_string, "%2A", "*");

  return replaced_string;
}

void Worker::populateStore(std::vector<uint8_t> data_vector)
{
  // BOOST_ASSERT(m_store.empty());
  std::cerr << "Loading input ..." << std::endl;

  // std::vector<uint8_t> buffer(m_options.maxSegmentSize);
  std::vector<uint8_t> buffer(m_options.maxSegmentSize);
  int i = 0;
  while(m_options.maxSegmentSize * i < data_vector.size()) {
    // is.read(reinterpret_cast<char*>(buffer.data()), buffer.size());
    if(m_options.maxSegmentSize * (i + 1) < data_vector.size()) {
      std::copy(data_vector.begin() + m_options.maxSegmentSize * i,
                data_vector.begin() + m_options.maxSegmentSize * (i + 1), buffer.begin());
    } else {
      buffer.resize(data_vector.end() - (data_vector.begin() + m_options.maxSegmentSize * i));
      std::copy(data_vector.begin() + m_options.maxSegmentSize * i, data_vector.end(),
                buffer.begin());
    }
    const auto nCharsRead = buffer.size();
    std::cerr << nCharsRead << std::endl;

    if(nCharsRead > 0) {
      // ndn::Name interestname = interest.getName();
      auto data =
          ndn::make_shared<ndn::Data>(ndn::Name(m_versionedPrefix).appendSegment(m_store.size()));
      data->setFreshnessPeriod(10_s);
      data->setContent(buffer.data(), static_cast<size_t>(nCharsRead));
      m_store.push_back(data);
    }
    i++;
  }

  if(m_store.empty()) {
    // ndn::Name interestname = interest.getName();
    auto data = ndn::make_shared<ndn::Data>(ndn::Name(m_versionedPrefix).appendSegment(0));
    data->setFreshnessPeriod(10_s);
    m_store.push_back(data);
  }

  auto finalBlockId = ndn::Name::Component::fromSegment(m_store.size() - 1);
  for(const auto& data : m_store) {
    data->setFinalBlock(finalBlockId);
    // m_key_chain.sign(*data,m_options.signingInfo);
  }

  std::cerr << "Created " << m_store.size() << " chunks for prefix " << m_prefix << std::endl;
}

void Worker::processSegmentInterest(const ndn::Interest& interest)
{
  BOOST_ASSERT(m_store.size() > 0);

  // std::cerr << " Interest: " << interest << std::endl;

  const ndn::Name& name = interest.getName();
  // std::cout<< name[-1] << std::endl;
  ndn::shared_ptr<ndn::Data> data;

  if(name.size() == m_versionedPrefix.size() + 1 && name[-1].isSegment()) {
    const auto segmentNo = static_cast<size_t>(interest.getName()[-1].toSegment());
    // specific segment retrieval
    std::cerr << segmentNo << std::endl;
    if(segmentNo < m_store.size()) {
      data = m_store[segmentNo];
    }
  } else if(interest.matchesData(*m_store[0])) {
    std::cerr << "unspecified version or segment number, return first segment" << std::endl;
    data = m_store[0];
  }
  m_key_chain.sign(*data);
  if(data != nullptr) {
    std::cerr << "Data: " << *data << std::endl;
    m_ndn_face.put(*data);
  } else {
    std::cerr << "Interest cannot be satisfied, sending Nack" << std::endl;
    m_ndn_face.put(ndn::lp::Nack(interest));
  }
}

ext: cpp


text:
//
//  Rational.hpp
//
//
//  https://gist.github.com/sklaw/10473569
//

#ifndef F2_H
#define F2_H

#include <iostream>
#include <stdint.h>
#include <Eigen/Dense>
#include "../basic.h"

class F2 {
    friend F2 operator+(const F2& left, const F2& right);
    friend F2 operator-(const F2& left, const F2& right);
    friend F2 operator*(const F2& left, const F2& right);
    friend F2 operator/(const F2& left, const F2& right);
    friend bool operator==(const F2& left, const F2& right);
    friend bool operator!=(const F2& left, const F2& right);
    friend bool operator<(const F2& left, const F2& right);
    friend bool operator>(const F2& left, const F2& right);
    friend bool operator<=(const F2& left, const F2& right);
    friend bool operator>=(const F2& left, const F2& right);
    friend std::ostream& operator<<(std::ostream& out, const F2& obj);
    friend std::istream& operator>>(std::istream& in, F2& obj);

public:
    F2(): value(0) {}
    F2(int_t x): value(x) {}

    F2& operator=(const F2& obj);
    F2& operator+=(const F2& obj);
    F2& operator-=(const F2& obj);
    F2& operator*=(const F2& obj);
    F2& operator/=(const F2& obj);
    F2 operator+() const;
    F2 operator-() const;

    uint8_t getValue() const { return value; }

private:
    uint8_t value; // 0 or 1
};

namespace Eigen {
template<> struct NumTraits<F2>
 : NumTraits<int_t> // permits to get the epsilon, dummy_precision, lowest, highest functions
{
  typedef F2 Real;
  typedef F2 NonInteger;
  typedef F2 Nested;
  enum {
    IsComplex = 0,
    IsInteger = 0,
    IsSigned = 0,
    RequireInitialization = 0,
    ReadCost = 1,
    AddCost = 2,
    MulCost = 2
  };
    static inline Real epsilon() { return 0; }
    static inline Real dummy_precision() { return 0; }
};
}

inline F2 abs(const F2&  x)  { return x; }
inline F2 abs2(const F2& x)  { return x*x; }

#endif

ext: hpp


text:
#include "log.h"
//#include <boost/circular_buffer.hpp>
#include <iostream>
#include <fstream>
using namespace std;

//const int LOG_LENGTH = 256;
//const std::string LOG_FILE = "log.txt";

Log::Log()
{
    //m_cbErrors.set_capacity(LOG_LENGTH);
    //m_LogFile.open(LOG_FILE, ios_base::trunc);
    //ios::sync_with_stdio(false);
}

void Log::write(std::string s, Log::Level lev)
{
    auto l = lock();
    std::function<void()> after_unlock;
    {
        auto& th = this_log();

        if(th.m_SilenceFlags == Log::Silencer::ALL)
            return;

        if(!th.m_bCapture)
        {
            ostringstream line;
            if(lev == Log::LL_ERROR)
            {
                for(unsigned i=0;i<th.m_Indents;++i)
                    line << "  ";
                line << s;
                if(!(th.m_SilenceFlags & Log::Silencer::ERRORS))
                    cerr << line.str() << endl;
                if(th.m_bCapture)
                    th.m_Captured.push_back(line.str());
                th.on_log(s, lev);
                auto _this = this;
                after_unlock = [_this,s,lev]{
                    _this->on_log(s,lev);
                };
            }
            else
            {
                // apparently no capturing of non-error messages yet
                if(lev == LL_WARNING &&
                    (th.m_SilenceFlags & Log::Silencer::WARNINGS))
                    return;

                if(lev == LL_INFO &&
                    (th.m_SilenceFlags & Log::Silencer::INFO))
                    return;

                for(unsigned i=0;i<th.m_Indents;++i)
                    line << "  ";
                if(lev == LL_WARNING)
                    line << "[WARNING] ";
                line << s;
                cout << line.str() << endl;
                th.on_log(s, lev);
                auto _this = this;
                after_unlock = [_this,s,lev]{
                    _this->on_log(s,lev);
                };
            }
        }
    }

    if(after_unlock)
        after_unlock();

    //else if(lev == LL_DEBUG)
    //    cout << "[DEBUG] ";

    //if(m_LogFile.is_open())
    //{
    //    for(unsigned i=0;i<m_Indent;++i)
    //        m_LogFile << "  ";
    //    if(lev == LL_WARNING)
    //        m_LogFile << "[WARNING] ";
    //    else if(lev == LL_ERROR)
    //        m_LogFile << "[ERROR] ";
    //    //else if(lev == LL_DEBUG)
    //    //    m_LogFile << "[DEBUG] ";
    //    m_LogFile << s << endl;
    //}
}

std::string Log :: consume_prefix(std::string s)
{
    vector<string> lines_in;
    vector<string> lines_out;
    boost::split(lines_in, s, boost::is_any_of("\n"));
    for(auto&& line: lines_in)
    {
        if(boost::starts_with(line, "("))
        {
            const std::string end_prefix = "): ";
            auto itr = line.find(end_prefix);
            if(itr != string::npos)
                line = line.substr(itr + end_prefix.length());
        }
        lines_out.push_back(line);
    }
    return boost::join(lines_out, "\n");
}

max_stars_repo_name: flipcoder/kit

max_issues_repo_name: flipcoder/kit

max_forks_repo_name: flipcoder/kit


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

#include <chain.h>
#include <chainparams.h>
#include <config.h>
#include <pow.h>
#include <random.h>
<<<<<<< HEAD
=======
#include <util/system.h>

>>>>>>> c3f3e6af6... Move util files to directory
#include <test/test_bitcoin.h>
#include <util.h>

#include <boost/test/unit_test.hpp>

static CBlockIndex GetBlockIndex(CBlockIndex *pindexPrev, int64_t nTimeInterval,
                                 uint32_t nBits) {
    CBlockIndex block;
    block.pprev = pindexPrev;
    block.nHeight = pindexPrev->nHeight + 1;
    block.nTime = pindexPrev->nTime + nTimeInterval;
    block.nBits = nBits;

    block.nChainWork = pindexPrev->nChainWork + GetBlockProof(block);
    return block;
}

static void make_blocks(std::vector<CBlockIndex>& blocks, DummyConfig& config) {

  blocks.resize(100);
  const int interval = 2 * 60; // 2 minutes
  const Consensus::Params &params = config.GetChainParams().GetConsensus();
  const arith_uint256 powLimit = UintToArith256(params.powLimit);
  arith_uint256 currentPow = powLimit >> 1;
  uint32_t initialBits = currentPow.GetCompact();

  // Genesis block.
  const uint256 zeroHash = uint256();
  blocks[0] = CBlockIndex();
  blocks[0].nHeight = 0;
  blocks[0].nTime = 1269211443;
  blocks[0].nBits = initialBits;
  blocks[0].nChainWork = GetBlockProof(blocks[0]);
  blocks[0].phashBlock = &zeroHash;

  // Pile up some blocks.
  for (size_t i = 1; i < 100; i++) {
    blocks[i] = GetBlockIndex(&blocks[i - 1], interval, initialBits);
    blocks[i].phashBlock = &zeroHash;
  }
}

BOOST_FIXTURE_TEST_SUITE(pow_tests, BasicTestingSetup)

/* Test calculation of next difficulty target with no constraints applying */
BOOST_AUTO_TEST_CASE(get_next_work) {
    DummyConfig config(CBaseChainParams::MAIN);
    std::vector<CBlockIndex> blocks;
    make_blocks(blocks,config);
    CBlockHeader blkHeader = blocks[99].GetBlockHeader();
    BOOST_CHECK_EQUAL(GetNextWorkRequired(&blocks[99], &blkHeader, config), 0x1c7ffffe);
}

// TBD

/* Test the constraint on the upper bound for next work */
// BOOST_AUTO_TEST_CASE(get_next_work_pow_limit) {
/* Test the constraint on the lower bound for actual time taken */
// BOOST_AUTO_TEST_CASE(get_next_work_lower_limit_actual) {
/* Test the constraint on the upper bound for actual time taken */
// BOOST_AUTO_TEST_CASE(get_next_work_upper_limit_actual) {

BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test) {
    DummyConfig config(CBaseChainParams::MAIN);

    std::vector<CBlockIndex> blocks(10000);
    for (int i = 0; i < 10000; i++) {
        blocks[i].pprev = i ? &blocks[i - 1] : nullptr;
        blocks[i].nHeight = i;
        blocks[i].nTime =
            1269211443 +
            i * config.GetChainParams().GetConsensus().nPowTargetSpacing;
        blocks[i].nBits = 0x207fffff; /* target 0x7fffff000... */
        blocks[i].nChainWork =
            i ? blocks[i - 1].nChainWork + GetBlockProof(blocks[i])
              : arith_uint256(0);
    }

    for (int j = 0; j < 1000; j++) {
        CBlockIndex *p1 = &blocks[InsecureRandRange(10000)];
        CBlockIndex *p2 = &blocks[InsecureRandRange(10000)];
        CBlockIndex *p3 = &blocks[InsecureRandRange(10000)];

        int64_t tdiff = GetBlockProofEquivalentTime(
            *p1, *p2, *p3, config.GetChainParams().GetConsensus());
        BOOST_CHECK_EQUAL(tdiff, p1->GetBlockTime() - p2->GetBlockTime());
    }
}

BOOST_AUTO_TEST_CASE(retargeting_test) {
    DummyConfig config(CBaseChainParams::MAIN);

    const int long_interval = 24 * 60; // 24 minutes

    std::vector<CBlockIndex> blocks(200);

    const Consensus::Params &params = config.GetChainParams().GetConsensus();
    const arith_uint256 powLimit = UintToArith256(params.powLimit);
    arith_uint256 currentPow = powLimit >> 1;
    uint32_t initialBits = currentPow.GetCompact();

    // Genesis block.
    blocks[0] = CBlockIndex();
    blocks[0].nHeight = 0;
    blocks[0].nTime = 1269211443;
    blocks[0].nBits = initialBits;

    blocks[0].nChainWork = GetBlockProof(blocks[0]);

    // Pile up some blocks.
    for (size_t i = 1; i < 100; i++) {
        blocks[i] = GetBlockIndex(&blocks[i - 1], params.nPowTargetSpacing, initialBits);
    }

    CBlockHeader blkHeaderDummy;

    // Now we expect the difficulty to decrease.
    blocks[100] = GetBlockIndex(&blocks[99], long_interval, initialBits);
    BOOST_CHECK_EQUAL(GetNextWorkRequired(&blocks[100], &blkHeaderDummy, config), 0x1D009188);

    blocks[101] =  GetBlockIndex(&blocks[100], long_interval, currentPow.GetCompact());
    BOOST_CHECK_EQUAL(GetNextWorkRequired(&blocks[101], &blkHeaderDummy, config), 0x1D00A2D3);

    // Drop down to minimum difficulty
    for (int i=0; i<33; i++) {
      blocks[102+i] = GetBlockIndex(&blocks[101+i], long_interval, powLimit.GetCompact());
    }
    // Once we reached the minimal difficulty, we stick with it.
    for (int i=0; i<32; i++) {
      blocks[135+i] = GetBlockIndex(&blocks[134+i], long_interval, powLimit.GetCompact());
      BOOST_CHECK_EQUAL(GetNextWorkRequired(&blocks[135+i], &blkHeaderDummy, config),powLimit.GetCompact());
    }
}

BOOST_AUTO_TEST_SUITE_END()

max_stars_repo_name: ComputerCraftr/devault

max_issues_repo_name: ComputerCraftr/devault

max_forks_repo_name: ComputerCraftr/devault


text:
#include <albert/bt/torrent_resolver.hpp>

#include <cstddef>
#include <cstdint>

#include <memory>
#include <utility>

#include <boost/bind/bind.hpp>

#include <albert/bencode/bencoding.hpp>
#include <albert/bt/peer.hpp>
#include <albert/bt/peer_connection.hpp>
#include <albert/krpc/krpc.hpp>
#include <albert/log/log.hpp>
#include <albert/u160/u160.hpp>

namespace albert::bt {

std::mutex TorrentResolver::pointers_lock_;
std::set<TorrentResolver*> TorrentResolver::pointers_;

TorrentResolver::TorrentResolver(
    boost::asio::io_service &io,
    u160::U160 info_hash,
    u160::U160 self,
    uint32_t bind_ip,
    uint16_t bind_port,
    bool use_utp,
    std::chrono::high_resolution_clock::time_point expiration_at
    )
    :io_(io), timer_(io_), info_hash_(info_hash), self_(self), bind_ip_(bind_ip), bind_port_(bind_port), use_utp_(use_utp),
    has_metadata_(false), expiration_at_(expiration_at) {
  {
    std::unique_lock _(pointers_lock_);
    pointers_.insert(this);
  }

}

TorrentResolver::TorrentResolver(
    boost::asio::io_service &io,
    u160::U160 info_hash,
    u160::U160 self,
    uint32_t bind_ip,
    uint16_t bind_port,
    bool use_utp,
    const bencoding::DictNode &info)
    :io_(io), timer_(io_),
     info_hash_(info_hash), self_(self), bind_ip_(bind_ip), bind_port_(bind_port),
     use_utp_(use_utp), info_(info), has_metadata_(true) {
  {
    std::unique_lock _(pointers_lock_);
    pointers_.insert(this);
  }

  timer_.expires_at(boost::asio::chrono::steady_clock::now());
  timer_.async_wait(std::bind(&TorrentResolver::timer_handler, this, std::placeholders::_1));
}

TorrentResolver::~TorrentResolver() {
  {
    std::unique_lock _(pointers_lock_);
    pointers_.erase(this);
  }
  for (auto &pc : peer_connections_) {
    pc.second->close();
  }
}

void TorrentResolver::add_peer(uint32_t ip, uint16_t port) {
  using namespace std::placeholders;

  auto pc = albert::common::make_shared<peer::PeerConnection>(
      io_,
      self(),
      info_hash_,
      bind_ip_,
      bind_port_,
      ip,
      port,
      use_utp_);
  auto pc2 = pc;
  peer_connections_[{ip, port}] = pc;

  pc->connect(
      [weak_resolver = weak_from_this(), ip, port](const boost::system::error_code &e) {
        if (e) {
          auto resolver = weak_resolver.lock();
          if (resolver) {
            if (resolver->peer_connections_.find({ip, port}) != resolver->peer_connections_.end()) {
              auto &pc = resolver->peer_connections_.at({ip, port});
              std::string key = pc->failed_reason();
              resolver->deleted_peers_stat_[key]++;
              resolver->peer_connections_.erase({ip, port});
            }
          }
        }
      },
      [resolver_weak = weak_from_this(), pc_weak = albert::common::wp<peer::PeerConnection>(pc)](int total_pieces, size_t metadata_size) {
        auto resolver = resolver_weak.lock();
        if (resolver) {
          resolver->handshake_handler(pc_weak, total_pieces, metadata_size);
        }
      });
}

void TorrentResolver::piece_handler(wp<peer::PeerConnection> pc, int piece, const std::vector<uint8_t> &data) {
  if (piece >= 0 && piece < pieces_.size()) {
    if (pieces_[piece].empty()) {
      pieces_[piece] = data;
      LOG(info) << "TorrentResolver: " << info_hash_.to_string() << ", got piece " << piece
                << ", piece: " << pieces_got() << "/" << pieces_.size()
                << ", data: " << data_got() << "/" << metadata_size_;
    } else {
      LOG(info) << "already have piece " << piece << ", ignored";
    }
  } else {
    LOG(error) << "Invalid piece id " << piece << ", " << pieces_.size() << " in total";
  }
  if (finished()) {
    LOG(info) << "torrent finished " << info_hash_.to_string();
    auto info_data = merged_pieces();
    auto calculated_hash = u160::U160::hash(info_data.data(), info_data.size());
    if (calculated_hash == info_hash_) {
      std::stringstream ss(std::string((const char*)info_data.data(), info_data.size()));
      auto info = bencoding::Node::decode(ss);
      std::map<std::string, std::shared_ptr<bencoding::Node>> dict;
      dict["announce"] = std::make_shared<bencoding::DictNode>(std::map<std::string, std::shared_ptr<bencoding::Node>>());
      dict["info"] = info;
      bencoding::DictNode torrent(std::move(dict));
      if (torrent_handler_) {
        torrent_handler_(torrent);
        // TODO: this will cause SEGV?
//        torrent_handler_ = nullptr;
      }
    } else {
      LOG(error) << "hash of Torrent.info(" << calculated_hash.to_string() << ") not match info-hash(" << info_hash_.to_string() << ")";
    }
  }
}

u160::U160 TorrentResolver::self() const {
  return self_;
}

std::vector<uint8_t> TorrentResolver::merged_pieces() const {
  std::vector<uint8_t> ret(metadata_size_);
  auto it = ret.begin();
  for (const auto &piece : pieces_) {
    it = std::copy(piece.begin(), piece.end(), it);
  }
  assert(it == ret.end());
  return ret;
}
bool TorrentResolver::finished() const {
  return data_got() == metadata_size_;
}
void TorrentResolver::set_torrent_handler(std::function<void(const bencoding::DictNode &torrent)> handler) {
  torrent_handler_ = std::move(handler);
}
void TorrentResolver::handshake_handler(wp<peer::PeerConnection> weak_pc, int total_pieces, size_t metadata_size) {
  if (auto pc = weak_pc.lock(); !pc) {
    LOG(error) << "PeerConnection gone before handshake was handled info_hash: " << this->info_hash_.to_string();
  } else {
    if (total_pieces == 0) {
      LOG(error) << "Peer invalid data, total_pieces == 0, " << pc->peer().to_string();
      pc->close();
    }
    if (this->pieces_.size() == 0) {
      this->pieces_.resize(total_pieces);
      this->metadata_size_ = metadata_size;
    } else {
      if (pieces_.size() != total_pieces || metadata_size_ != metadata_size) {
        LOG(error) << "Peer total_pieces or metadata_size not matched, refusing: " << pc->peer().to_string();
        pc->close();
      }
    }
    pc->interest([weak_pc](){
      if (auto pc = weak_pc.lock(); pc) {
        LOG(info) << "TorrentResolver: peer unchoked " << pc->peer().to_string();
      }
    });
    using namespace boost::placeholders;
    pc->start_metadata_transfer(
        [weak_resolver = weak_from_this(), weak_pc](int piece, const std::vector<uint8_t> &data) {
          if (auto resolver = weak_resolver.lock(); resolver) {
            resolver->piece_handler(weak_pc, piece, data);
          }
        }
    );
  }
}
size_t TorrentResolver::pieces_got() const {
  size_t n = 0;
  for (auto &piece : pieces_) {
    if (!piece.empty())
      n++;
  }
  return n;
}
size_t TorrentResolver::data_got() const {
  size_t n = 0;
  for (auto &piece : pieces_) {
    n += piece.size();
  }
  return n;
}
size_t TorrentResolver::connected_peers() const {
  size_t ret = 0;
  for (auto &item : peer_connections_) {
    if (item.second->status() == peer::ConnectionStatus::Connected) {
      ret++;
    }
  }
  return ret;
}

void TorrentResolver::timer_handler(const boost::system::error_code &e) {
  if (e) {
    LOG(error) << "TorrentResolver timer_handler error";
    return;
  }

  std::list<std::tuple<uint32_t, uint16_t>> to_delete;
  for (auto &item : peer_connections_) {
    if (item.second->status() == peer::ConnectionStatus::Disconnected) {
      to_delete.push_back(item.first);
    }
  }
  if (to_delete.size() > 0) {
    LOG(info) << "TorrentResolver: deleted " << to_delete.size() << " failed connections";
  }
  for (auto &ep : to_delete) {
    auto &pc = peer_connections_.at(ep);
    std::string key = pc->failed_reason();
    deleted_peers_stat_[key]++;
    peer_connections_.erase(ep);
  }

  timer_.expires_at(boost::asio::chrono::steady_clock::now() + timer_interval_);
  timer_.async_wait(std::bind(&TorrentResolver::timer_handler, this, std::placeholders::_1));
}
size_t TorrentResolver::memory_size() const {
  auto ret = sizeof(*this);
  for (auto &pc : peer_connections_) {
    ret += sizeof(pc.first);
    ret += pc.second->memory_size();
  }
  for (auto &piece : pieces_) {
    ret += piece.size();
  }
  return ret;
}
std::map<std::string, size_t> TorrentResolver::peers_stat() const {
  std::map<std::string, size_t> ret = deleted_peers_stat_;
  for (auto &pc : peer_connections_) {
    if (pc.second->status() == peer::ConnectionStatus::Connecting) {
      ret["connecting"]++;
    } else if (pc.second->status() == peer::ConnectionStatus::Connected) {
      ret["connected"]++;
    } else /* disconnected */ {
      ret[pc.second->failed_reason()]++;
    }
  }
  return ret;
}

}


text:
/**
 *    Copyright (C) 2022-present MongoDB, Inc.
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the Server Side Public License, version 1,
 *    as published by MongoDB, Inc.
 *
 *    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
 *    Server Side Public License for more details.
 *
 *    You should have received a copy of the Server Side Public License
 *    along with this program. If not, see
 *    <http://www.mongodb.com/licensing/server-side-public-license>.
 *
 *    As a special exception, the copyright holders give permission to link the
 *    code of portions of this program with the OpenSSL library under certain
 *    conditions as described in each individual source file and distribute
 *    linked combinations including the program with the OpenSSL library. You
 *    must comply with the Server Side Public License in all respects for
 *    all of the code used other than as permitted herein. If you modify file(s)
 *    with this exception, you may extend this exception to your version of the
 *    file(s), but you are not obligated to do so. If you do not wish to do so,
 *    delete this exception statement from your version. If you delete this
 *    exception statement from all source files in the program, then also delete
 *    it in the license file.
 */

#include <boost/optional/optional_io.hpp>

#include "mongo/db/storage/historical_ident_tracker.h"
#include "mongo/unittest/unittest.h"

namespace mongo {

TEST(HistoricalIdentTracker, RecordHistoricalIdents) {
    HistoricalIdentTracker tracker;

    const std::string ident = "ident";
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.a"), UUID::gen(), Timestamp(10, 10));
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.b"), UUID::gen(), Timestamp(20, 20));
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.c"), UUID::gen(), Timestamp(21, 21));
    tracker.recordDrop(ident, /*nss=*/NamespaceString("test.d"), UUID::gen(), Timestamp(25, 25));

    ASSERT_EQ(tracker.lookup(ident, Timestamp::min())->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(9, 9))->first, NamespaceString("test.a"));

    ASSERT_EQ(tracker.lookup(ident, Timestamp(10, 10))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(15, 15))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(19, 19))->first, NamespaceString("test.b"));

    ASSERT_EQ(tracker.lookup(ident, Timestamp(20, 20))->first, NamespaceString("test.c"));

    ASSERT_EQ(tracker.lookup(ident, Timestamp(21, 21))->first, NamespaceString("test.d"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(24, 24))->first, NamespaceString("test.d"));

    ASSERT(!tracker.lookup(ident, Timestamp(25, 25)));
    ASSERT(!tracker.lookup(ident, Timestamp::max()));
}

TEST(HistoricalIdentTracker, SkipRecordingNullTimestamps) {
    HistoricalIdentTracker tracker;

    const std::string ident = "ident";
    tracker.recordRename(ident, /*oldNss=*/NamespaceString("test.a"), UUID::gen(), Timestamp(1));
    tracker.recordRename(ident, /*oldNss=*/NamespaceString("test.b"), UUID::gen(), Timestamp(2));
    tracker.recordDrop(ident, /*nss=*/NamespaceString("test.c"), UUID::gen(), Timestamp(3));

    ASSERT(!tracker.lookup(ident, Timestamp::min()));
    ASSERT(!tracker.lookup(ident, Timestamp(1)));
    ASSERT(!tracker.lookup(ident, Timestamp(2)));
    ASSERT(!tracker.lookup(ident, Timestamp(3)));
    ASSERT(!tracker.lookup(ident, Timestamp::max()));
}

TEST(HistoricalIdentTracker, RemoveEntriesOlderThanSingle) {
    HistoricalIdentTracker tracker;

    const std::string ident = "ident";
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.a"), UUID::gen(), Timestamp(50, 50));

    ASSERT_EQ(tracker.lookup(ident, Timestamp::min())->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(49, 49))->first, NamespaceString("test.a"));

    ASSERT(!tracker.lookup(ident, Timestamp(50, 50)));

    tracker.removeEntriesOlderThan(Timestamp::min());
    tracker.removeEntriesOlderThan(Timestamp(49, 49));

    ASSERT_EQ(tracker.lookup(ident, Timestamp::min())->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(49, 49))->first, NamespaceString("test.a"));

    tracker.removeEntriesOlderThan(Timestamp(50, 50));

    ASSERT(!tracker.lookup(ident, Timestamp::min()));
    ASSERT(!tracker.lookup(ident, Timestamp(49, 49)));
    ASSERT(!tracker.lookup(ident, Timestamp(50, 50)));
}

TEST(HistoricalIdentTracker, RemoveEntriesOlderThanMultiple) {
    HistoricalIdentTracker tracker;

    const std::string ident = "ident";
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.a"), UUID::gen(), Timestamp(10, 10));
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.b"), UUID::gen(), Timestamp(20, 20));
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.c"), UUID::gen(), Timestamp(21, 21));

    tracker.removeEntriesOlderThan(Timestamp::min());
    tracker.removeEntriesOlderThan(Timestamp(5, 5));
    tracker.removeEntriesOlderThan(Timestamp(9, 9));

    ASSERT_EQ(tracker.lookup(ident, Timestamp::min())->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(9, 9))->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(10, 10))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(19, 19))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(20, 20))->first, NamespaceString("test.c"));

    tracker.removeEntriesOlderThan(Timestamp(15, 15));

    ASSERT(!tracker.lookup(ident, Timestamp::min()));
    ASSERT(!tracker.lookup(ident, Timestamp(9, 9)));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(10, 10))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(19, 19))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(20, 20))->first, NamespaceString("test.c"));

    tracker.removeEntriesOlderThan(Timestamp(21, 21));

    ASSERT(!tracker.lookup(ident, Timestamp::min()));
    ASSERT(!tracker.lookup(ident, Timestamp(9, 9)));
    ASSERT(!tracker.lookup(ident, Timestamp(10, 10)));
    ASSERT(!tracker.lookup(ident, Timestamp(19, 19)));
    ASSERT(!tracker.lookup(ident, Timestamp(20, 20)));
    ASSERT(!tracker.lookup(ident, Timestamp::max()));
}

TEST(HistoricalIdentTracker, RollbackToSingle) {
    HistoricalIdentTracker tracker;

    const std::string ident = "ident";
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.a"), UUID::gen(), Timestamp(10, 10));

    tracker.rollbackTo(Timestamp(10, 10));
    tracker.rollbackTo(Timestamp::max());

    ASSERT_EQ(tracker.lookup(ident, Timestamp::min())->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(9, 9))->first, NamespaceString("test.a"));

    tracker.rollbackTo(Timestamp(9, 9));

    ASSERT(!tracker.lookup(ident, Timestamp::min()));
    ASSERT(!tracker.lookup(ident, Timestamp(9, 9)));
    ASSERT(!tracker.lookup(ident, Timestamp(10, 10)));
    ASSERT(!tracker.lookup(ident, Timestamp::max()));
}

TEST(HistoricalIdentTracker, RollbackToMultiple) {
    HistoricalIdentTracker tracker;

    const std::string ident = "ident";
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.a"), UUID::gen(), Timestamp(10, 10));
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.b"), UUID::gen(), Timestamp(20, 20));
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.c"), UUID::gen(), Timestamp(21, 21));

    tracker.rollbackTo(Timestamp::max());
    tracker.rollbackTo(Timestamp(22, 22));
    tracker.rollbackTo(Timestamp(21, 21));

    ASSERT_EQ(tracker.lookup(ident, Timestamp::min())->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(9, 9))->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(10, 10))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(19, 19))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(20, 20))->first, NamespaceString("test.c"));

    tracker.rollbackTo(Timestamp(15, 15));

    ASSERT_EQ(tracker.lookup(ident, Timestamp::min())->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(9, 9))->first, NamespaceString("test.a"));
    ASSERT(!tracker.lookup(ident, Timestamp(10, 10)));
    ASSERT(!tracker.lookup(ident, Timestamp(19, 19)));
    ASSERT(!tracker.lookup(ident, Timestamp(20, 20)));

    tracker.rollbackTo(Timestamp::min());

    ASSERT(!tracker.lookup(ident, Timestamp::min()));
    ASSERT(!tracker.lookup(ident, Timestamp(9, 9)));
    ASSERT(!tracker.lookup(ident, Timestamp(10, 10)));
    ASSERT(!tracker.lookup(ident, Timestamp(19, 19)));
    ASSERT(!tracker.lookup(ident, Timestamp(20, 20)));
    ASSERT(!tracker.lookup(ident, Timestamp::max()));
}

TEST(HistoricalIdentTracker, PinAndUnpinTimestamp) {
    HistoricalIdentTracker tracker;

    const std::string ident = "ident";
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.a"), UUID::gen(), Timestamp(10, 10));

    tracker.pinAtTimestamp(Timestamp(5, 5));
    tracker.removeEntriesOlderThan(Timestamp::max());

    ASSERT_EQ(tracker.lookup(ident, Timestamp::min())->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(9, 9))->first, NamespaceString("test.a"));

    tracker.pinAtTimestamp(Timestamp(9, 9));
    tracker.removeEntriesOlderThan(Timestamp::max());

    ASSERT_EQ(tracker.lookup(ident, Timestamp::min())->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(9, 9))->first, NamespaceString("test.a"));

    tracker.unpin();
    tracker.removeEntriesOlderThan(Timestamp::max());

    ASSERT(!tracker.lookup(ident, Timestamp::min()));
    ASSERT(!tracker.lookup(ident, Timestamp(9, 9)));
    ASSERT(!tracker.lookup(ident, Timestamp(10, 10)));
    ASSERT(!tracker.lookup(ident, Timestamp::max()));
}

TEST(HistoricalIdentTracker, PinnedTimestampRemoveEntriesOlderThan) {
    HistoricalIdentTracker tracker;

    const std::string ident = "ident";
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.a"), UUID::gen(), Timestamp(10, 10));
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.b"), UUID::gen(), Timestamp(20, 20));
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.c"), UUID::gen(), Timestamp(21, 21));

    tracker.pinAtTimestamp(Timestamp(5, 5));
    tracker.removeEntriesOlderThan(Timestamp::max());

    ASSERT_EQ(tracker.lookup(ident, Timestamp::min())->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(9, 9))->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(10, 10))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(19, 19))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(20, 20))->first, NamespaceString("test.c"));

    tracker.pinAtTimestamp(Timestamp(9, 9));
    tracker.removeEntriesOlderThan(Timestamp(9, 9));
    tracker.removeEntriesOlderThan(Timestamp(10, 10));
    tracker.removeEntriesOlderThan(Timestamp::max());

    ASSERT_EQ(tracker.lookup(ident, Timestamp::min())->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(9, 9))->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(10, 10))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(19, 19))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(20, 20))->first, NamespaceString("test.c"));

    tracker.pinAtTimestamp(Timestamp(15, 15));
    tracker.removeEntriesOlderThan(Timestamp::max());

    ASSERT(!tracker.lookup(ident, Timestamp::min()));
    ASSERT(!tracker.lookup(ident, Timestamp(9, 9)));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(10, 10))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(19, 19))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(20, 20))->first, NamespaceString("test.c"));

    tracker.pinAtTimestamp(Timestamp(21, 21));
    tracker.removeEntriesOlderThan(Timestamp::max());

    ASSERT(!tracker.lookup(ident, Timestamp::min()));
    ASSERT(!tracker.lookup(ident, Timestamp(9, 9)));
    ASSERT(!tracker.lookup(ident, Timestamp(10, 10)));
    ASSERT(!tracker.lookup(ident, Timestamp(19, 19)));
    ASSERT(!tracker.lookup(ident, Timestamp(20, 20)));
}

TEST(HistoricalIdentTracker, PinnedTimestampRollbackTo) {
    HistoricalIdentTracker tracker;

    const std::string ident = "ident";
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.a"), UUID::gen(), Timestamp(10, 10));
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.b"), UUID::gen(), Timestamp(20, 20));
    tracker.recordRename(
        ident, /*oldNss=*/NamespaceString("test.c"), UUID::gen(), Timestamp(21, 21));

    tracker.pinAtTimestamp(Timestamp(30, 30));
    tracker.rollbackTo(Timestamp::min());

    ASSERT_EQ(tracker.lookup(ident, Timestamp::min())->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(9, 9))->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(10, 10))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(19, 19))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(20, 20))->first, NamespaceString("test.c"));

    tracker.pinAtTimestamp(Timestamp(21, 21));
    tracker.rollbackTo(Timestamp::min());

    ASSERT_EQ(tracker.lookup(ident, Timestamp::min())->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(9, 9))->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(10, 10))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(19, 19))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(20, 20))->first, NamespaceString("test.c"));

    tracker.pinAtTimestamp(Timestamp(20, 20));
    tracker.rollbackTo(Timestamp::min());

    ASSERT_EQ(tracker.lookup(ident, Timestamp::min())->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(9, 9))->first, NamespaceString("test.a"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(10, 10))->first, NamespaceString("test.b"));
    ASSERT_EQ(tracker.lookup(ident, Timestamp(19, 19))->first, NamespaceString("test.b"));
    ASSERT(!tracker.lookup(ident, Timestamp(20, 20)));

    tracker.pinAtTimestamp(Timestamp(5, 5));
    tracker.rollbackTo(Timestamp::min());

    ASSERT(!tracker.lookup(ident, Timestamp::min()));
    ASSERT(!tracker.lookup(ident, Timestamp(9, 9)));
    ASSERT(!tracker.lookup(ident, Timestamp(10, 10)));
    ASSERT(!tracker.lookup(ident, Timestamp(19, 19)));
    ASSERT(!tracker.lookup(ident, Timestamp(20, 20)));
}

}  // namespace mongo

max_stars_repo_name: benety/mongo

max_issues_repo_name: benety/mongo

max_forks_repo_name: benety/mongo