OpenDDS::DCPS::DisjointSequence Class Reference

#include <DisjointSequence.h>

List of all members.

Public Member Functions

 DisjointSequence ()
void reset ()
bool empty () const
SequenceNumber low () const
SequenceNumber high () const
SequenceNumber cumulative_ack () const
SequenceNumber last_ack () const
bool disjoint () const
bool contains (SequenceNumber value) const
bool insert (const SequenceRange &range, OPENDDS_VECTOR(SequenceRange)&added)
bool insert (const SequenceRange &range)
 Insert all numbers between range.first and range.second (both inclusive).
bool insert (SequenceNumber value)
 Shorthand for "insert(SequenceRange(value, value))".
bool insert (SequenceNumber value, CORBA::ULong num_bits, const CORBA::Long bits[])
bool to_bitmap (CORBA::Long bitmap[], CORBA::ULong length, CORBA::ULong &num_bits, bool invert=false) const
 OPENDDS_VECTOR (SequenceRange) missing_sequence_ranges() const
 Returns missing ranges of SequenceNumbers (internal gaps in the sequence).
 OPENDDS_VECTOR (SequenceRange) present_sequence_ranges() const
void dump () const

Static Public Member Functions

static bool fill_bitmap_range (CORBA::ULong low, CORBA::ULong high, CORBA::Long bitmap[], CORBA::ULong length, CORBA::ULong &num_bits)
 Set the bits in range [low, high] in the bitmap, updating num_bits.

Private Types

typedef bool(* SRCompare )(const SequenceRange &, const SequenceRange &)

Private Member Functions

typedef OPENDDS_SET_CMP (SequenceRange, SRCompare) RangeSet
bool insert_i (const SequenceRange &range, OPENDDS_VECTOR(SequenceRange)*gaps=0)
bool insert_bitmap_range (RangeSet::iterator &iter, const SequenceRange &sr)

Static Private Member Functions

static void validate (const SequenceRange &range)
static bool SequenceRange_LessThan (const SequenceRange &lhs, const SequenceRange &rhs)

Private Attributes

RangeSet sequences_

Detailed Description

Data structure for a set of SequenceNumbers. Sequence numbers can be inserted as single numbers, ranges, or RTPS-style bitmaps. The DisjointSequence can then be queried for contiguous ranges and internal gaps.

Definition at line 26 of file DisjointSequence.h.


Member Typedef Documentation

typedef bool(* OpenDDS::DCPS::DisjointSequence::SRCompare)(const SequenceRange &, const SequenceRange &) [private]

Definition at line 118 of file DisjointSequence.h.


Constructor & Destructor Documentation

ACE_INLINE OpenDDS::DCPS::DisjointSequence::DisjointSequence (  ) 

Definition at line 54 of file DisjointSequence.inl.

00055   : sequences_(SequenceRange_LessThan)
00056 {
00057 }


Member Function Documentation

bool OpenDDS::DCPS::DisjointSequence::contains ( SequenceNumber  value  )  const

Definition at line 328 of file DisjointSequence.cpp.

Referenced by OpenDDS::DCPS::RtpsUdpDataLink::process_data_i().

00329 {
00330   RangeSet::const_iterator iter =
00331     sequences_.lower_bound(SequenceRange(0 /*ignored*/, value));
00332   return iter != sequences_.end() && iter->first <= value;
00333 }

Here is the caller graph for this function:

ACE_INLINE SequenceNumber OpenDDS::DCPS::DisjointSequence::cumulative_ack (  )  const

Gets the high end of the lowest contiguous range. Named after the use case of tracking received messages (which may be received out-of-order) and then determining the largest value that the receiver is allowed to acknowledge (under a cumulative-acking protocol). If empty(), returns SEQUENCENUMBER_UNKNOWN.

Definition at line 26 of file DisjointSequence.inl.

References OpenDDS::RTPS::SEQUENCENUMBER_UNKNOWN, and sequences_.

