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: 82.2 % 387 318
Test Date: 2026-08-23 08:52:56 Functions: 88.9 % 54 48

            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
        

Generated by: LCOV version 2.0-1