Ejemplo n.º 1
0
        /// <summary>Decodes an encoded value from an <paramref name="input"/>
        /// stream.</summary>
        /// <param name="input">An input stream to decode.</param>
        /// <returns>A decoded value.</returns>
        /// <exception cref="ArgumentException">Thrown when a given
        /// <paramref name="input"/> stream is not readable.</exception>
        /// <exception cref="DecodingException">Thrown when a binary
        /// representation of an <paramref name="input"/> stream is not a valid
        /// Bencodex encoding.</exception>
        public IValue Decode(Stream input)
        {
            if (!input.CanRead)
            {
                throw new ArgumentException(
                          "stream cannot be read",
                          nameof(input)
                          );
            }

            var    buffer = new ByteChunkQueue();
            IValue value  = Decode(buffer, input);

            if (buffer.Empty)
            {
                buffer.ReadFrom(input, 1);
            }

            if (!buffer.Empty)
            {
                long offset = input.Position;
                throw new DecodingException(
                          $"an unexpected byte at {offset - buffer.ByteLength}: " +
                          $"0x{buffer.FirstByte:x}"
                          );
            }

            return(value);
        }
Ejemplo n.º 2
0
        private byte[] DecodeBinary(ByteChunkQueue buffer, Stream input)
        {
            const byte colon  = 0x3a; // ':'
            long       length = DecodeDigits(colon, buffer, input, long.Parse);

            if (length < 1)
            {
                return(new byte[0]);
            }

            byte[] popped = buffer.Pop(length);
            if (popped.LongLength < length)
            {
                byte[] result = new byte[length];
                popped.CopyTo(result, 0);

                // FIXME: These Int64 to Int32 casts should be corrected.
                input.Read(
                    result,
                    (int)popped.LongLength,
                    (int)(length - popped.LongLength)
                    );
                return(result);
            }

            return(popped);
        }
Ejemplo n.º 3
0
        private string DecodeText(ByteChunkQueue buffer, Stream input)
        {
            byte[] singleByteBuffer = buffer.Pop(1);
            if (singleByteBuffer.Length < 1)
            {
                throw new DecodingException(
                          $"expected 'u' (0x75) at {input.Position - 1}, " +
                          "but the stream terminates"
                          );
            }

            if (singleByteBuffer[0] != 0x75)
            {
                throw new DecodingException(
                          $"expected 'u' (0x75) at {input.Position - 1}, " +
                          $"but 0x{singleByteBuffer[0]:x} is given"
                          );
            }

            long pos = input.Position;

            byte[] utf8 = DecodeBinary(buffer, input);
            try
            {
                return(Encoding.UTF8.GetString(utf8));
            }
            catch (ArgumentException e)
            {
                throw new DecodingException(
                          $"expected a UTF-8 sequence at {pos}",
                          e
                          );
            }
        }
Ejemplo n.º 4
0
        public void EdgeCase1()
        {
            var q = new ByteChunkQueue();

            q.Append(new byte[] { 1, 2, 3, 4 });
            byte[] popped = q.Pop(4);
            Assert.Equal(new byte[] { 1, 2, 3, 4 }, popped);
            Assert.EndsWith("[]", q.ToString());
        }
Ejemplo n.º 5
0
        private T DecodeDigits <T>(
            byte terminator,
            ByteChunkQueue buffer,
            Stream input,
            Func <string, T> converter
            )
        {
            if (buffer.Empty)
            {
                int read = buffer.ReadFrom(input, 1);
                if (read < 1)
                {
                    throw new DecodingException(
                              $"expected one or more digits at {input.Position}, " +
                              "but the byte stream terminates"
                              );
                }
            }

            long pos;

            while ((pos = buffer.IndexOf(terminator)) < 0)
            {
                int read = buffer.ReadFrom(input, 8);
                if (read < 0)
                {
                    throw new DecodingException(
                              $"expected a byte 0x{terminator:x} at " +
                              $"{input.Position}, but the byte stream terminates"
                              );
                }
            }

            byte[] digitBytes = buffer.Pop(pos);
            if (!digitBytes.All(b => b >= 0x30 && b < 0x40))
            {
                long digitsOffset =
                    input.Position - buffer.ByteLength - digitBytes.LongLength;
                throw new DecodingException(
                          $"expected 10-base digits at {digitsOffset}: " +
                          BitConverter.ToString(digitBytes)
                          );
            }

            buffer.Pop(1);  // pop terminator
            string digits = Encoding.ASCII.GetString(digitBytes);

            return(converter(digits));
        }
