Beispiel #1
0
        public static object Decode(Stream stream, bool nullForCollectionEnd = false)
        {
            var firstOrNothing = stream.ReadByte();

            if (firstOrNothing == -1)
            {
                throw new DecodingException("Unexpected end of stream while expecting next value.");
            }
            var first = (byte)firstOrNothing;

            switch (first)
            {
            case (byte)'i':
                var valueDigits = new List <byte> {
                };

                while (true)
                {
                    var nextOrNothing = stream.ReadByte();
                    if (nextOrNothing == -1)
                    {
                        throw new DecodingException(
                                  "Unexpected end of stream while parsing integer.");
                    }
                    var next = (byte)nextOrNothing;

                    if (valueDigits.Count == 0 && next == '0')
                    {
                        // Leading zeros are not allowed, so this must be i0e.
                        var endOrNothing = stream.ReadByte();
                        if (endOrNothing == -1)
                        {
                            throw new DecodingException("Unexpected end of stream while parsing zero integer.");
                        }
                        var end = (byte)endOrNothing;
                        if (end != 'e')
                        {
                            throw new DecodingException($"Expected 'e' after 'i0', got: {end} '{char.ConvertFromUtf32(end)}'.");
                        }
                        return((Int64)0);
                    }

                    if (valueDigits.Count == 1 && next == '0' && valueDigits[0] == '-')
                    {
                        // Negative zero and leading zeros are not allowed.
                        throw new DecodingException("Unexpected '-0' parsing integer.");
                    }

                    if (next == 'e')
                    {
                        if (valueDigits.Count == 0)
                        {
                            throw new DecodingException(
                                      "Unexpected 'e' before any digits while parsing integer.");
                        }
                        else
                        {
                            break;     // valid end of integer
                        }
                    }

                    if (next == '-')
                    {
                        if (valueDigits.Count > 0)
                        {
                            throw new DecodingException(
                                      "Unexpected hyphen-minus after first character of integer value.");
                        }
                    }
                    else if (!('0' <= next && next <= '9'))
                    {
                        throw new DecodingException(
                                  $"Expected ASCII digit while parsing integer, got: {next} '{char.ConvertFromUtf32(next)}'.");
                    }

                    valueDigits.Add(next);
                }

                var valueDigitsString = valueDigits.ToArray().FromASCII();
                try {
                    return(Int64.Parse(valueDigitsString));
                } catch (System.OverflowException exception) {
                    throw new DecodingException(
                              $"Integer out of supported 64-bit bounds: {valueDigitsString}", exception);
                }

            case (byte)'0':
                // Must be the empty string.
                var secondOrNothing = stream.ReadByte();
                if (secondOrNothing == -1)
                {
                    throw new DecodingException("Unexpected end of stream while parsing empty string.");
                }
                var second = (byte)secondOrNothing;
                if (second != ':')
                {
                    throw new DecodingException($"Expected ':' after leading '0', got: {second} '{char.ConvertFromUtf32(second)}'.");
                }
                return(new byte[] {});

            case (byte)'1':
            case (byte)'2':
            case (byte)'3':
            case (byte)'4':
            case (byte)'5':
            case (byte)'6':
            case (byte)'7':
            case (byte)'8':
            case (byte)'9':
                var lengthDigits = new List <byte> {
                    first
                };

                while (true)
                {
                    var nextOrNothing = stream.ReadByte();
                    if (nextOrNothing == -1)
                    {
                        throw new DecodingException(
                                  "Unexpected end of stream while parsing length.");
                    }
                    var next = (byte)nextOrNothing;

                    if (next == ':')
                    {
                        break;     // valid end of length
                    }

                    if (next == '-')
                    {
                        if (lengthDigits.Count > 0)
                        {
                            throw new DecodingException(
                                      "Unexpected hyphen-minus after first character of integer length.");
                        }
                    }
                    else if (!('0' <= next && next <= '9'))
                    {
                        throw new DecodingException(
                                  $"Expected ASCII digit while parsing length, got: {next}");
                    }

                    lengthDigits.Add(next);
                }

                var   lengthDigitsString = lengthDigits.ToArray().FromASCII();
                Int32 length;
                try {
                    length = Int32.Parse(lengthDigitsString);
                } catch (System.OverflowException exception) {
                    throw new DecodingException(
                              $"Length out of supported 32-bit bounds: {lengthDigitsString}", exception);
                }

                var buffer = new byte[length];
                var read   = stream.Read(buffer, 0, (int)length);
                if (read < length)
                {
                    throw new DecodingException(
                              "Unexpected end of stream while reading string content.");
                }
                return(buffer);

            case (byte)'l':
                var list = new List <object> {
                };
                while (true)
                {
                    var next = Decode(stream, nullForCollectionEnd: true);
                    if (next == null)
                    {
                        break;     // valid end of list
                    }
                    list.Add(next);
                }
                return(list);

            case (byte)'d':
                var dictionary =
                    new Dictionary <byte[], object>(ByteArrayComparer.Instance);
                byte[] previousKey = null;

                while (true)
                {
                    var key = Decode(stream, nullForCollectionEnd: true);
                    if (key == null)
                    {
                        break;     // valid end of dictionary
                    }
                    else if (!(key is byte[]))
                    {
                        throw new DecodingException(
                                  $"Expected byte string for dictionary key, got: {key.GetType()}");
                    }
                    var typedKey = (byte[])key;
                    if (previousKey != null)
                    {
                        if (!ByteArrayComparer.Ascending(previousKey, typedKey))
                        {
                            throw new DecodingException(
                                      "Dictionary key was in the wrong order or duplicated.");
                        }
                    }
                    previousKey = typedKey;
                    var value = Decode(stream);
                    dictionary.Add(typedKey, value);
                }
                return(dictionary);

            case (byte)'e':
                if (nullForCollectionEnd)
                {
                    return(null);    // valid expected end of collection
                }
                else
                {
                    throw new DecodingException(
                              $"Found end-of-collection indicator 'e' while expecting value.");
                }

            default:
                throw new DecodingException(
                          $"Unexpected initial byte in value: {first} '{char.ConvertFromUtf32(first)}'.");
            }
        }
Beispiel #2
0
 static ByteArrayComparer()
 {
     Instance = new ByteArrayComparer();
 }