/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

  1. /***************************************************************************
  2. p2pfragmenttracker.cpp - description
  3. -------------------
  4. begin : Wed Dec 5 2007
  5. copyright : (C) 2007 by Diederik van der Boor
  6. email : "vdboor" --at-- "codingdomain.com"
  7. ***************************************************************************/
  8. /***************************************************************************
  9. * *
  10. * This program is free software; you can redistribute it and/or modify *
  11. * it under the terms of the GNU General Public License as published by *
  12. * the Free Software Foundation; either version 2 of the License, or *
  13. * (at your option) any later version. *
  14. * *
  15. ***************************************************************************/
  16. #include "p2pfragmenttracker.h"
  17. #include "../../kmessdebug.h"
  18. #define ADDDIFF(orig, new) ( (new) > (orig) ? (new) - (orig) : 0 )
  19. /**
  20. * Constructor
  21. */
  22. P2PFragmentTracker::P2PFragmentTracker()
  23. : messageID_(0)
  24. , totalSize_(0)
  25. , transferredBytes_(0)
  26. {
  27. }
  28. /**
  29. * Destructor
  30. */
  31. P2PFragmentTracker::~P2PFragmentTracker()
  32. {
  33. }
  34. /**
  35. * @brief Return a string with the various received parts.
  36. * @returns String with 1..100 120..200 to indicate the received parts.
  37. */
  38. QString P2PFragmentTracker::getDebugMap() const
  39. {
  40. // Add header
  41. QString result( "id=" + QString::number( messageID_ ) +
  42. ", got " + QString::number( transferredBytes_ ) +
  43. " bytes of " + QString::number( totalSize_ ) );
  44. if( ! ranges_.isEmpty() )
  45. {
  46. result += ", ranges:";
  47. }
  48. // Add range details
  49. unsigned long realCount = 0;
  50. foreach( Range *range, ranges_ )
  51. {
  52. // Update string and counter
  53. result += " " + QString::number( range->start ) + ".." + QString::number( range->end );
  54. realCount += ( range->end - range->start );
  55. }
  56. // Test if counters are correct
  57. #ifdef KMESSTEST
  58. KMESS_ASSERT( realCount == transferredBytes_ );
  59. #endif
  60. if( realCount != transferredBytes_ )
  61. {
  62. result += " ERROR realCount=" + QString::number( realCount );
  63. }
  64. return "fragmentedMessage[ " + result + " ]";
  65. }
  66. /**
  67. * @brief Return the identifier of the tracked message.
  68. * @return The message identifier.
  69. */
  70. quint32 P2PFragmentTracker::getMessageID() const
  71. {
  72. return messageID_;
  73. }
  74. /**
  75. * @brief Return the number of bytes transferred.
  76. * @returns Number of bytes transferred.
  77. */
  78. quint32 P2PFragmentTracker::getTransferredBytes() const
  79. {
  80. // TODO: for KMess 1.6, remove this
  81. // Just soo close before release don't assume transferredBytes_ will always be correct
  82. quint32 realCount = 0;
  83. foreach( Range *range, ranges_ )
  84. {
  85. realCount += ( range->end - range->start );
  86. }
  87. if( transferredBytes_ != realCount )
  88. {
  89. kmWarning() << "P2PFragmentTracker: Invalid byte count for " << getDebugMap();
  90. return realCount;
  91. }
  92. return transferredBytes_;
  93. }
  94. /**
  95. * @brief Initialize the tracker with a new message.
  96. * @param messageID Unique identifier of the message.
  97. * @param totalSize The total size of the message.
  98. *
  99. * Resets the current state.
  100. */
  101. void P2PFragmentTracker::initialize( quint32 messageID, quint32 totalSize )
  102. {
  103. // Reset fields
  104. messageID_ = messageID;
  105. transferredBytes_ = 0;
  106. totalSize_ = totalSize;
  107. // Clear collection
  108. qDeleteAll( ranges_ );
  109. ranges_.clear();
  110. }
  111. /**
  112. * @brief Return whether the fragment tracker is complete.
  113. * @return Returns true if all fragments are transferred.
  114. */
  115. bool P2PFragmentTracker::isComplete() const
  116. {
  117. // Check the preconditions, otherwise don't even check the ranges.
  118. if( totalSize_== 0 || ranges_.count() != 1 || getTransferredBytes() < totalSize_ )
  119. {
  120. return false;
  121. }
  122. // Check the last remaining range.
  123. const Range *range = ranges_.first();
  124. return range->start == 0 && range->end >= totalSize_;
  125. }
  126. /**
  127. * @brief Return whether the tracker is empty, no data added yet.
  128. * @return Returns true if there is no data in the tracker.
  129. */
  130. bool P2PFragmentTracker::isEmpty() const
  131. {
  132. return ranges_.isEmpty();
  133. }
  134. /**
  135. * @brief Return whether the tracker is initialized to receive a part for the given message ID.
  136. * @param messageID Message identifier to verify.
  137. * @return Returns true if the tracker is initialized to receive parts of the given message ID.
  138. */
  139. bool P2PFragmentTracker::isInitialized( quint32 messageID ) const
  140. {
  141. return ( messageID_ == messageID );
  142. }
  143. /**
  144. * @brief // Register the arrival of a new fragment, updates the counters.
  145. * @param offset The starting point of the fragment.
  146. * @param size The length of the fragment.
  147. */
  148. void P2PFragmentTracker::registerFragment( quint32 offset, quint32 size )
  149. {
  150. // Calculate end, check unsafe values.
  151. quint32 newEnd = offset + size;
  152. if( newEnd > totalSize_ )
  153. {
  154. newEnd = totalSize_;
  155. }
  156. if( offset > totalSize_ )
  157. {
  158. return;
  159. }
  160. // This algorithm also allows overlapping parts
  161. // just to be on the safe side.
  162. // Example ranges:
  163. // --------------------------------
  164. // __A__ ___B___ __C__
  165. // | | | | | |
  166. //
  167. //
  168. // |__4__| |__3__| |__2__| |__1__|
  169. //
  170. //
  171. // |___________5_________|
  172. //
  173. // |____________6____________|
  174. //
  175. //
  176. // Posibilities for new range:
  177. // 1: after last one
  178. // 2: after current, but overlap next one.
  179. // 3: after previous, but before next
  180. // 4: append current
  181. // 4b: even fill the gap with the previous element.
  182. // 5: append current but span over next
  183. // 6: after current, span over next, but C contains the real new end.
  184. //
  185. // Unhandled option: fragment overlapping an existing one completely.
  186. Range *nextRange = 0; // as in "after current"
  187. Range *range = 0;
  188. // Initialize with first range.
  189. if( ranges_.isEmpty() )
  190. {
  191. range = new Range;
  192. range->start = offset;
  193. range->end = newEnd;
  194. ranges_.append( range );
  195. transferredBytes_ = size;
  196. }
  197. else
  198. {
  199. // More items, see where to add.
  200. int last = ranges_.count() - 1;
  201. bool merge = false;
  202. int i;
  203. for( i = last; i >= 0; i-- )
  204. {
  205. nextRange = range;
  206. range = ranges_.at( i );
  207. if( offset > range->end )
  208. {
  209. // new range starts after current item.
  210. // See it does not touche/overlap the next item, otherwise update that one instead.
  211. if( nextRange == 0 || newEnd < nextRange->start )
  212. {
  213. // No overlap, insert between current and next element.
  214. // Option 1 or 3
  215. i++;
  216. range = new Range;
  217. range->start = offset;
  218. range->end = newEnd;
  219. ranges_.insert( i, range );
  220. transferredBytes_ += size;
  221. return;
  222. }
  223. else
  224. {
  225. // Option 2.
  226. range = nextRange; // had to update this one after all.
  227. transferredBytes_ += ADDDIFF( offset, range->start );
  228. transferredBytes_ += ADDDIFF( range->end, newEnd );
  229. range->start = offset;
  230. range->end = qMax( nextRange->end, newEnd );
  231. // Fix next item, for next test
  232. i++;
  233. nextRange = ( i < last ) ? ranges_.at( i + 1 ) : 0;
  234. merge = true;
  235. break;
  236. }
  237. }
  238. else if( offset >= range->start )
  239. {
  240. // New range appends current item.
  241. // Option 4
  242. transferredBytes_ += ADDDIFF( range->end, newEnd );
  243. range->end = qMax( range->end, newEnd );
  244. // See if the item can be merged.
  245. // Option 4b and 5
  246. merge = true;
  247. break;
  248. }
  249. else if( i == 0 && offset < range->start )
  250. {
  251. // See if the item should be added before all elements.
  252. range = new Range;
  253. range->start = offset;
  254. range->end = newEnd;
  255. ranges_.insert( i, range );
  256. transferredBytes_ += size;
  257. merge = true;
  258. break;
  259. }
  260. }
  261. // Found an item.
  262. // See if item can be merged with next ones.
  263. if( merge )
  264. {
  265. while( nextRange != 0 && nextRange->start <= range->end )
  266. {
  267. quint32 rangeSize = ( nextRange->end - nextRange->start );
  268. transferredBytes_ -= rangeSize;
  269. if( nextRange->end > range->end ) // last item has highest range
  270. {
  271. transferredBytes_ += ( nextRange->end - range->end );
  272. }
  273. range->end = qMax( range->end, nextRange->end );
  274. delete ranges_.takeAt( i + 1 );
  275. last--;
  276. nextRange = ( i < last ) ? ranges_.at( i + 1 ) : 0;
  277. }
  278. }
  279. }
  280. }