public void Peek() { var str = "Hello World!"; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(str))) using (var bs = new BencodeStream(ms)) { Assert.AreEqual('H', bs.Peek()); } }
public void EndOfStream() { var str = "Hello World!"; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(str))) using (var bs = new BencodeStream(ms)) { bs.Read(12); Assert.IsTrue(bs.EndOfStream); Assert.AreEqual(-1, bs.Read()); } }
public void PeekAfterSeek() { var str = "abcdefghijkl"; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(str))) using (var bs = new BencodeStream(ms)) { Assert.AreEqual(0, bs.Position); Assert.AreEqual('a', bs.PeekChar()); bs.Seek(1, SeekOrigin.Current); Assert.AreEqual(1, bs.Position); Assert.AreEqual('b', bs.PeekChar()); } }
public void PeekAfterPositionChangeOnBaseStream() { var str = "abcdefghijkl"; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(str))) using (var bs = new BencodeStream(ms)) { Assert.AreEqual(0, bs.Position); Assert.AreEqual('a', bs.PeekChar()); bs.BaseStream.Position = 1; Assert.AreEqual(1, bs.Position); Assert.AreEqual('b', bs.PeekChar()); } }
public void PeekAreChangedAfterRead() { var str = "abcdefghijkl"; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(str))) using (var bs = new BencodeStream(ms)) { Assert.Equal('a', bs.Peek()); Assert.Equal('a', bs.Read()); Assert.Equal('b', bs.Peek()); Assert.Equal('b', bs.Read()); Assert.Equal('c', bs.Peek()); Assert.Equal('c', bs.Read()); Assert.Equal('d', bs.Peek()); Assert.Equal('d', bs.Read()); Assert.Equal('e', bs.Peek()); Assert.Equal('e', bs.Read()); } }
public void ReadPrevious() { var str = "Hello World!"; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(str))) using (var bs = new BencodeStream(ms)) { Assert.AreEqual(-1, bs.ReadPrevious()); Assert.AreEqual('H', bs.Read()); Assert.AreEqual('H', bs.ReadPrevious()); Assert.AreEqual('e', bs.Read()); Assert.AreEqual('e', bs.ReadPrevious()); bs.Position = 20; Assert.AreEqual(-1, bs.ReadPrevious()); } }
public void ReadMoreBytesThanInStream() { using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("Hello World!"))) using (var bs = new BencodeStream(ms)) { var bytes = bs.Read(20); Assert.AreEqual(12, bytes.Length); Assert.AreEqual("Hello World!", Encoding.UTF8.GetString(bytes)); } }
public void ReadChangeStreamPosition() { var str = "Hello World!"; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(str))) using (var bs = new BencodeStream(ms)) { Assert.Equal('H', bs.Read()); Assert.Equal('e', bs.Read()); bs.Position -= 1; Assert.Equal('e', bs.Read()); } }
public void ReadZeroBytes() { using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("Hello World!"))) using (var bs = new BencodeStream(ms)) { var bytes = bs.Read(0); Assert.AreEqual(0, bytes.Length); Assert.AreEqual("", Encoding.UTF8.GetString(bytes)); } }
public void ReadPreviousUnaffectedByPeek() { var str = "Hello World!"; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(str))) using (var bs = new BencodeStream(ms)) { bs.Read(1); Assert.AreEqual('H', bs.ReadPrevious()); Assert.AreEqual('e', bs.Peek()); Assert.AreEqual('H', bs.ReadPrevious()); } }
public static BString DecodeString(BencodeStream stream, Encoding encoding) { if (stream == null) throw new ArgumentNullException("stream"); if (encoding == null) throw new ArgumentNullException("encoding"); // Minimum valid bencode string is '0:' meaning an empty string if (stream.Length < 2) throw new BencodeDecodingException<BString>("Minimum valid stream length is 2 (an empty string: '0:')", stream.Position); var lengthChars = new List<char>(); while (!stream.EndOfStream) { var c = stream.ReadChar(); // Break when we reach ':' if it is not the first character found if (lengthChars.Count > 0 && c == ':') break; // Character then must be a digit if (!c.IsDigit()) { if (lengthChars.Count == 0) throw new BencodeDecodingException<BString>(string.Format("Must begin with an integer but began with '{0}'", c), stream.Position); // We have found some digits but this is neither a digit nor a ':' as expected throw new BencodeDecodingException<BString>("Delimiter ':' was not found.", stream.Position); } // Because of memory limitations (~1-2 GB) we know for certain we cannot handle more than 10 digits (10GB) if (lengthChars.Count >= BString.LengthMaxDigits) { throw new UnsupportedBencodeException( string.Format("Length of string is more than {0} digits (>10GB) and is not supported (max is ~1-2GB).", BString.LengthMaxDigits), stream.Position); } lengthChars.Add(c); } var stringLength = long.Parse(lengthChars.AsString()); // Int32.MaxValue is ~2GB and is the absolute maximum that can be handled in memory if (stringLength > int.MaxValue) { throw new UnsupportedBencodeException( string.Format("Length of string is {0:N0} but maximum supported length is {1:N0}.", stringLength, int.MaxValue), stream.Position); } // TODO: Catch possible OutOfMemoryException when stringLength is close Int32.MaxValue ? var bytes = stream.Read((int)stringLength); // If the two don't match we've reached the end of the stream before reading the expected number of chars if (bytes.Length != stringLength) { throw new BencodeDecodingException<BString>( string.Format("Expected string to be {0:N0} bytes long but could only read {1:N0} bytes.", stringLength, bytes.Length), stream.Position); } return new BString(bytes, encoding); }
public static BNumber DecodeNumber(BencodeStream stream) { if (stream == null) throw new ArgumentNullException("stream"); if (stream.Length < 3) throw new BencodeDecodingException<BNumber>("Minimum valid length of stream is 3 ('i0e').", stream.Position); // Numbers must start with 'i' if (stream.ReadChar() != 'i') throw new BencodeDecodingException<BNumber>(string.Format("Must begin with 'i' but began with '{0}'.", stream.ReadPreviousChar()), stream.Position); var isNegative = false; var digits = new List<char>(); while (stream.Peek() != 'e' && stream.Peek() != -1) { // We do not support numbers that cannot be stored as a long (Int64) if (digits.Count >= BNumber.MaxDigits) { throw new UnsupportedBencodeException( string.Format( "The number '{0}' has more than 19 digits and cannot be stored as a long (Int64) and therefore is not supported.", digits.AsString()), stream.Position); } var c = stream.ReadChar(); // There may be only one '-' if (c == '-' && !isNegative) { // '-' must be the first char after the beginning 'i' if (digits.Count > 0) throw new BencodeDecodingException<BNumber>("A '-' must be directly after 'i' and before any digits.", stream.Position); isNegative = true; continue; } // If it is not a digit at this point it is invalid if (!c.IsDigit()) throw new BencodeDecodingException<BNumber>(string.Format("Must only contain digits and a single prefixed '-'. Invalid character '{0}'", c), stream.Position); digits.Add(c); } // We need at least one digit if (digits.Count < 1) throw new BencodeDecodingException<BNumber>("It contains no digits.", stream.Position); // Leading zeros are not valid if (digits[0] == '0' && digits.Count > 1) throw new BencodeDecodingException<BNumber>("Leading '0's are not valid.", stream.Position); // '-0' is not valid either if (digits[0] == '0' && digits.Count == 1 && isNegative) throw new BencodeDecodingException<BNumber>("'-0' is not a valid number.", stream.Position); if (stream.ReadChar() != 'e') throw new BencodeDecodingException<BNumber>("Missing end character 'e'.", stream.Position); if (isNegative) digits.Insert(0, '-'); long number; if (!long.TryParse(digits.AsString(), out number)) { // This should only happen if the number is bigger than 9,223,372,036,854,775,807 (or smaller than the negative version) throw new UnsupportedBencodeException( string.Format( "The value {0} cannot be stored as a long (Int64) and is therefore not supported. The supported values range from {1:N0} to {2:N0}", digits.AsString(), long.MinValue, long.MaxValue), stream.Position); } return new BNumber(number); }
public static BList DecodeList(BencodeStream stream, Encoding encoding) { if (stream == null) throw new ArgumentNullException("stream"); if (encoding == null) throw new ArgumentNullException("encoding"); if (stream.Length < 2) throw new BencodeDecodingException<BList>("Minimum valid length is 2 (an empty list: 'le')", stream.Position); // Lists must start with 'l' if (stream.ReadChar() != 'l') throw new BencodeDecodingException<BList>(string.Format("Must begin with 'l' but began with '{0}'.", stream.ReadPreviousChar()), stream.Position); var list = new BList(); // Loop until next character is the end character 'e' or end of stream while (stream.Peek() != 'e' && stream.Peek() != -1) { // Decode next object in stream var bObject = Decode(stream, encoding); if (bObject == null) throw new BencodeDecodingException<BList>(string.Format("Invalid object beginning with '{0}'", stream.PeekChar()), stream.Position); list.Add(bObject); } if (stream.ReadChar() != 'e') throw new BencodeDecodingException<BList>("Missing end character 'e'.", stream.Position); return list; }
public static BDictionary DecodeDictionary(BencodeStream stream, Encoding encoding) { if (stream == null) throw new ArgumentNullException("stream"); if (encoding == null) throw new ArgumentNullException("encoding"); var startPosition = stream.Position; if (stream.Length < 2) throw new BencodeDecodingException<BDictionary>("Minimum valid length is 2 (an empty dictionary: 'de')", startPosition); // Dictionaries must start with 'd' if (stream.ReadChar() != 'd') throw new BencodeDecodingException<BDictionary>(string.Format("Must begin with 'd' but began with '{0}'", stream.ReadPreviousChar()), startPosition); var dictionary = new BDictionary(); // Loop until next character is the end character 'e' or end of stream while (stream.Peek() != 'e' && stream.Peek() != -1) { // Decode next string in stream as the key BString key; try { key = DecodeString(stream, encoding); } catch (BencodeDecodingException<BString> ex) { throw new BencodeDecodingException<BDictionary>("Dictionary keys must be strings.", stream.Position); } // Decode next object in stream as the value var value = Decode(stream, encoding); if (value == null) throw new BencodeDecodingException<BDictionary>("All keys must have a corresponding value.", stream.Position); dictionary.Add(key, value); } if (stream.ReadChar() != 'e') throw new BencodeDecodingException<BDictionary>("Missing end character 'e'.", stream.Position); return dictionary; }
/// <summary> /// Decodes the specified stream using the specified encoding. /// </summary> /// <param name="stream">The stream to decode.</param> /// /// <param name="encoding">The encoding used by <see cref="BString"/> when calling <c>ToString()</c> with no arguments.</param> /// <returns>An <see cref="IBObject"/> representing the bencoded stream.</returns> /// <exception cref="ArgumentNullException">stream</exception> public static IBObject Decode(BencodeStream stream, Encoding encoding) { if (stream == null) throw new ArgumentNullException("stream"); if (encoding == null) throw new ArgumentNullException("encoding"); switch (stream.PeekChar()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return DecodeString(stream, encoding); case 'i': return DecodeNumber(stream); case 'l': return DecodeList(stream, encoding); case 'd': return DecodeDictionary(stream, encoding); } // TODO: Throw BencodeDecodingException because next char was not a valid start of a BObject? return null; }
public void ReadPreviousAtStartOfStream() { var str = "Hello World!"; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(str))) using (var bs = new BencodeStream(ms)) { Assert.AreEqual(-1, bs.ReadPrevious()); } }
public void ReadPreviousChar() { var str = "Hello World!"; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(str))) using (var bs = new BencodeStream(ms)) { Assert.AreEqual(default(char), bs.ReadPreviousChar()); bs.Read(1); Assert.AreEqual('H', bs.ReadPreviousChar()); } }
public void PeekAreChangedAfterReadSingleByte() { var str = "abcdefghijkl"; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(str))) using (var bs = new BencodeStream(ms)) { byte[] bytes; Assert.AreEqual('a', bs.Peek()); bytes = bs.Read(1); Assert.AreEqual('a', (char)bytes[0]); Assert.AreEqual('b', bs.Peek()); bytes = bs.Read(1); Assert.AreEqual('b', (char)bytes[0]); Assert.AreEqual('c', bs.Peek()); } }
public void ReadUnnaffectedByReadPrevious() { var str = "abcdefghijkl"; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(str))) using (var bs = new BencodeStream(ms)) { Assert.AreEqual('a', bs.Read()); bs.ReadPrevious(); Assert.AreEqual('b', bs.Read()); } }
public void PeekAtEndOfStreamThenReadSingleByte() { var str = "abcdefghijkl"; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(str))) using (var bs = new BencodeStream(ms)) { bs.Read(12); Assert.AreEqual(-1, bs.Peek()); Assert.AreEqual(-1, bs.Read()); } }
public void Read() { var str = "Hello World!"; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(str))) using (var bs = new BencodeStream(ms)) { Assert.AreEqual('H', bs.Read()); Assert.AreEqual('e', bs.Read()); Assert.AreEqual('l', bs.Read()); Assert.AreEqual('l', bs.Read()); Assert.AreEqual('o', bs.Read()); Assert.AreEqual(' ', bs.Read()); Assert.AreEqual('W', bs.Read()); Assert.AreEqual('o', bs.Read()); Assert.AreEqual('r', bs.Read()); Assert.AreEqual('l', bs.Read()); Assert.AreEqual('d', bs.Read()); Assert.AreEqual('!', bs.Read()); Assert.AreEqual(-1, bs.Read()); } }
public void ReadBytesChangesStreamPosition() { var str = "Hello World!"; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(str))) using (var bs = new BencodeStream(ms)) { Assert.AreEqual(0, bs.Position); var bytes = bs.Read(str.Length); Assert.AreEqual(12, bytes.Length); Assert.AreEqual(str, Encoding.UTF8.GetString(bytes)); Assert.AreEqual(12, bs.Position); } }
/// <summary> /// Encodes the torrent and writes it to the stream. /// </summary> /// <param name="stream"></param> /// <returns></returns> protected override void EncodeObject(BencodeStream stream) { var torrent = ToBDictionary(); torrent.EncodeTo(stream); }
public void PeekDoesNotAdvanceStreamPosition() { var str = "Hello World!"; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(str))) using (var bs = new BencodeStream(ms)) { Assert.Equal(0, bs.Position); Assert.Equal('H', bs.Peek()); Assert.Equal(0, bs.Position); Assert.Equal('H', bs.Peek()); Assert.Equal(0, bs.Position); } }