From cff9f8ecc05e4f7fafd448f92d2752cc8047cbe4 Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 21 Sep 2026 20:23:01 -0400 Subject: [PATCH] split bucket --- libi2pd_client/TorrentsDHT.cpp | 98 ++++++++++++++++++++++++++++++---- libi2pd_client/TorrentsDHT.h | 30 +++++++++-- 2 files changed, 113 insertions(+), 15 deletions(-) diff --git a/libi2pd_client/TorrentsDHT.cpp b/libi2pd_client/TorrentsDHT.cpp index b8eddfc2..3f1e1888 100644 --- a/libi2pd_client/TorrentsDHT.cpp +++ b/libi2pd_client/TorrentsDHT.cpp @@ -17,24 +17,86 @@ namespace i2p { namespace torrents { + NodeID Bucket::GetMiddleID () const + { + NodeID middleID = start; + int bit = std::max (start.FindLowestBit (), next ? next->start.FindLowestBit () : -1) + 1; + middleID[bit >> 3] |= (0x80 >> (bit & 0x07)); + return middleID; + } + + void Bucket::Split () + { + auto middleID = GetMiddleID (); + auto newBucket = new Bucket (middleID); + newBucket->next = next; + next = newBucket; + // move some nodes + auto it = nodes.begin (); + while (it != nodes.end ()) + { + if ((*it)->id < middleID) + it++; // stay in old bucket + else + { + // move to new bucket + auto node = *it; + it = nodes.erase (it); + newBucket->nodes.push_back (node); + } + } + } RoutingTable::RoutingTable (const NodeID& ourNode): m_OurNode (ourNode) { - m_Buckets.push_back (std::make_shared ()); + m_Buckets = new Bucket; } - std::shared_ptr RoutingTable::FindBucket (const Torrent::InfoHash& id) const + RoutingTable::~RoutingTable () { - if (m_Buckets.empty ()) return nullptr; - auto it = m_Buckets.begin (); - while (std::next (it) != m_Buckets.end ()) + while (m_Buckets) { - if (id < (*std::next (it))->start) - return *it; - it++; + auto bucket = m_Buckets; + m_Buckets = m_Buckets->next; + delete bucket; + } + } + + Bucket * RoutingTable::FindBucket (const Torrent::InfoHash& id) const + { + if (!m_Buckets) return nullptr; + auto bucket = m_Buckets; + while (bucket->next) + { + if (id < bucket->next->start) + return bucket; + bucket = bucket->next; + } + return bucket; + } + + void RoutingTable::RemoveEmptyBuckers () + { + if (m_Buckets) + { + auto prev = m_Buckets, bucket = m_Buckets->next; + while (bucket) + { + if (bucket->nodes.empty ()) + { + prev->next = bucket->next; + auto tmp = bucket; + bucket = bucket->next; + delete tmp; + } + else + { + prev = bucket; + bucket = bucket->next; + } + } } - return *it; } std::shared_ptr RoutingTable::AddNode (const NodeID& id, i2p::data::IdentHash& peer, uint16_t port) @@ -42,8 +104,22 @@ namespace torrents if (id == m_OurNode) return nullptr; auto bucket = FindBucket (id); if (!bucket) return nullptr; - auto node = std::make_shared(id, peer, port); - bucket->nodes.emplace_back (node); + if (bucket->IsFull () && !bucket->IsInBucket (m_OurNode)) return nullptr; + + std::shared_ptr node; + do + { + bucket->Split (); + bucket = FindBucket (id); + } + while (bucket->IsFull ()); + + if (bucket) + { + node = std::make_shared(id, peer, port); + bucket->nodes.emplace_back (node); + } + RemoveEmptyBuckers (); return node; } diff --git a/libi2pd_client/TorrentsDHT.h b/libi2pd_client/TorrentsDHT.h index 8098efc0..eee4e3fb 100644 --- a/libi2pd_client/TorrentsDHT.h +++ b/libi2pd_client/TorrentsDHT.h @@ -31,13 +31,27 @@ namespace torrents struct NodeID: public Torrent::InfoHash { static constexpr size_t len = std::tuple_size::value; - constexpr Distance operator^(const Torrent::InfoHash& hash) + constexpr Distance operator^(const Torrent::InfoHash& hash) const { Distance d; for (size_t i = 0; i < size (); i++) d[i] = (*this)[i] ^ hash[i]; return d; } + constexpr int FindLowestBit () const // -1 in not found + { + for (int i = size () - 1; i >= 0; i--) + { + uint8_t byte = (*this)[i]; + if (byte) + { + for (int j = 7; j >= 0; j--) + if (byte & (0x80 >> j)) + return i*8 + j; + } + } + return -1; + } }; struct Node @@ -53,10 +67,16 @@ namespace torrents constexpr size_t MAX_BUCKET_CAPACITY = 8; struct Bucket { + Bucket * next; std::list > nodes; NodeID start; - Bucket (): start{} {} + Bucket (): next (nullptr), start{} {} + Bucket (const NodeID& start1): next (nullptr), start (start1) {} + bool IsFull () const { return nodes.size () >= MAX_BUCKET_CAPACITY; } + bool IsInBucket (const NodeID& id) const { return id >= start && (!next || id < next->start); } + NodeID GetMiddleID () const; + void Split (); }; class RoutingTable @@ -64,17 +84,19 @@ namespace torrents public: RoutingTable (const NodeID& ourNode); + ~RoutingTable (); std::shared_ptr AddNode (const NodeID& id, i2p::data::IdentHash& peer, uint16_t port); std::list, Distance> > FindClosestNodes (const Torrent::InfoHash& infoHash, size_t num) const; private: - std::shared_ptr FindBucket (const Torrent::InfoHash& id) const; + Bucket * FindBucket (const Torrent::InfoHash& id) const; + void RemoveEmptyBuckers (); private: - std::list > m_Buckets; + Bucket * m_Buckets; NodeID m_OurNode; };