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-08-03 02:19:58 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             :  */
       4             : 
       5             : #include "base/bitset.h"
       6             : 
       7             : #include <cassert>
       8             : #include <sstream>
       9             : #include <string>
      10             : #include <string.h>
      11             : 
      12             : #include "base/util.h"
      13             : #include "base/string_util.h"
      14             : 
      15             : using namespace std;
      16             : 
      17             : //
      18             : // Provides the same functionality as ffsl.  Needed as ffsl is not supported
      19             : // on all platforms. Note that the positions are numbered 1 through 64, with
      20             : // a return value of 0 indicating that there are no set bits.
      21             : //
      22     6694758 : static int find_first_set64(uint64_t value) {
      23             :     int bit;
      24             : 
      25     6694758 :     int lower = static_cast<int>(value);
      26     6694758 :     if ((bit = ffs(lower)) > 0)
      27     4557566 :         return bit;
      28             : 
      29     2137192 :     int upper = value >> 32;
      30     2137192 :     if ((bit = ffs(upper)) > 0)
      31       41145 :         return 32 + bit;
      32             : 
      33     2096047 :     return 0;
      34             : }
      35             : 
      36      646774 : static int find_first_clear64(uint64_t value) {
      37      646774 :     return find_first_set64(~value);
      38             : }
      39             : 
      40             : //
      41             : // Provides the same functionality as fls.  Needed as fls is not supported
      42             : // on all platforms. Note that the positions are numbered 1 through 32, with
      43             : // a return value of 0 indicating that there are no set bits.
      44             : //
      45        4480 : static int find_last_set32(uint32_t value) {
      46        4480 :     if (value == 0)
      47        2052 :         return 0;
      48             : 
      49        2428 :     int bit = 32;
      50        2428 :     if ((value & 0xFFFF0000U) == 0) {
      51        2065 :         value <<= 16;
      52        2065 :         bit -= 16;
      53             :     }
      54        2428 :     if ((value & 0xFF000000U) == 0) {
      55        2062 :         value <<= 8;
      56        2062 :         bit -= 8;
      57             :     }
      58        2428 :     if ((value & 0xF0000000U) == 0) {
      59        2078 :         value <<= 4;
      60        2078 :         bit -= 4;
      61             :     }
      62        2428 :     if ((value & 0xC0000000U) == 0) {
      63        1635 :         value <<= 2;
      64        1635 :         bit -= 2;
      65             :     }
      66        2428 :     if ((value & 0x80000000U) == 0) {
      67        1451 :         value <<= 1;
      68        1451 :         bit -= 1;
      69             :     }
      70             : 
      71        2428 :     return bit;
      72             : }
      73             : 
      74             : //
      75             : // Provides the same functionality as flsl.  Needed as flsl is not supported
      76             : // on all platforms. Note that the positions are numbered 1 through 64, with
      77             : // a return value of 0 indicating that there are no set bits.
      78             : //
      79        2428 : static int find_last_set64(uint64_t value) {
      80             :     int bit;
      81             : 
      82        2428 :     int upper = value >> 32;
      83        2428 :     if ((bit = find_last_set32(upper)) > 0)
      84         376 :         return 32 + bit;
      85             : 
      86        2052 :     int lower = static_cast<int>(value);
      87        2052 :     if ((bit = find_last_set32(lower)) > 0)
      88        2052 :         return bit;
      89             : 
      90           0 :     return 0;
      91             : }
      92             : 
      93             : //
      94             : // Return the number of set bits. K&R method.
      95             : //
      96       19659 : static int num_bits_set(uint64_t value) {
      97       19659 :     int count = 0;
      98     1070635 :     while (value != 0) {
      99     1050976 :         value &= value - 1;
     100     1050976 :         count++;
     101             :     }
     102       19659 :     return count;
     103             : }
     104             : 
     105             : // Position pos is w.r.t the entire bitset, starts at 0.
     106             : // Index    idx is the block number i.e. the index in the vector, starts at 0.
     107             : // Offset   offset is w.r.t a given 64 bit block, starts at 0.
     108    12928107 : static inline size_t block_index(size_t pos) {
     109    12928107 :     return pos / 64;
     110             : }
     111             : 
     112    17134173 : static inline size_t block_offset(size_t pos) {
     113    17134173 :     return pos % 64;
     114             : }
     115             : 
     116     4600917 : static inline size_t bit_position(size_t idx, size_t offset) {
     117     4600917 :     return (idx * 64 + offset);
     118             : }
     119             : 
     120             : const size_t BitSet::npos;
     121             : 
     122             : //
     123             : // Set bit at given position, growing the vector if needed.
     124             : //
     125     6073348 : BitSet &BitSet::set(size_t pos) {
     126     6073348 :     size_t idx = block_index(pos);
     127     6073330 :     if (idx >= blocks_.size())
     128     1463526 :         blocks_.resize(idx + 1);
     129     6073332 :     blocks_[idx] |= 1LL << block_offset(pos);
     130     6073599 :     return *this;
     131             : }
     132             : 
     133             : //
     134             : // Reset bit at given position, shrinking the vector if possible.
     135             : //
     136     1988911 : BitSet &BitSet::reset(size_t pos) {
     137     1988911 :     size_t idx = block_index(pos);
     138     1988900 :     if (idx < blocks_.size()) {
     139     1988816 :         blocks_[idx] &= ~(1LL << block_offset(pos));
     140     1988801 :         compact();
     141             :     }
     142     1988763 :     return *this;
     143             : }
     144             : 
     145             : // Test bit at given position.
     146      551878 : bool BitSet::test(size_t pos) const {
     147      551878 :     size_t idx = block_index(pos);
     148      551876 :     if (idx < blocks_.size()) {
     149      454878 :         return ((blocks_[idx] & (1LL << block_offset(pos))) != 0);
     150             :     } else {
     151       96996 :         return false;
     152             :     }
     153             : }
     154             : 
     155             : //
     156             : // Shortcut to reset all bits in the bitset.
     157             : //
     158       20596 : void BitSet::clear() {
     159       20596 :     blocks_.resize(0);
     160       20596 : }
     161             : 
     162             : //
     163             : // Return true if there are no bits in the bitset.
     164             : //
     165     6285490 : bool BitSet::empty() const {
     166     6285490 :     return (blocks_.size() == 0);
     167             : }
     168             : 
     169             : //
     170             : // Return true if no bits are set.
     171             : //
     172      368985 : bool BitSet::none() const {
     173      368985 :     return (blocks_.size() == 0);
     174             : }
     175             : 
     176             : //
     177             : // Return true at least one bit is set.
     178             : //
     179         642 : bool BitSet::any() const {
     180         642 :     return (blocks_.size() != 0);
     181             : }
     182             : 
     183             : //
     184             : // Return the raw number of bits in the bitset. Simply depends on the number
     185             : // of blocks in the vector.
     186             : //
     187       72460 : size_t BitSet::size() const {
     188       72460 :     return blocks_.size() * 64;
     189             : }
     190             : 
     191             : //
     192             : // Return total number of set bits.
     193             : //
     194        4332 : size_t BitSet::count() const {
     195        4332 :     size_t count = 0;
     196       23991 :     for (size_t idx = 0; idx < blocks_.size(); idx++) {
     197       19659 :         count += num_bits_set(blocks_[idx]);
     198             :     }
     199        4332 :     return count;
     200             : }
     201             : 
     202             : //
     203             : // Shrink the underlying vector as much as possible.  All trailing blocks
     204             : // that are 0 can be removed.
     205             : //
     206             : // Note that the for loop does not handle idx 0 since the loop  variable
     207             : // is unsigned.
     208             : //
     209     5282126 : void BitSet::compact() {
     210     5282126 :     if (blocks_.size() == 0)
     211      624741 :         return;
     212             : 
     213     4672380 :     for (size_t idx = blocks_.size() - 1; idx > 0; idx--) {
     214      109590 :         if (blocks_[idx] != 0) {
     215       94481 :             blocks_.resize(idx + 1);
     216       94481 :             return;
     217             :         }
     218             :     }
     219             : 
     220     4563092 :     if (blocks_[0] != 0) {
     221     1671977 :         blocks_.resize(1);
     222     1671971 :         return;
     223             :     }
     224             : 
     225     2890918 :     blocks_.clear();
     226             : }
     227             : 
     228             : //
     229             : // Sanity check a bitset.  The last block must never be 0.  Always called
     230             : // after any compaction is done or in cases where no compaction is needed.
     231             : //
     232    12148933 : void BitSet::check_invariants() {
     233    12148933 :     size_t mysize = blocks_.size();
     234    12148447 :     if (mysize != 0)
     235     8082055 :         assert(blocks_[mysize -1] != 0);
     236    12148190 : }
     237             : 
     238             : //
     239             : // Return the position of the first set bit.  Needs to compensate for the
     240             : // return value convention used by find_first_set64.
     241             : //
     242     2981252 : size_t BitSet::find_first() const {
     243     2981444 :     for (size_t idx = 0; idx < blocks_.size(); idx++) {
     244     1735066 :         int bit = find_first_set64(blocks_[idx]);
     245     1736276 :         if (bit > 0)
     246     1736084 :             return bit_position(idx, bit - 1);
     247             :     }
     248     1246490 :     return BitSet::npos;
     249             : }
     250             : 
     251             : //
     252             : // Return the position of the next set bit.  Needs to compensate for the
     253             : // return value convention used by find_first_set64.
     254             : //
     255     4304402 : size_t BitSet::find_next(size_t pos) const {
     256     4304402 :     size_t idx = block_index(pos);
     257             : 
     258             :     // If the block index is beyond the vector, we're done.
     259     4304056 :     if (idx >= blocks_.size())
     260        1087 :         return BitSet::npos;
     261             : 
     262             :     // If the offset is not 63, clear out the bits from 0 through offset
     263             :     // and look for the first set bit.
     264     4302939 :     if (block_offset(pos) < 63) {
     265     4302414 :         uint64_t temp = blocks_[idx] & ~((1LL << (block_offset(pos) + 1)) - 1);
     266     4302101 :         int bit = find_first_set64(temp);
     267     4303278 :         if (bit > 0)
     268     2563816 :             return bit_position(idx, bit - 1);
     269             :     }
     270             : 
     271             :     // Go through all blocks after the start block for the pos and see if
     272             :     // there's a set bit.
     273     1746570 :     for (idx++; idx < blocks_.size(); idx++) {
     274       10445 :         int bit = find_first_set64(blocks_[idx]);
     275       10456 :         if (bit > 0)
     276        3736 :             return bit_position(idx, bit - 1);
     277             :     }
     278     1736107 :     return BitSet::npos;
     279             : }
     280             : 
     281             : //
     282             : // Return the position of the last set bit.  Needs to compensate for the
     283             : // return value convention used by find_last_set64.
     284             : //
     285             : // Note that we only need to look at the last block since that must have
     286             : // at least one bit set.
     287             : //
     288        3718 : size_t BitSet::find_last() const {
     289        3718 :     if (blocks_.size() == 0)
     290        1290 :         return BitSet::npos;
     291             : 
     292        2428 :     size_t idx = blocks_.size() - 1;
     293        2428 :     int bit = find_last_set64(blocks_[idx]);
     294        2428 :     if (bit > 0)
     295        2428 :         return bit_position(idx, bit - 1);
     296             : 
     297           0 :     return BitSet::npos;
     298             : }
     299             : 
     300             : //
     301             : // Return the position of the first clear bit.  It could be beyond the last
     302             : // block in the vector. This is fine as we automatically grow the vector if
     303             : // needed from set().
     304             : //
     305             : // Need to compensate for return value convention used by find_first_clear64.
     306             : //
     307      360700 : size_t BitSet::find_first_clear() const {
     308      702212 :     for (size_t idx = 0; idx < blocks_.size(); idx++) {
     309      629984 :         int bit = find_first_clear64(blocks_[idx]);
     310      629984 :         if (bit > 0) {
     311      288472 :             return bit_position(idx, bit - 1);
     312             :         }
     313             :     }
     314       72228 :     return size();
     315             : }
     316             : 
     317             : //
     318             : // Return the position of the next clear bit.  It could be beyond the last
     319             : // block in the vector. This is fine as we automatically grow the vector if
     320             : // needed from set().
     321             : //
     322             : // Need to compensate for return value convention used by find_first_clear64.
     323             : //
     324       10508 : size_t BitSet::find_next_clear(size_t pos) const {
     325       10508 :     size_t idx = block_index(pos);
     326             : 
     327             :     // If the block index is beyond the vector, we're done.
     328       10508 :     if (idx >= blocks_.size())
     329        3383 :         return pos + 1;
     330             : 
     331             :     // If the offset is not 63, set all the bits from 0 through offset and
     332             :     // look for the first clear bit.
     333        7125 :     if (block_offset(pos) < 63) {
     334        7028 :         uint64_t temp = blocks_[idx] | ((1LL << (block_offset(pos) + 1)) - 1);
     335        7028 :         int bit = find_first_clear64(temp);
     336        7028 :         if (bit > 0)
     337        3941 :             return bit_position(idx, bit - 1);
     338             :     }
     339             : 
     340             :     // Go through all blocks after the start block for the pos and see if
     341             :     // there's a clear bit.
     342        9968 :     for (idx++; idx < blocks_.size(); idx++) {
     343        9762 :         int bit = find_first_clear64(blocks_[idx]);
     344        9762 :         if (bit > 0) {
     345        2978 :             return bit_position(idx, bit - 1);
     346             :         }
     347             :     }
     348         206 :     return size();
     349             : }
     350             : 
     351             : //
     352             : // Return (*this & rhs != 0).
     353             : //
     354         642 : bool BitSet::intersects(const BitSet &rhs) const {
     355         642 :     size_t minsize = std::min(blocks_.size(), rhs.blocks_.size());
     356        1188 :     for (size_t idx = 0; idx < minsize; idx++) {
     357        1168 :         if (blocks_[idx] & rhs.blocks_[idx])
     358         622 :             return true;
     359             :     }
     360          20 :     return false;
     361             : }
     362             : 
     363             : //
     364             : // Return (*this == rhs).
     365             : //
     366             : // Note that it's fine to first compare the number of blocks in the vectors
     367             : // since we always shrink the vectors whenever possible.
     368             : //
     369     1380568 : bool BitSet::operator==(const BitSet &rhs) const {
     370     1380568 :     if (blocks_.size() != rhs.blocks_.size())
     371     1239045 :         return false;
     372      296797 :     for (size_t idx = 0; idx < blocks_.size(); idx++) {
     373      159510 :         if (blocks_[idx] != rhs.blocks_[idx])
     374        4232 :             return false;
     375             :     }
     376      137272 :     return true;
     377             : }
     378             : 
     379             : //
     380             : // Return (*this != rhs).
     381             : //
     382      735644 : bool BitSet::operator!=(const BitSet &rhs) const {
     383      735644 :     return !operator==(rhs);
     384             : }
     385             : 
     386             : //
     387             : // Return (*this & rhs).
     388             : //
     389        4690 : BitSet BitSet::operator&(const BitSet &rhs) const {
     390        4690 :     BitSet temp;
     391        4690 :     temp.BuildIntersection(*this, rhs);
     392        4690 :     temp.check_invariants();
     393        4690 :     return temp;
     394           0 : }
     395             : 
     396             : //
     397             : // Return (*this | rhs).
     398             : //
     399         642 : BitSet BitSet::operator|(const BitSet &rhs) const {
     400         642 :     BitSet temp;
     401         642 :     size_t minsize = std::min(blocks_.size(), rhs.blocks_.size());
     402         642 :     size_t maxsize = std::max(blocks_.size(), rhs.blocks_.size());
     403         642 :     temp.blocks_.resize(maxsize);
     404             : 
     405             :     // Process common blocks.
     406        3578 :     for (size_t idx = 0; idx < minsize; idx++) {
     407        2936 :         temp.blocks_[idx] = blocks_[idx] | rhs.blocks_[idx];
     408             :     }
     409             : 
     410             :     // Process blocks that exist in LHS only. It's a noop if RHS is bigger.
     411        1262 :     for (size_t idx = minsize; idx < blocks_.size(); idx++) {
     412         620 :         temp.blocks_[idx] = blocks_[idx];
     413             :     }
     414             : 
     415             :     // Process blocks that exist in RHS only. It's a noop if LHS is bigger.
     416        1262 :     for (size_t idx = minsize; idx < rhs.blocks_.size(); idx++) {
     417         620 :         temp.blocks_[idx] = rhs.blocks_[idx];
     418             :     }
     419             : 
     420         642 :     temp.check_invariants();
     421         642 :     return temp;
     422           0 : }
     423             : 
     424             : //
     425             : // Implement (*this &= rhs).
     426             : //
     427             : // Note that we can't simply resize the vector to minsize since we may be
     428             : // able to shrink it even more depending on the values in the blocks.
     429             : //
     430         640 : BitSet &BitSet::operator&=(const BitSet &rhs) {
     431         640 :     size_t minsize = std::min(blocks_.size(), rhs.blocks_.size());
     432        3560 :     for (size_t idx = 0; idx < minsize; idx++) {
     433        2920 :         blocks_[idx] &= rhs.blocks_[idx];
     434             :     }
     435        1252 :     for (size_t idx = minsize; idx < blocks_.size(); idx++) {
     436         612 :         blocks_[idx] = 0;
     437             :     }
     438         640 :     compact();
     439         640 :     check_invariants();
     440         640 :     return *this;
     441             : }
     442             : 
     443             : //
     444             : // Implement (*this |= rhs).
     445             : //
     446             : // Note that we grow the vector only once instead of doing it multiple
     447             : // times.
     448             : //
     449     5712400 : BitSet &BitSet::operator|=(const BitSet &rhs) {
     450     5712400 :     if (blocks_.size() < rhs.blocks_.size())
     451     2389115 :         blocks_.resize(rhs.blocks_.size());
     452     9236780 :     for (size_t idx = 0; idx < rhs.blocks_.size(); idx++) {
     453     3525114 :         blocks_[idx] |= rhs.blocks_[idx];
     454             :     }
     455     5709736 :     check_invariants();
     456     5710801 :     return *this;
     457             : }
     458             : 
     459             : //
     460             : // Identical to operator|=.
     461             : //
     462     2243638 : void BitSet::Set(const BitSet &rhs) {
     463     2243638 :     this->operator|=(rhs);
     464     2243014 :     check_invariants();
     465     2242835 : }
     466             : 
     467             : //
     468             : // Implement (*this &= ~rhs).
     469             : //
     470     2645033 : void BitSet::Reset(const BitSet &rhs) {
     471     2645033 :     size_t minsize = std::min(blocks_.size(), rhs.blocks_.size());
     472     4529787 :     for (size_t idx = 0; idx < minsize; idx++) {
     473     1884970 :         blocks_[idx] &= ~rhs.blocks_[idx];
     474             :     }
     475     2644817 :     compact();
     476     2644438 :     check_invariants();
     477     2644264 : }
     478             : 
     479             : //
     480             : // Implement (*this = lhs & ~rhs).
     481             : //
     482             : // Note that we won't enter the second for loop at all if lhs is not bigger
     483             : // than rhs.  Need to compact only for this case, but it is cheap enough to
     484             : // try (and do nothing) when lhs is bigger than rhs.
     485             : //
     486      648283 : void BitSet::BuildComplement(const BitSet &lhs, const BitSet &rhs) {
     487      648283 :     blocks_.clear();
     488      648281 :     blocks_.resize(lhs.blocks_.size());
     489      648296 :     size_t minsize = std::min(blocks_.size(), rhs.blocks_.size());
     490     1273233 :     for (size_t idx = 0; idx < minsize; idx++) {
     491      624938 :         blocks_[idx] = lhs.blocks_[idx] & ~rhs.blocks_[idx];
     492             :     }
     493      656542 :     for (size_t idx = minsize; idx < lhs.blocks_.size(); idx++) {
     494        8247 :         blocks_[idx] = lhs.blocks_[idx];
     495             :     }
     496      648283 :     compact();
     497      648287 :     check_invariants();
     498      648275 : }
     499             : 
     500             : //
     501             : // Implement (*this = lhs & rhs).
     502             : //
     503             : // We avoid the need to compact or to resize multiple times by building
     504             : // the blocks in reverse order.
     505             : //
     506             : // Note that the for loop does not handle idx 0 since the loop  variable
     507             : // is unsigned.
     508             : //
     509     1099444 : void BitSet::BuildIntersection(const BitSet &lhs, const BitSet &rhs) {
     510     1099444 :     blocks_.clear();
     511     1099469 :     size_t minsize = std::min(lhs.blocks_.size(), rhs.blocks_.size());
     512             : 
     513     1099437 :     if (minsize == 0)
     514      195597 :         return;
     515             : 
     516      906200 :     for (size_t idx = minsize - 1; idx > 0; idx--) {
     517        2360 :         if (lhs.blocks_[idx] & rhs.blocks_[idx]) {
     518        1560 :             if (blocks_.size() == 0)
     519         482 :                 blocks_.resize(idx + 1);
     520        1560 :             blocks_[idx] = lhs.blocks_[idx] & rhs.blocks_[idx];
     521             :         }
     522             :     }
     523             : 
     524      903840 :     if (lhs.blocks_[0] & rhs.blocks_[0]) {
     525      886986 :         if (blocks_.size() == 0)
     526      886541 :             blocks_.resize(1);
     527      887031 :         blocks_[0] = lhs.blocks_[0] & rhs.blocks_[0];
     528             :     }
     529             : 
     530      903863 :     check_invariants();
     531             : }
     532             : 
     533             : //
     534             : // Return true if *this contains rhs.  Implemented as (rhs & ~*this != 0).
     535             : //
     536      933516 : bool BitSet::Contains(const BitSet &rhs) const {
     537      933516 :     if (blocks_.size() < rhs.blocks_.size())
     538        1708 :         return false;
     539     1234542 :     for (size_t idx = 0; idx < rhs.blocks_.size(); idx++) {
     540      306634 :         if (rhs.blocks_[idx] & ~blocks_[idx])
     541        4022 :             return false;
     542             :     }
     543      927840 :     return true;
     544             : }
     545             : 
     546             : //
     547             : // Returns string representation of the bitset. A character in the string is
     548             : // '1' if the corresponding bit is set, and '0' if it is not.  The character
     549             : // position i in the string corresponds to bit position i in the bitset.
     550             : //
     551        3205 : string BitSet::ToString() const {
     552        3205 :     size_t last_pos = find_last();
     553        3205 :     if (last_pos == BitSet::npos)
     554        1289 :         return string();
     555             : 
     556        1916 :     string str(last_pos + 1, '0');
     557        6519 :     for (size_t pos = find_first(); pos != BitSet::npos; pos = find_next(pos)) {
     558        4603 :         str[pos] = '1';
     559             :     }
     560        1916 :     return str;
     561        1916 : }
     562             : 
     563             : //
     564             : // Initialize the bitset from the provided string representation. The string
     565             : // must use the same format as described in ToString above. We traverse the
     566             : // string in reverse order to ensure that we do not have to resize multiple
     567             : // times.
     568             : //
     569             : // Note that the for loop does not handle str_idx 0 since the loop variable
     570             : // is unsigned.
     571             : //
     572          65 : void BitSet::FromString(string str) {
     573          65 :     blocks_.clear();
     574             : 
     575          65 :     if (str.length() == 0)
     576           1 :         return;
     577             : 
     578       31773 :     for (size_t str_idx = str.length() - 1; str_idx > 0; str_idx--) {
     579       31709 :         if (str[str_idx] == '1')
     580        2395 :             set(str_idx);
     581             :     }
     582             : 
     583          64 :     if (str[0] == '1')
     584          64 :         set(0);
     585             : }
     586             : 
     587             : //
     588             : // Returns numbered string representation of the bitset. The numbers are the
     589             : // bit positions that are set in the bitset.  Consecutive bit positions are
     590             : // represented as x-y and a comma is used as a separator for non-consecutive
     591             : // positions.
     592             : //
     593          66 : string BitSet::ToNumberedString() const {
     594          66 :     if (empty())
     595           1 :         return "-";
     596             : 
     597          65 :     ostringstream oss;
     598          65 :     bool range = false;
     599          65 :     size_t last_pos = BitSet::npos;
     600         203 :     for (size_t pos = find_first(); pos != BitSet::npos;
     601         138 :          last_pos = pos, pos = find_next(pos)) {
     602         138 :         if (last_pos == BitSet::npos) {
     603          65 :             oss << integerToString(pos);
     604          73 :         } else if (pos == last_pos + 1) {
     605          42 :             range = true;
     606          31 :         } else if (range) {
     607          10 :             oss << "-" << integerToString(last_pos);
     608          10 :             oss << "," << integerToString(pos);
     609          10 :             range = false;
     610             :         } else {
     611          21 :             oss << "," << integerToString(pos);
     612             :         }
     613             :     }
     614             : 
     615          65 :     if (range)
     616          17 :        oss << "-" << integerToString(last_pos);
     617             : 
     618          65 :     return oss.str();
     619          65 : }

Generated by: LCOV version 1.14