Referenced by OpenDDS::DCPS::WriteDataContainer::data_delivered(), OpenDDS::DCPS::ReliableSession::deliver_held_data(), OpenDDS::DCPS::RtpsUdpDataLink::generate_nack_frags(), OpenDDS::DCPS::RtpsUdpDataLink::marshal_gaps(), OpenDDS::DCPS::RtpsUdpDataLink::HeldDataDeliveryHandler::notify_delivery(), OpenDDS::DCPS::RtpsUdpDataLink::process_data_i(), OpenDDS::DCPS::ReliableSession::ready_to_deliver(), OpenDDS::DCPS::RtpsUdpDataLink::send_ack_nacks(), OpenDDS::DCPS::ReliableSession::send_naks(), OpenDDS::DCPS::WriteDataContainer::sequence_acknowledged(), OpenDDS::DCPS::RtpsUdpDataLink::WriterInfo::should_nack(), and to_bitmap().

00027 {
00028   return sequences_.empty()
00029     ? SequenceNumber::SEQUENCENUMBER_UNKNOWN()
00030     : sequences_.begin()->second;
00031 }

Here is the caller graph for this function:

ACE_INLINE bool OpenDDS::DCPS::DisjointSequence::disjoint (  )  const
void OpenDDS::DCPS::DisjointSequence::dump ( void   )  const

Definition at line 345 of file DisjointSequence.cpp.

References LM_DEBUG, and sequences_.

Referenced by OpenDDS::DCPS::RtpsUdpDataLink::received(), OpenDDS::DCPS::RtpsUdpDataLink::send_directed_nack_replies(), OpenDDS::DCPS::RtpsUdpDataLink::send_nack_replies(), and OpenDDS::DCPS::ReliableSession::send_naks().

00346 {
00347   ACE_DEBUG((LM_DEBUG, "(%P|%t) DisjointSequence[%X]::dump included ranges of "
00348                        "SequenceNumbers:\n", this));
00349   for (RangeSet::const_iterator iter = sequences_.begin();
00350        iter != sequences_.end(); ++iter) {
00351     ACE_DEBUG((LM_DEBUG, "(%P|%t) DisjointSequence[%X]::dump\t%q-%q\n",
00352                this, iter->first.getValue(), iter->second.getValue()));
00353   }
00354 }

Here is the caller graph for this function:

ACE_INLINE bool OpenDDS::DCPS::DisjointSequence::empty ( void   )  const
bool OpenDDS::DCPS::DisjointSequence::fill_bitmap_range ( CORBA::ULong  low,
CORBA::ULong  high,
CORBA::Long  bitmap[],
CORBA::ULong  length,
CORBA::ULong num_bits 
) [static]

Set the bits in range [low, high] in the bitmap, updating num_bits.

Definition at line 234 of file DisjointSequence.cpp.

Referenced by OpenDDS::DCPS::RtpsUdpDataLink::extend_bitmap_range(), OpenDDS::DCPS::TransportReassembly::get_gaps(), OpenDDS::DCPS::RtpsUdpDataLink::send_ack_nacks(), and to_bitmap().

00237 {
00238   bool clamped = false;
00239   CORBA::ULong idx_low = low / 32, bit_low = low % 32,
00240                idx_high = high / 32, bit_high = high % 32;
00241 
00242   if (idx_low >= length) {
00243     return false;
00244   }
00245   if (idx_high >= length) {
00246     // clamp to largest number we can represent
00247     high = length * 32 - 1;
00248     idx_high = length - 1;
00249     bit_high = high % 32;
00250     clamped = true;
00251   }
00252 
00253   // clear any full Longs between the last bit we wrote and idx_low
00254   for (CORBA::ULong i = (num_bits + 31) / 32; i < idx_low; ++i) {
00255     bitmap[i] = 0;
00256   }
00257 
00258   // write the Long at idx_low, preserving bits that may already be there
00259   CORBA::ULong x = bitmap[idx_low]; // use unsigned for bitwise operators
00260   //    clear the bits in x in the range [bit_last, bit_low)
00261   if (num_bits > 0) {
00262     const size_t bit_last = ((num_bits - 1) / 32 == idx_low)
00263                             ? ((num_bits - 1) % 32 + 1) : 0;
00264     for (size_t m = bit_last; m < bit_low; ++m) {
00265       x &= ~(1 << (31 - m));
00266     }
00267   } else {
00268     x = 0;
00269   }
00270   //    set the bits in x in the range [bit_low, limit)
00271   const size_t limit = (idx_high == idx_low) ? bit_high : 31;
00272   for (size_t b = bit_low; b <= limit; ++b) {
00273     x |= (1 << (31 - b));
00274   }
00275   bitmap[idx_low] = x;
00276 
00277   // any full Longs inside the current range are set to all 1's
00278   for (CORBA::ULong elt = idx_low + 1; elt < idx_high; ++elt) {
00279     bitmap[elt] = 0xFFFFFFFF;
00280   }
00281 
00282   if (idx_high > idx_low) {
00283     // write the Long at idx_high, no need to preserve bits since this is
00284     // the first iteration that's writing it
00285     x = 0;
00286     for (size_t b = 0; b <= bit_high; ++b) {
00287       x |= (1 << (31 - b));
00288     }
00289     bitmap[idx_high] = x;
00290   }
00291 
00292   num_bits = high + 1;
00293   return !clamped;
00294 }

