Add mruset and use it for setInventoryKnown
This commit is contained in:
parent
fbbd42a535
commit
c4341fa6ab
4 changed files with 157 additions and 1 deletions
|
@ -118,6 +118,7 @@ HEADERS += src/qt/bitcoingui.h \
|
||||||
src/init.h \
|
src/init.h \
|
||||||
src/headers.h \
|
src/headers.h \
|
||||||
src/irc.h \
|
src/irc.h \
|
||||||
|
src/mruset.h \
|
||||||
src/json/json_spirit_writer_template.h \
|
src/json/json_spirit_writer_template.h \
|
||||||
src/json/json_spirit_writer.h \
|
src/json/json_spirit_writer.h \
|
||||||
src/json/json_spirit_value.h \
|
src/json/json_spirit_value.h \
|
||||||
|
|
63
src/mruset.h
Normal file
63
src/mruset.h
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright (c) 2012 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
#ifndef BITCOIN_MRUSET_H
|
||||||
|
#define BITCOIN_MRUSET_H
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
template <typename T> class mruset
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef T key_type;
|
||||||
|
typedef T value_type;
|
||||||
|
typedef typename std::set<T>::iterator iterator;
|
||||||
|
typedef typename std::set<T>::const_iterator const_iterator;
|
||||||
|
typedef typename std::set<T>::size_type size_type;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::set<T> set;
|
||||||
|
std::deque<T> queue;
|
||||||
|
size_type nMaxSize;
|
||||||
|
|
||||||
|
public:
|
||||||
|
mruset(size_type nMaxSizeIn = 0) { nMaxSize = nMaxSizeIn; }
|
||||||
|
iterator begin() const { return set.begin(); }
|
||||||
|
iterator end() const { return set.end(); }
|
||||||
|
size_type size() const { return set.size(); }
|
||||||
|
bool empty() const { return set.empty(); }
|
||||||
|
iterator find(const key_type& k) const { return set.find(k); }
|
||||||
|
size_type count(const key_type& k) const { return set.count(k); }
|
||||||
|
bool inline friend operator==(const mruset<T>& a, const mruset<T>& b) { return a.set == b.set; }
|
||||||
|
bool inline friend operator==(const mruset<T>& a, const std::set<T>& b) { return a.set == b; }
|
||||||
|
bool inline friend operator<(const mruset<T>& a, const mruset<T>& b) { return a.set < b.set; }
|
||||||
|
std::pair<iterator, bool> insert(const key_type& x)
|
||||||
|
{
|
||||||
|
std::pair<iterator, bool> ret = set.insert(x);
|
||||||
|
if (ret.second)
|
||||||
|
{
|
||||||
|
if (nMaxSize && queue.size() == nMaxSize)
|
||||||
|
{
|
||||||
|
set.erase(queue.front());
|
||||||
|
queue.pop_front();
|
||||||
|
}
|
||||||
|
queue.push_back(x);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
size_type max_size() const { return nMaxSize; }
|
||||||
|
size_type max_size(size_type s)
|
||||||
|
{
|
||||||
|
if (s)
|
||||||
|
while (queue.size() >= s)
|
||||||
|
{
|
||||||
|
set.erase(queue.front());
|
||||||
|
queue.pop_front();
|
||||||
|
}
|
||||||
|
nMaxSize = s;
|
||||||
|
return nMaxSize;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -14,6 +14,7 @@
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "mruset.h"
|
||||||
#include "netbase.h"
|
#include "netbase.h"
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
|
|
||||||
|
@ -154,7 +155,7 @@ public:
|
||||||
std::set<uint256> setKnown;
|
std::set<uint256> setKnown;
|
||||||
|
|
||||||
// inventory based relay
|
// inventory based relay
|
||||||
std::set<CInv> setInventoryKnown;
|
mruset<CInv> setInventoryKnown;
|
||||||
std::vector<CInv> vInventoryToSend;
|
std::vector<CInv> vInventoryToSend;
|
||||||
CCriticalSection cs_inventory;
|
CCriticalSection cs_inventory;
|
||||||
std::multimap<int64, CInv> mapAskFor;
|
std::multimap<int64, CInv> mapAskFor;
|
||||||
|
@ -193,6 +194,7 @@ public:
|
||||||
fGetAddr = false;
|
fGetAddr = false;
|
||||||
vfSubscribe.assign(256, false);
|
vfSubscribe.assign(256, false);
|
||||||
nMisbehavior = 0;
|
nMisbehavior = 0;
|
||||||
|
setInventoryKnown.max_size(SendBufferSize() / 1000);
|
||||||
|
|
||||||
// Be shy and don't send version until we hear
|
// Be shy and don't send version until we hear
|
||||||
if (!fInbound)
|
if (!fInbound)
|
||||||
|
|
90
src/test/mruset_tests.cpp
Normal file
90
src/test/mruset_tests.cpp
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include "mruset.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define NUM_TESTS 16
|
||||||
|
#define MAX_SIZE 100
|
||||||
|
|
||||||
|
class mrutester
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
mruset<int> mru;
|
||||||
|
std::set<int> set;
|
||||||
|
|
||||||
|
public:
|
||||||
|
mrutester() { mru.max_size(MAX_SIZE); }
|
||||||
|
int size() const { return set.size(); }
|
||||||
|
|
||||||
|
void insert(int n)
|
||||||
|
{
|
||||||
|
mru.insert(n);
|
||||||
|
set.insert(n);
|
||||||
|
BOOST_CHECK(mru == set);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE(mruset_tests)
|
||||||
|
|
||||||
|
// Test that an mruset behaves like a set, as long as no more than MAX_SIZE elements are in it
|
||||||
|
BOOST_AUTO_TEST_CASE(mruset_like_set)
|
||||||
|
{
|
||||||
|
|
||||||
|
for (int nTest=0; nTest<NUM_TESTS; nTest++)
|
||||||
|
{
|
||||||
|
mrutester tester;
|
||||||
|
while (tester.size() < MAX_SIZE)
|
||||||
|
tester.insert(GetRandInt(2 * MAX_SIZE));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that an mruset's size never exceeds its max_size
|
||||||
|
BOOST_AUTO_TEST_CASE(mruset_limited_size)
|
||||||
|
{
|
||||||
|
for (int nTest=0; nTest<NUM_TESTS; nTest++)
|
||||||
|
{
|
||||||
|
mruset<int> mru(MAX_SIZE);
|
||||||
|
for (int nAction=0; nAction<3*MAX_SIZE; nAction++)
|
||||||
|
{
|
||||||
|
int n = GetRandInt(2 * MAX_SIZE);
|
||||||
|
mru.insert(n);
|
||||||
|
BOOST_CHECK(mru.size() <= MAX_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 16-bit permutation function
|
||||||
|
int static permute(int n)
|
||||||
|
{
|
||||||
|
// hexadecimals of pi; verified to be linearly independent
|
||||||
|
static const int table[16] = {0x243F, 0x6A88, 0x85A3, 0x08D3, 0x1319, 0x8A2E, 0x0370, 0x7344,
|
||||||
|
0xA409, 0x3822, 0x299F, 0x31D0, 0x082E, 0xFA98, 0xEC4E, 0x6C89};
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
for (int bit=0; bit<16; bit++)
|
||||||
|
if (n & (1<<bit))
|
||||||
|
ret ^= table[bit];
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that an mruset acts like a moving window, if no duplcate elements are added
|
||||||
|
BOOST_AUTO_TEST_CASE(mruset_window)
|
||||||
|
{
|
||||||
|
mruset<int> mru(MAX_SIZE);
|
||||||
|
for (int n=0; n<10*MAX_SIZE; n++)
|
||||||
|
{
|
||||||
|
mru.insert(permute(n));
|
||||||
|
|
||||||
|
set<int> tester;
|
||||||
|
for (int m=max(0,n-MAX_SIZE+1); m<=n; m++)
|
||||||
|
tester.insert(permute(m));
|
||||||
|
|
||||||
|
BOOST_CHECK(mru == tester);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
Loading…
Reference in a new issue