New: Implemented Torrent Download Clients: uTorrent, Transmission and Deluge. And several public and private Torrent Indexers.

This commit is contained in:
MythJuha
2014-05-13 19:57:46 +02:00
committed by Taloth Saldono
parent ffa814f387
commit 67cd5703a1
134 changed files with 11018 additions and 99 deletions
@@ -0,0 +1,321 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
namespace MonoTorrent.BEncoding
{
/// <summary>
/// Class representing a BEncoded Dictionary
/// </summary>
public class BEncodedDictionary : BEncodedValue, IDictionary<BEncodedString, BEncodedValue>
{
#region Member Variables
private SortedDictionary<BEncodedString, BEncodedValue> dictionary;
#endregion
#region Constructors
/// <summary>
/// Create a new BEncodedDictionary
/// </summary>
public BEncodedDictionary()
{
this.dictionary = new SortedDictionary<BEncodedString, BEncodedValue>();
}
#endregion
#region Encode/Decode Methods
/// <summary>
/// Encodes the dictionary to a byte[]
/// </summary>
/// <param name="buffer">The buffer to encode the data to</param>
/// <param name="offset">The offset to start writing the data to</param>
/// <returns></returns>
public override int Encode(byte[] buffer, int offset)
{
int written = 0;
//Dictionaries start with 'd'
buffer[offset] = (byte)'d';
written++;
foreach (KeyValuePair<BEncodedString, BEncodedValue> keypair in this)
{
written += keypair.Key.Encode(buffer, offset + written);
written += keypair.Value.Encode(buffer, offset + written);
}
// Dictionaries end with 'e'
buffer[offset + written] = (byte)'e';
written++;
return written;
}
/// <summary>
///
/// </summary>
/// <param name="reader"></param>
internal override void DecodeInternal(RawReader reader)
{
DecodeInternal(reader, reader.StrictDecoding);
}
private void DecodeInternal(RawReader reader, bool strictDecoding)
{
BEncodedString key = null;
BEncodedValue value = null;
BEncodedString oldkey = null;
if (reader.ReadByte() != 'd')
throw new BEncodingException("Invalid data found. Aborting"); // Remove the leading 'd'
while ((reader.PeekByte() != -1) && (reader.PeekByte() != 'e'))
{
key = (BEncodedString)BEncodedValue.Decode(reader); // keys have to be BEncoded strings
if (oldkey != null && oldkey.CompareTo(key) > 0)
if (strictDecoding)
throw new BEncodingException(String.Format(
"Illegal BEncodedDictionary. The attributes are not ordered correctly. Old key: {0}, New key: {1}",
oldkey, key));
oldkey = key;
value = BEncodedValue.Decode(reader); // the value is a BEncoded value
dictionary.Add(key, value);
}
if (reader.ReadByte() != 'e') // remove the trailing 'e'
throw new BEncodingException("Invalid data found. Aborting");
}
public static BEncodedDictionary DecodeTorrent(byte[] bytes)
{
return DecodeTorrent(new MemoryStream(bytes));
}
public static BEncodedDictionary DecodeTorrent(Stream s)
{
return DecodeTorrent(new RawReader(s));
}
/// <summary>
/// Special decoding method for torrent files - allows dictionary attributes to be out of order for the
/// overall torrent file, but imposes strict rules on the info dictionary.
/// </summary>
/// <returns></returns>
public static BEncodedDictionary DecodeTorrent(RawReader reader)
{
BEncodedString key = null;
BEncodedValue value = null;
BEncodedDictionary torrent = new BEncodedDictionary();
if (reader.ReadByte() != 'd')
throw new BEncodingException("Invalid data found. Aborting"); // Remove the leading 'd'
while ((reader.PeekByte() != -1) && (reader.PeekByte() != 'e'))
{
key = (BEncodedString)BEncodedValue.Decode(reader); // keys have to be BEncoded strings
if (reader.PeekByte() == 'd')
{
value = new BEncodedDictionary();
if (key.Text.ToLower().Equals("info"))
((BEncodedDictionary)value).DecodeInternal(reader, true);
else
((BEncodedDictionary)value).DecodeInternal(reader, false);
}
else
value = BEncodedValue.Decode(reader); // the value is a BEncoded value
torrent.dictionary.Add(key, value);
}
if (reader.ReadByte() != 'e') // remove the trailing 'e'
throw new BEncodingException("Invalid data found. Aborting");
return torrent;
}
#endregion
#region Helper Methods
/// <summary>
/// Returns the size of the dictionary in bytes using UTF8 encoding
/// </summary>
/// <returns></returns>
public override int LengthInBytes()
{
int length = 0;
length += 1; // Dictionaries start with 'd'
foreach (KeyValuePair<BEncodedString, BEncodedValue> keypair in this.dictionary)
{
length += keypair.Key.LengthInBytes();
length += keypair.Value.LengthInBytes();
}
length += 1; // Dictionaries end with 'e'
return length;
}
#endregion
#region Overridden Methods
public override bool Equals(object obj)
{
BEncodedValue val;
BEncodedDictionary other = obj as BEncodedDictionary;
if (other == null)
return false;
if (this.dictionary.Count != other.dictionary.Count)
return false;
foreach (KeyValuePair<BEncodedString, BEncodedValue> keypair in this.dictionary)
{
if (!other.TryGetValue(keypair.Key, out val))
return false;
if (!keypair.Value.Equals(val))
return false;
}
return true;
}
public override int GetHashCode()
{
int result = 0;
foreach (KeyValuePair<BEncodedString, BEncodedValue> keypair in dictionary)
{
result ^= keypair.Key.GetHashCode();
result ^= keypair.Value.GetHashCode();
}
return result;
}
public override string ToString()
{
return System.Text.Encoding.UTF8.GetString(Encode());
}
#endregion
#region IDictionary and IList methods
public void Add(BEncodedString key, BEncodedValue value)
{
this.dictionary.Add(key, value);
}
public void Add(KeyValuePair<BEncodedString, BEncodedValue> item)
{
this.dictionary.Add(item.Key, item.Value);
}
public void Clear()
{
this.dictionary.Clear();
}
public bool Contains(KeyValuePair<BEncodedString, BEncodedValue> item)
{
if (!this.dictionary.ContainsKey(item.Key))
return false;
return this.dictionary[item.Key].Equals(item.Value);
}
public bool ContainsKey(BEncodedString key)
{
return this.dictionary.ContainsKey(key);
}
public void CopyTo(KeyValuePair<BEncodedString, BEncodedValue>[] array, int arrayIndex)
{
this.dictionary.CopyTo(array, arrayIndex);
}
public int Count
{
get { return this.dictionary.Count; }
}
//public int IndexOf(KeyValuePair<BEncodedString, IBEncodedValue> item)
//{
// return this.dictionary.IndexOf(item);
//}
//public void Insert(int index, KeyValuePair<BEncodedString, IBEncodedValue> item)
//{
// this.dictionary.Insert(index, item);
//}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(BEncodedString key)
{
return this.dictionary.Remove(key);
}
public bool Remove(KeyValuePair<BEncodedString, BEncodedValue> item)
{
return this.dictionary.Remove(item.Key);
}
//public void RemoveAt(int index)
//{
// this.dictionary.RemoveAt(index);
//}
public bool TryGetValue(BEncodedString key, out BEncodedValue value)
{
return this.dictionary.TryGetValue(key, out value);
}
public BEncodedValue this[BEncodedString key]
{
get { return this.dictionary[key]; }
set { this.dictionary[key] = value; }
}
//public KeyValuePair<BEncodedString, IBEncodedValue> this[int index]
//{
// get { return this.dictionary[index]; }
// set { this.dictionary[index] = value; }
//}
public ICollection<BEncodedString> Keys
{
get { return this.dictionary.Keys; }
}
public ICollection<BEncodedValue> Values
{
get { return this.dictionary.Values; }
}
public IEnumerator<KeyValuePair<BEncodedString, BEncodedValue>> GetEnumerator()
{
return this.dictionary.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.dictionary.GetEnumerator();
}
#endregion
}
}
+219
View File
@@ -0,0 +1,219 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace MonoTorrent.BEncoding
{
/// <summary>
/// Class representing a BEncoded list
/// </summary>
public class BEncodedList : BEncodedValue, IList<BEncodedValue>
{
#region Member Variables
private List<BEncodedValue> list;
#endregion
#region Constructors
/// <summary>
/// Create a new BEncoded List with default capacity
/// </summary>
public BEncodedList()
: this(new List<BEncodedValue>())
{
}
/// <summary>
/// Create a new BEncoded List with the supplied capacity
/// </summary>
/// <param name="capacity">The initial capacity</param>
public BEncodedList(int capacity)
: this(new List<BEncodedValue>(capacity))
{
}
public BEncodedList(IEnumerable<BEncodedValue> list)
{
if (list == null)
throw new ArgumentNullException("list");
this.list = new List<BEncodedValue>(list);
}
private BEncodedList(List<BEncodedValue> value)
{
this.list = value;
}
#endregion
#region Encode/Decode Methods
/// <summary>
/// Encodes the list to a byte[]
/// </summary>
/// <param name="buffer">The buffer to encode the list to</param>
/// <param name="offset">The offset to start writing the data at</param>
/// <returns></returns>
public override int Encode(byte[] buffer, int offset)
{
int written = 0;
buffer[offset] = (byte)'l';
written++;
for (int i = 0; i < this.list.Count; i++)
written += this.list[i].Encode(buffer, offset + written);
buffer[offset + written] = (byte)'e';
written++;
return written;
}
/// <summary>
/// Decodes a BEncodedList from the given StreamReader
/// </summary>
/// <param name="reader"></param>
internal override void DecodeInternal(RawReader reader)
{
if (reader.ReadByte() != 'l') // Remove the leading 'l'
throw new BEncodingException("Invalid data found. Aborting");
while ((reader.PeekByte() != -1) && (reader.PeekByte() != 'e'))
list.Add(BEncodedValue.Decode(reader));
if (reader.ReadByte() != 'e') // Remove the trailing 'e'
throw new BEncodingException("Invalid data found. Aborting");
}
#endregion
#region Helper Methods
/// <summary>
/// Returns the size of the list in bytes
/// </summary>
/// <returns></returns>
public override int LengthInBytes()
{
int length = 0;
length += 1; // Lists start with 'l'
for (int i=0; i < this.list.Count; i++)
length += this.list[i].LengthInBytes();
length += 1; // Lists end with 'e'
return length;
}
#endregion
#region Overridden Methods
public override bool Equals(object obj)
{
BEncodedList other = obj as BEncodedList;
if (other == null)
return false;
for (int i = 0; i < this.list.Count; i++)
if (!this.list[i].Equals(other.list[i]))
return false;
return true;
}
public override int GetHashCode()
{
int result = 0;
for (int i = 0; i < list.Count; i++)
result ^= list[i].GetHashCode();
return result;
}
public override string ToString()
{
return System.Text.Encoding.UTF8.GetString(Encode());
}
#endregion
#region IList methods
public void Add(BEncodedValue item)
{
this.list.Add(item);
}
public void AddRange (IEnumerable<BEncodedValue> collection)
{
list.AddRange (collection);
}
public void Clear()
{
this.list.Clear();
}
public bool Contains(BEncodedValue item)
{
return this.list.Contains(item);
}
public void CopyTo(BEncodedValue[] array, int arrayIndex)
{
this.list.CopyTo(array, arrayIndex);
}
public int Count
{
get { return this.list.Count; }
}
public int IndexOf(BEncodedValue item)
{
return this.list.IndexOf(item);
}
public void Insert(int index, BEncodedValue item)
{
this.list.Insert(index, item);
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(BEncodedValue item)
{
return this.list.Remove(item);
}
public void RemoveAt(int index)
{
this.list.RemoveAt(index);
}
public BEncodedValue this[int index]
{
get { return this.list[index]; }
set { this.list[index] = value; }
}
public IEnumerator<BEncodedValue> GetEnumerator()
{
return this.list.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
}
}
+209
View File
@@ -0,0 +1,209 @@
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
namespace MonoTorrent.BEncoding
{
/// <summary>
/// Class representing a BEncoded number
/// </summary>
public class BEncodedNumber : BEncodedValue, IComparable<BEncodedNumber>
{
#region Member Variables
/// <summary>
/// The value of the BEncodedNumber
/// </summary>
public long Number
{
get { return number; }
set { number = value; }
}
internal long number;
#endregion
#region Constructors
public BEncodedNumber()
: this(0)
{
}
/// <summary>
/// Create a new BEncoded number with the given value
/// </summary>
/// <param name="initialValue">The inital value of the BEncodedNumber</param>
public BEncodedNumber(long value)
{
this.number = value;
}
public static implicit operator BEncodedNumber(long value)
{
return new BEncodedNumber(value);
}
#endregion
#region Encode/Decode Methods
/// <summary>
/// Encodes this number to the supplied byte[] starting at the supplied offset
/// </summary>
/// <param name="buffer">The buffer to write the data to</param>
/// <param name="offset">The offset to start writing the data at</param>
/// <returns></returns>
public override int Encode(byte[] buffer, int offset)
{
long number = this.number;
int written = offset;
buffer[written++] = (byte)'i';
if (number < 0)
{
buffer[written++] = (byte)'-';
number = -number;
}
// Reverse the number '12345' to get '54321'
long reversed = 0;
for (long i = number; i != 0; i /= 10)
reversed = reversed * 10 + i % 10;
// Write each digit of the reversed number to the array. We write '1'
// first, then '2', etc
for (long i = reversed; i != 0; i /= 10)
buffer[written++] = (byte)(i % 10 + '0');
if (number == 0)
buffer[written++] = (byte)'0';
// If the original number ends in one or more zeros, they are lost
// when we reverse the number. We add them back in here.
for (long i = number; i % 10 == 0 && number != 0; i /= 10)
buffer[written++] = (byte)'0';
buffer[written++] = (byte)'e';
return written - offset;
}
/// <summary>
/// Decodes a BEncoded number from the supplied RawReader
/// </summary>
/// <param name="reader">RawReader containing a BEncoded Number</param>
internal override void DecodeInternal(RawReader reader)
{
int sign = 1;
if (reader == null)
throw new ArgumentNullException("reader");
if (reader.ReadByte() != 'i') // remove the leading 'i'
throw new BEncodingException("Invalid data found. Aborting.");
if (reader.PeekByte() == '-')
{
sign = -1;
reader.ReadByte ();
}
int letter;
while (((letter = reader.PeekByte()) != -1) && letter != 'e')
{
if(letter < '0' || letter > '9')
throw new BEncodingException("Invalid number found.");
number = number * 10 + (letter - '0');
reader.ReadByte ();
}
if (reader.ReadByte() != 'e') //remove the trailing 'e'
throw new BEncodingException("Invalid data found. Aborting.");
number *= sign;
}
#endregion
#region Helper Methods
/// <summary>
/// Returns the length of the encoded string in bytes
/// </summary>
/// <returns></returns>
public override int LengthInBytes()
{
long number = this.number;
int count = 2; // account for the 'i' and 'e'
if (number == 0)
return count + 1;
if (number < 0)
{
number = -number;
count++;
}
for (long i = number; i != 0; i /= 10)
count++;
return count;
}
public int CompareTo(object other)
{
if (other is BEncodedNumber || other is long || other is int)
return CompareTo((BEncodedNumber)other);
return -1;
}
public int CompareTo(BEncodedNumber other)
{
if (other == null)
throw new ArgumentNullException("other");
return this.number.CompareTo(other.number);
}
public int CompareTo(long other)
{
return this.number.CompareTo(other);
}
#endregion
#region Overridden Methods
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
BEncodedNumber obj2 = obj as BEncodedNumber;
if (obj2 == null)
return false;
return (this.number == obj2.number);
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return this.number.GetHashCode();
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public override string ToString()
{
return (this.number.ToString());
}
#endregion
}
}
+220
View File
@@ -0,0 +1,220 @@
using System;
using System.IO;
using System.Collections;
using System.Text;
using MonoTorrent.Common;
using MonoTorrent.Messages;
namespace MonoTorrent.BEncoding
{
/// <summary>
/// Class representing a BEncoded string
/// </summary>
public class BEncodedString : BEncodedValue, IComparable<BEncodedString>
{
#region Member Variables
/// <summary>
/// The value of the BEncodedString
/// </summary>
public string Text
{
get { return Encoding.UTF8.GetString(textBytes); }
set { textBytes = Encoding.UTF8.GetBytes(value); }
}
/// <summary>
/// The underlying byte[] associated with this BEncodedString
/// </summary>
public byte[] TextBytes
{
get { return this.textBytes; }
}
private byte[] textBytes;
#endregion
#region Constructors
/// <summary>
/// Create a new BEncodedString using UTF8 encoding
/// </summary>
public BEncodedString()
: this(new byte[0])
{
}
/// <summary>
/// Create a new BEncodedString using UTF8 encoding
/// </summary>
/// <param name="value"></param>
public BEncodedString(char[] value)
: this(System.Text.Encoding.UTF8.GetBytes(value))
{
}
/// <summary>
/// Create a new BEncodedString using UTF8 encoding
/// </summary>
/// <param name="value">Initial value for the string</param>
public BEncodedString(string value)
: this(System.Text.Encoding.UTF8.GetBytes(value))
{
}
/// <summary>
/// Create a new BEncodedString using UTF8 encoding
/// </summary>
/// <param name="value"></param>
public BEncodedString(byte[] value)
{
this.textBytes = value;
}
public static implicit operator BEncodedString(string value)
{
return new BEncodedString(value);
}
public static implicit operator BEncodedString(char[] value)
{
return new BEncodedString(value);
}
public static implicit operator BEncodedString(byte[] value)
{
return new BEncodedString(value);
}
#endregion
#region Encode/Decode Methods
/// <summary>
/// Encodes the BEncodedString to a byte[] using the supplied Encoding
/// </summary>
/// <param name="buffer">The buffer to encode the string to</param>
/// <param name="offset">The offset at which to save the data to</param>
/// <param name="e">The encoding to use</param>
/// <returns>The number of bytes encoded</returns>
public override int Encode(byte[] buffer, int offset)
{
int written = offset;
written += Message.WriteAscii(buffer, written, textBytes.Length.ToString ());
written += Message.WriteAscii(buffer, written, ":");
written += Message.Write(buffer, written, textBytes);
return written - offset;
}
/// <summary>
/// Decodes a BEncodedString from the supplied StreamReader
/// </summary>
/// <param name="reader">The StreamReader containing the BEncodedString</param>
internal override void DecodeInternal(RawReader reader)
{
if (reader == null)
throw new ArgumentNullException("reader");
int letterCount;
string length = string.Empty;
while ((reader.PeekByte() != -1) && (reader.PeekByte() != ':')) // read in how many characters
length += (char)reader.ReadByte(); // the string is
if (reader.ReadByte() != ':') // remove the ':'
throw new BEncodingException("Invalid data found. Aborting");
if (!int.TryParse(length, out letterCount))
throw new BEncodingException(string.Format("Invalid BEncodedString. Length was '{0}' instead of a number", length));
this.textBytes = new byte[letterCount];
if (reader.Read(textBytes, 0, letterCount) != letterCount)
throw new BEncodingException("Couldn't decode string");
}
#endregion
#region Helper Methods
public string Hex
{
get { return BitConverter.ToString(TextBytes); }
}
public override int LengthInBytes()
{
// The length is equal to the length-prefix + ':' + length of data
int prefix = 1; // Account for ':'
// Count the number of characters needed for the length prefix
for (int i = textBytes.Length; i != 0; i = i/10)
prefix += 1;
if (textBytes.Length == 0)
prefix++;
return prefix + textBytes.Length;
}
public int CompareTo(object other)
{
return CompareTo(other as BEncodedString);
}
public int CompareTo(BEncodedString other)
{
if (other == null)
return 1;
int difference=0;
int length = this.textBytes.Length > other.textBytes.Length ? other.textBytes.Length : this.textBytes.Length;
for (int i = 0; i < length; i++)
if ((difference = this.textBytes[i].CompareTo(other.textBytes[i])) != 0)
return difference;
if (this.textBytes.Length == other.textBytes.Length)
return 0;
return this.textBytes.Length > other.textBytes.Length ? 1 : -1;
}
#endregion
#region Overridden Methods
public override bool Equals(object obj)
{
if (obj == null)
return false;
BEncodedString other;
if (obj is string)
other = new BEncodedString((string)obj);
else if (obj is BEncodedString)
other = (BEncodedString)obj;
else
return false;
return Toolbox.ByteMatch(this.textBytes, other.textBytes);
}
public override int GetHashCode()
{
int hash = 0;
for (int i = 0; i < this.textBytes.Length; i++)
hash += this.textBytes[i];
return hash;
}
public override string ToString()
{
return System.Text.Encoding.UTF8.GetString(textBytes);
}
#endregion
}
}
@@ -0,0 +1,30 @@
using System;
using System.Text;
using System.Runtime.Serialization;
namespace MonoTorrent.BEncoding
{
[Serializable]
public class BEncodingException : Exception
{
public BEncodingException()
: base()
{
}
public BEncodingException(string message)
: base(message)
{
}
public BEncodingException(string message, Exception innerException)
: base(message, innerException)
{
}
protected BEncodingException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}
+203
View File
@@ -0,0 +1,203 @@
using System;
using System.IO;
using System.Text;
namespace MonoTorrent.BEncoding
{
/// <summary>
/// Base interface for all BEncoded values.
/// </summary>
public abstract class BEncodedValue
{
internal abstract void DecodeInternal(RawReader reader);
/// <summary>
/// Encodes the BEncodedValue into a byte array
/// </summary>
/// <returns>Byte array containing the BEncoded Data</returns>
public byte[] Encode()
{
byte[] buffer = new byte[LengthInBytes()];
if (Encode(buffer, 0) != buffer.Length)
throw new BEncodingException("Error encoding the data");
return buffer;
}
/// <summary>
/// Encodes the BEncodedValue into the supplied buffer
/// </summary>
/// <param name="buffer">The buffer to encode the information to</param>
/// <param name="offset">The offset in the buffer to start writing the data</param>
/// <returns></returns>
public abstract int Encode(byte[] buffer, int offset);
public static T Clone <T> (T value)
where T : BEncodedValue
{
Check.Value (value);
return (T) BEncodedValue.Decode (value.Encode ());
}
/// <summary>
/// Interface for all BEncoded values
/// </summary>
/// <param name="data">The byte array containing the BEncoded data</param>
/// <returns></returns>
public static BEncodedValue Decode(byte[] data)
{
if (data == null)
throw new ArgumentNullException("data");
using (RawReader stream = new RawReader(new MemoryStream(data)))
return (Decode(stream));
}
internal static BEncodedValue Decode(byte[] buffer, bool strictDecoding)
{
return Decode(buffer, 0, buffer.Length, strictDecoding);
}
/// <summary>
/// Decode BEncoded data in the given byte array
/// </summary>
/// <param name="buffer">The byte array containing the BEncoded data</param>
/// <param name="offset">The offset at which the data starts at</param>
/// <param name="length">The number of bytes to be decoded</param>
/// <returns>BEncodedValue containing the data that was in the byte[]</returns>
public static BEncodedValue Decode(byte[] buffer, int offset, int length)
{
return Decode(buffer, offset, length, true);
}
public static BEncodedValue Decode(byte[] buffer, int offset, int length, bool strictDecoding)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset < 0 || length < 0)
throw new IndexOutOfRangeException("Neither offset or length can be less than zero");
if (offset > buffer.Length - length)
throw new ArgumentOutOfRangeException("length");
using (RawReader reader = new RawReader(new MemoryStream(buffer, offset, length), strictDecoding))
return (BEncodedValue.Decode(reader));
}
/// <summary>
/// Decode BEncoded data in the given stream
/// </summary>
/// <param name="stream">The stream containing the BEncoded data</param>
/// <returns>BEncodedValue containing the data that was in the stream</returns>
public static BEncodedValue Decode(Stream stream)
{
if (stream == null)
throw new ArgumentNullException("stream");
return Decode(new RawReader(stream));
}
/// <summary>
/// Decode BEncoded data in the given RawReader
/// </summary>
/// <param name="reader">The RawReader containing the BEncoded data</param>
/// <returns>BEncodedValue containing the data that was in the stream</returns>
public static BEncodedValue Decode(RawReader reader)
{
if (reader == null)
throw new ArgumentNullException("reader");
BEncodedValue data;
switch (reader.PeekByte())
{
case ('i'): // Integer
data = new BEncodedNumber();
break;
case ('d'): // Dictionary
data = new BEncodedDictionary();
break;
case ('l'): // List
data = new BEncodedList();
break;
case ('1'): // String
case ('2'):
case ('3'):
case ('4'):
case ('5'):
case ('6'):
case ('7'):
case ('8'):
case ('9'):
case ('0'):
data = new BEncodedString();
break;
default:
throw new BEncodingException("Could not find what value to decode");
}
data.DecodeInternal(reader);
return data;
}
/// <summary>
/// Interface for all BEncoded values
/// </summary>
/// <param name="data">The byte array containing the BEncoded data</param>
/// <returns></returns>
public static T Decode<T>(byte[] data) where T : BEncodedValue
{
return (T)BEncodedValue.Decode(data);
}
/// <summary>
/// Decode BEncoded data in the given byte array
/// </summary>
/// <param name="buffer">The byte array containing the BEncoded data</param>
/// <param name="offset">The offset at which the data starts at</param>
/// <param name="length">The number of bytes to be decoded</param>
/// <returns>BEncodedValue containing the data that was in the byte[]</returns>
public static T Decode<T>(byte[] buffer, int offset, int length) where T : BEncodedValue
{
return BEncodedValue.Decode<T>(buffer, offset, length, true);
}
public static T Decode<T>(byte[] buffer, int offset, int length, bool strictDecoding) where T : BEncodedValue
{
return (T)BEncodedValue.Decode(buffer, offset, length, strictDecoding);
}
/// <summary>
/// Decode BEncoded data in the given stream
/// </summary>
/// <param name="stream">The stream containing the BEncoded data</param>
/// <returns>BEncodedValue containing the data that was in the stream</returns>
public static T Decode<T>(Stream stream) where T : BEncodedValue
{
return (T)BEncodedValue.Decode(stream);
}
public static T Decode<T>(RawReader reader) where T : BEncodedValue
{
return (T)BEncodedValue.Decode(reader);
}
/// <summary>
/// Returns the size of the byte[] needed to encode this BEncodedValue
/// </summary>
/// <returns></returns>
public abstract int LengthInBytes();
}
}
+129
View File
@@ -0,0 +1,129 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace MonoTorrent.BEncoding
{
public class RawReader : Stream
{
bool hasPeek;
Stream input;
byte[] peeked;
bool strictDecoding;
public bool StrictDecoding
{
get { return strictDecoding; }
}
public RawReader(Stream input)
: this(input, true)
{
}
public RawReader(Stream input, bool strictDecoding)
{
this.input = input;
this.peeked = new byte[1];
this.strictDecoding = strictDecoding;
}
public override bool CanRead
{
get { return input.CanRead; }
}
public override bool CanSeek
{
get { return input.CanSeek; }
}
public override bool CanWrite
{
get { return false; }
}
public override void Flush()
{
throw new NotSupportedException();
}
public override long Length
{
get { return input.Length; }
}
public int PeekByte()
{
if (!hasPeek)
hasPeek = Read(peeked, 0, 1) == 1;
return hasPeek ? peeked[0] : -1;
}
public override int ReadByte()
{
if (hasPeek)
{
hasPeek = false;
return peeked[0];
}
return base.ReadByte();
}
public override long Position
{
get
{
if (hasPeek)
return input.Position - 1;
return input.Position;
}
set
{
if (value != Position)
{
hasPeek = false;
input.Position = value;
}
}
}
public override int Read(byte[] buffer, int offset, int count)
{
int read = 0;
if (hasPeek && count > 0)
{
hasPeek = false;
buffer[offset] = peeked[0];
offset++;
count--;
read++;
}
read += input.Read(buffer, offset, count);
return read;
}
public override long Seek(long offset, SeekOrigin origin)
{
long val;
if (hasPeek && origin == SeekOrigin.Current)
val = input.Seek(offset - 1, origin);
else
val = input.Seek(offset, origin);
hasPeek = false;
return val;
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
}
}