Here is the caller graph for this function:

ACE_INLINE SequenceNumber OpenDDS::DCPS::DisjointSequence::high (  )  const
bool OpenDDS::DCPS::DisjointSequence::insert ( SequenceNumber  value,
CORBA::ULong  num_bits,
const CORBA::Long  bits[] 
)

Insert using the RTPS compact representation of a set. The three parameters, taken together, describe a set with each 1 bit starting at the msb of bits[0] and extending through num_bits (which are located at the next least significant bits of bits[0] followed by the msb of bits[1], etc.) indicating presence of the number (value + bit_index) in the set. bit_index is 0-based. Precondition: the array 'bits' has at least ceil(num_bits / 32) entries.

Definition at line 89 of file DisjointSequence.cpp.

References OpenDDS::DCPS::SequenceNumber::getValue(), insert_bitmap_range(), and sequences_.

00091 {
00092   bool inserted = false;
00093   RangeSet::iterator iter = sequences_.end();
00094   SequenceNumber::Value range_start = 0;
00095   const SequenceNumber::Value val = value.getValue();
00096 
00097   // See RTPS v2.1 section 9.4.2.6 SequenceNumberSet
00098   for (CORBA::ULong i = 0, x = 0, bit = 0; i < num_bits; ++i, ++bit) {
00099 
00100     if (bit == 32) bit = 0;
00101 
00102     if (bit == 0) {
00103       x = static_cast<CORBA::ULong>(bits[i / 32]);
00104       if (x == 0) {
00105         // skip an entire Long if it's all 0's (adds 32 due to ++i)
00106         i += 31;
00107         bit = 31;
00108         //FUTURE: this could be generalized with something like the x86 "bsr"
00109         //        instruction using compiler intrinsics, VC++ _BitScanReverse()
00110         //        and GCC __builtin_clz()
00111         continue;
00112       }
00113     }
00114 
00115     if (x & (1 << (31 - bit))) {
00116       if (range_start == 0) {
00117         range_start = val + i;
00118       }
00119 
00120     } else if (range_start != 0) {
00121       // this is a "0" bit and we've previously seen a "1": insert a range
00122       const SequenceNumber::Value to_insert = val + i - 1;
00123       if (insert_bitmap_range(iter, SequenceRange(range_start, to_insert))) {
00124         inserted = true;
00125       }
00126       range_start = 0;
00127 
00128       if (iter != sequences_.end() && iter->second.getValue() != to_insert) {
00129         // skip ahead: next gap in sequence must be past iter->second
00130         CORBA::ULong next_i = CORBA::ULong(iter->second.getValue() - val);
00131         bit = next_i % 32;
00132         if (next_i / 32 != i / 32) {
00133           x = static_cast<CORBA::ULong>(bits[next_i / 32]);
00134         }
00135         i = next_i;
00136       }
00137     }
00138   }
00139 
00140   if (range_start != 0) {
00141     // iteration finished before we saw a "0" (inside a range)
00142     SequenceNumber range_end = (value + num_bits).previous();
00143     if (insert_bitmap_range(iter, SequenceRange(range_start, range_end))) {
00144       return true;
00145     }
00146   }
00147   return inserted;
00148 }