Ejemplo n.º 6
0
        public void EdgeCase2()
        {
            var q = new ByteChunkQueue();

            q.Append(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 });
            byte[] popped = q.Pop(7);
            Assert.Equal(new byte[] { 1, 2, 3, 4, 5, 6, 7 }, popped);
            Assert.EndsWith("[01-02-03-04-05-06-07|08]", q.ToString());
            q.Append(new byte[] { 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10 });
            Assert.EndsWith("[01-02-03-04-05-06-07|08 09-0A-0B-0C-0D-0E-0F-10]", q.ToString());

            popped = q.Pop(1);
            Assert.Equal(new byte[] { 8 }, popped);
            Assert.EndsWith("[09-0A-0B-0C-0D-0E-0F-10]", q.ToString());

            popped = q.Pop(1);
            Assert.Equal(new byte[] { 9 }, popped);
            Assert.EndsWith("[09|0A-0B-0C-0D-0E-0F-10]", q.ToString());
        }
Ejemplo n.º 7
0
        private IValue Decode(ByteChunkQueue buffer, Stream input)
        {
            if (buffer.Empty)
            {
                buffer.ReadFrom(input, 1);
            }

            long pos = input.Position - buffer.ByteLength;

            switch (buffer.FirstByte)
            {
            case null:
                throw new DecodingException(
                          $"stream terminates unexpectedly at {pos}"
                          );

            case 0x6e:     // 'n'
                buffer.Pop(1);
                return(default(Null));

            case 0x74:     // 't'
                buffer.Pop(1);
                return(new Bencodex.Types.Boolean(true));

            case 0x66:     // 'f'
                buffer.Pop(1);
                return(new Bencodex.Types.Boolean(false));

            case 0x69:     // 'i'
                buffer.Pop(1);
                if (buffer.Empty)
                {
                    buffer.ReadFrom(input, 1);
                }

                bool negative = false;
                if (buffer.FirstByte == 0x2d)     // '-'
                {
                    buffer.Pop(1);
                    negative = true;
                }

                const byte e       = 0x65; // 'e'
                BigInteger integer =
                    DecodeDigits(e, buffer, input, BigInteger.Parse);
                return(new Integer(negative ? -integer : integer));

            case 0x75:     // 'u'
                string text = DecodeText(buffer, input);
                return(new Text(text));

            case 0x6c:     // 'l'
                buffer.Pop(1);
                var elements = new List <IValue>();
                while (true)
                {
                    if (buffer.Empty)
                    {
                        buffer.ReadFrom(input, 1);
                    }

                    if (buffer.FirstByte == 0x65)     // 'e'
                    {
                        buffer.Pop(1);
                        break;
                    }

                    IValue element = Decode(buffer, input);
                    elements.Add(element);
                }

                return(new List(elements));

            case 0x64:     // 'd'
                buffer.Pop(1);
                var pairs = new List <KeyValuePair <IKey, IValue> >();
                while (true)
                {
                    if (buffer.Empty)
                    {
                        buffer.ReadFrom(input, 1);
                    }

                    byte?firstByte = buffer.FirstByte;
                    if (firstByte == null)
                    {
                        throw new DecodingException(
                                  $"expected 'e' (0x65) at {pos}, but " +
                                  "the stream terminates unexpectedly"
                                  );
                    }

                    if (firstByte == 0x65)     // 'e'
                    {
                        buffer.Pop(1);
                        break;
                    }

                    IKey key;
                    if (firstByte == 0x75)     // 'u'
                    {
                        string textKey = DecodeText(buffer, input);
                        key = new Text(textKey);
                    }
                    else if (firstByte >= 0x30 && firstByte < 0x40)
                    {
                        byte[] binaryKey = DecodeBinary(buffer, input);
                        key = new Binary(binaryKey);
                    }
                    else
                    {
                        throw new DecodingException(
                                  $"an unexpected byte 0x{firstByte:x} at {pos}"
                                  );
                    }

                    IValue value = Decode(buffer, input);
                    pairs.Add(new KeyValuePair <IKey, IValue>(key, value));
                }

                return(new Dictionary(pairs));

            case 0x30:     // '0'
            case 0x31:     // '1'
            case 0x32:     // '2'
            case 0x33:     // '3'
            case 0x34:     // '4'
            case 0x35:     // '5'
            case 0x36:     // '6'
            case 0x37:     // '7'
            case 0x38:     // '8'
            case 0x39:     // '9'
                byte[] binary = DecodeBinary(buffer, input);
                return(new Binary(binary));
            }

            throw new DecodingException(
                      $"an unexpected byte 0x{buffer.FirstByte:x} at {pos}"
                      );
        }
