LCOV - code coverage report
Current view: top level - src/jamidht/swarm - routing_table.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 75.5 % 364 275
Test Date: 2026-06-13 09:18:46 Functions: 62.0 % 71 44

            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          927 : Bucket::Bucket(const NodeId& id)
      35          927 :     : lowerLimit_(id)
      36          927 : {}
      37              : 
      38              : bool
      39         1410 : Bucket::addNode(const std::shared_ptr<dhtnet::ChannelSocketInterface>& socket)
      40              : {
      41         1410 :     return addNode(NodeInfo(socket));
      42              : }
      43              : 
      44              : bool
      45         1687 : Bucket::addNode(NodeInfo&& info)
      46              : {
      47         1687 :     auto nodeId = info.socket->deviceId();
      48         1687 :     if (nodes.try_emplace(nodeId, std::move(info)).second) {
      49         1686 :         connecting_nodes.erase(nodeId);
      50         1686 :         known_nodes.erase(nodeId);
      51         1685 :         mobile_nodes.erase(nodeId);
      52         1686 :         return true;
      53              :     }
      54            1 :     return false;
      55              : }
      56              : 
      57              : bool
      58         1413 : Bucket::removeNode(const NodeId& nodeId)
      59              : {
      60         1413 :     auto node = nodes.find(nodeId);
      61         1413 :     auto isMobile = node->second.isMobile_;
      62         1413 :     if (node == nodes.end())
      63            3 :         return false;
      64         1410 :     nodes.erase(nodeId);
      65         1409 :     if (isMobile) {
      66           99 :         addMobileNode(nodeId);
      67              :     } else {
      68         1310 :         addKnownNode(nodeId);
      69              :     }
      70              : 
      71         1407 :     return true;
      72              : }
      73              : 
      74              : std::set<NodeId>
      75         4075 : Bucket::getNodeIds() const
      76              : {
      77         4075 :     std::set<NodeId> nodesId;
      78        14280 :     for (auto const& key : nodes)
      79        10200 :         nodesId.insert(key.first);
      80         4078 :     return nodesId;
      81            0 : }
      82              : 
      83              : bool
      84         9376 : Bucket::hasNode(const NodeId& nodeId) const
      85              : {
      86         9376 :     return nodes.find(nodeId) != nodes.end();
      87              : }
      88              : 
      89              : bool
      90         4260 : Bucket::addKnownNode(const NodeId& nodeId)
      91              : {
      92         4260 :     if (!hasNode(nodeId)) {
      93         2932 :         if (known_nodes.emplace(nodeId).second) {
      94         2638 :             return true;
      95              :         }
      96              :     }
      97         1618 :     return false;
      98              : }
      99              : 
     100              : NodeId
     101           69 : Bucket::getKnownNode(unsigned index) const
     102              : {
     103           69 :     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           68 :     auto it = known_nodes.begin();
     107              :     std::advance(it, index);
     108              : 
     109          136 :     return *it;
     110              : }
     111              : 
     112              : bool
     113          113 : Bucket::addMobileNode(const NodeId& nodeId)
     114              : {
     115          113 :     if (!hasNode(nodeId)) {
     116          112 :         if (mobile_nodes.emplace(nodeId).second) {
     117          112 :             known_nodes.erase(nodeId);
     118          112 :             return true;
     119              :         }
     120              :     }
     121            1 :     return false;
     122              : }
     123              : 
     124              : bool
     125         1486 : Bucket::addConnectingNode(const NodeId& nodeId)
     126              : {
     127         1486 :     if (!hasNode(nodeId)) {
     128         1491 :         if (connecting_nodes.emplace(nodeId).second) {
     129         1389 :             known_nodes.erase(nodeId);
     130         1390 :             mobile_nodes.erase(nodeId);
     131         1391 :             return true;
     132              :         }
     133              :     }
     134          104 :     return false;
     135              : }
     136              : 
     137              : std::set<NodeId>
     138         1653 : Bucket::getKnownNodesRandom(unsigned numberNodes, std::mt19937_64& rd) const
     139              : {
     140         1653 :     std::set<NodeId> nodesToReturn;
     141              : 
     142         1656 :     if (getKnownNodesSize() <= numberNodes)
     143         1590 :         return getKnownNodes();
     144              : 
     145           67 :     std::uniform_int_distribution<unsigned> distrib(0, getKnownNodesSize() - 1);
     146              : 
     147          135 :     while (nodesToReturn.size() < numberNodes) {
     148           68 :         nodesToReturn.emplace(getKnownNode(distrib(rd)));
     149              :     }
     150              : 
     151           67 :     return nodesToReturn;
     152         1654 : }
     153              : 
     154              : asio::steady_timer&
     155            0 : Bucket::getNodeTimer(const std::shared_ptr<dhtnet::ChannelSocketInterface>& socket)
     156              : {
     157            0 :     auto node = nodes.find(socket->deviceId());
     158            0 :     if (node == nodes.end()) {
     159            0 :         throw std::range_error("Unable to find timer " + socket->deviceId().toString());
     160              :     }
     161            0 :     return node->second.refresh_timer;
     162              : }
     163              : 
     164              : bool
     165           14 : Bucket::shutdownNode(const NodeId& nodeId)
     166              : {
     167           14 :     auto node = nodes.find(nodeId);
     168           14 :     if (node != nodes.end()) {
     169            7 :         auto& socket = node->second.socket;
     170            7 :         auto node = socket->deviceId();
     171           14 :         dht::ThreadPool::io().run([socket] { socket->shutdown(); });
     172            7 :         removeNode(node);
     173            7 :         return true;
     174              :     }
     175            7 :     return false;
     176              : }
     177              : 
     178              : void
     179          957 : Bucket::shutdownAllNodes()
     180              : {
     181         1847 :     while (not nodes.empty()) {
     182          890 :         auto it = nodes.begin();
     183         1410 :         dht::ThreadPool::io().run([socket = it->second.socket] { socket->shutdown(); });
     184          890 :         auto nodeId = it->first;
     185          890 :         removeNode(nodeId);
     186              :     }
     187          957 : }
     188              : 
     189              : void
     190            0 : Bucket::printBucket(unsigned number) const
     191              : {
     192            0 :     JAMI_ERROR("BUCKET Number: {:d}", number);
     193              : 
     194            0 :     unsigned nodeNum = 1;
     195            0 :     for (auto it = nodes.begin(); it != nodes.end(); ++it) {
     196            0 :         JAMI_DEBUG("Node {:s}   Id: {:s}  isMobile: {:s}",
     197              :                    std::to_string(nodeNum),
     198              :                    it->first.toString(),
     199              :                    std::to_string(it->second.isMobile_));
     200            0 :         nodeNum++;
     201              :     }
     202            0 :     JAMI_ERROR("Mobile Nodes");
     203            0 :     nodeNum = 0;
     204            0 :     for (auto it = mobile_nodes.begin(); it != mobile_nodes.end(); ++it) {
     205            0 :         JAMI_DEBUG("Node {:s}   Id: {:s}", std::to_string(nodeNum), (*it).toString());
     206            0 :         nodeNum++;
     207              :     }
     208              : 
     209            0 :     JAMI_ERROR("Known Nodes");
     210            0 :     nodeNum = 0;
     211            0 :     for (auto it = known_nodes.begin(); it != known_nodes.end(); ++it) {
     212            0 :         JAMI_DEBUG("Node {:s}   Id: {:s}", std::to_string(nodeNum), (*it).toString());
     213            0 :         nodeNum++;
     214              :     }
     215            0 :     JAMI_ERROR("Connecting_nodes");
     216            0 :     nodeNum = 0;
     217            0 :     for (auto it = connecting_nodes.begin(); it != connecting_nodes.end(); ++it) {
     218            0 :         JAMI_DEBUG("Node {:s}   Id: {:s}", std::to_string(nodeNum), (*it).toString());
     219            0 :         nodeNum++;
     220              :     }
     221            0 : };
     222              : 
     223              : void
     224          196 : Bucket::changeMobility(const NodeId& nodeId, bool isMobile)
     225              : {
     226          196 :     auto itn = nodes.find(nodeId);
     227          196 :     if (itn != nodes.end()) {
     228          196 :         itn->second.isMobile_ = isMobile;
     229              :     }
     230          196 : }
     231              : 
     232              : // For tests
     233              : 
     234              : std::set<std::shared_ptr<dhtnet::ChannelSocketInterface>>
     235            1 : Bucket::getNodeSockets() const
     236              : {
     237            1 :     std::set<std::shared_ptr<dhtnet::ChannelSocketInterface>> sockets;
     238            3 :     for (auto const& info : nodes)
     239            2 :         sockets.insert(info.second.socket);
     240            1 :     return sockets;
     241            0 : }
     242              : 
     243              : // ####################################################################################################
     244              : 
     245          594 : RoutingTable::RoutingTable()
     246              : {
     247          594 :     buckets.emplace_back(NodeId::zero());
     248          594 : }
     249              : 
     250              : bool
     251         2632 : RoutingTable::isEmpty() const
     252              : {
     253         3724 :     for (const auto& bucket : buckets) {
     254         2635 :         if (!bucket.isEmpty()) {
     255         1543 :             return false;
     256              :         }
     257              :     }
     258         1089 :     return true;
     259              : }
     260              : 
     261              : bool
     262           10 : RoutingTable::addNode(const std::shared_ptr<dhtnet::ChannelSocketInterface>& socket)
     263              : {
     264           10 :     auto bucket = findBucket(socket->deviceId());
     265           20 :     return addNode(socket, bucket);
     266              : }
     267              : 
     268              : bool
     269         2259 : RoutingTable::addNode(const std::shared_ptr<dhtnet::ChannelSocketInterface>& channel,
     270              :                       std::list<Bucket>::iterator& bucket)
     271              : {
     272         2259 :     NodeId nodeId = channel->deviceId();
     273              : 
     274         2259 :     if (bucket->hasNode(nodeId) || id_ == nodeId) {
     275          880 :         return false;
     276              :     }
     277              : 
     278         1709 :     while (bucket->isFull()) {
     279          473 :         if (contains(bucket, id_)) {
     280          331 :             split(bucket);
     281          331 :             bucket = findBucket(nodeId);
     282              : 
     283              :         } else {
     284          142 :             return bucket->addNode(std::move(channel));
     285              :         }
     286              :     }
     287         1236 :     return bucket->addNode(std::move(channel));
     288              : }
     289              : 
     290              : bool
     291          513 : RoutingTable::removeNode(const NodeId& nodeId)
     292              : {
     293          513 :     return findBucket(nodeId)->removeNode(nodeId);
     294              : }
     295              : 
     296              : bool
     297         1249 : RoutingTable::hasNode(const NodeId& nodeId)
     298              : {
     299         1249 :     return findBucket(nodeId)->hasNode(nodeId);
     300              : }
     301              : 
     302              : bool
     303         3292 : RoutingTable::addKnownNode(const NodeId& nodeId)
     304              : {
     305         3292 :     if (id_ == nodeId)
     306          512 :         return false;
     307              : 
     308         2773 :     auto bucket = findBucket(nodeId);
     309         2779 :     if (bucket == buckets.end())
     310            0 :         return false;
     311              : 
     312         2777 :     return bucket->addKnownNode(nodeId);
     313              : }
     314              : 
     315              : bool
     316           15 : RoutingTable::addMobileNode(const NodeId& nodeId)
     317              : {
     318           15 :     if (id_ == nodeId)
     319            1 :         return false;
     320              : 
     321           14 :     auto bucket = findBucket(nodeId);
     322              : 
     323           14 :     if (bucket == buckets.end())
     324            0 :         return false;
     325              : 
     326           14 :     bucket->addMobileNode(nodeId);
     327           14 :     return true;
     328              : }
     329              : 
     330              : void
     331            2 : RoutingTable::removeMobileNode(const NodeId& nodeId)
     332              : {
     333            2 :     return findBucket(nodeId)->removeMobileNode(nodeId);
     334              : }
     335              : 
     336              : bool
     337            7 : RoutingTable::hasMobileNode(const NodeId& nodeId)
     338              : {
     339            7 :     return findBucket(nodeId)->hasMobileNode(nodeId);
     340              : };
     341              : 
     342              : bool
     343         1343 : RoutingTable::addConnectingNode(const NodeId& nodeId)
     344              : {
     345         1343 :     if (id_ == nodeId)
     346            1 :         return false;
     347              : 
     348         1339 :     auto bucket = findBucket(nodeId);
     349              : 
     350         1339 :     if (bucket == buckets.end())
     351            0 :         return 0;
     352              : 
     353         1339 :     bucket->addConnectingNode(nodeId);
     354         1343 :     return 1;
     355              : }
     356              : 
     357              : void
     358            0 : RoutingTable::removeConnectingNode(const NodeId& nodeId)
     359              : {
     360            0 :     findBucket(nodeId)->removeConnectingNode(nodeId);
     361            0 : }
     362              : 
     363              : std::list<Bucket>::iterator
     364        14932 : RoutingTable::findBucket(const NodeId& nodeId)
     365              : {
     366        14932 :     if (buckets.empty())
     367            0 :         throw std::runtime_error("No bucket");
     368              : 
     369        14924 :     auto b = buckets.begin();
     370              : 
     371              :     while (true) {
     372        29375 :         auto next = std::next(b);
     373        29375 :         if (next == buckets.end())
     374        14940 :             return b;
     375        21758 :         if (std::memcmp(nodeId.data(), next->getLowerLimit().data(), nodeId.size()) < 0)
     376         7314 :             return b;
     377        14459 :         b = next;
     378        14459 :     }
     379              : }
     380              : 
     381              : std::vector<NodeId>
     382         1285 : RoutingTable::closestNodes(const NodeId& nodeId, unsigned count)
     383              : {
     384         1285 :     std::vector<NodeId> closestNodes;
     385         1285 :     auto bucket = findBucket(nodeId);
     386         3678 :     auto sortedBucketInsert = [&](const std::list<Bucket>::iterator& b) {
     387         3678 :         auto nodes = b->getNodeIds();
     388        13203 :         for (auto n : nodes) {
     389         9517 :             if (n != nodeId) {
     390         8248 :                 auto here = std::find_if(closestNodes.begin(), closestNodes.end(), [&nodeId, &n](NodeId& NodeId) {
     391        43258 :                     return nodeId.xorCmp(n, NodeId) < 0;
     392              :                 });
     393              : 
     394         8261 :                 closestNodes.insert(here, n);
     395              :             }
     396              :         }
     397         3668 :     };
     398              : 
     399         1283 :     auto itn = bucket;
     400         1850 :     auto itp = (bucket == buckets.begin()) ? buckets.end() : std::prev(bucket);
     401         5565 :     while (itn != buckets.end() || itp != buckets.end()) {
     402         2991 :         if (itn != buckets.end()) {
     403         2473 :             sortedBucketInsert(itn);
     404         2475 :             itn = std::next(itn);
     405              :         }
     406         2992 :         if (itp != buckets.end()) {
     407         1205 :             sortedBucketInsert(itp);
     408         1847 :             itp = (itp == buckets.begin()) ? buckets.end() : std::prev(itp);
     409              :         }
     410              :     }
     411              : 
     412         1289 :     if (closestNodes.size() > count) {
     413          807 :         closestNodes.resize(count);
     414              :     }
     415              : 
     416         2576 :     return closestNodes;
     417            0 : }
     418              : 
     419              : void
     420            0 : RoutingTable::printRoutingTable() const
     421              : {
     422            0 :     int counter = 1;
     423            0 :     JAMI_DEBUG("SWARM: {:s} ", id_.toString());
     424            0 :     for (auto it = buckets.begin(); it != buckets.end(); ++it) {
     425            0 :         it->printBucket(counter);
     426            0 :         counter++;
     427              :     }
     428            0 :     JAMI_DEBUG("_____________________________________________________________________________");
     429            0 : }
     430              : 
     431              : void
     432            2 : RoutingTable::shutdownNode(const NodeId& nodeId)
     433              : {
     434            2 :     findBucket(nodeId)->shutdownNode(nodeId);
     435            2 : }
     436              : 
     437              : std::vector<NodeId>
     438          101 : RoutingTable::getNodes() const
     439              : {
     440          101 :     std::lock_guard lock(mutex_);
     441          101 :     std::vector<NodeId> ret;
     442          477 :     for (const auto& b : buckets) {
     443          376 :         const auto& nodes = b.getNodeIds();
     444          376 :         ret.insert(ret.end(), nodes.begin(), nodes.end());
     445          376 :     }
     446          202 :     return ret;
     447          101 : }
     448              : 
     449              : std::vector<NodeId>
     450            1 : RoutingTable::getKnownNodes() const
     451              : {
     452            1 :     std::vector<NodeId> ret;
     453            2 :     for (const auto& b : buckets) {
     454            1 :         const auto& nodes = b.getKnownNodes();
     455            1 :         ret.insert(ret.end(), nodes.begin(), nodes.end());
     456              :     }
     457            1 :     return ret;
     458            0 : }
     459              : 
     460              : std::vector<NodeId>
     461            2 : RoutingTable::getMobileNodes() const
     462              : {
     463            2 :     std::vector<NodeId> ret;
     464            4 :     for (const auto& b : buckets) {
     465            2 :         const auto& nodes = b.getMobileNodes();
     466            2 :         ret.insert(ret.end(), nodes.begin(), nodes.end());
     467              :     }
     468            2 :     return ret;
     469            0 : }
     470              : 
     471              : std::vector<NodeId>
     472            1 : RoutingTable::getConnectingNodes() const
     473              : {
     474            1 :     std::vector<NodeId> ret;
     475            2 :     for (const auto& b : buckets) {
     476            1 :         const auto& nodes = b.getConnectingNodes();
     477            1 :         ret.insert(ret.end(), nodes.begin(), nodes.end());
     478              :     }
     479            1 :     return ret;
     480            0 : }
     481              : 
     482              : std::vector<NodeId>
     483            0 : RoutingTable::getBucketMobileNodes() const
     484              : {
     485            0 :     std::vector<NodeId> ret;
     486            0 :     auto bucket = findBucket(id_);
     487            0 :     const auto& nodes = bucket->getMobileNodes();
     488            0 :     ret.insert(ret.end(), nodes.begin(), nodes.end());
     489              : 
     490            0 :     return ret;
     491            0 : }
     492              : 
     493              : bool
     494         4673 : RoutingTable::contains(const std::list<Bucket>::iterator& bucket, const NodeId& nodeId) const
     495              : {
     496         4673 :     return NodeId::cmp(bucket->getLowerLimit(), nodeId) <= 0
     497        10915 :            && (std::next(bucket) == buckets.end() || NodeId::cmp(nodeId, std::next(bucket)->getLowerLimit()) < 0);
     498              : }
     499              : 
     500              : std::vector<NodeId>
     501           16 : RoutingTable::getAllNodes() const
     502              : {
     503           16 :     std::vector<NodeId> ret;
     504           32 :     for (const auto& b : buckets) {
     505           16 :         const auto& nodes = b.getNodeIds();
     506           16 :         const auto& knownNodes = b.getKnownNodes();
     507           16 :         const auto& mobileNodes = b.getMobileNodes();
     508           16 :         const auto& connectingNodes = b.getConnectingNodes();
     509           16 :         ret.reserve(ret.size() + nodes.size() + knownNodes.size() + mobileNodes.size() + connectingNodes.size());
     510           16 :         ret.insert(ret.end(), nodes.begin(), nodes.end());
     511           16 :         ret.insert(ret.end(), knownNodes.begin(), knownNodes.end());
     512           16 :         ret.insert(ret.end(), mobileNodes.begin(), mobileNodes.end());
     513           16 :         ret.insert(ret.end(), connectingNodes.begin(), connectingNodes.end());
     514           16 :     }
     515           16 :     return ret;
     516            0 : }
     517              : 
     518              : std::vector<NodeId>
     519         1765 : RoutingTable::getConnectedNodes() const
     520              : {
     521         1765 :     std::vector<NodeId> ret;
     522         6466 :     for (const auto& b : buckets) {
     523         4701 :         const auto& nodes = b.getNodes();
     524         4701 :         const auto& mobiles = b.getMobileNodes();
     525         4701 :         ret.reserve(ret.size() + nodes.size() + mobiles.size());
     526        19222 :         for (const auto& n : nodes)
     527        14525 :             ret.emplace_back(n.first);
     528         4699 :         ret.insert(ret.end(), mobiles.begin(), mobiles.end());
     529              :     }
     530         1765 :     return ret;
     531            0 : }
     532              : 
     533              : void
     534           11 : RoutingTable::deleteNode(const NodeId& nodeId)
     535              : {
     536           11 :     auto bucket = findBucket(nodeId);
     537           11 :     bucket->shutdownNode(nodeId);
     538           11 :     bucket->removeConnectingNode(nodeId);
     539           11 :     bucket->removeKnownNode(nodeId);
     540           11 :     bucket->removeMobileNode(nodeId);
     541           11 : }
     542              : 
     543              : inline std::chrono::system_clock::time_point
     544            0 : systemTimeFromSteady(std::chrono::steady_clock::time_point t,
     545              :                      const std::chrono::steady_clock::time_point& now,
     546              :                      const std::chrono::system_clock::time_point& nowSystem)
     547              : {
     548            0 :     return nowSystem + std::chrono::duration_cast<std::chrono::system_clock::duration>(t - now);
     549              : }
     550              : 
     551              : std::vector<RoutingTable::NodeStats>
     552            0 : RoutingTable::getRoutingTableStats() const
     553              : {
     554            0 :     std::vector<NodeStats> stats;
     555            0 :     auto now = std::chrono::steady_clock::now();
     556            0 :     auto nowSystem = std::chrono::system_clock::now();
     557            0 :     std::lock_guard lock(mutex_);
     558            0 :     for (const auto& bucket : buckets) {
     559            0 :         for (const auto& [id, info] : bucket.getNodes()) {
     560            0 :             if (auto channel = std::dynamic_pointer_cast<dhtnet::ChannelSocket>(info.socket)) {
     561            0 :                 stats.push_back({id.toString(),
     562              :                                  "connected",
     563            0 :                                  channel->getRemoteAddress().toString(true),
     564            0 :                                  systemTimeFromSteady(channel->getStartTime(), now, nowSystem),
     565            0 :                                  info.isMobile_});
     566              :             } else {
     567            0 :                 stats.push_back(
     568            0 :                     {id.toString(), "connected", "", std::chrono::system_clock::time_point::min(), info.isMobile_});
     569            0 :             }
     570              :         }
     571            0 :         for (const auto& id : bucket.getKnownNodes()) {
     572            0 :             stats.push_back({id.toString(), "known", "", std::chrono::system_clock::time_point::min(), false});
     573              :         }
     574            0 :         for (const auto& id : bucket.getMobileNodes()) {
     575            0 :             stats.push_back({id.toString(), "mobile", "", std::chrono::system_clock::time_point::min(), true});
     576              :         }
     577            0 :         for (const auto& id : bucket.getConnectingNodes()) {
     578            0 :             stats.push_back({id.toString(), "connecting", "", std::chrono::system_clock::time_point::min(), false});
     579              :         }
     580              :     }
     581            0 :     return stats;
     582            0 : }
     583              : 
     584              : NodeId
     585          331 : RoutingTable::middle(std::list<Bucket>::iterator& it) const
     586              : {
     587          331 :     unsigned bit = depth(it);
     588          331 :     if (bit >= 8 * HASH_LEN)
     589            0 :         throw std::out_of_range("End of table");
     590              : 
     591          331 :     NodeId id = it->getLowerLimit();
     592          331 :     id.setBit(bit, true);
     593          331 :     return id;
     594              : }
     595              : 
     596              : unsigned
     597          331 : RoutingTable::depth(std::list<Bucket>::iterator& bucket) const
     598              : {
     599          331 :     int bit1 = bucket->getLowerLimit().lowbit();
     600          843 :     int bit2 = std::next(bucket) != buckets.end() ? std::next(bucket)->getLowerLimit().lowbit() : -1;
     601          331 :     return std::max(bit1, bit2) + 1;
     602              : }
     603              : 
     604              : bool
     605          331 : RoutingTable::split(std::list<Bucket>::iterator& bucket)
     606              : {
     607          331 :     NodeId id = middle(bucket);
     608          662 :     auto newBucketIt = buckets.emplace(std::next(bucket), id);
     609              :     // Re-assign nodes
     610          331 :     auto& nodeSwap = bucket->getNodes();
     611              : 
     612          993 :     for (auto it = nodeSwap.begin(); it != nodeSwap.end();) {
     613          662 :         auto& node = *it;
     614              : 
     615          662 :         auto nodeId = it->first;
     616              : 
     617          662 :         if (!contains(bucket, nodeId)) {
     618          276 :             newBucketIt->addNode(std::move(node.second));
     619          276 :             it = nodeSwap.erase(it);
     620              :         } else {
     621          386 :             ++it;
     622              :         }
     623              :     }
     624              : 
     625          331 :     auto connectingSwap = bucket->getConnectingNodes();
     626          679 :     for (auto it = connectingSwap.begin(); it != connectingSwap.end();) {
     627          348 :         auto nodeId = *it;
     628              : 
     629          348 :         if (!contains(bucket, nodeId)) {
     630          150 :             newBucketIt->addConnectingNode(nodeId);
     631          150 :             it = connectingSwap.erase(it);
     632          150 :             bucket->removeConnectingNode(nodeId);
     633              :         } else {
     634          198 :             ++it;
     635              :         }
     636              :     }
     637              : 
     638          331 :     auto knownSwap = bucket->getKnownNodes();
     639          516 :     for (auto it = knownSwap.begin(); it != knownSwap.end();) {
     640          185 :         auto nodeId = *it;
     641              : 
     642          185 :         if (!contains(bucket, nodeId)) {
     643           74 :             newBucketIt->addKnownNode(nodeId);
     644           74 :             it = knownSwap.erase(it);
     645           74 :             bucket->removeKnownNode(nodeId);
     646              :         } else {
     647          111 :             ++it;
     648              :         }
     649              :     }
     650              : 
     651          331 :     auto mobileSwap = bucket->getMobileNodes();
     652          331 :     for (auto it = mobileSwap.begin(); it != mobileSwap.end();) {
     653            0 :         auto nodeId = *it;
     654              : 
     655            0 :         if (!contains(bucket, nodeId)) {
     656            0 :             newBucketIt->addMobileNode(nodeId);
     657            0 :             it = mobileSwap.erase(it);
     658            0 :             bucket->removeMobileNode(nodeId);
     659              :         } else {
     660            0 :             ++it;
     661              :         }
     662              :     }
     663              : 
     664          331 :     return true;
     665          331 : }
     666              : 
     667              : } // namespace jami
        

Generated by: LCOV version 2.0-1