Here is the call graph for this function:

ACE_INLINE bool OpenDDS::DCPS::DisjointSequence::insert ( SequenceNumber  value  ) 

Shorthand for "insert(SequenceRange(value, value))".

Definition at line 66 of file DisjointSequence.inl.

References insert_i().

00067 {
00068   return insert_i(SequenceRange(value, value));
00069 }

Here is the call graph for this function:

ACE_INLINE bool OpenDDS::DCPS::DisjointSequence::insert ( const SequenceRange range  ) 

Insert all numbers between range.first and range.second (both inclusive).

Definition at line 72 of file DisjointSequence.inl.

References insert_i().

00073 {
00074   return insert_i(range);
00075 }

Here is the call graph for this function:

ACE_INLINE bool OpenDDS::DCPS::DisjointSequence::insert ( const SequenceRange range,
OPENDDS_VECTOR(SequenceRange)&  added 
)

All insert() methods return true upon modifying the set and false if the set already contained the SequenceNumber(s) that were to be inserted. This is the general form of insert() whereby the caller receives a list of sub-ranges of 'range' that were not already in the DisjointSequence. For example, given a DisjointSequence 'seq' containing (1, 2, 5, 9), calling seq.insert(SequenceRange(4, 12), v) returns true and yields v = [(4, 4), (6, 8), (10, 12)] and seq = (1, 2, 4, ..., 12).

Definition at line 78 of file DisjointSequence.inl.

References insert_i().

Referenced by OpenDDS::DCPS::ReliableSession::check_header(), OpenDDS::DCPS::WriteDataContainer::data_delivered(), OpenDDS::DCPS::ReliableSession::expire_naks(), OpenDDS::DCPS::ReliableSession::nakack_received(), OpenDDS::DCPS::RtpsUdpDataLink::process_data_i(), OpenDDS::DCPS::RtpsUdpDataLink::process_heartbeat_i(), OpenDDS::DCPS::RtpsUdpDataLink::process_requested_changes(), OpenDDS::DCPS::RtpsUdpDataLink::received(), OpenDDS::DCPS::ReliableSession::record_header_received(), OpenDDS::DCPS::SingleSendBuffer::resend_i(), OpenDDS::DCPS::RtpsUdpDataLink::send_ack_nacks(), OpenDDS::DCPS::RtpsUdpDataLink::send_nackfrag_replies(), OpenDDS::DCPS::ReliableSession::send_naks(), and OpenDDS::DCPS::ReliableSession::syn_hook().

00080 {
00081   return insert_i(range, &gaps);
00082 }

Here is the call graph for this function:

Here is the caller graph for this function:

bool OpenDDS::DCPS::DisjointSequence::insert_bitmap_range ( RangeSet::iterator &  iter,
const SequenceRange sr 
) [private]

Definition at line 151 of file DisjointSequence.cpp.

References high(), low(), and sequences_.

Referenced by insert().

00153 {
00154   // This is similar to insert_i(), except it doesn't need an O(log(n)) search
00155   // of sequences_ every time to find the starting point, and it doesn't
00156   // compute the 'gaps'.
00157 
00158   const SequenceNumber::Value previous = range.first.getValue() - 1,
00159     next = range.second.getValue() + 1;
00160 
00161   if (!sequences_.empty()) {
00162     if (iter == sequences_.end()) {
00163       iter = sequences_.lower_bound(SequenceRange(1 /*ignored*/, previous));
00164     } else {
00165       // start where we left off last time and get the lower_bound(previous)
00166       for (; iter != sequences_.end() && iter->second < previous; ++iter) ;
00167     }
00168   }
00169 
00170   if (iter == sequences_.end() || iter->first > next) {
00171     // can't combine on either side, insert a new range
00172     iter = sequences_.insert(iter, range);
00173     return true;
00174   }
00175 
00176   if (iter->first <= range.first && iter->second >= range.second) {
00177     // range is already covered by this DisjointSet
00178     return false;
00179   }
00180 
00181   // find the right-most (highest) range we can use
00182   RangeSet::iterator right = iter;
00183   for (; right != sequences_.end() && right->second < next; ++right) ;
00184 
00185   SequenceNumber high = range.second;
00186   if (right != sequences_.end()
00187       && right->first <= next && right->first > range.first) {
00188     high = right->second;
00189     ++right;
00190   }
00191 
00192   const SequenceNumber low = std::min(iter->first, range.first);
00193   sequences_.erase(iter, right);
00194 
00195   iter = sequences_.insert(SequenceRange(low, high)).first;
00196   return true;
00197 }

