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

Generated by: LCOV version 2.0-1