Десериализовать из потока на определенную длину, где это не конец потока без LengthPrefix

Я пытаюсь увидеть, есть ли способ избавиться от чтения в моем потоке в MemoryStream перед десериализацией объекта, который был сохранен через ProtoBuf.Net.

Я не могу использовать функции Protobuf.Net With LengthPrefix, потому что я возвращаюсь и корректирую записи в файле по мере записи новых записей, чтобы файл можно было перемещать вперед или назад. Сериализованный класс не требует изменений, потому что я сохраняю 3 длины отдельно от фактического класса.

Это код, который я сейчас использую для чтения записи:

Private Function ReadEntry(ByVal br As BinaryReader) As PacketErrorLogEntry
    Dim activeRecord As PacketErrorLogEntry

    Dim OffsetPrevious As UInt32 = br.ReadUInt32()
    Dim RecordLength As UInt32 = br.ReadUInt32
    Dim OffsetNext As UInt32 = br.ReadUInt32
    Using ms As New MemoryStream
        Dim readLength As UInt32
        Dim bytesRead As UInt32
        Dim writeBuffer As Byte() = CType(Array.CreateInstance(GetType(Byte), _ 
                                          4096), Byte())
        Dim bytesToRead As UInt32 = CType(writeBuffer.Length, UInt32)
        If bytesToRead > RecordLength Then
            bytesToRead = RecordLength
        End If
        bytesRead = 0

        While readLength < RecordLength
            bytesRead = CType(br.BaseStream.Read(writeBuffer, 0, _ 
                              CType(bytesToRead, Integer)), UInt32)
            ms.Write(writeBuffer, 0, CType(bytesRead, Integer))
            readLength += bytesRead
        End While

        ms.Flush()
        ms.Position = 0
        activeRecord = Serializer.Deserialize(Of PacketErrorLogEntry)(ms)
        activeRecord.PreviousRecordLocation = OffsetPrevious
        activeRecord.NextRecordLocation = OffsetNext
    End Using
    'activeRecord = Serializer.Deserialize(Of PacketErrorLogEntry)(br.BaseStream, RecordLength)
    'activeRecord.PreviousRecordLocation = OffsetPrevious
    'activeRecord.NextRecordLocation = OffsetNext


    Return activeRecord
End Function

Я надеялся, что смогу достичь, так это того, что, передав параметр Length to Read функции Deserialize, я смогу избавиться от всего блока MemoryStream и просто вернуть свой объект.

Я использую BinaryReader / Writer для длины / смещения, чтобы я мог вернуться позже и просто перезаписать эти позиции обновленными значениями.


person Paul Farry    schedule 14.12.2011    source источник


Ответы (1)


Если вы используете v2, то это доступно в TypeModel API (который на самом деле является основным API; Serializer.Deserialize<T> API просто вызывает RuntimeTypeModel.Default.Deserialize). Существуют перегрузки, которые принимают количество потребляемых байтов. Один раз такой метод (на экземпляре TypeModel, скорее всего, RuntimeTypeModel.Default):

/// <summary>
/// Applies a protocol-buffer stream to an existing instance (which may be null).
/// </summary>
/// <param name="type">The type (including inheritance) to consider.</param>
/// <param name="value">The existing instance to be modified (can be null).</param>
/// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
/// <param name="length">The number of bytes to consume.</param>
/// <returns>The updated instance; this may be different to the instance argument if
/// either the original instance was null, or the stream defines a known sub-type of the
/// original instance.</returns>
public object Deserialize(Stream source, object value, Type type, int length);

Я также должен отметить, что API «с префиксом длины» также допускает использование различных стилей префикса с доступным int32 фиксированной длины (либо с прямым порядком, либо с прямым порядком байтов). Но ваш текущий подход тоже должен работать нормально.

Если вы используете v1, возможно, вам придется создать поток с ограниченной длиной .... или просто заимствовать тот, который v1 использует для внутренних целей (обратите внимание, что v2 не использует этот подход - он отслеживает оставшиеся активные байты в одном потоке плюс буфер) .

person Marc Gravell    schedule 14.12.2011
comment
Это здорово, спасибо, Марк. Надеюсь, это правильное использование. (Используя V2) activeRecord = New PacketErrorLogEntry ProtoBuf.Meta.RuntimeTypeModel.Default.Deserialize (br.BaseStream, activeRecord, GetType (PacketErrorLogEntry), CType (LengthPrevious, Integer)) - person Paul Farry; 14.12.2011
comment
@Paul в исходном коде вызывает Deserialize<T>, что более точно соответствует передаче null (Nothing) в параметр value. Передача существующего значения (activeRecord) ближе к вызову Merge<T>. Но пока он свежий и блестящий, в любом случае это должно быть примерно одно и то же (= New PacketErrorLogEntry и т. Д.) - person Marc Gravell; 14.12.2011