Ejemplo n.º 8
0
        public void TestByteChunkQueue()
        {
            var q = new ByteChunkQueue();

            // {}
            Assert.Equal(0, q.ByteLength);
            Assert.True(q.Empty);
            Assert.Throws <ArgumentException>(() => q.Pop(0));
            Assert.Throws <ArgumentException>(() => q.Pop(-1));
            Assert.Empty(q.Pop(1));
            Assert.Empty(q.Pop(2));
            Assert.True(q.StartsWith(new byte[0]));
            Assert.False(q.StartsWith(new byte[] { 1, 2 }));
            Assert.Null(q.FirstByte);
            Assert.Equal(-1, q.IndexOf(1));
            Assert.EndsWith("[]", q.ToString());

            q.Append(new byte[] { 1, 2, 3, 4 });

            // {1 2 3 4}
            Assert.Equal(4, q.ByteLength);
            Assert.False(q.Empty);
            Assert.True(q.StartsWith(new byte[0]));
            Assert.True(q.StartsWith(new byte[] { 1, 2 }));
            Assert.True(q.StartsWith(new byte[] { 1, 2, 3, 4 }));
            Assert.False(q.StartsWith(new byte[] { 1, 2, 3, 4, 5 }));
            Assert.False(q.StartsWith(new byte[] { 1, 3 }));
            Assert.Equal((byte)1, q.FirstByte);
            Assert.Equal(0, q.IndexOf(1));
            Assert.Equal(2, q.IndexOf(3));
            Assert.Equal(-1, q.IndexOf(5));
            Assert.EndsWith("[01-02-03-04]", q.ToString());

            byte[] popped = q.Pop(2);

            // {1 2 > 3 4}
            Assert.Equal(new byte[] { 1, 2 }, popped);
            Assert.Equal(2, q.ByteLength);
            Assert.False(q.Empty);
            Assert.True(q.StartsWith(new byte[0]));
            Assert.True(q.StartsWith(new byte[] { 3 }));
            Assert.True(q.StartsWith(new byte[] { 3, 4 }));
            Assert.False(q.StartsWith(new byte[] { 3, 4, 5 }));
            Assert.False(q.StartsWith(new byte[] { 1, 2 }));
            Assert.Equal((byte)3, q.FirstByte);
            Assert.Equal(-1, q.IndexOf(1));
            Assert.Equal(0, q.IndexOf(3));
            Assert.Equal(1, q.IndexOf(4));
            Assert.Equal(-1, q.IndexOf(5));
            Assert.EndsWith("[01-02|03-04]", q.ToString());

            var input = new MemoryStream(
                new byte[] { 5, 6, 7, 8, 9, 10, 11, 12 }
                );

            q.ReadFrom(input, 4);

            // {1 2 > 3 4, 5 6 7 8}
            Assert.Equal(6, q.ByteLength);
            Assert.False(q.Empty);
            Assert.True(q.StartsWith(new byte[0]));
            Assert.True(q.StartsWith(new byte[] { 3 }));
            Assert.True(q.StartsWith(new byte[] { 3, 4 }));
            Assert.True(q.StartsWith(new byte[] { 3, 4, 5 }));
            Assert.True(q.StartsWith(new byte[] { 3, 4, 5, 6, 7, 8 }));
            Assert.False(q.StartsWith(new byte[] { 3, 4, 5, 6, 7, 8, 9 }));
            Assert.False(q.StartsWith(new byte[] { 1, 2 }));
            Assert.Equal((byte)3, q.FirstByte);
            Assert.Equal(-1, q.IndexOf(1));
            Assert.Equal(0, q.IndexOf(3));
            Assert.Equal(4, q.IndexOf(7));
            Assert.Equal(-1, q.IndexOf(9));
            Assert.EndsWith("[01-02|03-04 05-06-07-08]", q.ToString());

            popped = q.Pop(4);

            // {5 6 > 7 8}
            Assert.Equal(new byte[] { 3, 4, 5, 6 }, popped);
            Assert.Equal(2, q.ByteLength);
            Assert.False(q.Empty);
            Assert.True(q.StartsWith(new byte[0]));
            Assert.True(q.StartsWith(new byte[] { 7 }));
            Assert.True(q.StartsWith(new byte[] { 7, 8 }));
            Assert.False(q.StartsWith(new byte[] { 7, 8, 9 }));
            Assert.False(q.StartsWith(new byte[] { 1, 2 }));
            Assert.False(q.StartsWith(new byte[] { 3 }));
            Assert.Equal((byte)7, q.FirstByte);
            Assert.Equal(-1, q.IndexOf(3));
            Assert.Equal(0, q.IndexOf(7));
            Assert.Equal(1, q.IndexOf(8));
            Assert.Equal(-1, q.IndexOf(9));
            Assert.EndsWith("[05-06|07-08]", q.ToString());

            q.ReadFrom(input, 3);

            // {5 6 > 7 8, 9 10 11}
            Assert.Equal(5, q.ByteLength);
            Assert.False(q.Empty);
            Assert.True(q.StartsWith(new byte[0]));
            Assert.True(q.StartsWith(new byte[] { 7 }));
            Assert.True(q.StartsWith(new byte[] { 7, 8 }));
            Assert.True(q.StartsWith(new byte[] { 7, 8, 9 }));
            Assert.True(q.StartsWith(new byte[] { 7, 8, 9, 10, 11 }));
            Assert.False(q.StartsWith(new byte[] { 7, 8, 9, 10, 11, 12 }));
            Assert.False(q.StartsWith(new byte[] { 1, 2 }));
            Assert.False(q.StartsWith(new byte[] { 3 }));
            Assert.Equal((byte)7, q.FirstByte);
            Assert.Equal(-1, q.IndexOf(3));
            Assert.Equal(-1, q.IndexOf(6));
            Assert.Equal(0, q.IndexOf(7));
            Assert.Equal(2, q.IndexOf(9));
            Assert.Equal(4, q.IndexOf(11));
            Assert.Equal(-1, q.IndexOf(12));
            Assert.EndsWith("[05-06|07-08 09-0A-0B]", q.ToString());

            q.ReadFrom(input, 10);

            // {5 6 > 7 8, 9 10 11, 12}
            Assert.Equal(6, q.ByteLength);
            Assert.False(q.Empty);
            Assert.True(q.StartsWith(new byte[0]));
            Assert.True(q.StartsWith(new byte[] { 7 }));
            Assert.True(q.StartsWith(new byte[] { 7, 8, 9 }));
            Assert.True(q.StartsWith(new byte[] { 7, 8, 9, 10, 11 }));
            Assert.True(q.StartsWith(new byte[] { 7, 8, 9, 10, 11, 12 }));
            Assert.False(q.StartsWith(new byte[] { 7, 8, 9, 10, 11, 12, 13 }));
            Assert.False(q.StartsWith(new byte[] { 1, 2 }));
            Assert.False(q.StartsWith(new byte[] { 3 }));
            Assert.Equal((byte)7, q.FirstByte);
            Assert.Equal(-1, q.IndexOf(6));
            Assert.Equal(0, q.IndexOf(7));
            Assert.Equal(5, q.IndexOf(12));
            Assert.Equal(-1, q.IndexOf(13));
            Assert.EndsWith("[05-06|07-08 09-0A-0B 0C]", q.ToString());

            q.Append(new byte[] { 13, 14 });

            // {5 6 > 7 8, 9 10 11, 12, 13 14}
            Assert.Equal(8, q.ByteLength);
            Assert.False(q.Empty);
            Assert.True(q.StartsWith(new byte[0]));
            Assert.True(q.StartsWith(new byte[] { 7 }));
            Assert.True(q.StartsWith(new byte[] { 7, 8, 9 }));
            Assert.True(q.StartsWith(new byte[] { 7, 8, 9, 10, 11, 12 }));
            Assert.True(q.StartsWith(new byte[] { 7, 8, 9, 10, 11, 12, 13 }));
            Assert.False(
                q.StartsWith(new byte[] { 7, 8, 9, 10, 11, 12, 13, 14, 15 })
                );
            Assert.False(q.StartsWith(new byte[] { 1, 2 }));
            Assert.False(q.StartsWith(new byte[] { 3 }));
            Assert.Equal((byte)7, q.FirstByte);
            Assert.Equal(-1, q.IndexOf(6));
            Assert.Equal(0, q.IndexOf(7));
            Assert.Equal(7, q.IndexOf(14));
            Assert.Equal(-1, q.IndexOf(15));
            Assert.EndsWith("[05-06|07-08 09-0A-0B 0C 0D-0E]", q.ToString());

            q.Append(new byte[] { 15 });

            // {5 6 > 7 8, 9 10 11, 12, 13 14, 15}
            Assert.Equal(9, q.ByteLength);
            Assert.False(q.Empty);
            Assert.True(q.StartsWith(new byte[0]));
            Assert.True(q.StartsWith(new byte[] { 7 }));
            Assert.True(q.StartsWith(new byte[] { 7, 8, 9 }));
            Assert.True(
                q.StartsWith(new byte[] { 7, 8, 9, 10, 11, 12, 13, 14, 15 })
                );
            Assert.False(
                q.StartsWith(new byte[] { 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 })
                );
            Assert.False(q.StartsWith(new byte[] { 1, 2 }));
            Assert.False(q.StartsWith(new byte[] { 3 }));
            Assert.Equal((byte)7, q.FirstByte);
            Assert.Equal(-1, q.IndexOf(6));
            Assert.Equal(0, q.IndexOf(7));
            Assert.Equal(8, q.IndexOf(15));
            Assert.Equal(-1, q.IndexOf(16));
            Assert.EndsWith("[05-06|07-08 09-0A-0B 0C 0D-0E 0F]", q.ToString());

            popped = q.Pop(7);

            // {13 > 14, 15}
            Assert.Equal(new byte[] { 7, 8, 9, 10, 11, 12, 13 }, popped);
            Assert.Equal(2, q.ByteLength);
            Assert.False(q.Empty);
            Assert.True(q.StartsWith(new byte[0]));
            Assert.True(q.StartsWith(new byte[] { 14 }));
            Assert.True(q.StartsWith(new byte[] { 14, 15 }));
            Assert.False(q.StartsWith(new byte[] { 14, 15, 16 }));
            Assert.False(q.StartsWith(new byte[] { 1, 2 }));
            Assert.False(q.StartsWith(new byte[] { 7 }));
            Assert.False(q.StartsWith(new byte[] { 7, 8, 9 }));
            Assert.Equal((byte)14, q.FirstByte);
            Assert.Equal(-1, q.IndexOf(7));
            Assert.Equal(-1, q.IndexOf(13));
            Assert.Equal(0, q.IndexOf(14));
            Assert.Equal(1, q.IndexOf(15));
            Assert.Equal(-1, q.IndexOf(16));
            Assert.EndsWith("[0D|0E 0F]", q.ToString());

            popped = q.Pop(10);

            // {}
            Assert.Equal(new byte[] { 14, 15 }, popped);
            Assert.Equal(0, q.ByteLength);
            Assert.True(q.Empty);
            Assert.True(q.StartsWith(new byte[0]));
            Assert.False(q.StartsWith(new byte[] { 1, 2 }));
            Assert.False(q.StartsWith(new byte[] { 14 }));
            Assert.False(q.StartsWith(new byte[] { 14, 15 }));
            Assert.False(q.StartsWith(new byte[] { 14, 15, 16 }));
            Assert.Null(q.FirstByte);
            Assert.Equal(-1, q.IndexOf(13));
            Assert.Equal(-1, q.IndexOf(15));
            Assert.Equal(-1, q.IndexOf(16));
            Assert.EndsWith("[]", q.ToString());
        }