Here is the call graph for this function:

Here is the caller graph for this function:

bool OpenDDS::DCPS::DisjointSequence::insert_i ( const SequenceRange range,
OPENDDS_VECTOR(SequenceRange)*  gaps = 0 
) [private]

Definition at line 28 of file DisjointSequence.cpp.

References sequences_, validate(), and OpenDDS::DCPS::SequenceNumber::ZERO().

Referenced by insert().

00030 {
00031   validate(range);
00032 
00033   RangeSet::iterator range_above = sequences_.lower_bound(range);
00034   if (range_above != sequences_.end()
00035       && range_above->first <= range.first) {
00036     return false; // already have this range, nothing to insert
00037   }
00038 
00039   SequenceRange newRange = range;
00040   if (range_above != sequences_.end()
00041       && ++SequenceNumber(newRange.second) >= range_above->first) {
00042     // newRange overlaps range_above, replace range_above with modified newRange
00043     newRange.second = range_above->second;
00044     // move to just past this iterator for the erase
00045     ++range_above;
00046   }
00047 
00048   const SequenceNumber::Value previous = range.first.getValue() - 1;
00049   // find the lower_bound for the SequenceNumber just before this range
00050   // to see if any ranges need to combine
00051   const RangeSet::iterator range_below =
00052     sequences_.lower_bound(SequenceRange(1 /*ignored*/,
00053                                          (previous > 0) ? previous
00054                                          : SequenceNumber::ZERO()));
00055   if (range_below != sequences_.end()) {
00056     // if low end falls inside of the range_below range
00057     // then combine
00058     if (newRange.first > range_below->first) {
00059       newRange.first = range_below->first;
00060     }
00061 
00062     if (gaps) {
00063       RangeSet::iterator gap_iter = range_below;
00064       if (range.first < gap_iter->second) {
00065         gaps->push_back(SequenceRange(range.first,
00066                                       gap_iter->second.previous()));
00067       }
00068       SequenceNumber last_gap = gap_iter++->second;
00069       for (; gap_iter != range_above; ++gap_iter) {
00070         const SequenceNumber in_range =
00071           std::min(gap_iter->first.previous().getValue(),
00072                    range.second.getValue());
00073         gaps->push_back(SequenceRange(++last_gap, in_range));
00074         last_gap = gap_iter->second;
00075       }
00076       if (last_gap < range.second) {
00077         gaps->push_back(SequenceRange(++last_gap, range.second));
00078       }
00079     }
00080 
00081     sequences_.erase(range_below, range_above);
00082   }
00083 
00084   sequences_.insert(newRange);
00085   return true;
00086 }

Here is the call graph for this function:

Here is the caller graph for this function:

ACE_INLINE SequenceNumber OpenDDS::DCPS::DisjointSequence::last_ack (  )  const

Gets the low end of the highest contiguous range, may be thought of as the inverse of cumulative_ack(). If empty(), returns SEQUENCENUMBER_UNKNOWN.

Definition at line 34 of file DisjointSequence.inl.

References OpenDDS::RTPS::SEQUENCENUMBER_UNKNOWN, and sequences_.

Referenced by OpenDDS::DCPS::RtpsUdpDataLink::generate_nack_frags(), and OpenDDS::DCPS::RtpsUdpDataLink::send_ack_nacks().

00035 {
00036   return sequences_.empty()
00037     ? SequenceNumber::SEQUENCENUMBER_UNKNOWN()
00038     : sequences_.rbegin()->first;
00039 }

Here is the caller graph for this function:

