LCOV - code coverage report
Current view: top level - root/contrail/src/contrail-common/base - bitset.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 277 281 98.6 %
Date: 2026-11-09 02:10:49 Functions: 39 39 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  * Copyright Contributors to the OpenSDN project
       4             :  */
       5             : 
       6             : #include "base/bitset.h"
       7             : 
       8             : #include <cassert>
       9             : #include <sstream>
      10             : #include <string>
      11             : #include <string.h>
      12             : 
      13             : #include "base/util.h"
      14             : #include "base/string_util.h"
      15             : 
      16             : using namespace std;
      17             : 
      18             : //
      19             : // Provides the same functionality as ffsl.  Needed as ffsl is not supported
      20             : // on all platforms. Note that the positions are numbered 1 through 64, with
      21             : // a return value of 0 indicating that there are no set bits.
      22             : //
      23     6555043 : static int find_first_set64(uint64_t value) {
      24             :     int bit;
      25             : 
      26     6555043 :     int lower = static_cast<int>(value);
      27     6555043 :     if ((bit = ffs(lower)) > 0)
      28     4447205 :         return bit;
      29             : 
      30     2107838 :     int upper = value >> 32;
      31     2107838 :     if ((bit = ffs(upper)) > 0)
      32       41145 :         return 32 + bit;
      33             : 
      34     2066693 :     return 0;
      35             : }
      36             : 
      37      645734 : static int find_first_clear64(uint64_t value) {
      38      645734 :     return find_first_set64(~value);
      39             : }
      40             : 
      41             : //
      42             : // Provides the same functionality as fls.  Needed as fls is not supported
      43             : // on all platforms. Note that the positions are numbered 1 through 32, with
      44             : // a return value of 0 indicating that there are no set bits.
      45             : //
      46        4016 : static int find_last_set32(uint32_t value) {
      47        4016 :     if (value == 0)
      48        1820 :         return 0;
      49             : 
      50        2196 :     int bit = 32;
      51        2196 :     if ((value & 0xFFFF0000U) == 0) {
      52        1833 :         value <<= 16;
      53        1833 :         bit -= 16;
      54             :     }
      55        2196 :     if ((value & 0xFF000000U) == 0) {
      56        1830 :         value <<= 8;
      57        1830 :         bit -= 8;
      58             :     }
      59        2196 :     if ((value & 0xF0000000U) == 0) {
      60        1846 :         value <<= 4;
      61        1846 :         bit -= 4;
      62             :     }
      63        2196 :     if ((value & 0xC0000000U) == 0) {
      64        1535 :         value <<= 2;
      65        1535 :         bit -= 2;
      66             :     }
      67        2196 :     if ((value & 0x80000000U) == 0) {
      68        1365 :         value <<= 1;
      69        1365 :         bit -= 1;
      70             :     }
      71             : 
      72        2196 :     return bit;
      73             : }
      74             : 
      75             : //
      76             : // Provides the same functionality as flsl.  Needed as flsl is not supported
      77             : // on all platforms. Note that the positions are numbered 1 through 64, with
      78             : // a return value of 0 indicating that there are no set bits.
      79             : //
      80        2196 : static int find_last_set64(uint64_t value) {
      81             :     int bit;
      82             : 
      83        2196 :     int upper = value >> 32;
      84        2196 :     if ((bit = find_last_set32(upper)) > 0)
      85         376 :         return 32 + bit;
      86             : 
      87        1820 :     int lower = static_cast<int>(value);
      88        1820 :     if ((bit = find_last_set32(lower)) > 0)
      89        1820 :         return bit;
      90             : 
      91           0 :     return 0;
      92             : }
      93             : 
      94             : //
      95             : // Return the number of set bits. K&R method.
      96             : //
      97       19647 : static int num_bits_set(uint64_t value) {
      98       19647 :     int count = 0;
      99     1070611 :     while (value != 0) {
     100     1050964 :         value &= value - 1;
     101     1050964 :         count++;
     102             :     }
     103       19647 :     return count;
     104             : }
     105             : 
     106             : // Position pos is w.r.t the entire bitset, starts at 0.
     107             : // Index    idx is the block number i.e. the index in the vector, starts at 0.
     108             : // Offset   offset is w.r.t a given 64 bit block, starts at 0.
     109    12749283 : static inline size_t block_index(size_t pos) {
     110    12749283 :     return pos / 64;
     111             : }
     112             : 
     113    16845989 : static inline size_t block_offset(size_t pos) {
     114    16845989 :     return pos % 64;
     115             : }
     116             : 
     117     4490403 : static inline size_t bit_position(size_t idx, size_t offset) {
     118     4490403 :     return (idx * 64 + offset);
     119             : }
     120             : 
     121             : const size_t BitSet::npos;
     122             : 
     123             : //
     124             : // Set bit at given position, growing the vector if needed.
     125             : //
     126     6009251 : BitSet &BitSet::set(size_t pos) {
     127     6009251 :     size_t idx = block_index(pos);
     128     6009234 :     if (idx >= blocks_.size())
     129     1441733 :         blocks_.resize(idx + 1);
     130     6009255 :     blocks_[idx] |= 1LL << block_offset(pos);
     131     6009683 :     return *this;
     132             : }
     133             : 
     134             : //
     135             : // Reset bit at given position, shrinking the vector if possible.
     136             : //
     137     1982503 : BitSet &BitSet::reset(size_t pos) {
     138     1982503 :     size_t idx = block_index(pos);
     139     1982497 :     if (idx < blocks_.size()) {
     140     1982414 :         blocks_[idx] &= ~(1LL << block_offset(pos));
     141     1982400 :         compact();
     142             :     }
     143     1982375 :     return *this;
     144             : }
     145             : 
     146             : // Test bit at given position.
     147      553505 : bool BitSet::test(size_t pos) const {
     148      553505 :     size_t idx = block_index(pos);
     149      553502 :     if (idx < blocks_.size()) {
     150      457548 :         return ((blocks_[idx] & (1LL << block_offset(pos))) != 0);
     151             :     } else {
     152       95954 :         return false;
     153             :     }
     154             : }
     155             : 
     156             : //
     157             : // Shortcut to reset all bits in the bitset.
     158             : //
     159       20580 : void BitSet::clear() {
     160       20580 :     blocks_.resize(0);
     161       20580 : }
     162             : 
     163             : //
     164             : // Return true if there are no bits in the bitset.
     165             : //
     166     6278481 : bool BitSet::empty() const {
     167     6278481 :     return (blocks_.size() == 0);
     168             : }
     169             : 
     170             : //
     171             : // Return true if no bits are set.
     172             : //
     173      367821 : bool BitSet::none() const {
     174      367821 :     return (blocks_.size() == 0);
     175             : }
     176             : 
     177             : //
     178             : // Return true at least one bit is set.
     179             : //
     180         642 : bool BitSet::any() const {
     181         642 :     return (blocks_.size() != 0);
     182             : }
     183             : 
     184             : //
     185             : // Return the raw number of bits in the bitset. Simply depends on the number
     186             : // of blocks in the vector.
     187             : //
     188       71033 : size_t BitSet::size() const {
     189       71033 :     return blocks_.size() * 64;
     190             : }
     191             : 
     192             : //
     193             : // Return total number of set bits.
     194             : //
     195        4320 : size_t BitSet::count() const {
     196        4320 :     size_t count = 0;
     197       23967 :     for (size_t idx = 0; idx < blocks_.size(); idx++) {
     198       19647 :         count += num_bits_set(blocks_[idx]);
     199             :     }
     200        4320 :     return count;
     201             : }
     202             : 
     203             : //
     204             : // Shrink the underlying vector as much as possible.  All trailing blocks
     205             : // that are 0 can be removed.
     206             : //
     207             : // Note that the for loop does not handle idx 0 since the loop  variable
     208             : // is unsigned.
     209             : //
     210     5273106 : void BitSet::compact() {
     211     5273106 :     if (blocks_.size() == 0)
     212      628861 :         return;
     213             : 
     214     4659118 :     for (size_t idx = blocks_.size() - 1; idx > 0; idx--) {
     215      109990 :         if (blocks_[idx] != 0) {
     216       94881 :             blocks_.resize(idx + 1);
     217       94881 :             return;
     218             :         }
     219             :     }
     220             : 
     221     4549416 :     if (blocks_[0] != 0) {
     222     1677509 :         blocks_.resize(1);
     223     1677513 :         return;
     224             :     }
     225             : 
     226     2871806 :     blocks_.clear();
     227             : }
     228             : 
     229             : //
     230             : // Sanity check a bitset.  The last block must never be 0.  Always called
     231             : // after any compaction is done or in cases where no compaction is needed.
     232             : //
     233    12165800 : void BitSet::check_invariants() {
     234    12165800 :     size_t mysize = blocks_.size();
     235    12164814 :     if (mysize != 0)
     236     8124209 :         assert(blocks_[mysize -1] != 0);
     237    12164524 : }
     238             : 
     239             : //
     240             : // Return the position of the first set bit.  Needs to compensate for the
     241             : // return value convention used by find_first_set64.
     242             : //
     243     2921794 : size_t BitSet::find_first() const {
     244     2921986 :     for (size_t idx = 0; idx < blocks_.size(); idx++) {
     245     1706342 :         int bit = find_first_set64(blocks_[idx]);
     246     1707208 :         if (bit > 0)
     247     1707016 :             return bit_position(idx, bit - 1);
     248             :     }
     249     1215750 :     return BitSet::npos;
     250             : }
     251             : 
     252             : //
     253             : // Return the position of the next set bit.  Needs to compensate for the
     254             : // return value convention used by find_first_set64.
     255             : //
     256     4194716 : size_t BitSet::find_next(size_t pos) const {
     257     4194716 :     size_t idx = block_index(pos);
     258             : 
     259             :     // If the block index is beyond the vector, we're done.
     260     4194618 :     if (idx >= blocks_.size())
     261        1087 :         return BitSet::npos;
     262             : 
     263             :     // If the offset is not 63, clear out the bits from 0 through offset
     264             :     // and look for the first set bit.
     265     4193371 :     if (block_offset(pos) < 63) {
     266     4192820 :         uint64_t temp = blocks_[idx] & ~((1LL << (block_offset(pos) + 1)) - 1);
     267     4192448 :         int bit = find_first_set64(temp);
     268     4193800 :         if (bit > 0)
     269     2483640 :             return bit_position(idx, bit - 1);
     270             :     }
     271             : 
     272             :     // Go through all blocks after the start block for the pos and see if
     273             :     // there's a set bit.
     274     1717292 :     for (idx++; idx < blocks_.size(); idx++) {
     275       10450 :         int bit = find_first_set64(blocks_[idx]);
     276       10456 :         if (bit > 0)
     277        3736 :             return bit_position(idx, bit - 1);
     278             :     }
     279     1706831 :     return BitSet::npos;
     280             : }
     281             : 
     282             : //
     283             : // Return the position of the last set bit.  Needs to compensate for the
     284             : // return value convention used by find_last_set64.
     285             : //
     286             : // Note that we only need to look at the last block since that must have
     287             : // at least one bit set.
     288             : //
     289        3418 : size_t BitSet::find_last() const {
     290        3418 :     if (blocks_.size() == 0)
     291        1222 :         return BitSet::npos;
     292             : 
     293        2196 :     size_t idx = blocks_.size() - 1;
     294        2196 :     int bit = find_last_set64(blocks_[idx]);
     295        2196 :     if (bit > 0)
     296        2196 :         return bit_position(idx, bit - 1);
     297             : 
     298           0 :     return BitSet::npos;
     299             : }
     300             : 
     301             : //
     302             : // Return the position of the first clear bit.  It could be beyond the last
     303             : // block in the vector. This is fine as we automatically grow the vector if
     304             : // needed from set().
     305             : //
     306             : // Need to compensate for return value convention used by find_first_clear64.
     307             : //
     308      358255 : size_t BitSet::find_first_clear() const {
     309      699767 :     for (size_t idx = 0; idx < blocks_.size(); idx++) {
     310      628966 :         int bit = find_first_clear64(blocks_[idx]);
     311      628966 :         if (bit > 0) {
     312      287454 :             return bit_position(idx, bit - 1);
     313             :         }
     314             :     }
     315       70801 :     return size();
     316             : }
     317             : 
     318             : //
     319             : // Return the position of the next clear bit.  It could be beyond the last
     320             : // block in the vector. This is fine as we automatically grow the vector if
     321             : // needed from set().
     322             : //
     323             : // Need to compensate for return value convention used by find_first_clear64.
     324             : //
     325       10423 : size_t BitSet::find_next_clear(size_t pos) const {
     326       10423 :     size_t idx = block_index(pos);
     327             : 
     328             :     // If the block index is beyond the vector, we're done.
     329       10423 :     if (idx >= blocks_.size())
     330        3320 :         return pos + 1;
     331             : 
     332             :     // If the offset is not 63, set all the bits from 0 through offset and
     333             :     // look for the first clear bit.
     334        7103 :     if (block_offset(pos) < 63) {
     335        7006 :         uint64_t temp = blocks_[idx] | ((1LL << (block_offset(pos) + 1)) - 1);
     336        7006 :         int bit = find_first_clear64(temp);
     337        7006 :         if (bit > 0)
     338        3919 :             return bit_position(idx, bit - 1);
     339             :     }
     340             : 
     341             :     // Go through all blocks after the start block for the pos and see if
     342             :     // there's a clear bit.
     343        9968 :     for (idx++; idx < blocks_.size(); idx++) {
     344        9762 :         int bit = find_first_clear64(blocks_[idx]);
     345        9762 :         if (bit > 0) {
     346        2978 :             return bit_position(idx, bit - 1);
     347             :         }
     348             :     }
     349         206 :     return size();
     350             : }
     351             : 
     352             : //
     353             : // Return (*this & rhs != 0).
     354             : //
     355         642 : bool BitSet::intersects(const BitSet &rhs) const {
     356         642 :     size_t minsize = std::min(blocks_.size(), rhs.blocks_.size());
     357        1188 :     for (size_t idx = 0; idx < minsize; idx++) {
     358        1168 :         if (blocks_[idx] & rhs.blocks_[idx])
     359         622 :             return true;
     360             :     }
     361          20 :     return false;
     362             : }
     363             : 
     364             : //
     365             : // Return (*this == rhs).
     366             : //
     367             : // Note that it's fine to first compare the number of blocks in the vectors
     368             : // since we always shrink the vectors whenever possible.
     369             : //
     370     1351578 : bool BitSet::operator==(const BitSet &rhs) const {
     371     1351578 :     if (blocks_.size() != rhs.blocks_.size())
     372     1207968 :         return false;
     373      301068 :     for (size_t idx = 0; idx < blocks_.size(); idx++) {
     374      161552 :         if (blocks_[idx] != rhs.blocks_[idx])
     375        4121 :             return false;
     376             :     }
     377      139478 :     return true;
     378             : }
     379             : 
     380             : //
     381             : // Return (*this != rhs).
     382             : //
     383      722088 : bool BitSet::operator!=(const BitSet &rhs) const {
     384      722088 :     return !operator==(rhs);
     385             : }
     386             : 
     387             : //
     388             : // Return (*this & rhs).
     389             : //
     390        4571 : BitSet BitSet::operator&(const BitSet &rhs) const {
     391        4571 :     BitSet temp;
     392        4571 :     temp.BuildIntersection(*this, rhs);
     393        4571 :     temp.check_invariants();
     394        4571 :     return temp;
     395           0 : }
     396             : 
     397             : //
     398             : // Return (*this | rhs).
     399             : //
     400         642 : BitSet BitSet::operator|(const BitSet &rhs) const {
     401         642 :     BitSet temp;
     402         642 :     size_t minsize = std::min(blocks_.size(), rhs.blocks_.size());
     403         642 :     size_t maxsize = std::max(blocks_.size(), rhs.blocks_.size());
     404         642 :     temp.blocks_.resize(maxsize);
     405             : 
     406             :     // Process common blocks.
     407        3578 :     for (size_t idx = 0; idx < minsize; idx++) {
     408        2936 :         temp.blocks_[idx] = blocks_[idx] | rhs.blocks_[idx];
     409             :     }
     410             : 
     411             :     // Process blocks that exist in LHS only. It's a noop if RHS is bigger.
     412        1262 :     for (size_t idx = minsize; idx < blocks_.size(); idx++) {
     413         620 :         temp.blocks_[idx] = blocks_[idx];
     414             :     }
     415             : 
     416             :     // Process blocks that exist in RHS only. It's a noop if LHS is bigger.
     417        1262 :     for (size_t idx = minsize; idx < rhs.blocks_.size(); idx++) {
     418         620 :         temp.blocks_[idx] = rhs.blocks_[idx];
     419             :     }
     420             : 
     421         642 :     temp.check_invariants();
     422         642 :     return temp;
     423           0 : }
     424             : 
     425             : //
     426             : // Implement (*this &= rhs).
     427             : //
     428             : // Note that we can't simply resize the vector to minsize since we may be
     429             : // able to shrink it even more depending on the values in the blocks.
     430             : //
     431         640 : BitSet &BitSet::operator&=(const BitSet &rhs) {
     432         640 :     size_t minsize = std::min(blocks_.size(), rhs.blocks_.size());
     433        3560 :     for (size_t idx = 0; idx < minsize; idx++) {
     434        2920 :         blocks_[idx] &= rhs.blocks_[idx];
     435             :     }
     436        1252 :     for (size_t idx = minsize; idx < blocks_.size(); idx++) {
     437         612 :         blocks_[idx] = 0;
     438             :     }
     439         640 :     compact();
     440         640 :     check_invariants();
     441         640 :     return *this;
     442             : }
     443             : 
     444             : //
     445             : // Implement (*this |= rhs).
     446             : //
     447             : // Note that we grow the vector only once instead of doing it multiple
     448             : // times.
     449             : //
     450     5727898 : BitSet &BitSet::operator|=(const BitSet &rhs) {
     451     5727898 :     if (blocks_.size() < rhs.blocks_.size())
     452     2400008 :         blocks_.resize(rhs.blocks_.size());
     453     9278314 :     for (size_t idx = 0; idx < rhs.blocks_.size(); idx++) {
     454     3550800 :         blocks_[idx] |= rhs.blocks_[idx];
     455             :     }
     456     5725246 :     check_invariants();
     457     5726417 :     return *this;
     458             : }
     459             : 
     460             : //
     461             : // Identical to operator|=.
     462             : //
     463     2261773 : void BitSet::Set(const BitSet &rhs) {
     464     2261773 :     this->operator|=(rhs);
     465     2261008 :     check_invariants();
     466     2260835 : }
     467             : 
     468             : //
     469             : // Implement (*this &= ~rhs).
     470             : //
     471     2658102 : void BitSet::Reset(const BitSet &rhs) {
     472     2658102 :     size_t minsize = std::min(blocks_.size(), rhs.blocks_.size());
     473     4551819 :     for (size_t idx = 0; idx < minsize; idx++) {
     474     1894056 :         blocks_[idx] &= ~rhs.blocks_[idx];
     475             :     }
     476     2657763 :     compact();
     477     2657260 :     check_invariants();
     478     2657053 : }
     479             : 
     480             : //
     481             : // Implement (*this = lhs & ~rhs).
     482             : //
     483             : // Note that we won't enter the second for loop at all if lhs is not bigger
     484             : // than rhs.  Need to compact only for this case, but it is cheap enough to
     485             : // try (and do nothing) when lhs is bigger than rhs.
     486             : //
     487      632680 : void BitSet::BuildComplement(const BitSet &lhs, const BitSet &rhs) {
     488      632680 :     blocks_.clear();
     489      632683 :     blocks_.resize(lhs.blocks_.size());
     490      632666 :     size_t minsize = std::min(blocks_.size(), rhs.blocks_.size());
     491     1241767 :     for (size_t idx = 0; idx < minsize; idx++) {
     492      609116 :         blocks_[idx] = lhs.blocks_[idx] & ~rhs.blocks_[idx];
     493             :     }
     494      640958 :     for (size_t idx = minsize; idx < lhs.blocks_.size(); idx++) {
     495        8307 :         blocks_[idx] = lhs.blocks_[idx];
     496             :     }
     497      632646 :     compact();
     498      632633 :     check_invariants();
     499      632625 : }
     500             : 
     501             : //
     502             : // Implement (*this = lhs & rhs).
     503             : //
     504             : // We avoid the need to compact or to resize multiple times by building
     505             : // the blocks in reverse order.
     506             : //
     507             : // Note that the for loop does not handle idx 0 since the loop  variable
     508             : // is unsigned.
     509             : //
     510     1086999 : void BitSet::BuildIntersection(const BitSet &lhs, const BitSet &rhs) {
     511     1086999 :     blocks_.clear();
     512     1086982 :     size_t minsize = std::min(lhs.blocks_.size(), rhs.blocks_.size());
     513             : 
     514     1086942 :     if (minsize == 0)
     515      195324 :         return;
     516             : 
     517      893978 :     for (size_t idx = minsize - 1; idx > 0; idx--) {
     518        2360 :         if (lhs.blocks_[idx] & rhs.blocks_[idx]) {
     519        1560 :             if (blocks_.size() == 0)
     520         482 :                 blocks_.resize(idx + 1);
     521        1560 :             blocks_[idx] = lhs.blocks_[idx] & rhs.blocks_[idx];
     522             :         }
     523             :     }
     524             : 
     525      891618 :     if (lhs.blocks_[0] & rhs.blocks_[0]) {
     526      876082 :         if (blocks_.size() == 0)
     527      875623 :             blocks_.resize(1);
     528      876087 :         blocks_[0] = lhs.blocks_[0] & rhs.blocks_[0];
     529             :     }
     530             : 
     531      891596 :     check_invariants();
     532             : }
     533             : 
     534             : //
     535             : // Return true if *this contains rhs.  Implemented as (rhs & ~*this != 0).
     536             : //
     537      932465 : bool BitSet::Contains(const BitSet &rhs) const {
     538      932465 :     if (blocks_.size() < rhs.blocks_.size())
     539        1642 :         return false;
     540     1247874 :     for (size_t idx = 0; idx < rhs.blocks_.size(); idx++) {
     541      320985 :         if (rhs.blocks_[idx] & ~blocks_[idx])
     542        4042 :             return false;
     543             :     }
     544      926835 :     return true;
     545             : }
     546             : 
     547             : //
     548             : // Returns string representation of the bitset. A character in the string is
     549             : // '1' if the corresponding bit is set, and '0' if it is not.  The character
     550             : // position i in the string corresponds to bit position i in the bitset.
     551             : //
     552        2905 : string BitSet::ToString() const {
     553        2905 :     size_t last_pos = find_last();
     554        2905 :     if (last_pos == BitSet::npos)
     555        1221 :         return string();
     556             : 
     557        1684 :     string str(last_pos + 1, '0');
     558        5919 :     for (size_t pos = find_first(); pos != BitSet::npos; pos = find_next(pos)) {
     559        4235 :         str[pos] = '1';
     560             :     }
     561        1684 :     return str;
     562        1684 : }
     563             : 
     564             : //
     565             : // Initialize the bitset from the provided string representation. The string
     566             : // must use the same format as described in ToString above. We traverse the
     567             : // string in reverse order to ensure that we do not have to resize multiple
     568             : // times.
     569             : //
     570             : // Note that the for loop does not handle str_idx 0 since the loop variable
     571             : // is unsigned.
     572             : //
     573          65 : void BitSet::FromString(string str) {
     574          65 :     blocks_.clear();
     575             : 
     576          65 :     if (str.length() == 0)
     577           1 :         return;
     578             : 
     579       31773 :     for (size_t str_idx = str.length() - 1; str_idx > 0; str_idx--) {
     580       31709 :         if (str[str_idx] == '1')
     581        2395 :             set(str_idx);
     582             :     }
     583             : 
     584          64 :     if (str[0] == '1')
     585          64 :         set(0);
     586             : }
     587             : 
     588             : //
     589             : // Returns numbered string representation of the bitset. The numbers are the
     590             : // bit positions that are set in the bitset.  Consecutive bit positions are
     591             : // represented as x-y and a comma is used as a separator for non-consecutive
     592             : // positions.
     593             : //
     594          68 : string BitSet::ToNumberedString() const {
     595          68 :     if (empty())
     596           1 :         return "-";
     597             : 
     598          67 :     ostringstream oss;
     599          67 :     bool range = false;
     600          67 :     size_t last_pos = BitSet::npos;
     601         207 :     for (size_t pos = find_first(); pos != BitSet::npos;
     602         140 :          last_pos = pos, pos = find_next(pos)) {
     603         140 :         if (last_pos == BitSet::npos) {
     604          67 :             oss << integerToString(pos);
     605          73 :         } else if (pos == last_pos + 1) {
     606          42 :             range = true;
     607          31 :         } else if (range) {
     608          10 :             oss << "-" << integerToString(last_pos);
     609          10 :             oss << "," << integerToString(pos);
     610          10 :             range = false;
     611             :         } else {
     612          21 :             oss << "," << integerToString(pos);
     613             :         }
     614             :     }
     615             : 
     616          67 :     if (range)
     617          17 :        oss << "-" << integerToString(last_pos);
     618             : 
     619          67 :     return oss.str();
     620          67 : }

Generated by: LCOV version 1.14