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());
     }
 }
示例#5
0
        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));
     }
 }
示例#8
0
 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));
     }
 }
示例#10
0
 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());
     }
 }
示例#11
0
        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);
        }
示例#12
0
        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);
        }
示例#13
0
        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;
        }
示例#14
0
        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;
        }
示例#15
0
        /// <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;
        }
示例#16
0
 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());
     }
 }
示例#17
0
 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());
     }
 }
示例#18
0
        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());
            }
        }
示例#19
0
 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());
     }
 }
示例#20
0
 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());
     }
 }
示例#21
0
 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());
     }
 }
示例#22
0
        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);
            }
        }
示例#23
0
 /// <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);
 }
示例#24
0
 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);
     }
 }