private static void ConvertArray(Stream input, ValueTag tag, JsonWriter writer)
        {
            var length       = input.ReadPackedValueUInt32();
            var basePosition = input.Position;
            var endPosition  = basePosition + length;

            if (endPosition > input.Length)
            {
                throw new EndOfStreamException();
            }

            writer.WriteStartArray();
            var elementTag = new ValueTag(input.ReadValueU8());

            if (elementTag.Type != ValueType.Invalid)
            {
                do
                {
                    ConvertValue(input, elementTag, writer);
                    elementTag = new ValueTag(input.ReadValueU8());
                }while (elementTag.Type != ValueType.Invalid);
            }
            writer.WriteEndArray();

            if (input.Position != endPosition)
            {
                throw new FormatException();
            }
        }
        private static void ConvertDictionary(Stream input, ValueTag tag, JsonWriter writer)
        {
            var length       = input.ReadPackedValueUInt32();
            var basePosition = input.Position;
            var endPosition  = basePosition + length;

            if (endPosition > input.Length)
            {
                throw new EndOfStreamException();
            }

            writer.WriteStartObject();
            var propertyTag = new ValueTag(input.ReadValueU8());

            while (propertyTag.Type != ValueType.Invalid)
            {
                var propertyName = input.ReadStringZ(Encoding.UTF8);
                writer.WritePropertyName(propertyName);
                ConvertValue(input, propertyTag, writer);
                propertyTag = new ValueTag(input.ReadValueU8());
            }
            writer.WriteEndObject();

            if (input.Position != endPosition)
            {
                throw new FormatException();
            }
        }
        private static string ReadString(Stream input, ValueTag tag)
        {
            var length       = input.ReadPackedValueUInt32();
            var basePosition = input.Position;
            var endPosition  = basePosition + length;

            if (endPosition > input.Length)
            {
                throw new EndOfStreamException();
            }

            var value = input.ReadStringZ(Encoding.UTF8);

            if (input.Position != endPosition)
            {
                throw new FormatException();
            }

            return(value);
        }
        private static void Convert(Stream input, JsonWriter writer)
        {
            var tag = new ValueTag(input.ReadValueU8());

            ConvertValue(input, tag, writer);
        }
        private static void ConvertValue(Stream input, ValueTag tag, JsonWriter writer)
        {
            switch (tag.Type)
            {
            case ValueType.Array:
            {
                ConvertArray(input, tag, writer);
                break;
            }

            case ValueType.Object:
            {
                ConvertDictionary(input, tag, writer);
                break;
            }

            case ValueType.Bool:
            {
                writer.WriteValue(input.ReadValueU8() != 0);
                break;
            }

            case ValueType.String:
            {
                writer.WriteValue(ReadString(input, tag));
                break;
            }

            case ValueType.Int32:
            {
                writer.WriteValue(input.ReadValueS32(Endian.Little));
                break;
            }

            case ValueType.Int64:
            {
                writer.WriteValue(input.ReadValueS64(Endian.Little));
                break;
            }

            case ValueType.Float64:
            {
                writer.WriteValue(input.ReadValueF64(Endian.Little));
                break;
            }

            case ValueType.Guid:
            {
                writer.WriteValue(input.ReadValueGuid(Endian.Big));
                break;
            }

            case ValueType.SHA1:
            {
                var bytes = input.ReadBytes(20);
                writer.WriteStartObject();
                var oldFormatting = writer.Formatting;
                writer.Formatting = Formatting.None;
                writer.WritePropertyName("type");
                writer.WriteValue("sha1");
                writer.WritePropertyName("value");
                writer.WriteValue(BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant());
                writer.WriteEndObject();
                writer.Formatting = oldFormatting;
                break;
            }

            case ValueType.Bytes:
            {
                var length       = input.ReadPackedValueUInt32();
                var basePosition = input.Position;
                var endPosition  = basePosition + length;
                if (endPosition > input.Length)
                {
                    throw new EndOfStreamException();
                }

                if (length > int.MaxValue)
                {
                    throw new FormatException();
                }

                var bytes = input.ReadBytes((int)length);

                if (input.Position != endPosition)
                {
                    throw new FormatException();
                }

                writer.WriteStartObject();
                var oldFormatting = writer.Formatting;
                writer.Formatting = Formatting.None;
                writer.WritePropertyName("type");
                writer.WriteValue("bytes");
                writer.WritePropertyName("value");
                writer.WriteValue(BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant());
                writer.WriteEndObject();
                writer.Formatting = oldFormatting;
                break;
            }

            default:
            {
                throw new NotSupportedException();
            }
            }
        }