/kmess-2.0.6.1/src/network/extra/p2pfragmenttracker.cpp
# · C++ · 335 lines · 170 code · 55 blank · 110 comment · 32 complexity · 86d071f5591ac4c895cf9167838cfb36 MD5 · raw file
- /***************************************************************************
- p2pfragmenttracker.cpp - description
- -------------------
- begin : Wed Dec 5 2007
- copyright : (C) 2007 by Diederik van der Boor
- email : "vdboor" --at-- "codingdomain.com"
- ***************************************************************************/
- /***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- ***************************************************************************/
- #include "p2pfragmenttracker.h"
- #include "../../kmessdebug.h"
- #define ADDDIFF(orig, new) ( (new) > (orig) ? (new) - (orig) : 0 )
- /**
- * Constructor
- */
- P2PFragmentTracker::P2PFragmentTracker()
- : messageID_(0)
- , totalSize_(0)
- , transferredBytes_(0)
- {
- }
- /**
- * Destructor
- */
- P2PFragmentTracker::~P2PFragmentTracker()
- {
- }
- /**
- * @brief Return a string with the various received parts.
- * @returns String with 1..100 120..200 to indicate the received parts.
- */
- QString P2PFragmentTracker::getDebugMap() const
- {
- // Add header
- QString result( "id=" + QString::number( messageID_ ) +
- ", got " + QString::number( transferredBytes_ ) +
- " bytes of " + QString::number( totalSize_ ) );
- if( ! ranges_.isEmpty() )
- {
- result += ", ranges:";
- }
- // Add range details
- unsigned long realCount = 0;
- foreach( Range *range, ranges_ )
- {
- // Update string and counter
- result += " " + QString::number( range->start ) + ".." + QString::number( range->end );
- realCount += ( range->end - range->start );
- }
- // Test if counters are correct
- #ifdef KMESSTEST
- KMESS_ASSERT( realCount == transferredBytes_ );
- #endif
- if( realCount != transferredBytes_ )
- {
- result += " ERROR realCount=" + QString::number( realCount );
- }
- return "fragmentedMessage[ " + result + " ]";
- }
- /**
- * @brief Return the identifier of the tracked message.
- * @return The message identifier.
- */
- quint32 P2PFragmentTracker::getMessageID() const
- {
- return messageID_;
- }
- /**
- * @brief Return the number of bytes transferred.
- * @returns Number of bytes transferred.
- */
- quint32 P2PFragmentTracker::getTransferredBytes() const
- {
- // TODO: for KMess 1.6, remove this
- // Just soo close before release don't assume transferredBytes_ will always be correct
- quint32 realCount = 0;
- foreach( Range *range, ranges_ )
- {
- realCount += ( range->end - range->start );
- }
- if( transferredBytes_ != realCount )
- {
- kmWarning() << "P2PFragmentTracker: Invalid byte count for " << getDebugMap();
- return realCount;
- }
- return transferredBytes_;
- }
- /**
- * @brief Initialize the tracker with a new message.
- * @param messageID Unique identifier of the message.
- * @param totalSize The total size of the message.
- *
- * Resets the current state.
- */
- void P2PFragmentTracker::initialize( quint32 messageID, quint32 totalSize )
- {
- // Reset fields
- messageID_ = messageID;
- transferredBytes_ = 0;
- totalSize_ = totalSize;
- // Clear collection
- qDeleteAll( ranges_ );
- ranges_.clear();
- }
- /**
- * @brief Return whether the fragment tracker is complete.
- * @return Returns true if all fragments are transferred.
- */
- bool P2PFragmentTracker::isComplete() const
- {
- // Check the preconditions, otherwise don't even check the ranges.
- if( totalSize_== 0 || ranges_.count() != 1 || getTransferredBytes() < totalSize_ )
- {
- return false;
- }
- // Check the last remaining range.
- const Range *range = ranges_.first();
- return range->start == 0 && range->end >= totalSize_;
- }
- /**
- * @brief Return whether the tracker is empty, no data added yet.
- * @return Returns true if there is no data in the tracker.
- */
- bool P2PFragmentTracker::isEmpty() const
- {
- return ranges_.isEmpty();
- }
- /**
- * @brief Return whether the tracker is initialized to receive a part for the given message ID.
- * @param messageID Message identifier to verify.
- * @return Returns true if the tracker is initialized to receive parts of the given message ID.
- */
- bool P2PFragmentTracker::isInitialized( quint32 messageID ) const
- {
- return ( messageID_ == messageID );
- }
- /**
- * @brief // Register the arrival of a new fragment, updates the counters.
- * @param offset The starting point of the fragment.
- * @param size The length of the fragment.
- */
- void P2PFragmentTracker::registerFragment( quint32 offset, quint32 size )
- {
- // Calculate end, check unsafe values.
- quint32 newEnd = offset + size;
- if( newEnd > totalSize_ )
- {
- newEnd = totalSize_;
- }
- if( offset > totalSize_ )
- {
- return;
- }
- // This algorithm also allows overlapping parts
- // just to be on the safe side.
- // Example ranges:
- // --------------------------------
- // __A__ ___B___ __C__
- // | | | | | |
- //
- //
- // |__4__| |__3__| |__2__| |__1__|
- //
- //
- // |___________5_________|
- //
- // |____________6____________|
- //
- //
- // Posibilities for new range:
- // 1: after last one
- // 2: after current, but overlap next one.
- // 3: after previous, but before next
- // 4: append current
- // 4b: even fill the gap with the previous element.
- // 5: append current but span over next
- // 6: after current, span over next, but C contains the real new end.
- //
- // Unhandled option: fragment overlapping an existing one completely.
- Range *nextRange = 0; // as in "after current"
- Range *range = 0;
- // Initialize with first range.
- if( ranges_.isEmpty() )
- {
- range = new Range;
- range->start = offset;
- range->end = newEnd;
- ranges_.append( range );
- transferredBytes_ = size;
- }
- else
- {
- // More items, see where to add.
- int last = ranges_.count() - 1;
- bool merge = false;
- int i;
- for( i = last; i >= 0; i-- )
- {
- nextRange = range;
- range = ranges_.at( i );
- if( offset > range->end )
- {
- // new range starts after current item.
- // See it does not touche/overlap the next item, otherwise update that one instead.
- if( nextRange == 0 || newEnd < nextRange->start )
- {
- // No overlap, insert between current and next element.
- // Option 1 or 3
- i++;
- range = new Range;
- range->start = offset;
- range->end = newEnd;
- ranges_.insert( i, range );
- transferredBytes_ += size;
- return;
- }
- else
- {
- // Option 2.
- range = nextRange; // had to update this one after all.
- transferredBytes_ += ADDDIFF( offset, range->start );
- transferredBytes_ += ADDDIFF( range->end, newEnd );
- range->start = offset;
- range->end = qMax( nextRange->end, newEnd );
- // Fix next item, for next test
- i++;
- nextRange = ( i < last ) ? ranges_.at( i + 1 ) : 0;
- merge = true;
- break;
- }
- }
- else if( offset >= range->start )
- {
- // New range appends current item.
- // Option 4
- transferredBytes_ += ADDDIFF( range->end, newEnd );
- range->end = qMax( range->end, newEnd );
- // See if the item can be merged.
- // Option 4b and 5
- merge = true;
- break;
- }
- else if( i == 0 && offset < range->start )
- {
- // See if the item should be added before all elements.
- range = new Range;
- range->start = offset;
- range->end = newEnd;
- ranges_.insert( i, range );
- transferredBytes_ += size;
- merge = true;
- break;
- }
- }
- // Found an item.
- // See if item can be merged with next ones.
- if( merge )
- {
- while( nextRange != 0 && nextRange->start <= range->end )
- {
- quint32 rangeSize = ( nextRange->end - nextRange->start );
- transferredBytes_ -= rangeSize;
- if( nextRange->end > range->end ) // last item has highest range
- {
- transferredBytes_ += ( nextRange->end - range->end );
- }
- range->end = qMax( range->end, nextRange->end );
- delete ranges_.takeAt( i + 1 );
- last--;
- nextRange = ( i < last ) ? ranges_.at( i + 1 ) : 0;
- }
- }
- }
- }