diff --git a/MatFileHandler/DataElementReader.cs b/MatFileHandler/DataElementReader.cs index c5e2679..9da8ab6 100755 --- a/MatFileHandler/DataElementReader.cs +++ b/MatFileHandler/DataElementReader.cs @@ -343,30 +343,33 @@ namespace MatFileHandler return new MatStructureArray(flags, dimensions, name, fields); } - private DataElement Read(Stream stream) - { - using (var reader = new BinaryReader(stream)) - { - return Read(reader); - } - } - private DataElement ReadCompressed(Tag tag, BinaryReader reader) { reader.ReadBytes(2); - var compressedData = reader.ReadBytes(tag.Length - 6); - reader.ReadBytes(4); - var resultStream = new MemoryStream(); - using (var compressedStream = new MemoryStream(compressedData)) + + DataElement element; + + using (var substream = new Substream(reader.BaseStream, tag.Length - 6)) { - using (var stream = new DeflateStream(compressedStream, CompressionMode.Decompress, leaveOpen: true)) + using (var deflateStream = new DeflateStream(substream, CompressionMode.Decompress)) + using (var bufferedStream = new BufferedStream(deflateStream)) + using (var positionTrackingStream = new PositionTrackingStream(bufferedStream)) + using (var innerReader = new BinaryReader(positionTrackingStream)) { - stream.CopyTo(resultStream); + element = Read(innerReader); + } + + if (substream.Position != substream.Length) + { + // In the pathological case that the deflate stream did not read the full + // length, then read out the rest manually (normally 1 byte). + reader.ReadBytes((int)(substream.Length - substream.Position)); } } - resultStream.Position = 0; - return Read(resultStream); + reader.ReadBytes(4); + + return element; } private DataElement ReadMatrix(Tag tag, BinaryReader reader) diff --git a/MatFileHandler/Substream.cs b/MatFileHandler/Substream.cs new file mode 100644 index 0000000..df32ef0 --- /dev/null +++ b/MatFileHandler/Substream.cs @@ -0,0 +1,68 @@ +// Copyright 2017-2018 Alexander Luzgarev + +using System; +using System.IO; + +namespace MatFileHandler +{ + /// + /// A stream which reads a finite section of another stream. + /// + internal sealed class Substream : Stream + { + private readonly Stream _baseStream; + private long _bytesRead; + + /// + /// Initializes a new instance of the class. + /// + /// The to wrap. + /// The number of bytes readable from this . + public Substream(Stream baseStream, long length) + { + _baseStream = baseStream; + Length = length; + } + + /// + public override bool CanRead => _baseStream.CanRead; + + /// + public override bool CanSeek => false; + + /// + public override bool CanWrite => false; + + /// + public override long Length { get; } + + /// + public override long Position + { + get => _bytesRead; + set => throw new NotSupportedException(); + } + + /// + public override void Flush() => _baseStream.Flush(); + + /// + public override int Read(byte[] buffer, int offset, int count) + { + int bytesRead = _baseStream.Read(buffer, offset, (int)Math.Min(count, Length - _bytesRead)); + + _bytesRead += bytesRead; + + return bytesRead; + } + + /// + public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); + + /// + public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + + /// + public override void SetLength(long value) => throw new NotSupportedException(); + } +}