Author: alanmc
Date: 2006-11-05 14:08:19 -0500 (Sun, 05 Nov 2006)
New Revision: 67390
Added:
trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/
trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/EndGamePicker.cs
trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/IPiecePicker.cs
trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/PieceManager.cs
trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/StandardPicker.cs
Removed:
trunk/bitsharp/src/MonoTorrent.Client/Managers/PieceManager.cs
Modified:
trunk/bitsharp/src/MonoTorrent.Client/Managers/TorrentManager.cs
trunk/bitsharp/src/MonoTorrent.Client/MonoTorrent.Client.csproj
trunk/bitsharp/src/TestClient/main.cs
Log:
75% of the way to getting an EndGamePicker written. This refactor paves the way
to finish off implementation of the EndGame mode, which should speed up the
last few % of a torrent hugely.
Deleted: trunk/bitsharp/src/MonoTorrent.Client/Managers/PieceManager.cs
===================================================================
--- trunk/bitsharp/src/MonoTorrent.Client/Managers/PieceManager.cs
2006-11-05 19:02:24 UTC (rev 67389)
+++ trunk/bitsharp/src/MonoTorrent.Client/Managers/PieceManager.cs
2006-11-05 19:08:19 UTC (rev 67390)
@@ -1,451 +0,0 @@
-//
-// PieceManager.cs
-//
-// Authors:
-// Alan McGovern [EMAIL PROTECTED]
-//
-// Copyright (C) 2006 Alan McGovern
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-
-
-using System;
-using System.Collections.Generic;
-using MonoTorrent.Client.PeerMessages;
-using MonoTorrent.Common;
-
-namespace MonoTorrent.Client
-{
- /// <summary>
- /// Contains the logic for choosing what piece to download next
- /// </summary>
- internal class PieceManager
- {
- #region Events
- /// <summary>
- /// Event that's fired every time a Piece changes
- /// </summary>
- public event EventHandler<PieceEventArgs> OnPieceChanged;
- #endregion
-
-
- #region Member Variables
- private int[] priorities;
-
- /// <summary>
- /// The bitfield for the torrent
- /// </summary>
- public BitField MyBitField
- {
- get { return this.bitfield; }
- }
- private BitField bitfield;
-
- private BitField bufferBitfield;
- private BitField previousBitfield;
- private BitField isInterestingBuffer;
-
-
- private TorrentFile[] torrentFiles;
- private Dictionary<PeerConnectionID, List<Piece>> requests;
-
- /// <summary>
- /// Requests the next available piece from the supplied peer
- /// </summary>
- /// <param name="id">The peer to request a piece from</param>
- /// <returns>A RequestMessage for the next free piece</returns>
- //public RequestMessage RequestPiece(PeerConnectionID id, Peers
otherPeers)
- //{
- // throw new NotImplementedException();
- // //lock (this.bufferField)
- // //{
- // // Piece piece;
-
- // // if (id.Peer.Connection.SuggestedPieces.Count > 0)
- // // {
- // //
while(this.mybitField[id.Peer.Connection.SuggestedPieces[0]]
- // // && id.Peer.Connection.SuggestedPieces.Count > 0)
- // // id.Peer.Connection.SuggestedPieces.RemoveAt(0);
-
- // // if (id.Peer.Connection.SuggestedPieces.Count != 0)
- // // {
- // // piece = new
Piece(id.Peer.Connection.SuggestedPieces[0], id.TorrentManager.Torrent);
- // // piece[0].Requested = true;
- // //
this.mybitField[id.Peer.Connection.SuggestedPieces[0]] = true; // Flag this
one as downloaded. If it hashfails, remove it
- // // this.requests.Add(id, piece);
- // // id.Peer.Connection.SuggestedPieces.RemoveAt(0);
- // // return piece[0].CreateRequest();
- // // }
- // // }
- // // lock (this.mybitField)
- // // Array.Copy(this.mybitField.Array, 0,
bufferField.Array, 0, this.mybitField.Array.Length);
-
- // // bufferField.Not();
- // // bufferField.And(id.Peer.Connection.BitField);
-
- // // if (requests.ContainsKey(id)) // There's a pending
request on this peer
- // // {
- // // piece = requests[id];
- // // foreach (Block block in piece)
- // // {
- // // if (!block.Requested)
- // // {
- // // block.Requested = true;
- // // return block.CreateRequest();
- // // }
- // // }
-
- // // return null; // We've requested all the blocks
in this piece
- // // }
-
- // // int i = -1;
- // // for (int k = 0; k < (this.priorities.Length - 1); k++)
// ignore "DoNotDownloads"
- // // {
- // // for (int j = 0; j < this.torrentFiles.Length; j++)
- // // {
- // // if (this.torrentFiles[j].Priority != (Priority)k)
- // // continue;
-
- // // i =
bufferField.FirstTrue(this.torrentFiles[j].StartPieceIndex,
this.torrentFiles[j].EndPieceIndex);
- // // if (i == -1)
- // // continue;
-
- // // break;
- // // }
- // // }
-
- // // if (i == -1)
- // // return null;
-
- // // piece = new Piece(i, id.TorrentManager.Torrent);
- // // piece[0].Requested = true;
- // // this.mybitField[i] = true; // Flag this one as
downloaded. If it hashfails, remove it
- // // this.requests.Add(id, piece);
- // // return piece[0].CreateRequest();
- // //}
- //}
-
- /// <summary>
- /// Returns the number of outstanding requests
- /// </summary>
- public int CurrentRequestCount()
- {
- int result = 0;
- lock (this.requests)
- {
- foreach (KeyValuePair<PeerConnectionID, List<Piece>> keypair
in this.requests)
- result += keypair.Value.Count;
- }
- return result;
- }
- #endregion
-
-
- #region Constructors
- /// <summary>
- ///
- /// </summary>
- /// <param name="bitField"></param>
- public PieceManager(BitField bitField, TorrentFile[] torrentFiles)
- {
- this.torrentFiles = torrentFiles;
- this.requests = new Dictionary<PeerConnectionID, List<Piece>>(32);
- this.isInterestingBuffer = new BitField(bitField.Length);
- this.priorities = (int[])Enum.GetValues(typeof(Priority));
- Array.Sort<int>(this.priorities);
- Array.Reverse(this.priorities);
-
- this.bitfield = bitField;
- this.bufferBitfield = new BitField(bitfield.Length);
- this.previousBitfield = new BitField(bitfield.Length);
- this.torrentFiles = torrentFiles;
- this.requests = new Dictionary<PeerConnectionID, List<Piece>>();
-
- // Order the priorities in decending order of priority. i.e.
Immediate is first, and DoNotDownload is last
- this.priorities = (int[])Enum.GetValues(typeof(Priority));
- Array.Sort<int>(this.priorities);
- Array.Reverse(this.priorities);
- }
- #endregion
-
-
- #region Methods
- public RequestMessage PickPiece(PeerConnectionID id, Peers otherPeers)
- {
- int requestIndex = 0;
- Priority highestPriorityFound = Priority.DoNotDownload;
-
- lock (this.bitfield)
- {
- // If there is already a request there, request the next block.
- if (this.requests.ContainsKey(id))
- {
- List<Piece> reqs = this.requests[id];
- for (int i = 0; i < reqs.Count; i++)
- for (int j = 0; j < reqs[i].Blocks.Length; j++)
- if (!reqs[i][j].Requested)
- {
- reqs[i][j].Requested = true;
- return reqs[i][j].CreateRequest();
- }
- }
-
- // We get here if there were no free blocks to request.
- // We see if the peer has suggested any pieces we should
request
- while (id.Peer.Connection.SuggestedPieces.Count > 0)
- {
- // If we already have that piece, then remove it from the
suggested pieces
- if (this.bitfield[id.Peer.Connection.SuggestedPieces[0]])
- {
- id.Peer.Connection.SuggestedPieces.RemoveAt(0);
- continue;
- }
-
- requestIndex = id.Peer.Connection.SuggestedPieces[0];
- id.Peer.Connection.SuggestedPieces.RemoveAt(0);
- this.bitfield[requestIndex] = true;
- return this.GenerateRequest(id, requestIndex);
- }
-
- // Now we see what pieces the peer has that we don't have and
try and request one
- Buffer.BlockCopy(this.bitfield.Array, 0, bufferBitfield.Array,
0, this.bufferBitfield.Array.Length * 4);
- this.bufferBitfield.Not();
- this.bufferBitfield.And(id.Peer.Connection.BitField);
-
- // Out of the pieces we have, we look for the highest priority
and store it here.
- // We then keep NANDing the bitfield until we no longer have
pieces with that priority.
- // Thats when we go back 1 step and download a piece with the
original "highestPriorityFound"
- highestPriorityFound =
HighestPriorityForAvailablePieces(this.bufferBitfield);
-
- if (highestPriorityFound == Priority.DoNotDownload)
- return null; // Nothing to download here.
-
- for (int i = 0; i < otherPeers.Count; i++)
- {
- Buffer.BlockCopy(this.bufferBitfield.Array, 0,
this.previousBitfield.Array, 0, this.bufferBitfield.Array.Length * 4);
-
this.bufferBitfield.And(otherPeers[i].Peer.Connection.BitField);
- if (this.bufferBitfield.AllFalse() || highestPriorityFound
!= HighestPriorityForAvailablePieces(this.bufferBitfield))
- break;
- }
-
- // Once we have a bitfield containing just the pieces we need,
we request one.
- // FIXME: we need to make sure we take one from the highest
priority available.
- // At the moment it just takes anything.
- requestIndex = this.previousBitfield.FirstTrue();
- if (requestIndex < 0)
- return null;
-
- this.bitfield[requestIndex] = true;
- return this.GenerateRequest(id, requestIndex);
- }
- }
-
-
- private Priority HighestPriorityForAvailablePieces(BitField bitField)
- {
- for (int i = 0; i < this.priorities.Length - 1; i++)
- for (int j = 0; j < this.torrentFiles.Length; j++)
- if (this.torrentFiles[j].Priority == (Priority)i)
- return (Priority)i;
-
- return Priority.DoNotDownload;
- }
-
-
- private RequestMessage GenerateRequest(PeerConnectionID id, int index)
- {
- if (!requests.ContainsKey(id))
- requests.Add(id, new List<Piece>(2));
-
- List<Piece> reqs = requests[id];
-
- Piece p = new Piece(index, id.TorrentManager.Torrent);
- reqs.Add(p);
- p[0].Requested = true;
- return p[0].CreateRequest();
- }
-
-
-
- /// <summary>
- /// Checks to see if a peer has a piece we want
- /// </summary>
- /// <param name="id">The peer to check to see if he's interesting or
not</param>
- /// <returns>True if the peer is interesting, false otherwise</returns>
- public bool IsInteresting(PeerConnectionID id)
- {
- lock (this.isInterestingBuffer) // I reuse a BitField as a
buffer so i don't have to keep allocating new ones
- {
- lock (this.bitfield)
- Array.Copy(this.bitfield.Array, 0,
isInterestingBuffer.Array, 0, this.bitfield.Array.Length);
-
- isInterestingBuffer.Not();
- isInterestingBuffer.And(id.Peer.Connection.BitField);
- if (!isInterestingBuffer.AllFalse())
- return true; // He's
interesting if he has a piece we want
-
- lock (this.requests)
- return (this.requests.ContainsKey(id)); // OR if we're
already requesting a piece off him.
- }
- }
-
-
- /// <summary>
- /// Removes any outstanding requests from the supplied peer
- /// </summary>
- /// <param name="id">The peer to remove outstanding requests
from</param>
- internal void RemoveRequests(PeerConnectionID id)
- {
- lock (this.requests)
- {
- if (this.requests.ContainsKey(id))
- {
- List<Piece> pieces = this.requests[id];
-
- for (int i = 0; i < pieces.Count; i++)
- this.bitfield[pieces[i].Index] = false;
-
- this.requests.Remove(id);
- }
- if (id.Peer.Connection != null)
- id.Peer.Connection.AmRequestingPiecesCount = 0;
- }
- }
-
-
- // FIXME: fix this up a little, it's a bit messy after the refactor.
- /// <summary>
- ///
- /// </summary>
- /// <param name="id"></param>
- /// <param name="recieveBuffer"></param>
- /// <param name="offset"></param>
- /// <param name="writeIndex"></param>
- /// <param name="p"></param>
- internal void ReceivedPieceMessage(PeerConnectionID id, byte[]
recieveBuffer, int offset, int writeIndex, int p, PieceMessage message)
- {
- lock (this.requests)
- {
- id.TorrentManager.FileManager.Write(recieveBuffer, offset,
writeIndex, p);
-
- if (this.OnPieceChanged != null)
- this.OnPieceChanged(id.TorrentManager, new
PieceEventArgs(p, PieceEvent.BlockWrittenToDisk));
-
- if (!this.requests.ContainsKey(id))
- return;
-#warning I should close the connection, but at the moment i'm in a bug fixing
mood
-
- Piece piece = null;
- List<Piece> pieces = this.requests[id];
-
- for (int i = 0; i < pieces.Count; i++)
- {
- if (pieces[i].Index != message.PieceIndex)
- continue;
-
- piece = pieces[i];
- break;
- }
-
- if (piece == null)
- {
- return;
-#warning Handle this properly. Does this mean that i should close off the
peers connection because they sent me a piece i didn't request?
- }
-
- foreach (Block block in piece)
- {
- if (block.StartOffset != message.StartOffset)
- continue;
-
- if (message.BlockLength != block.RequestLength)
- throw new Exception("Request length should match block
length");
-
- block.Received = true;
-
- if (this.OnPieceChanged != null)
- this.OnPieceChanged(id.TorrentManager, new
PieceEventArgs(message.PieceIndex, PieceEvent.BlockReceived));
-
- id.Peer.Connection.AmRequestingPiecesCount--;
- break;
- }
-
- if (!piece.AllBlocksReceived)
- return;
-
- if (this.OnPieceChanged != null)
- this.OnPieceChanged(id.TorrentManager, new
PieceEventArgs(piece.Index, PieceEvent.PieceReceived));
-
- bool result =
ToolBox.ByteMatch(id.TorrentManager.Torrent.Pieces[piece.Index],
id.TorrentManager.FileManager.GetHash(piece.Index));
- this.bitfield[message.PieceIndex] = result;
-
- id.TorrentManager.HashedPiece(new
PieceHashedEventArgs(piece.Index, result));
-
- if (result)
- {
- id.Peer.Connection.IsInterestingToMe =
this.IsInteresting(id);
- id.TorrentManager.PieceCompleted(piece.Index);
- }
-
- pieces.Remove(piece);
-
- if (pieces.Count == 0)
- this.requests.Remove(id);
- }
- }
-
-
- internal void ReceivedRejectRequest(PeerConnectionID id,
RejectRequestMessage rejectRequestMessage)
- {
- lock (this.requests)
- {
- if (!this.requests.ContainsKey(id))
- return;
-
- Piece p = null;
-
- foreach (Piece piece in this.requests[id])
- {
- if (piece.Index != rejectRequestMessage.PieceIndex)
- continue;
- p = piece;
- }
-
- if (p == null)
- {
- return;
-#warning Does this need to close the peers connection too?
- }
-
- foreach (Block block in p)
- {
- if (block.StartOffset != rejectRequestMessage.StartOffset)
- continue;
-
- block.Requested = false;
- break;
- }
- }
- }
- #endregion
- }
-}
\ No newline at end of file
Modified: trunk/bitsharp/src/MonoTorrent.Client/Managers/TorrentManager.cs
===================================================================
--- trunk/bitsharp/src/MonoTorrent.Client/Managers/TorrentManager.cs
2006-11-05 19:02:24 UTC (rev 67389)
+++ trunk/bitsharp/src/MonoTorrent.Client/Managers/TorrentManager.cs
2006-11-05 19:08:19 UTC (rev 67390)
@@ -114,7 +114,7 @@
/// <summary>
/// The piecemanager for this TorrentManager
/// </summary>
- internal PieceManager PieceManager
+ public PieceManager PieceManager
{
get { return this.pieceManager; }
//set { this.pieceManager = (PieceManager) value; }
@@ -805,7 +805,7 @@
/// </summary>
public double Progress
{
- get { return (((this.pieceManager.MyBitField.TrueCount -
this.pieceManager.CurrentRequestCount()) * 100.0) /
this.pieceManager.MyBitField.Length); }
+ get { return (this.pieceManager.MyBitField.TrueCount * 100.0) /
this.pieceManager.MyBitField.Length; }
}
Modified: trunk/bitsharp/src/MonoTorrent.Client/MonoTorrent.Client.csproj
===================================================================
--- trunk/bitsharp/src/MonoTorrent.Client/MonoTorrent.Client.csproj
2006-11-05 19:02:24 UTC (rev 67389)
+++ trunk/bitsharp/src/MonoTorrent.Client/MonoTorrent.Client.csproj
2006-11-05 19:08:19 UTC (rev 67390)
@@ -71,6 +71,9 @@
<Compile Include="PeerMessages\IPeerMessage.cs" />
<Compile Include="PeerMessages\UnknownMessage.cs" />
<Compile Include="Peers\Peer.cs" />
+ <Compile Include="PiecePicking\EndGamePicker.cs" />
+ <Compile Include="PiecePicking\IPiecePicker.cs" />
+ <Compile Include="PiecePicking\StandardPicker.cs" />
<Compile Include="ProtocolException.cs" />
<Compile Include="RateLimiter.cs" />
<Compile Include="Tests\AllowedFastAlgorithmTest.cs" />
@@ -86,7 +89,7 @@
<Compile Include="Managers\ConnectionManager.cs" />
<Compile Include="Managers\ConnectionMonitor.cs" />
<Compile Include="Managers\FileManager.cs" />
- <Compile Include="Managers\PieceManager.cs" />
+ <Compile Include="PiecePicking\PieceManager.cs" />
<Compile Include="Managers\TorrentManager.cs" />
<Compile Include="PeerConnections\PeerConnectionBase.cs" />
<Compile Include="PeerConnections\PeerConnectionId.cs" />
Added: trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/EndGamePicker.cs
===================================================================
--- trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/EndGamePicker.cs
2006-11-05 19:02:24 UTC (rev 67389)
+++ trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/EndGamePicker.cs
2006-11-05 19:08:19 UTC (rev 67390)
@@ -0,0 +1,112 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using MonoTorrent.Client.PeerMessages;
+
+namespace MonoTorrent.Client
+{
+ internal class EndGamePieceManager
+ {
+ /*
+ List<Piece> endGamePieces;
+ private RequestMessage EndGamePicker(PeerConnectionID id, Peers
otherPeers)
+ {
+ lock (this.myBitfield)
+ {
+ if (this.endGamePieces == null)
+ CalculateEndGamePieces(id);
+
+ for (int i = 0; i < this.endGamePieces.Count; i++)
+ {
+ if
(!id.Peer.Connection.BitField[this.endGamePieces[i].Index])
+ continue;
+
+ Piece p = this.endGamePieces[i];
+ for (int j = 0; j < p.Blocks.Length; j++)
+ {
+ if (p[j].Received)
+ continue;
+
+ p[j].Requested = true;
+ return p[j].CreateRequest();
+ }
+ }
+ }
+
+ return null; // He has nothing we want
+ }
+
+
+ private void CalculateEndGamePieces(PeerConnectionID id)
+ {
+ this.endGameActive = true;
+ this.endGamePieces = new List<Piece>();
+ for (int i = 0; i < this.myBitfield.Length; i++)
+ if (!this.myBitfield[i])
+ {
+ this.myBitfield[i] = true;
+ this.endGamePieces.Add(new Piece(i,
id.TorrentManager.Torrent));
+ }
+
+ foreach (KeyValuePair<PeerConnectionID, List<Piece>> keypair in
this.requests)
+ foreach (Piece p in keypair.Value)
+ this.endGamePieces.Add(p);
+ }
+
+
+ private void ReceivedEndGameMessage(PeerConnectionID id, byte[]
recieveBuffer, int offset, int writeIndex, int p, PieceMessage message)
+ {
+ Piece piece = null;
+ Block receivedBlock = null;
+
+ lock (this.requests)
+ {
+ for (int i = 0; i < this.endGamePieces.Count; i++)
+ {
+ if (this.endGamePieces[i].Index != message.PieceIndex)
+ continue;
+
+ piece = this.endGamePieces[i];
+ for (int j = 0; j < piece.Blocks.Length; j++)
+ if (piece[j].StartOffset == message.StartOffset)
+ receivedBlock = piece[j];
+
+ if (receivedBlock == null)
+ return;
+
+ id.Peer.Connection.AmRequestingPiecesCount--;
+ if (receivedBlock.RequestLength != message.BlockLength)
+ throw new Exception("Request length should match block
length");
+
+ receivedBlock.Received = true;
+ id.TorrentManager.FileManager.Write(recieveBuffer, offset,
writeIndex, p);
+
+
+ if (!piece.AllBlocksReceived)
+ return;
+
+ bool result =
ToolBox.ByteMatch(id.TorrentManager.Torrent.Pieces[piece.Index],
id.TorrentManager.FileManager.GetHash(piece.Index));
+ this.myBitfield[message.PieceIndex] = result;
+
+ id.TorrentManager.HashedPiece(new
PieceHashedEventArgs(piece.Index, result));
+
+ if (result)
+ {
+ id.Peer.Connection.IsInterestingToMe =
this.IsInteresting(id);
+ id.TorrentManager.PieceCompleted(piece.Index);
+ this.endGamePieces.Remove(piece);
+ }
+ else
+ {
+ for (int k = 0; k < piece.Blocks.Length; k++)
+ {
+ piece[k].Requested = false;
+ piece[k].Received = false;
+ }
+ }
+ }
+ }
+ }
+*/
+ }
+}
Property changes on:
trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/EndGamePicker.cs
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/IPiecePicker.cs
===================================================================
--- trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/IPiecePicker.cs
2006-11-05 19:02:24 UTC (rev 67389)
+++ trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/IPiecePicker.cs
2006-11-05 19:08:19 UTC (rev 67390)
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using MonoTorrent.Client.PeerMessages;
+
+namespace MonoTorrent.Client
+{
+ interface IPiecePicker
+ {
+ bool IsInteresting(PeerConnectionID id);
+ RequestMessage PickPiece(PeerConnectionID id, Peers otherPeers);
+ BitField MyBitField { get; }
+ int CurrentRequestCount();
+ void ReceivedRejectRequest(PeerConnectionID id, RejectRequestMessage
message);
+ void RemoveRequests(PeerConnectionID id);
+ void ReceivedPieceMessage(PeerConnectionID id, byte[] buffer, int
dataOffset, int writeIndex, int blockLength, PieceMessage message);
+ }
+}
Property changes on:
trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/IPiecePicker.cs
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/PieceManager.cs
===================================================================
--- trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/PieceManager.cs
2006-11-05 19:02:24 UTC (rev 67389)
+++ trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/PieceManager.cs
2006-11-05 19:08:19 UTC (rev 67390)
@@ -0,0 +1,114 @@
+//
+// PieceManager.cs
+//
+// Authors:
+// Alan McGovern [EMAIL PROTECTED]
+//
+// Copyright (C) 2006 Alan McGovern
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+
+using System;
+using System.Collections.Generic;
+using MonoTorrent.Client.PeerMessages;
+using MonoTorrent.Common;
+using MonoTorrent.Client;
+
+namespace MonoTorrent.Client
+{
+ /// <summary>
+ /// Contains the logic for choosing what piece to download next
+ /// </summary>
+ public class PieceManager
+ {
+ #region Events
+ /// <summary>
+ /// Event that's fired every time a Piece changes
+ /// </summary>
+ internal event EventHandler<PieceEventArgs> OnPieceChanged;
+ #endregion
+
+
+ #region Member Variables
+ private IPiecePicker piecePicker;
+ #endregion
+
+
+ #region Constructors
+ public PieceManager(BitField bitfield, TorrentFile[] files)
+ {
+ this.piecePicker = new StandardPicker(bitfield, files);
+ }
+ #endregion
+
+
+ #region Methods
+ internal bool IsInteresting(PeerConnectionID id)
+ {
+ return this.piecePicker.IsInteresting(id);
+ }
+
+
+ public bool InEndGameMode
+ {
+ get { return false; }
+ }
+
+
+ internal BitField MyBitField
+ {
+ get { return this.piecePicker.MyBitField; }
+ }
+
+
+ internal int CurrentRequestCount()
+ {
+ return this.piecePicker.CurrentRequestCount();
+ }
+
+
+ internal IPeerMessageInternal PickPiece(PeerConnectionID id, Peers
otherPeers)
+ {
+ return this.piecePicker.PickPiece(id, otherPeers);
+ }
+
+
+ internal void ReceivedRejectRequest(PeerConnectionID id,
RejectRequestMessage msg)
+ {
+ this.piecePicker.ReceivedRejectRequest(id, msg);
+ }
+
+
+ internal void RemoveRequests(PeerConnectionID id)
+ {
+ this.piecePicker.RemoveRequests(id);
+ }
+
+
+ internal void ReceivedPieceMessage(PeerConnectionID id, byte[] buffer,
int dataOffset, int writeIndex, int blockLength, PieceMessage message)
+ {
+ this.piecePicker.ReceivedPieceMessage(id, buffer, dataOffset,
writeIndex, blockLength, message);
+ }
+ #endregion
+ }
+}
Property changes on:
trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/PieceManager.cs
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/StandardPicker.cs
===================================================================
--- trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/StandardPicker.cs
2006-11-05 19:02:24 UTC (rev 67389)
+++ trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/StandardPicker.cs
2006-11-05 19:08:19 UTC (rev 67390)
@@ -0,0 +1,355 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using MonoTorrent.Common;
+using MonoTorrent.Client.PeerMessages;
+
+namespace MonoTorrent.Client
+{
+ internal class StandardPicker : IPiecePicker
+ {
+
+
+ #region Member Variables
+ private int[] priorities;
+
+ /// <summary>
+ /// The bitfield for the torrent
+ /// </summary>
+ public BitField MyBitField
+ {
+ get { return this.myBitfield; }
+ }
+ private BitField myBitfield;
+
+ private BitField bufferBitfield;
+ private BitField previousBitfield;
+ private BitField isInterestingBuffer;
+
+
+ private TorrentFile[] torrentFiles;
+ private Dictionary<PeerConnectionID, List<Piece>> requests;
+
+
+ /// <summary>
+ /// Returns the number of outstanding requests
+ /// </summary>
+ public int CurrentRequestCount()
+ {
+ int result = 0;
+ lock (this.requests)
+ {
+ foreach (KeyValuePair<PeerConnectionID, List<Piece>> keypair
in this.requests)
+ result += keypair.Value.Count;
+ }
+ return result;
+ }
+ #endregion
+
+
+ #region Constructors
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="bitField"></param>
+ internal StandardPicker(BitField bitField, TorrentFile[] torrentFiles)
+ {
+ this.torrentFiles = torrentFiles;
+ this.requests = new Dictionary<PeerConnectionID, List<Piece>>(32);
+ this.isInterestingBuffer = new BitField(bitField.Length);
+ this.priorities = (int[])Enum.GetValues(typeof(Priority));
+ Array.Sort<int>(this.priorities);
+ Array.Reverse(this.priorities);
+
+ this.myBitfield = bitField;
+ this.bufferBitfield = new BitField(myBitfield.Length);
+ this.previousBitfield = new BitField(myBitfield.Length);
+ this.torrentFiles = torrentFiles;
+ this.requests = new Dictionary<PeerConnectionID, List<Piece>>();
+
+ // Order the priorities in decending order of priority. i.e.
Immediate is first, and DoNotDownload is last
+ this.priorities = (int[])Enum.GetValues(typeof(Priority));
+ Array.Sort<int>(this.priorities);
+ Array.Reverse(this.priorities);
+ }
+ #endregion
+
+
+ #region Methods
+ public RequestMessage PickPiece(PeerConnectionID id, Peers otherPeers)
+ {
+ int requestIndex = 0;
+ Priority highestPriorityFound = Priority.DoNotDownload;
+
+ lock (this.myBitfield)
+ {
+ // If there is already a request there, request the next block.
+ if (this.requests.ContainsKey(id))
+ {
+ List<Piece> reqs = this.requests[id];
+ for (int i = 0; i < reqs.Count; i++)
+ for (int j = 0; j < reqs[i].Blocks.Length; j++)
+ if (!reqs[i][j].Requested)
+ {
+ reqs[i][j].Requested = true;
+ return reqs[i][j].CreateRequest();
+ }
+ }
+
+ // We get here if there were no free blocks to request.
+ // We see if the peer has suggested any pieces we should
request
+ while (id.Peer.Connection.SuggestedPieces.Count > 0)
+ {
+ // If we already have that piece, then remove it from the
suggested pieces
+ if
(AlreadyHaveOrRequested(id.Peer.Connection.SuggestedPieces[0]))
+ {
+ id.Peer.Connection.SuggestedPieces.RemoveAt(0);
+ continue;
+ }
+
+ requestIndex = id.Peer.Connection.SuggestedPieces[0];
+ id.Peer.Connection.SuggestedPieces.RemoveAt(0);
+ return this.GenerateRequest(id, requestIndex);
+ }
+
+ // Now we see what pieces the peer has that we don't have and
try and request one
+ Buffer.BlockCopy(this.myBitfield.Array, 0,
bufferBitfield.Array, 0, this.bufferBitfield.Array.Length * 4);
+ this.bufferBitfield.Not();
+ this.bufferBitfield.And(id.Peer.Connection.BitField);
+
+ // Out of the pieces we have, we look for the highest priority
and store it here.
+ // We then keep NANDing the bitfield until we no longer have
pieces with that priority.
+ // Thats when we go back 1 step and download a piece with the
original "highestPriorityFound"
+ highestPriorityFound =
HighestPriorityForAvailablePieces(this.bufferBitfield);
+
+ if (highestPriorityFound == Priority.DoNotDownload)
+ return null; // Nothing to download here.
+
+ for (int i = 0; i < otherPeers.Count; i++)
+ {
+ Buffer.BlockCopy(this.bufferBitfield.Array, 0,
this.previousBitfield.Array, 0, this.bufferBitfield.Array.Length * 4);
+
this.bufferBitfield.And(otherPeers[i].Peer.Connection.BitField);
+ if (this.bufferBitfield.AllFalse() || highestPriorityFound
!= HighestPriorityForAvailablePieces(this.bufferBitfield))
+ break;
+ }
+
+ // Once we have a bitfield containing just the pieces we need,
we request one.
+ // FIXME: we need to make sure we take one from the highest
priority available.
+ // At the moment it just takes anything.
+ requestIndex = 0;
+ while (true)
+ {
+ requestIndex =
this.previousBitfield.FirstTrue(requestIndex,
id.Peer.Connection.BitField.Length);
+
+ if (requestIndex == -1)
+ break;
+
+ if (AlreadyHaveOrRequested(requestIndex))
+ requestIndex++;
+ else
+ break;
+ }
+
+ if (requestIndex < 0)
+ return null;
+
+ return this.GenerateRequest(id, requestIndex);
+ }
+ }
+
+ private bool AlreadyHaveOrRequested(int index)
+ {
+ if (this.myBitfield[index])
+ return true;
+
+ foreach (KeyValuePair<PeerConnectionID, List<Piece>> keypair in
this.requests)
+ foreach (Piece p in keypair.Value)
+ if (p.Index == index)
+ return true;
+
+ return false;
+ }
+
+
+ private Priority HighestPriorityForAvailablePieces(BitField bitField)
+ {
+ for (int i = 0; i < this.priorities.Length - 1; i++)
+ for (int j = 0; j < this.torrentFiles.Length; j++)
+ if (this.torrentFiles[j].Priority == (Priority)i)
+ return (Priority)i;
+
+ return Priority.DoNotDownload;
+ }
+
+
+ private RequestMessage GenerateRequest(PeerConnectionID id, int index)
+ {
+ if (!requests.ContainsKey(id))
+ requests.Add(id, new List<Piece>(2));
+
+ List<Piece> reqs = requests[id];
+
+ Piece p = new Piece(index, id.TorrentManager.Torrent);
+ reqs.Add(p);
+ p[0].Requested = true;
+ return p[0].CreateRequest();
+ }
+
+
+ /// <summary>
+ /// Checks to see if a peer has a piece we want
+ /// </summary>
+ /// <param name="id">The peer to check to see if he's interesting or
not</param>
+ /// <returns>True if the peer is interesting, false otherwise</returns>
+ public bool IsInteresting(PeerConnectionID id)
+ {
+ lock (this.isInterestingBuffer) // I reuse a BitField as a
buffer so i don't have to keep allocating new ones
+ {
+ lock (this.myBitfield)
+ Array.Copy(this.myBitfield.Array, 0,
isInterestingBuffer.Array, 0, this.myBitfield.Array.Length);
+
+ isInterestingBuffer.Not();
+ isInterestingBuffer.And(id.Peer.Connection.BitField);
+ if (!isInterestingBuffer.AllFalse())
+ return true; // He's
interesting if he has a piece we want
+
+ lock (this.requests)
+ return (this.requests.ContainsKey(id)); // OR if we're
already requesting a piece off him.
+ }
+ }
+
+
+ /// <summary>
+ /// Removes any outstanding requests from the supplied peer
+ /// </summary>
+ /// <param name="id">The peer to remove outstanding requests
from</param>
+ public void RemoveRequests(PeerConnectionID id)
+ {
+ lock (this.requests)
+ {
+ if (this.requests.ContainsKey(id))
+ {
+ List<Piece> pieces = this.requests[id];
+
+ for (int i = 0; i < pieces.Count; i++)
+ this.myBitfield[pieces[i].Index] = false;
+
+ this.requests.Remove(id);
+ }
+ if (id.Peer.Connection != null)
+ id.Peer.Connection.AmRequestingPiecesCount = 0;
+ }
+ }
+
+
+ // FIXME: fix this up a little, it's a bit messy after the refactor.
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="id"></param>
+ /// <param name="recieveBuffer"></param>
+ /// <param name="offset"></param>
+ /// <param name="writeIndex"></param>
+ /// <param name="p"></param>
+
+ public void ReceivedPieceMessage(PeerConnectionID id, byte[]
recieveBuffer, int offset, int writeIndex, int p, PieceMessage message)
+ {
+ lock (this.requests)
+ {
+ if (!this.requests.ContainsKey(id))
+ return;
+#warning I should close the connection, but at the moment i'm in a bug fixing
mood
+
+ Piece piece = null;
+ List<Piece> pieces = this.requests[id];
+
+ for (int i = 0; i < pieces.Count; i++)
+ {
+ if (pieces[i].Index != message.PieceIndex)
+ continue;
+
+ piece = pieces[i];
+ break;
+ }
+
+ if (piece == null)
+ {
+ return;
+#warning Handle this properly. Does this mean that i should close off the
peers connection because they sent me a piece i didn't request?
+ }
+
+ foreach (Block block in piece)
+ {
+ if (block.StartOffset != message.StartOffset)
+ continue;
+
+ if (message.BlockLength != block.RequestLength)
+ throw new Exception("Request length should match block
length");
+
+ block.Received = true;
+
+
+ id.Peer.Connection.AmRequestingPiecesCount--;
+ break;
+ }
+
+ id.TorrentManager.FileManager.Write(recieveBuffer, offset,
writeIndex, p);
+
+
+ if (!piece.AllBlocksReceived)
+ return;
+
+ bool result =
ToolBox.ByteMatch(id.TorrentManager.Torrent.Pieces[piece.Index],
id.TorrentManager.FileManager.GetHash(piece.Index));
+ this.myBitfield[message.PieceIndex] = result;
+
+ id.TorrentManager.HashedPiece(new
PieceHashedEventArgs(piece.Index, result));
+
+ if (result)
+ {
+ id.Peer.Connection.IsInterestingToMe =
this.IsInteresting(id);
+ id.TorrentManager.PieceCompleted(piece.Index);
+ }
+
+ pieces.Remove(piece);
+
+ if (pieces.Count == 0)
+ this.requests.Remove(id);
+ }
+ }
+
+
+ public void ReceivedRejectRequest(PeerConnectionID id,
RejectRequestMessage rejectRequestMessage)
+ {
+ lock (this.requests)
+ {
+ if (!this.requests.ContainsKey(id))
+ return;
+
+ Piece p = null;
+
+ foreach (Piece piece in this.requests[id])
+ {
+ if (piece.Index != rejectRequestMessage.PieceIndex)
+ continue;
+ p = piece;
+ }
+
+ if (p == null)
+ {
+ return;
+#warning Does this need to close the peers connection too?
+ }
+
+ foreach (Block block in p)
+ {
+ if (block.StartOffset != rejectRequestMessage.StartOffset)
+ continue;
+
+ block.Requested = false;
+ break;
+ }
+ }
+ }
+ #endregion
+ }
+}
Property changes on:
trunk/bitsharp/src/MonoTorrent.Client/PiecePicking/StandardPicker.cs
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: trunk/bitsharp/src/TestClient/main.cs
===================================================================
--- trunk/bitsharp/src/TestClient/main.cs 2006-11-05 19:02:24 UTC (rev
67389)
+++ trunk/bitsharp/src/TestClient/main.cs 2006-11-05 19:08:19 UTC (rev
67390)
@@ -129,6 +129,7 @@
Console.WriteLine("Scrape downloaded:" +
manager.TrackerManager.CurrentTracker.Downloaded);
Console.WriteLine("Warning Message: " +
manager.TrackerManager.CurrentTracker.WarningMessage);
Console.WriteLine("Failure Message: " +
manager.TrackerManager.CurrentTracker.FailureMessage);
+ Console.WriteLine("Endgame Mode: " +
manager.PieceManager.InEndGameMode.ToString());
Console.WriteLine("\n");
}
}
_______________________________________________
Mono-patches maillist - [email protected]
http://lists.ximian.com/mailman/listinfo/mono-patches