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 1592 : Bucket::Bucket(const NodeId& id)
35 1592 : : lowerLimit_(id)
36 1592 : {}
37 :
38 : bool
39 2360 : Bucket::addNode(const std::shared_ptr<dhtnet::ChannelSocketInterface>& socket)
40 : {
41 2360 : return addNode(NodeInfo(socket));
42 : }
43 :
44 : bool
45 2733 : Bucket::addNode(NodeInfo&& info)
46 : {
47 2733 : auto nodeId = info.socket->deviceId();
48 2733 : info.isMobile_ |= hasMobileNode(nodeId);
49 2733 : if (nodes.try_emplace(nodeId, std::move(info)).second) {
50 2732 : connecting_nodes.erase(nodeId);
51 2732 : known_nodes.erase(nodeId);
52 2732 : mobile_nodes.erase(nodeId);
53 2732 : return true;
54 : }
55 1 : return false;
56 : }
57 :
58 : bool
59 2209 : Bucket::removeNode(const NodeId& nodeId)
60 : {
61 2209 : auto node = nodes.find(nodeId);
62 2209 : if (node == nodes.end())
63 3 : return false;
64 2206 : auto isMobile = node->second.isMobile_;
65 2206 : nodes.erase(node);
66 2206 : if (isMobile) {
67 135 : addMobileNode(nodeId);
68 : } else {
69 2071 : addKnownNode(nodeId);
70 : }
71 :
72 2206 : return true;
73 : }
74 :
75 : std::set<NodeId>
76 5803 : Bucket::getNodeIds() const
77 : {
78 5803 : std::set<NodeId> nodesId;
79 20669 : for (auto const& key : nodes)
80 14866 : nodesId.insert(key.first);
81 5803 : return nodesId;
82 0 : }
83 :
84 : bool
85 14062 : Bucket::hasNode(const NodeId& nodeId) const
86 : {
87 14062 : return nodes.find(nodeId) != nodes.end();
88 : }
89 :
90 : bool
91 5903 : Bucket::addKnownNode(const NodeId& nodeId)
92 : {
93 5903 : if (!hasNode(nodeId) && !hasMobileNode(nodeId)) {
94 4450 : if (known_nodes.emplace(nodeId).second) {
95 4050 : return true;
96 : }
97 : }
98 1853 : return false;
99 : }
100 :
101 : NodeId
102 98 : Bucket::getKnownNode(unsigned index) const
103 : {
104 98 : 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 97 : auto it = known_nodes.begin();
108 : std::advance(it, index);
109 :
110 194 : return *it;
111 : }
112 :
113 : bool
114 441 : Bucket::addMobileNode(const NodeId& nodeId)
115 : {
116 441 : bool changed = known_nodes.erase(nodeId) != 0;
117 441 : if (auto connected = nodes.find(nodeId); connected != nodes.end()) {
118 5 : changed |= mobile_nodes.erase(nodeId) != 0;
119 5 : changed |= !connected->second.isMobile_;
120 5 : connected->second.isMobile_ = true;
121 : } else {
122 436 : changed |= mobile_nodes.emplace(nodeId).second;
123 : }
124 441 : return changed;
125 : }
126 :
127 : bool
128 2559 : Bucket::addConnectingNode(const NodeId& nodeId)
129 : {
130 2559 : if (!hasNode(nodeId)) {
131 2559 : if (connecting_nodes.emplace(nodeId).second) {
132 2389 : known_nodes.erase(nodeId);
133 2389 : return true;
134 : }
135 : }
136 170 : return false;
137 : }
138 :
139 : std::set<NodeId>
140 5246 : Bucket::getKnownNodesRandom(unsigned numberNodes, std::mt19937_64& rd) const
141 : {
142 5246 : std::set<NodeId> nodesToReturn;
143 :
144 5246 : if (getKnownNodesSize() <= numberNodes)
145 5150 : return getKnownNodes();
146 :
147 96 : std::uniform_int_distribution<unsigned> distrib(0, getKnownNodesSize() - 1);
148 :
149 193 : while (nodesToReturn.size() < numberNodes) {
150 97 : nodesToReturn.emplace(getKnownNode(distrib(rd)));
151 : }
152 :
153 96 : return nodesToReturn;
154 5246 : }
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 22 : auto& socket = node->second.socket;
172 22 : auto node = socket->deviceId();
173 44 : dht::ThreadPool::io().run([socket] { socket->shutdown(); });
174 22 : removeNode(node);
175 22 : return true;
176 : }
177 6 : return false;
178 : }
179 :
180 : void
181 1623 : Bucket::shutdownAllNodes()
182 : {
183 2831 : while (not nodes.empty()) {
184 1208 : auto it = nodes.begin();
185 2036 : dht::ThreadPool::io().run([socket = it->second.socket] { socket->shutdown(); });
186 1208 : auto nodeId = it->first;
187 1208 : removeNode(nodeId);
188 : }
189 1623 : }
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 258 : Bucket::changeMobility(const NodeId& nodeId, bool isMobile)
227 : {
228 258 : auto itn = nodes.find(nodeId);
229 258 : if (itn != nodes.end()) {
230 252 : itn->second.isMobile_ = isMobile;
231 : }
232 258 : }
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 1196 : RoutingTable::RoutingTable()
248 : {
249 1196 : buckets.emplace_back(NodeId::zero());
250 1196 : }
251 :
252 : bool
253 12714 : RoutingTable::isEmpty() const
254 : {
255 18828 : for (const auto& bucket : buckets) {
256 12887 : if (!bucket.isEmpty()) {
257 6773 : return false;
258 : }
259 : }
260 5941 : 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 3703 : RoutingTable::addNode(const std::shared_ptr<dhtnet::ChannelSocketInterface>& channel,
272 : std::list<Bucket>::iterator& bucket)
273 : {
274 3703 : NodeId nodeId = channel->deviceId();
275 :
276 3703 : if (bucket->hasNode(nodeId) || id_ == nodeId) {
277 1375 : return false;
278 : }
279 :
280 2722 : while (bucket->isFull()) {
281 569 : if (contains(bucket, id_)) {
282 394 : split(bucket);
283 394 : bucket = findBucket(nodeId);
284 :
285 : } else {
286 175 : return bucket->addNode(std::move(channel));
287 : }
288 : }
289 2153 : return bucket->addNode(std::move(channel));
290 : }
291 :
292 : bool
293 976 : RoutingTable::removeNode(const NodeId& nodeId)
294 : {
295 976 : return findBucket(nodeId)->removeNode(nodeId);
296 : }
297 :
298 : bool
299 1889 : RoutingTable::hasNode(const NodeId& nodeId)
300 : {
301 1889 : return findBucket(nodeId)->hasNode(nodeId);
302 : }
303 :
304 : bool
305 5018 : RoutingTable::addKnownNode(const NodeId& nodeId)
306 : {
307 5018 : if (id_ == nodeId)
308 1443 : return false;
309 :
310 3575 : auto bucket = findBucket(nodeId);
311 3575 : if (bucket == buckets.end())
312 0 : return false;
313 :
314 3575 : return bucket->addKnownNode(nodeId);
315 : }
316 :
317 : bool
318 299 : RoutingTable::addMobileNode(const NodeId& nodeId)
319 : {
320 299 : if (id_ == nodeId)
321 2 : return false;
322 :
323 297 : auto bucket = findBucket(nodeId);
324 :
325 297 : if (bucket == buckets.end())
326 0 : return false;
327 :
328 297 : 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 2349 : RoutingTable::addConnectingNode(const NodeId& nodeId)
345 : {
346 2349 : if (id_ == nodeId)
347 1 : return false;
348 :
349 2348 : auto bucket = findBucket(nodeId);
350 :
351 2348 : if (bucket == buckets.end())
352 0 : return 0;
353 :
354 2348 : bucket->addConnectingNode(nodeId);
355 2348 : 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 20083 : RoutingTable::findBucket(const NodeId& nodeId)
366 : {
367 20083 : if (buckets.empty())
368 0 : throw std::runtime_error("No bucket");
369 :
370 20083 : auto b = buckets.begin();
371 :
372 : while (true) {
373 33634 : auto next = std::next(b);
374 33634 : if (next == buckets.end())
375 20083 : return b;
376 21029 : if (std::memcmp(nodeId.data(), next->getLowerLimit().data(), nodeId.size()) < 0)
377 7478 : return b;
378 13551 : b = next;
379 13551 : }
380 : }
381 :
382 : std::vector<NodeId>
383 2292 : RoutingTable::closestNodes(const NodeId& nodeId, unsigned count) const
384 : {
385 2292 : std::vector<NodeId> closestNodes;
386 2292 : auto bucket = findBucket(nodeId);
387 5303 : auto sortedBucketInsert = [&](const std::list<Bucket>::const_iterator& b) {
388 5303 : auto nodes = b->getNodeIds();
389 19304 : for (auto n : nodes) {
390 14001 : if (n != nodeId) {
391 11980 : auto here = std::find_if(closestNodes.begin(), closestNodes.end(), [&nodeId, &n](NodeId& NodeId) {
392 67687 : return nodeId.xorCmp(n, NodeId) < 0;
393 : });
394 :
395 11980 : closestNodes.insert(here, n);
396 : }
397 : }
398 5303 : };
399 :
400 2292 : auto itn = bucket;
401 3085 : auto itp = (bucket == buckets.begin()) ? buckets.end() : std::prev(bucket);
402 8975 : while (itn != buckets.end() || itp != buckets.end()) {
403 4391 : if (itn != buckets.end()) {
404 3903 : sortedBucketInsert(itn);
405 3903 : itn = std::next(itn);
406 : }
407 4391 : if (itp != buckets.end()) {
408 1400 : sortedBucketInsert(itp);
409 2007 : itp = (itp == buckets.begin()) ? buckets.end() : std::prev(itp);
410 : }
411 : }
412 :
413 2292 : if (closestNodes.size() > count) {
414 1069 : closestNodes.resize(count);
415 : }
416 :
417 4584 : 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 132 : RoutingTable::getNodes() const
440 : {
441 132 : std::lock_guard lock(mutex_);
442 132 : std::vector<NodeId> ret;
443 608 : for (const auto& b : buckets) {
444 476 : const auto& nodes = b.getNodeIds();
445 476 : ret.insert(ret.end(), nodes.begin(), nodes.end());
446 476 : }
447 264 : return ret;
448 132 : }
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 2562 : RoutingTable::getMobileNodes() const
463 : {
464 2562 : std::vector<NodeId> ret;
465 7322 : for (const auto& b : buckets) {
466 4760 : const auto& nodes = b.getMobileNodes();
467 4760 : ret.insert(ret.end(), nodes.begin(), nodes.end());
468 : }
469 2562 : return ret;
470 0 : }
471 :
472 : std::vector<NodeId>
473 2559 : RoutingTable::getMobileNodesToNotify() const
474 : {
475 2559 : std::vector<NodeId> ret;
476 2817 : for (const auto& mobile : getMobileNodes()) {
477 258 : const auto closest = closestNodes(mobile, MOBILE_WAKE_REDUNDANCY);
478 258 : if (closest.size() < MOBILE_WAKE_REDUNDANCY || mobile.xorCmp(id_, closest.back()) < 0)
479 144 : ret.emplace_back(mobile);
480 2817 : }
481 2559 : return ret;
482 0 : }
483 :
484 : std::vector<NodeId>
485 722 : RoutingTable::getKnownMobileNodes() const
486 : {
487 722 : std::vector<NodeId> ret;
488 2340 : for (const auto& b : buckets) {
489 1618 : const auto& nodes = b.getMobileNodes();
490 1618 : ret.insert(ret.end(), nodes.begin(), nodes.end());
491 4228 : for (const auto& [nodeId, info] : b.getNodes())
492 2610 : if (info.isMobile_)
493 827 : ret.emplace_back(nodeId);
494 : }
495 722 : 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 9018 : RoutingTable::contains(const std::list<Bucket>::iterator& bucket, const NodeId& nodeId) const
522 : {
523 9018 : return NodeId::cmp(bucket->getLowerLimit(), nodeId) <= 0
524 20436 : && (std::next(bucket) == buckets.end() || NodeId::cmp(nodeId, std::next(bucket)->getLowerLimit()) < 0);
525 : }
526 :
527 : std::vector<NodeId>
528 17 : RoutingTable::getAllNodes() const
529 : {
530 17 : std::vector<NodeId> ret;
531 17 : std::set<NodeId> addedNodes;
532 68 : auto appendUnique = [&ret, &addedNodes](const auto& nodes) {
533 89 : for (const auto& node : nodes)
534 21 : if (addedNodes.emplace(node).second)
535 20 : ret.emplace_back(node);
536 68 : };
537 34 : for (const auto& b : buckets) {
538 17 : const auto& nodes = b.getNodeIds();
539 17 : const auto& knownNodes = b.getKnownNodes();
540 17 : const auto& mobileNodes = b.getMobileNodes();
541 17 : const auto& connectingNodes = b.getConnectingNodes();
542 17 : ret.reserve(ret.size() + nodes.size() + knownNodes.size() + mobileNodes.size() + connectingNodes.size());
543 17 : appendUnique(nodes);
544 17 : appendUnique(knownNodes);
545 17 : appendUnique(mobileNodes);
546 17 : appendUnique(connectingNodes);
547 17 : }
548 34 : return ret;
549 17 : }
550 :
551 : std::vector<NodeId>
552 2552 : RoutingTable::getConnectedNodes() const
553 : {
554 2552 : std::vector<NodeId> ret;
555 7219 : for (const auto& b : buckets) {
556 4667 : const auto& nodes = b.getNodes();
557 4667 : ret.reserve(ret.size() + nodes.size());
558 16895 : for (const auto& n : nodes)
559 12228 : ret.emplace_back(n.first);
560 : }
561 2552 : 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 394 : RoutingTable::middle(std::list<Bucket>::iterator& it) const
622 : {
623 394 : unsigned bit = depth(it);
624 394 : if (bit >= 8 * HASH_LEN)
625 0 : throw std::out_of_range("End of table");
626 :
627 394 : NodeId id = it->getLowerLimit();
628 394 : id.setBit(bit, true);
629 394 : return id;
630 : }
631 :
632 : unsigned
633 394 : RoutingTable::depth(std::list<Bucket>::iterator& bucket) const
634 : {
635 394 : int bit1 = bucket->getLowerLimit().lowbit();
636 969 : int bit2 = std::next(bucket) != buckets.end() ? std::next(bucket)->getLowerLimit().lowbit() : -1;
637 394 : return std::max(bit1, bit2) + 1;
638 : }
639 :
640 : bool
641 394 : RoutingTable::split(std::list<Bucket>::iterator& bucket)
642 : {
643 394 : NodeId id = middle(bucket);
644 788 : auto newBucketIt = buckets.emplace(std::next(bucket), id);
645 : // Re-assign nodes
646 394 : auto& nodeSwap = bucket->getNodes();
647 :
648 1182 : for (auto it = nodeSwap.begin(); it != nodeSwap.end();) {
649 788 : auto& node = *it;
650 :
651 788 : auto nodeId = it->first;
652 :
653 788 : if (!contains(bucket, nodeId)) {
654 372 : newBucketIt->addNode(std::move(node.second));
655 372 : it = nodeSwap.erase(it);
656 : } else {
657 416 : ++it;
658 : }
659 : }
660 :
661 394 : auto connectingSwap = bucket->getConnectingNodes();
662 802 : for (auto it = connectingSwap.begin(); it != connectingSwap.end();) {
663 408 : auto nodeId = *it;
664 :
665 408 : if (!contains(bucket, nodeId)) {
666 209 : newBucketIt->addConnectingNode(nodeId);
667 209 : it = connectingSwap.erase(it);
668 209 : bucket->removeConnectingNode(nodeId);
669 : } else {
670 199 : ++it;
671 : }
672 : }
673 :
674 394 : auto knownSwap = bucket->getKnownNodes();
675 599 : for (auto it = knownSwap.begin(); it != knownSwap.end();) {
676 205 : auto nodeId = *it;
677 :
678 205 : if (!contains(bucket, nodeId)) {
679 95 : newBucketIt->addKnownNode(nodeId);
680 95 : it = knownSwap.erase(it);
681 95 : bucket->removeKnownNode(nodeId);
682 : } else {
683 110 : ++it;
684 : }
685 : }
686 :
687 394 : auto mobileSwap = bucket->getMobileNodes();
688 447 : for (auto it = mobileSwap.begin(); it != mobileSwap.end();) {
689 53 : auto nodeId = *it;
690 :
691 53 : if (!contains(bucket, nodeId)) {
692 9 : newBucketIt->addMobileNode(nodeId);
693 9 : it = mobileSwap.erase(it);
694 9 : bucket->removeMobileNode(nodeId);
695 : } else {
696 44 : ++it;
697 : }
698 : }
699 :
700 394 : return true;
701 394 : }
702 :
703 : } // namespace jami
|