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 1112 : Bucket::Bucket(const NodeId& id)
35 1112 : : lowerLimit_(id)
36 1112 : {}
37 :
38 : bool
39 1838 : Bucket::addNode(const std::shared_ptr<dhtnet::ChannelSocketInterface>& socket)
40 : {
41 1838 : return addNode(NodeInfo(socket));
42 : }
43 :
44 : bool
45 2248 : Bucket::addNode(NodeInfo&& info)
46 : {
47 2248 : auto nodeId = info.socket->deviceId();
48 2248 : if (nodes.try_emplace(nodeId, std::move(info)).second) {
49 2247 : connecting_nodes.erase(nodeId);
50 2247 : known_nodes.erase(nodeId);
51 2247 : mobile_nodes.erase(nodeId);
52 2247 : return true;
53 : }
54 1 : return false;
55 : }
56 :
57 : bool
58 1683 : Bucket::removeNode(const NodeId& nodeId)
59 : {
60 1683 : auto node = nodes.find(nodeId);
61 1688 : if (node == nodes.end())
62 3 : return false;
63 1685 : auto isMobile = node->second.isMobile_;
64 1684 : nodes.erase(node);
65 1683 : if (isMobile) {
66 118 : addMobileNode(nodeId);
67 : } else {
68 1565 : addKnownNode(nodeId);
69 : }
70 :
71 1678 : return true;
72 : }
73 :
74 : std::set<NodeId>
75 5657 : Bucket::getNodeIds() const
76 : {
77 5657 : std::set<NodeId> nodesId;
78 20281 : for (auto const& key : nodes)
79 14597 : nodesId.insert(key.first);
80 5651 : return nodesId;
81 0 : }
82 :
83 : bool
84 11465 : Bucket::hasNode(const NodeId& nodeId) const
85 : {
86 11465 : return nodes.find(nodeId) != nodes.end();
87 : }
88 :
89 : bool
90 4948 : Bucket::addKnownNode(const NodeId& nodeId)
91 : {
92 4948 : if (!hasNode(nodeId)) {
93 3493 : if (known_nodes.emplace(nodeId).second) {
94 3131 : return true;
95 : }
96 : }
97 1819 : return false;
98 : }
99 :
100 : NodeId
101 117 : Bucket::getKnownNode(unsigned index) const
102 : {
103 117 : if (index > known_nodes.size()) {
104 1 : throw std::out_of_range("End of table for get known Node Id " + std::to_string(index));
105 : }
106 116 : auto it = known_nodes.begin();
107 : std::advance(it, index);
108 :
109 232 : return *it;
110 : }
111 :
112 : bool
113 332 : Bucket::addMobileNode(const NodeId& nodeId)
114 : {
115 332 : if (!hasNode(nodeId)) {
116 327 : if (mobile_nodes.emplace(nodeId).second) {
117 273 : known_nodes.erase(nodeId);
118 273 : return true;
119 : }
120 : }
121 59 : return false;
122 : }
123 :
124 : bool
125 1873 : Bucket::addConnectingNode(const NodeId& nodeId)
126 : {
127 1873 : if (!hasNode(nodeId)) {
128 1870 : if (connecting_nodes.emplace(nodeId).second) {
129 1716 : known_nodes.erase(nodeId);
130 1720 : return true;
131 : }
132 : }
133 149 : return false;
134 : }
135 :
136 : std::set<NodeId>
137 2614 : Bucket::getKnownNodesRandom(unsigned numberNodes, std::mt19937_64& rd) const
138 : {
139 2614 : std::set<NodeId> nodesToReturn;
140 :
141 2625 : if (getKnownNodesSize() <= numberNodes)
142 2512 : return getKnownNodes();
143 :
144 109 : std::uniform_int_distribution<unsigned> distrib(0, getKnownNodesSize() - 1);
145 :
146 225 : while (nodesToReturn.size() < numberNodes) {
147 116 : nodesToReturn.emplace(getKnownNode(distrib(rd)));
148 : }
149 :
150 109 : return nodesToReturn;
151 2622 : }
152 :
153 : asio::steady_timer&
154 0 : Bucket::getNodeTimer(const std::shared_ptr<dhtnet::ChannelSocketInterface>& socket)
155 : {
156 0 : auto node = nodes.find(socket->deviceId());
157 0 : if (node == nodes.end()) {
158 0 : throw std::range_error("Unable to find timer " + socket->deviceId().toString());
159 : }
160 0 : return node->second.refresh_timer;
161 : }
162 :
163 : bool
164 29 : Bucket::shutdownNode(const NodeId& nodeId)
165 : {
166 29 : auto node = nodes.find(nodeId);
167 29 : if (node != nodes.end()) {
168 18 : auto& socket = node->second.socket;
169 18 : auto node = socket->deviceId();
170 36 : dht::ThreadPool::io().run([socket] { socket->shutdown(); });
171 18 : removeNode(node);
172 18 : return true;
173 : }
174 11 : return false;
175 : }
176 :
177 : void
178 1101 : Bucket::shutdownAllNodes()
179 : {
180 2099 : while (not nodes.empty()) {
181 998 : auto it = nodes.begin();
182 1612 : dht::ThreadPool::io().run([socket = it->second.socket] { socket->shutdown(); });
183 998 : auto nodeId = it->first;
184 998 : removeNode(nodeId);
185 : }
186 1101 : }
187 :
188 : void
189 0 : Bucket::printBucket(unsigned number) const
190 : {
191 0 : JAMI_ERROR("BUCKET Number: {:d}", number);
192 :
193 0 : unsigned nodeNum = 1;
194 0 : for (auto it = nodes.begin(); it != nodes.end(); ++it) {
195 0 : JAMI_DEBUG("Node {:s} Id: {:s} isMobile: {:s}",
196 : std::to_string(nodeNum),
197 : it->first.toString(),
198 : std::to_string(it->second.isMobile_));
199 0 : nodeNum++;
200 : }
201 0 : JAMI_ERROR("Mobile Nodes");
202 0 : nodeNum = 0;
203 0 : for (auto it = mobile_nodes.begin(); it != mobile_nodes.end(); ++it) {
204 0 : JAMI_DEBUG("Node {:s} Id: {:s}", std::to_string(nodeNum), (*it).toString());
205 0 : nodeNum++;
206 : }
207 :
208 0 : JAMI_ERROR("Known Nodes");
209 0 : nodeNum = 0;
210 0 : for (auto it = known_nodes.begin(); it != known_nodes.end(); ++it) {
211 0 : JAMI_DEBUG("Node {:s} Id: {:s}", std::to_string(nodeNum), (*it).toString());
212 0 : nodeNum++;
213 : }
214 0 : JAMI_ERROR("Connecting_nodes");
215 0 : nodeNum = 0;
216 0 : for (auto it = connecting_nodes.begin(); it != connecting_nodes.end(); ++it) {
217 0 : JAMI_DEBUG("Node {:s} Id: {:s}", std::to_string(nodeNum), (*it).toString());
218 0 : nodeNum++;
219 : }
220 0 : };
221 :
222 : void
223 232 : Bucket::changeMobility(const NodeId& nodeId, bool isMobile)
224 : {
225 232 : auto itn = nodes.find(nodeId);
226 233 : if (itn != nodes.end()) {
227 232 : itn->second.isMobile_ = isMobile;
228 : }
229 233 : }
230 :
231 : // For tests
232 :
233 : std::set<std::shared_ptr<dhtnet::ChannelSocketInterface>>
234 1 : Bucket::getNodeSockets() const
235 : {
236 1 : std::set<std::shared_ptr<dhtnet::ChannelSocketInterface>> sockets;
237 3 : for (auto const& info : nodes)
238 2 : sockets.insert(info.second.socket);
239 1 : return sockets;
240 0 : }
241 :
242 : // ####################################################################################################
243 :
244 708 : RoutingTable::RoutingTable()
245 : {
246 708 : buckets.emplace_back(NodeId::zero());
247 708 : }
248 :
249 : bool
250 5697 : RoutingTable::isEmpty() const
251 : {
252 7954 : for (const auto& bucket : buckets) {
253 5868 : if (!bucket.isEmpty()) {
254 3612 : return false;
255 : }
256 : }
257 2086 : return true;
258 : }
259 :
260 : bool
261 174 : RoutingTable::addNode(const std::shared_ptr<dhtnet::ChannelSocketInterface>& socket)
262 : {
263 174 : auto bucket = findBucket(socket->deviceId());
264 348 : return addNode(socket, bucket);
265 : }
266 :
267 : bool
268 2814 : RoutingTable::addNode(const std::shared_ptr<dhtnet::ChannelSocketInterface>& channel,
269 : std::list<Bucket>::iterator& bucket)
270 : {
271 2814 : NodeId nodeId = channel->deviceId();
272 :
273 2814 : if (bucket->hasNode(nodeId) || id_ == nodeId) {
274 1007 : return false;
275 : }
276 :
277 2207 : while (bucket->isFull()) {
278 577 : if (contains(bucket, id_)) {
279 402 : split(bucket);
280 402 : bucket = findBucket(nodeId);
281 :
282 : } else {
283 175 : return bucket->addNode(std::move(channel));
284 : }
285 : }
286 1630 : return bucket->addNode(std::move(channel));
287 : }
288 :
289 : bool
290 669 : RoutingTable::removeNode(const NodeId& nodeId)
291 : {
292 669 : return findBucket(nodeId)->removeNode(nodeId);
293 : }
294 :
295 : bool
296 1490 : RoutingTable::hasNode(const NodeId& nodeId)
297 : {
298 1490 : return findBucket(nodeId)->hasNode(nodeId);
299 : }
300 :
301 : bool
302 3698 : RoutingTable::addKnownNode(const NodeId& nodeId)
303 : {
304 3698 : if (id_ == nodeId)
305 518 : return false;
306 :
307 3173 : auto bucket = findBucket(nodeId);
308 3182 : if (bucket == buckets.end())
309 0 : return false;
310 :
311 3183 : return bucket->addKnownNode(nodeId);
312 : }
313 :
314 : bool
315 216 : RoutingTable::addMobileNode(const NodeId& nodeId)
316 : {
317 216 : if (id_ == nodeId)
318 2 : return false;
319 :
320 214 : auto bucket = findBucket(nodeId);
321 :
322 214 : if (bucket == buckets.end())
323 0 : return false;
324 :
325 214 : return bucket->addMobileNode(nodeId);
326 : }
327 :
328 : void
329 4 : RoutingTable::removeMobileNode(const NodeId& nodeId)
330 : {
331 4 : return findBucket(nodeId)->removeMobileNode(nodeId);
332 : }
333 :
334 : bool
335 12 : RoutingTable::hasMobileNode(const NodeId& nodeId)
336 : {
337 12 : return findBucket(nodeId)->hasMobileNode(nodeId);
338 : };
339 :
340 : bool
341 1653 : RoutingTable::addConnectingNode(const NodeId& nodeId)
342 : {
343 1653 : if (id_ == nodeId)
344 1 : return false;
345 :
346 1650 : auto bucket = findBucket(nodeId);
347 :
348 1651 : if (bucket == buckets.end())
349 0 : return 0;
350 :
351 1651 : bucket->addConnectingNode(nodeId);
352 1647 : return 1;
353 : }
354 :
355 : void
356 0 : RoutingTable::removeConnectingNode(const NodeId& nodeId)
357 : {
358 0 : findBucket(nodeId)->removeConnectingNode(nodeId);
359 0 : }
360 :
361 : std::list<Bucket>::iterator
362 15649 : RoutingTable::findBucket(const NodeId& nodeId)
363 : {
364 15649 : if (buckets.empty())
365 0 : throw std::runtime_error("No bucket");
366 :
367 15641 : auto b = buckets.begin();
368 :
369 : while (true) {
370 29874 : auto next = std::next(b);
371 29874 : if (next == buckets.end())
372 15668 : return b;
373 21669 : if (std::memcmp(nodeId.data(), next->getLowerLimit().data(), nodeId.size()) < 0)
374 7453 : return b;
375 14235 : b = next;
376 14235 : }
377 : }
378 :
379 : std::vector<NodeId>
380 1763 : RoutingTable::closestNodes(const NodeId& nodeId, unsigned count) const
381 : {
382 1763 : std::vector<NodeId> closestNodes;
383 1763 : auto bucket = findBucket(nodeId);
384 5114 : auto sortedBucketInsert = [&](const std::list<Bucket>::const_iterator& b) {
385 5114 : auto nodes = b->getNodeIds();
386 18792 : for (auto n : nodes) {
387 13684 : if (n != nodeId) {
388 12177 : auto here = std::find_if(closestNodes.begin(), closestNodes.end(), [&nodeId, &n](NodeId& NodeId) {
389 69218 : return nodeId.xorCmp(n, NodeId) < 0;
390 : });
391 :
392 12189 : closestNodes.insert(here, n);
393 : }
394 : }
395 5109 : };
396 :
397 1771 : auto itn = bucket;
398 2596 : auto itp = (bucket == buckets.begin()) ? buckets.end() : std::prev(bucket);
399 7699 : while (itn != buckets.end() || itp != buckets.end()) {
400 4156 : if (itn != buckets.end()) {
401 3498 : sortedBucketInsert(itn);
402 3501 : itn = std::next(itn);
403 : }
404 4158 : if (itp != buckets.end()) {
405 1619 : sortedBucketInsert(itp);
406 2412 : itp = (itp == buckets.begin()) ? buckets.end() : std::prev(itp);
407 : }
408 : }
409 :
410 1773 : if (closestNodes.size() > count) {
411 1147 : closestNodes.resize(count);
412 : }
413 :
414 3544 : return closestNodes;
415 0 : }
416 :
417 : void
418 0 : RoutingTable::printRoutingTable() const
419 : {
420 0 : int counter = 1;
421 0 : JAMI_DEBUG("SWARM: {:s} ", id_.toString());
422 0 : for (auto it = buckets.begin(); it != buckets.end(); ++it) {
423 0 : it->printBucket(counter);
424 0 : counter++;
425 : }
426 0 : JAMI_DEBUG("_____________________________________________________________________________");
427 0 : }
428 :
429 : void
430 2 : RoutingTable::shutdownNode(const NodeId& nodeId)
431 : {
432 2 : findBucket(nodeId)->shutdownNode(nodeId);
433 2 : }
434 :
435 : std::vector<NodeId>
436 144 : RoutingTable::getNodes() const
437 : {
438 144 : std::lock_guard lock(mutex_);
439 144 : std::vector<NodeId> ret;
440 664 : for (const auto& b : buckets) {
441 520 : const auto& nodes = b.getNodeIds();
442 520 : ret.insert(ret.end(), nodes.begin(), nodes.end());
443 520 : }
444 288 : return ret;
445 144 : }
446 :
447 : std::vector<NodeId>
448 1 : RoutingTable::getKnownNodes() const
449 : {
450 1 : std::vector<NodeId> ret;
451 2 : for (const auto& b : buckets) {
452 1 : const auto& nodes = b.getKnownNodes();
453 1 : ret.insert(ret.end(), nodes.begin(), nodes.end());
454 : }
455 1 : return ret;
456 0 : }
457 :
458 : std::vector<NodeId>
459 1868 : RoutingTable::getMobileNodes() const
460 : {
461 1868 : std::vector<NodeId> ret;
462 6053 : for (const auto& b : buckets) {
463 4187 : const auto& nodes = b.getMobileNodes();
464 4187 : ret.insert(ret.end(), nodes.begin(), nodes.end());
465 : }
466 1868 : return ret;
467 0 : }
468 :
469 : std::vector<NodeId>
470 1865 : RoutingTable::getMobileNodesToNotify() const
471 : {
472 1865 : std::vector<NodeId> ret;
473 2118 : for (const auto& mobile : getMobileNodes()) {
474 253 : const auto closest = closestNodes(mobile, MOBILE_WAKE_REDUNDANCY);
475 253 : if (closest.size() < MOBILE_WAKE_REDUNDANCY || mobile.xorCmp(id_, closest.back()) < 0)
476 109 : ret.emplace_back(mobile);
477 2118 : }
478 1864 : return ret;
479 0 : }
480 :
481 : std::vector<NodeId>
482 669 : RoutingTable::getKnownMobileNodes() const
483 : {
484 669 : std::vector<NodeId> ret;
485 2165 : for (const auto& b : buckets) {
486 1503 : const auto& nodes = b.getMobileNodes();
487 1505 : ret.insert(ret.end(), nodes.begin(), nodes.end());
488 3997 : for (const auto& [nodeId, info] : b.getNodes())
489 2493 : if (info.isMobile_)
490 777 : ret.emplace_back(nodeId);
491 : }
492 669 : return ret;
493 0 : }
494 :
495 : std::vector<NodeId>
496 1 : RoutingTable::getConnectingNodes() const
497 : {
498 1 : std::vector<NodeId> ret;
499 2 : for (const auto& b : buckets) {
500 1 : const auto& nodes = b.getConnectingNodes();
501 1 : ret.insert(ret.end(), nodes.begin(), nodes.end());
502 : }
503 1 : return ret;
504 0 : }
505 :
506 : std::vector<NodeId>
507 0 : RoutingTable::getBucketMobileNodes() const
508 : {
509 0 : std::vector<NodeId> ret;
510 0 : auto bucket = findBucket(id_);
511 0 : const auto& nodes = bucket->getMobileNodes();
512 0 : ret.insert(ret.end(), nodes.begin(), nodes.end());
513 :
514 0 : return ret;
515 0 : }
516 :
517 : bool
518 6198 : RoutingTable::contains(const std::list<Bucket>::iterator& bucket, const NodeId& nodeId) const
519 : {
520 6198 : return NodeId::cmp(bucket->getLowerLimit(), nodeId) <= 0
521 14968 : && (std::next(bucket) == buckets.end() || NodeId::cmp(nodeId, std::next(bucket)->getLowerLimit()) < 0);
522 : }
523 :
524 : std::vector<NodeId>
525 20 : RoutingTable::getAllNodes() const
526 : {
527 20 : std::vector<NodeId> ret;
528 20 : std::set<NodeId> addedNodes;
529 80 : auto appendUnique = [&ret, &addedNodes](const auto& nodes) {
530 98 : for (const auto& node : nodes)
531 18 : if (addedNodes.emplace(node).second)
532 17 : ret.emplace_back(node);
533 80 : };
534 40 : for (const auto& b : buckets) {
535 20 : const auto& nodes = b.getNodeIds();
536 20 : const auto& knownNodes = b.getKnownNodes();
537 20 : const auto& mobileNodes = b.getMobileNodes();
538 20 : const auto& connectingNodes = b.getConnectingNodes();
539 20 : ret.reserve(ret.size() + nodes.size() + knownNodes.size() + mobileNodes.size() + connectingNodes.size());
540 20 : appendUnique(nodes);
541 20 : appendUnique(knownNodes);
542 20 : appendUnique(mobileNodes);
543 20 : appendUnique(connectingNodes);
544 20 : }
545 40 : return ret;
546 20 : }
547 :
548 : std::vector<NodeId>
549 1834 : RoutingTable::getConnectedNodes() const
550 : {
551 1834 : std::vector<NodeId> ret;
552 5892 : for (const auto& b : buckets) {
553 4059 : const auto& nodes = b.getNodes();
554 4060 : ret.reserve(ret.size() + nodes.size());
555 15689 : for (const auto& n : nodes)
556 11634 : ret.emplace_back(n.first);
557 : }
558 1834 : return ret;
559 0 : }
560 :
561 : void
562 26 : RoutingTable::deleteNode(const NodeId& nodeId)
563 : {
564 26 : auto bucket = findBucket(nodeId);
565 26 : bucket->shutdownNode(nodeId);
566 26 : bucket->removeConnectingNode(nodeId);
567 26 : bucket->removeKnownNode(nodeId);
568 26 : bucket->removeMobileNode(nodeId);
569 26 : }
570 :
571 : inline std::chrono::system_clock::time_point
572 0 : systemTimeFromSteady(std::chrono::steady_clock::time_point t,
573 : const std::chrono::steady_clock::time_point& now,
574 : const std::chrono::system_clock::time_point& nowSystem)
575 : {
576 0 : return nowSystem + std::chrono::duration_cast<std::chrono::system_clock::duration>(t - now);
577 : }
578 :
579 : std::vector<RoutingTable::NodeStats>
580 2 : RoutingTable::getRoutingTableStats() const
581 : {
582 2 : std::vector<NodeStats> stats;
583 2 : auto now = std::chrono::steady_clock::now();
584 2 : auto nowSystem = std::chrono::system_clock::now();
585 2 : std::lock_guard lock(mutex_);
586 8 : for (const auto& bucket : buckets) {
587 11 : for (const auto& [id, info] : bucket.getNodes()) {
588 5 : if (auto channel = std::dynamic_pointer_cast<dhtnet::ChannelSocket>(info.socket)) {
589 0 : stats.push_back({id.toString(),
590 : "connected",
591 0 : channel->getRemoteAddress().toString(true),
592 0 : systemTimeFromSteady(channel->getStartTime(), now, nowSystem),
593 0 : info.isMobile_});
594 : } else {
595 5 : stats.push_back(
596 5 : {id.toString(), "connected", "", std::chrono::system_clock::time_point::min(), info.isMobile_});
597 5 : }
598 : }
599 6 : for (const auto& id : bucket.getKnownNodes()) {
600 0 : stats.push_back({id.toString(), "known", "", std::chrono::system_clock::time_point::min(), false});
601 : }
602 10 : for (const auto& id : bucket.getMobileNodes()) {
603 4 : if (!bucket.hasConnectingNode(id))
604 3 : stats.push_back({id.toString(), "mobile", "", std::chrono::system_clock::time_point::min(), true});
605 : }
606 7 : for (const auto& id : bucket.getConnectingNodes()) {
607 1 : stats.push_back({id.toString(),
608 : "connecting",
609 : "",
610 : std::chrono::system_clock::time_point::min(),
611 2 : bucket.hasMobileNode(id)});
612 : }
613 : }
614 4 : return stats;
615 38 : }
616 :
617 : NodeId
618 402 : RoutingTable::middle(std::list<Bucket>::iterator& it) const
619 : {
620 402 : unsigned bit = depth(it);
621 402 : if (bit >= 8 * HASH_LEN)
622 0 : throw std::out_of_range("End of table");
623 :
624 402 : NodeId id = it->getLowerLimit();
625 402 : id.setBit(bit, true);
626 402 : return id;
627 : }
628 :
629 : unsigned
630 402 : RoutingTable::depth(std::list<Bucket>::iterator& bucket) const
631 : {
632 402 : int bit1 = bucket->getLowerLimit().lowbit();
633 1001 : int bit2 = std::next(bucket) != buckets.end() ? std::next(bucket)->getLowerLimit().lowbit() : -1;
634 402 : return std::max(bit1, bit2) + 1;
635 : }
636 :
637 : bool
638 402 : RoutingTable::split(std::list<Bucket>::iterator& bucket)
639 : {
640 402 : NodeId id = middle(bucket);
641 804 : auto newBucketIt = buckets.emplace(std::next(bucket), id);
642 : // Re-assign nodes
643 402 : auto& nodeSwap = bucket->getNodes();
644 :
645 1206 : for (auto it = nodeSwap.begin(); it != nodeSwap.end();) {
646 804 : auto& node = *it;
647 :
648 804 : auto nodeId = it->first;
649 :
650 804 : if (!contains(bucket, nodeId)) {
651 409 : newBucketIt->addNode(std::move(node.second));
652 409 : it = nodeSwap.erase(it);
653 : } else {
654 395 : ++it;
655 : }
656 : }
657 :
658 402 : auto connectingSwap = bucket->getConnectingNodes();
659 817 : for (auto it = connectingSwap.begin(); it != connectingSwap.end();) {
660 415 : auto nodeId = *it;
661 :
662 415 : if (!contains(bucket, nodeId)) {
663 219 : newBucketIt->addConnectingNode(nodeId);
664 219 : it = connectingSwap.erase(it);
665 219 : bucket->removeConnectingNode(nodeId);
666 : } else {
667 196 : ++it;
668 : }
669 : }
670 :
671 402 : auto knownSwap = bucket->getKnownNodes();
672 600 : for (auto it = knownSwap.begin(); it != knownSwap.end();) {
673 198 : auto nodeId = *it;
674 :
675 198 : if (!contains(bucket, nodeId)) {
676 98 : newBucketIt->addKnownNode(nodeId);
677 98 : it = knownSwap.erase(it);
678 98 : bucket->removeKnownNode(nodeId);
679 : } else {
680 100 : ++it;
681 : }
682 : }
683 :
684 402 : auto mobileSwap = bucket->getMobileNodes();
685 405 : for (auto it = mobileSwap.begin(); it != mobileSwap.end();) {
686 3 : auto nodeId = *it;
687 :
688 3 : if (!contains(bucket, nodeId)) {
689 0 : newBucketIt->addMobileNode(nodeId);
690 0 : it = mobileSwap.erase(it);
691 0 : bucket->removeMobileNode(nodeId);
692 : } else {
693 3 : ++it;
694 : }
695 : }
696 :
697 402 : return true;
698 402 : }
699 :
700 : } // namespace jami
|