ACE_INLINE SequenceNumber OpenDDS::DCPS::DisjointSequence::low (  )  const
typedef OpenDDS::DCPS::DisjointSequence::OPENDDS_SET_CMP ( SequenceRange  ,
SRCompare   
) [private]
OpenDDS::DCPS::DisjointSequence::OPENDDS_VECTOR ( SequenceRange   )  const

Returns a representation of the members of the sequence as a list of contiguous ranges (each Range is inclusive on both sides).

OpenDDS::DCPS::DisjointSequence::OPENDDS_VECTOR ( SequenceRange   )  const

Returns missing ranges of SequenceNumbers (internal gaps in the sequence).

ACE_INLINE void OpenDDS::DCPS::DisjointSequence::reset ( void   ) 

Definition at line 60 of file DisjointSequence.inl.

References sequences_.

Referenced by OpenDDS::DCPS::WriterInfo::reset_coherent_info(), and OpenDDS::DCPS::ReliableSession::syn_hook().

00061 {
00062   sequences_.clear();
00063 }

Here is the caller graph for this function:

static bool OpenDDS::DCPS::DisjointSequence::SequenceRange_LessThan ( const SequenceRange lhs,
const SequenceRange rhs 
) [inline, static, private]

Definition at line 112 of file DisjointSequence.h.

00114   {
00115     return lhs.second < rhs.second;
00116   }

bool OpenDDS::DCPS::DisjointSequence::to_bitmap ( CORBA::Long  bitmap[],
CORBA::ULong  length,
CORBA::ULong num_bits,
bool  invert = false 
) const

Inverse of insert(value, num_bits, bits). Populates array of bitmap[length] with the bitmap of ranges above the cumulative_ack() value. Sets the number of significant (used) bits in num_bits. The 'base' of the bitmap is one larger than cumulative_ack(). Returns true if the entire DisjointSequence was able to fit in bitmap[]. Returning false is not an error, it's just that the higher-valued ranges didn't fit. If invert is true, the 1's in the bitmap represent the missing_sequence_ranges() instead of the present_sequence_ranges(). Precondition: the array 'bits' has 'length' entries allocated.

Definition at line 200 of file DisjointSequence.cpp.

References cumulative_ack(), disjoint(), fill_bitmap_range(), OpenDDS::DCPS::SequenceNumber::getValue(), high(), low(), and sequences_.

Referenced by OpenDDS::DCPS::RtpsUdpDataLink::marshal_gaps(), and OpenDDS::DCPS::RtpsUdpDataLink::send_ack_nacks().

00202 {
00203   // num_bits will be 1 more than the index of the last bit we wrote
00204   num_bits = 0;
00205   if (!disjoint()) {
00206     return true;
00207   }
00208 
00209   const SequenceNumber base = ++SequenceNumber(cumulative_ack());
00210 
00211   for (RangeSet::const_iterator iter = sequences_.begin(), prev = iter++;
00212        iter != sequences_.end(); ++iter, ++prev) {
00213 
00214     CORBA::ULong low = 0, high = 0;
00215 
00216     if (invert) {
00217       low = CORBA::ULong(prev->second.getValue() + 1 - base.getValue());
00218       high = CORBA::ULong(iter->first.getValue() - 1 - base.getValue());
00219 
00220     } else {
00221       low = CORBA::ULong(iter->first.getValue() - base.getValue());
00222       high = CORBA::ULong(iter->second.getValue() - base.getValue());
00223     }
00224 
00225     if (!fill_bitmap_range(low, high, bitmap, length, num_bits)) {
00226       return false;
00227     }
00228   }
00229 
00230   return true;
00231 }

Here is the call graph for this function:

Here is the caller graph for this function:

void OpenDDS::DCPS::DisjointSequence::validate ( const SequenceRange range  )  [static, private]

Definition at line 336 of file DisjointSequence.cpp.

Referenced by insert_i().

00337 {
00338   if (range.first > range.second) {
00339     throw std::runtime_error("SequenceNumber range invalid, range must "
00340                              "be ascending.");
00341   }
00342 }

Here is the caller graph for this function:


Member Data Documentation


The documentation for this class was generated from the following files:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated on 10 Aug 2018 for OpenDDS by  doxygen 1.6.1