Question

I am using protobuf-net in a network client/server application. Each packet of data is zipped with a BZip2Input/Output stream to compress my messages. This has worked fine for several weeks, but I have recently modified the server's objects, and am getting an exception that the BZip2Input stream does not support seeking (which it does not). I imagine this is because protobuf detects it can skip some fields, and tries to do so expediently.

Is there a way to disable this behavior, and force protobuf-net to skip fields by reading the data? (Not seeking).

Update: added the full stack trace below. Based on your comment, the stream must have a bug in it's CanSeek callback. What do you think?

Server First Chance Exception: BZip2InputStream Seek n
ot supported Stack Trace:    at ICSharpCode.SharpZipLib.BZip2.BZip2InputStream.S
eek(Int64 offset, SeekOrigin origin)
Server_NewRequestReceived Exception: System.NotSupportedException: BZip2InputStream Seek not supported
   at ICSharpCode.SharpZipLib.BZip2.BZip2InputStream.Seek(Int64 offset, origin)
   at ProtoBuf.ProtoReader.Seek(Stream source, Int32 count, Byte[] buffer)
   at ProtoBuf.ProtoReader.SkipField()
   at proto_2(Object, ProtoReader)
   at ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSeriali    zer.Read(Object value, ProtoReader source)
   at ProtoBuf.Meta.RuntimeTypeModel.Deserialize(Int32 key, Object value, source)
   at ProtoBuf.Meta.TypeModel.TryDeserializeAuxiliaryType(ProtoReader reader, format, Int32 tag, Type type, ref Object value, Boolean skipOtherFields, asListItem, Boolean autoCreate, Boolean insideList)
   at ProtoBuf.Meta.TypeModel.TryDeserializeList(TypeModel model, ader, DataFormat format, Int32 tag, Type listType, Type itemType, ref Object value)

   at ProtoBuf.Meta.TypeModel.TryDeserializeAuxiliaryType(ProtoReader reader, format, Int32 tag, Type type, ref Object value, Boolean skipOtherFields, asListItem, Boolean autoCreate, Boolean insideList)
   at ProtoBuf.Meta.TypeModel.DeserializeCore(ProtoReader reader, Type type, value, Boolean noAutoCreate)
   at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type, SerializationContext context)
   at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type)
   at ProtoBuf.Serializer.Deserialize(Stream source)

Created this class to get around the SharpZipLib bug.

    public class NonSeekableStreamWrapper : Stream
    {
        public Stream BaseStream { get; private set; }

        public NonSeekableStreamWrapper(Stream baseStream)
        {
            BaseStream = baseStream;
        }

        public override void Flush()
        {
            BaseStream.Flush();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return BaseStream.Seek(offset, origin);
        }

        public override void SetLength(long value)
        {
            BaseStream.SetLength(value);
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            return BaseStream.Read(buffer, offset, count);
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            BaseStream.Write(buffer, offset, count);
        }

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override bool CanWrite
        {
            get { return false; }
        }

        public override long Length
        {
            get { return BaseStream.Length; }
        }

        public override long Position { get; set; }
    }
Was it helpful?

Solution

That is interesting. I would be very interested in a full stacktrace when that happens, because AFAIK it checks the correct APIs for this, for example (from ProtoReader.Seek).

if (source.CanSeek)
{
    source.Seek(count, SeekOrigin.Current);
    // ...
}
else
{
    // ... does it the hard way, even if that means a Read loop
}

So no, it doesn't currently have an option for this because it trusts the streams to tell the truth. Of course, it could simply be a bug on my part, hence the request for a stacktrace.

If this is a genuine "thing", it would probably be possible to add such a mechanism.


Edit: yup, this is a bug in BZip2InputStream.cs in SharpZipLib; it has:

    /// <summary>
    /// Gets a value indicating whether the current stream supports seeking.
    /// </summary>
    public override bool CanSeek {
        get {
            return baseStream.CanSeek;
        }
    }

but it also has:

    /// <summary>
    /// Set the streams position.  This operation is not supported and will throw a NotSupportedException
    /// </summary>
    /// <param name="offset">A byte offset relative to the <paramref name="origin"/> parameter.</param>
    /// <param name="origin">A value of type <see cref="SeekOrigin"/> indicating the reference point used to obtain the new position.</param>
    /// <returns>The new position of the stream.</returns>
    /// <exception cref="NotSupportedException">Any access</exception>
    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotSupportedException("BZip2InputStream Seek not supported");
    }

So, it can never seek, but it claims it can if the base stream can.

It should have reported false instead of echoing the base-stream's CanSeek.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top