Line data Source code
1 : /*
2 : * Copyright (C) 2004-2026 Savoir-faire Linux Inc.
3 : *
4 : * This program is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "routing_table.h"
19 :
20 : #include <dhtnet/multiplexed_socket.h>
21 : #include <opendht/infohash.h>
22 : #include <opendht/thread_pool.h>
23 :
24 : #include <math.h>
25 : #include <iterator>
26 : #include <stdlib.h>
27 :
28 : using namespace std::placeholders;
29 :
30 : namespace jami {
31 :
32 : using namespace dht;
33 :
34 1185 : Bucket::Bucket(const NodeId& id)
35 1185 : : lowerLimit_(id)
36 1185 : {}
37 :
38 : bool
39 1816 : Bucket::addNode(const std::shared_ptr<dhtnet::ChannelSocketInterface>& socket)
40 : {
41 1816 : return addNode(NodeInfo(socket));
42 : }
43 :
44 : bool
45 2191 : Bucket::addNode(NodeInfo&& info)
46 : {
47 2191 : auto nodeId = info.socket->deviceId();
48 2191 : info.isMobile_ |= hasMobileNode(nodeId);
49 2190 : if (nodes.try_emplace(nodeId, std::move(info)).second) {
50 2188 : connecting_nodes.erase(nodeId);
51 2189 : known_nodes.erase(nodeId);
52 2189 : mobile_nodes.erase(nodeId);
53 2189 : return true;
54 : }
55 1 : return false;
56 : }
57 :
58 : bool
59 1666 : Bucket::removeNode(const NodeId& nodeId)
60 : {
61 1666 : auto node = nodes.find(nodeId);
62 1666 : if (node == nodes.end())
63 3 : return false;
64 1663 : auto isMobile = node->second.isMobile_;
65 1662 : nodes.erase(node);
66 1658 : if (isMobile) {
67 125 : addMobileNode(nodeId);
68 : } else {
69 1533 : addKnownNode(nodeId);
70 : }
71 :
72 1662 : return true;
73 : }
74 :
75 : std::set<NodeId>
76 5331 : Bucket::getNodeIds() const
77 : {
78 5331 : std::set<NodeId> nodesId;
79 19089 : for (auto const& key : nodes)
80 13758 : nodesId.insert(key.first);
81 5327 : return nodesId;
82 0 : }
83 :
84 : bool
85 10555 : Bucket::hasNode(const NodeId& nodeId) const
86 : {
87 10555 : return nodes.find(nodeId) != nodes.end();
88 : }
89 :
90 : bool
91 4789 : Bucket::addKnownNode(const NodeId& nodeId)
92 : {
93 4789 : if (!hasNode(nodeId) && !hasMobileNode(nodeId)) {
94 3382 : if (known_nodes.emplace(nodeId).second) {
95 3050 : return true;
96 : }
97 : }
98 1747 : return false;
99 : }
100 :
101 : NodeId
102 129 : Bucket::getKnownNode(unsigned index) const
103 : {
104 129 : if (index > known_nodes.size()) {
105 1 : throw std::out_of_range("End of table for get known Node Id " + std::to_string(index));
106 : }
107 128 : auto it = known_nodes.begin();
108 : std::advance(it, index);
109 :
110 256 : return *it;
111 : }
112 :
113 : bool
114 343 : Bucket::addMobileNode(const NodeId& nodeId)
115 : {
116 343 : bool changed = known_nodes.erase(nodeId) != 0;
117 344 : if (auto connected = nodes.find(nodeId); connected != nodes.end()) {
118 6 : changed |= mobile_nodes.erase(nodeId) != 0;
119 6 : changed |= !connected->second.isMobile_;
120 6 : connected->second.isMobile_ = true;
121 : } else {
122 338 : changed |= mobile_nodes.emplace(nodeId).second;
123 : }
124 343 : return changed;
125 : }
126 :
127 : bool
128 1775 : Bucket::addConnectingNode(const NodeId& nodeId)
129 : {
130 1775 : if (!hasNode(nodeId)) {
131 1775 : if (connecting_nodes.emplace(nodeId).second) {
132 1648 : known_nodes.erase(nodeId);
133 1646 : return true;
134 : }
135 : }
136 130 : return false;
137 : }
138 :
139 : std::set<NodeId>
140 2357 : Bucket::getKnownNodesRandom(unsigned numberNodes, std::mt19937_64& rd) const
141 : {
142 2357 : std::set<NodeId> nodesToReturn;
143 :
144 2355 : if (getKnownNodesSize() <= numberNodes)
145 2233 : return getKnownNodes();
146 :
147 124 : std::uniform_int_distribution<unsigned> distrib(0, getKnownNodesSize() - 1);
148 :
149 252 : while (nodesToReturn.size() < numberNodes) {
150 128 : nodesToReturn.emplace(getKnownNode(distrib(rd)));
151 : }
152 :
153 124 : return nodesToReturn;
154 2356 : }
155 :
156 : asio::steady_timer&
157 0 : Bucket::getNodeTimer(const std::shared_ptr<dhtnet::ChannelSocketInterface>& socket)
158 : {
159 0 : auto node = nodes.find(socket->deviceId());
160 0 : if (node == nodes.end()) {
161 0 : throw std::range_error("Unable to find timer " + socket->deviceId().toString());
162 : }
163 0 : return node->second.refresh_timer;
164 : }
165 :
166 : bool
167 28 : Bucket::shutdownNode(const NodeId& nodeId)
168 : {
169 28 : auto node = nodes.find(nodeId);
170 28 : if (node != nodes.end()) {
171 14 : auto& socket = node->second.socket;
172 14 : auto node = socket->deviceId();
173 28 : dht::ThreadPool::io().run([socket] { socket->shutdown(); });
174 14 : removeNode(node);
175 14 : return true;
176 : }
177 14 : return false;
178 : }
179 :
180 : void
181 1179 : Bucket::shutdownAllNodes()
182 : {
183 2183 : while (not nodes.empty()) {
184 1004 : auto it = nodes.begin();
185 1636 : dht::ThreadPool::io().run([socket = it->second.socket] { socket->shutdown(); });
186 1004 : auto nodeId = it->first;
187 1004 : removeNode(nodeId);
188 : }
189 1179 : }
190 :
191 : void
192 0 : Bucket::printBucket(unsigned number) const
193 : {
194 0 : JAMI_ERROR("BUCKET Number: {:d}", number);
195 :
196 0 : unsigned nodeNum = 1;
197 0 : for (auto it = nodes.begin(); it != nodes.end(); ++it) {
198 0 : JAMI_DEBUG("Node {:s} Id: {:s} isMobile: {:s}",
199 : std::to_string(nodeNum),
200 : it->first.toString(),
201 : std::to_string(it->second.isMobile_));
202 0 : nodeNum++;
203 : }
204 0 : JAMI_ERROR("Mobile Nodes");
205 0 : nodeNum = 0;
206 0 : for (auto it = mobile_nodes.begin(); it != mobile_nodes.end(); ++it) {
207 0 : JAMI_DEBUG("Node {:s} Id: {:s}", std::to_string(nodeNum), (*it).toString());
208 0 : nodeNum++;
209 : }
210 :
211 0 : JAMI_ERROR("Known Nodes");
212 0 : nodeNum = 0;
213 0 : for (auto it = known_nodes.begin(); it != known_nodes.end(); ++it) {
214 0 : JAMI_DEBUG("Node {:s} Id: {:s}", std::to_string(nodeNum), (*it).toString());
215 0 : nodeNum++;
216 : }
217 0 : JAMI_ERROR("Connecting_nodes");
218 0 : nodeNum = 0;
219 0 : for (auto it = connecting_nodes.begin(); it != connecting_nodes.end(); ++it) {
220 0 : JAMI_DEBUG("Node {:s} Id: {:s}", std::to_string(nodeNum), (*it).toString());
221 0 : nodeNum++;
222 : }
223 0 : };
224 :
225 : void
226 239 : Bucket::changeMobility(const NodeId& nodeId, bool isMobile)
227 : {
228 239 : auto itn = nodes.find(nodeId);
229 241 : if (itn != nodes.end()) {
230 240 : itn->second.isMobile_ = isMobile;
231 : }
232 241 : }
233 :
234 : // For tests
235 :
236 : std::set<std::shared_ptr<dhtnet::ChannelSocketInterface>>
237 1 : Bucket::getNodeSockets() const
238 : {
239 1 : std::set<std::shared_ptr<dhtnet::ChannelSocketInterface>> sockets;
240 3 : for (auto const& info : nodes)
241 2 : sockets.insert(info.second.socket);
242 1 : return sockets;
243 0 : }
244 :
245 : // ####################################################################################################
246 :
247 771 : RoutingTable::RoutingTable()
248 : {
249 771 : buckets.emplace_back(NodeId::zero());
250 771 : }
251 :
252 : bool
253 5771 : RoutingTable::isEmpty() const
254 : {
255 8198 : for (const auto& bucket : buckets) {
256 5828 : if (!bucket.isEmpty()) {
257 3402 : return false;
258 : }
259 : }
260 2369 : return true;
261 : }
262 :
263 : bool
264 175 : RoutingTable::addNode(const std::shared_ptr<dhtnet::ChannelSocketInterface>& socket)
265 : {
266 175 : auto bucket = findBucket(socket->deviceId());
267 350 : return addNode(socket, bucket);
268 : }
269 :
270 : bool
271 2646 : RoutingTable::addNode(const std::shared_ptr<dhtnet::ChannelSocketInterface>& channel,
272 : std::list<Bucket>::iterator& bucket)
273 : {
274 2646 : NodeId nodeId = channel->deviceId();
275 :
276 2646 : if (bucket->hasNode(nodeId) || id_ == nodeId) {
277 863 : return false;
278 : }
279 :
280 2196 : while (bucket->isFull()) {
281 590 : if (contains(bucket, id_)) {
282 412 : split(bucket);
283 412 : bucket = findBucket(nodeId);
284 :
285 : } else {
286 178 : return bucket->addNode(std::move(channel));
287 : }
288 : }
289 1606 : return bucket->addNode(std::move(channel));
290 : }
291 :
292 : bool
293 644 : RoutingTable::removeNode(const NodeId& nodeId)
294 : {
295 644 : return findBucket(nodeId)->removeNode(nodeId);
296 : }
297 :
298 : bool
299 1340 : RoutingTable::hasNode(const NodeId& nodeId)
300 : {
301 1340 : return findBucket(nodeId)->hasNode(nodeId);
302 : }
303 :
304 : bool
305 3695 : RoutingTable::addKnownNode(const NodeId& nodeId)
306 : {
307 3695 : if (id_ == nodeId)
308 618 : return false;
309 :
310 3074 : auto bucket = findBucket(nodeId);
311 3074 : if (bucket == buckets.end())
312 0 : return false;
313 :
314 3075 : return bucket->addKnownNode(nodeId);
315 : }
316 :
317 : bool
318 219 : RoutingTable::addMobileNode(const NodeId& nodeId)
319 : {
320 219 : if (id_ == nodeId)
321 2 : return false;
322 :
323 217 : auto bucket = findBucket(nodeId);
324 :
325 217 : if (bucket == buckets.end())
326 0 : return false;
327 :
328 217 : return bucket->addMobileNode(nodeId);
329 : }
330 :
331 : void
332 4 : RoutingTable::removeMobileNode(const NodeId& nodeId)
333 : {
334 4 : return findBucket(nodeId)->removeMobileNode(nodeId);
335 : }
336 :
337 : bool
338 14 : RoutingTable::hasMobileNode(const NodeId& nodeId)
339 : {
340 14 : return findBucket(nodeId)->hasMobileNode(nodeId);
341 : };
342 :
343 : bool
344 1590 : RoutingTable::addConnectingNode(const NodeId& nodeId)
345 : {
346 1590 : if (id_ == nodeId)
347 1 : return false;
348 :
349 1587 : auto bucket = findBucket(nodeId);
350 :
351 1587 : if (bucket == buckets.end())
352 0 : return 0;
353 :
354 1588 : bucket->addConnectingNode(nodeId);
355 1588 : return 1;
356 : }
357 :
358 : void
359 0 : RoutingTable::removeConnectingNode(const NodeId& nodeId)
360 : {
361 0 : findBucket(nodeId)->removeConnectingNode(nodeId);
362 0 : }
363 :
364 : std::list<Bucket>::iterator
365 15111 : RoutingTable::findBucket(const NodeId& nodeId)
366 : {
367 15111 : if (buckets.empty())
368 0 : throw std::runtime_error("No bucket");
369 :
370 15103 : auto b = buckets.begin();
371 :
372 : while (true) {
373 26063 : auto next = std::next(b);
374 26063 : if (next == buckets.end())
375 15120 : return b;
376 18614 : if (std::memcmp(nodeId.data(), next->getLowerLimit().data(), nodeId.size()) < 0)
377 7661 : return b;
378 10982 : b = next;
379 10982 : }
380 : }
381 :
382 : std::vector<NodeId>
383 1742 : RoutingTable::closestNodes(const NodeId& nodeId, unsigned count) const
384 : {
385 1742 : std::vector<NodeId> closestNodes;
386 1742 : auto bucket = findBucket(nodeId);
387 4777 : auto sortedBucketInsert = [&](const std::list<Bucket>::const_iterator& b) {
388 4777 : auto nodes = b->getNodeIds();
389 17554 : for (auto n : nodes) {
390 12802 : if (n != nodeId) {
391 11321 : auto here = std::find_if(closestNodes.begin(), closestNodes.end(), [&nodeId, &n](NodeId& NodeId) {
392 63756 : return nodeId.xorCmp(n, NodeId) < 0;
393 : });
394 :
395 11331 : closestNodes.insert(here, n);
396 : }
397 : }
398 4760 : };
399 :
400 1743 : auto itn = bucket;
401 2421 : auto itp = (bucket == buckets.begin()) ? buckets.end() : std::prev(bucket);
402 7418 : while (itn != buckets.end() || itp != buckets.end()) {
403 3929 : if (itn != buckets.end()) {
404 3526 : sortedBucketInsert(itn);
405 3526 : itn = std::next(itn);
406 : }
407 3928 : if (itp != buckets.end()) {
408 1253 : sortedBucketInsert(itp);
409 1828 : itp = (itp == buckets.begin()) ? buckets.end() : std::prev(itp);
410 : }
411 : }
412 :
413 1744 : if (closestNodes.size() > count) {
414 1097 : closestNodes.resize(count);
415 : }
416 :
417 3492 : return closestNodes;
418 0 : }
419 :
420 : void
421 0 : RoutingTable::printRoutingTable() const
422 : {
423 0 : int counter = 1;
424 0 : JAMI_DEBUG("SWARM: {:s} ", id_.toString());
425 0 : for (auto it = buckets.begin(); it != buckets.end(); ++it) {
426 0 : it->printBucket(counter);
427 0 : counter++;
428 : }
429 0 : JAMI_DEBUG("_____________________________________________________________________________");
430 0 : }
431 :
432 : void
433 2 : RoutingTable::shutdownNode(const NodeId& nodeId)
434 : {
435 2 : findBucket(nodeId)->shutdownNode(nodeId);
436 2 : }
437 :
438 : std::vector<NodeId>
439 147 : RoutingTable::getNodes() const
440 : {
441 147 : std::lock_guard lock(mutex_);
442 147 : std::vector<NodeId> ret;
443 683 : for (const auto& b : buckets) {
444 536 : const auto& nodes = b.getNodeIds();
445 536 : ret.insert(ret.end(), nodes.begin(), nodes.end());
446 536 : }
447 294 : return ret;
448 147 : }
449 :
450 : std::vector<NodeId>
451 1 : RoutingTable::getKnownNodes() const
452 : {
453 1 : std::vector<NodeId> ret;
454 2 : for (const auto& b : buckets) {
455 1 : const auto& nodes = b.getKnownNodes();
456 1 : ret.insert(ret.end(), nodes.begin(), nodes.end());
457 : }
458 1 : return ret;
459 0 : }
460 :
461 : std::vector<NodeId>
462 1870 : RoutingTable::getMobileNodes() const
463 : {
464 1870 : std::vector<NodeId> ret;
465 5957 : for (const auto& b : buckets) {
466 4088 : const auto& nodes = b.getMobileNodes();
467 4088 : ret.insert(ret.end(), nodes.begin(), nodes.end());
468 : }
469 1869 : return ret;
470 0 : }
471 :
472 : std::vector<NodeId>
473 1867 : RoutingTable::getMobileNodesToNotify() const
474 : {
475 1867 : std::vector<NodeId> ret;
476 2115 : for (const auto& mobile : getMobileNodes()) {
477 248 : const auto closest = closestNodes(mobile, MOBILE_WAKE_REDUNDANCY);
478 248 : if (closest.size() < MOBILE_WAKE_REDUNDANCY || mobile.xorCmp(id_, closest.back()) < 0)
479 91 : ret.emplace_back(mobile);
480 2114 : }
481 1866 : return ret;
482 0 : }
483 :
484 : std::vector<NodeId>
485 688 : RoutingTable::getKnownMobileNodes() const
486 : {
487 688 : std::vector<NodeId> ret;
488 2227 : for (const auto& b : buckets) {
489 1545 : const auto& nodes = b.getMobileNodes();
490 1546 : ret.insert(ret.end(), nodes.begin(), nodes.end());
491 4196 : for (const auto& [nodeId, info] : b.getNodes())
492 2652 : if (info.isMobile_)
493 806 : ret.emplace_back(nodeId);
494 : }
495 692 : return ret;
496 0 : }
497 :
498 : std::vector<NodeId>
499 1 : RoutingTable::getConnectingNodes() const
500 : {
501 1 : std::vector<NodeId> ret;
502 2 : for (const auto& b : buckets) {
503 1 : const auto& nodes = b.getConnectingNodes();
504 1 : ret.insert(ret.end(), nodes.begin(), nodes.end());
505 : }
506 1 : return ret;
507 0 : }
508 :
509 : std::vector<NodeId>
510 0 : RoutingTable::getBucketMobileNodes() const
511 : {
512 0 : std::vector<NodeId> ret;
513 0 : auto bucket = findBucket(id_);
514 0 : const auto& nodes = bucket->getMobileNodes();
515 0 : ret.insert(ret.end(), nodes.begin(), nodes.end());
516 :
517 0 : return ret;
518 0 : }
519 :
520 : bool
521 6012 : RoutingTable::contains(const std::list<Bucket>::iterator& bucket, const NodeId& nodeId) const
522 : {
523 6012 : return NodeId::cmp(bucket->getLowerLimit(), nodeId) <= 0
524 13957 : && (std::next(bucket) == buckets.end() || NodeId::cmp(nodeId, std::next(bucket)->getLowerLimit()) < 0);
525 : }
526 :
527 : std::vector<NodeId>
528 20 : RoutingTable::getAllNodes() const
529 : {
530 20 : std::vector<NodeId> ret;
531 20 : std::set<NodeId> addedNodes;
532 80 : auto appendUnique = [&ret, &addedNodes](const auto& nodes) {
533 92 : for (const auto& node : nodes)
534 12 : if (addedNodes.emplace(node).second)
535 11 : ret.emplace_back(node);
536 80 : };
537 40 : for (const auto& b : buckets) {
538 20 : const auto& nodes = b.getNodeIds();
539 20 : const auto& knownNodes = b.getKnownNodes();
540 20 : const auto& mobileNodes = b.getMobileNodes();
541 20 : const auto& connectingNodes = b.getConnectingNodes();
542 20 : ret.reserve(ret.size() + nodes.size() + knownNodes.size() + mobileNodes.size() + connectingNodes.size());
543 20 : appendUnique(nodes);
544 20 : appendUnique(knownNodes);
545 20 : appendUnique(mobileNodes);
546 20 : appendUnique(connectingNodes);
547 20 : }
548 40 : return ret;
549 20 : }
550 :
551 : std::vector<NodeId>
552 1839 : RoutingTable::getConnectedNodes() const
553 : {
554 1839 : std::vector<NodeId> ret;
555 5820 : for (const auto& b : buckets) {
556 3982 : const auto& nodes = b.getNodes();
557 3983 : ret.reserve(ret.size() + nodes.size());
558 14670 : for (const auto& n : nodes)
559 10688 : ret.emplace_back(n.first);
560 : }
561 1839 : return ret;
562 0 : }
563 :
564 : void
565 25 : RoutingTable::deleteNode(const NodeId& nodeId)
566 : {
567 25 : auto bucket = findBucket(nodeId);
568 25 : bucket->shutdownNode(nodeId);
569 25 : bucket->removeConnectingNode(nodeId);
570 25 : bucket->removeKnownNode(nodeId);
571 25 : bucket->removeMobileNode(nodeId);
572 25 : }
573 :
574 : inline std::chrono::system_clock::time_point
575 0 : systemTimeFromSteady(std::chrono::steady_clock::time_point t,
576 : const std::chrono::steady_clock::time_point& now,
577 : const std::chrono::system_clock::time_point& nowSystem)
578 : {
579 0 : return nowSystem + std::chrono::duration_cast<std::chrono::system_clock::duration>(t - now);
580 : }
581 :
582 : std::vector<RoutingTable::NodeStats>
583 2 : RoutingTable::getRoutingTableStats() const
584 : {
585 2 : std::vector<NodeStats> stats;
586 2 : auto now = std::chrono::steady_clock::now();
587 2 : auto nowSystem = std::chrono::system_clock::now();
588 2 : std::lock_guard lock(mutex_);
589 8 : for (const auto& bucket : buckets) {
590 11 : for (const auto& [id, info] : bucket.getNodes()) {
591 5 : if (auto channel = std::dynamic_pointer_cast<dhtnet::ChannelSocket>(info.socket)) {
592 0 : stats.push_back({id.toString(),
593 : "connected",
594 0 : channel->getRemoteAddress().toString(true),
595 0 : systemTimeFromSteady(channel->getStartTime(), now, nowSystem),
596 0 : info.isMobile_});
597 : } else {
598 5 : stats.push_back(
599 5 : {id.toString(), "connected", "", std::chrono::system_clock::time_point::min(), info.isMobile_});
600 5 : }
601 : }
602 6 : for (const auto& id : bucket.getKnownNodes()) {
603 0 : stats.push_back({id.toString(), "known", "", std::chrono::system_clock::time_point::min(), false});
604 : }
605 10 : for (const auto& id : bucket.getMobileNodes()) {
606 4 : if (!bucket.hasConnectingNode(id))
607 3 : stats.push_back({id.toString(), "mobile", "", std::chrono::system_clock::time_point::min(), true});
608 : }
609 7 : for (const auto& id : bucket.getConnectingNodes()) {
610 1 : stats.push_back({id.toString(),
611 : "connecting",
612 : "",
613 : std::chrono::system_clock::time_point::min(),
614 2 : bucket.hasMobileNode(id)});
615 : }
616 : }
617 4 : return stats;
618 38 : }
619 :
620 : NodeId
621 412 : RoutingTable::middle(std::list<Bucket>::iterator& it) const
622 : {
623 412 : unsigned bit = depth(it);
624 412 : if (bit >= 8 * HASH_LEN)
625 0 : throw std::out_of_range("End of table");
626 :
627 412 : NodeId id = it->getLowerLimit();
628 412 : id.setBit(bit, true);
629 412 : return id;
630 : }
631 :
632 : unsigned
633 412 : RoutingTable::depth(std::list<Bucket>::iterator& bucket) const
634 : {
635 412 : int bit1 = bucket->getLowerLimit().lowbit();
636 1035 : int bit2 = std::next(bucket) != buckets.end() ? std::next(bucket)->getLowerLimit().lowbit() : -1;
637 412 : return std::max(bit1, bit2) + 1;
638 : }
639 :
640 : bool
641 412 : RoutingTable::split(std::list<Bucket>::iterator& bucket)
642 : {
643 412 : NodeId id = middle(bucket);
644 824 : auto newBucketIt = buckets.emplace(std::next(bucket), id);
645 : // Re-assign nodes
646 412 : auto& nodeSwap = bucket->getNodes();
647 :
648 1236 : for (auto it = nodeSwap.begin(); it != nodeSwap.end();) {
649 824 : auto& node = *it;
650 :
651 824 : auto nodeId = it->first;
652 :
653 824 : if (!contains(bucket, nodeId)) {
654 374 : newBucketIt->addNode(std::move(node.second));
655 374 : it = nodeSwap.erase(it);
656 : } else {
657 450 : ++it;
658 : }
659 : }
660 :
661 412 : auto connectingSwap = bucket->getConnectingNodes();
662 813 : for (auto it = connectingSwap.begin(); it != connectingSwap.end();) {
663 401 : auto nodeId = *it;
664 :
665 401 : if (!contains(bucket, nodeId)) {
666 189 : newBucketIt->addConnectingNode(nodeId);
667 189 : it = connectingSwap.erase(it);
668 189 : bucket->removeConnectingNode(nodeId);
669 : } else {
670 212 : ++it;
671 : }
672 : }
673 :
674 412 : auto knownSwap = bucket->getKnownNodes();
675 600 : for (auto it = knownSwap.begin(); it != knownSwap.end();) {
676 188 : auto nodeId = *it;
677 :
678 188 : if (!contains(bucket, nodeId)) {
679 69 : newBucketIt->addKnownNode(nodeId);
680 69 : it = knownSwap.erase(it);
681 69 : bucket->removeKnownNode(nodeId);
682 : } else {
683 119 : ++it;
684 : }
685 : }
686 :
687 412 : auto mobileSwap = bucket->getMobileNodes();
688 415 : for (auto it = mobileSwap.begin(); it != mobileSwap.end();) {
689 3 : auto nodeId = *it;
690 :
691 3 : if (!contains(bucket, nodeId)) {
692 0 : newBucketIt->addMobileNode(nodeId);
693 0 : it = mobileSwap.erase(it);
694 0 : bucket->removeMobileNode(nodeId);
695 : } else {
696 3 : ++it;
697 : }
698 : }
699 :
700 412 : return true;
701 412 : }
702 :
703 : } // namespace jami
|