Read compressed elements without loading into memory

This commit is contained in:
Robert Hague 2025-04-05 11:52:04 +02:00
parent 37feeb5863
commit e8bf3f89ee
2 changed files with 87 additions and 16 deletions

View File

@ -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)

View File

@ -0,0 +1,68 @@
// Copyright 2017-2018 Alexander Luzgarev
using System;
using System.IO;
namespace MatFileHandler
{
/// <summary>
/// A stream which reads a finite section of another stream.
/// </summary>
internal sealed class Substream : Stream
{
private readonly Stream _baseStream;
private long _bytesRead;
/// <summary>
/// Initializes a new instance of the <see cref="Substream"/> class.
/// </summary>
/// <param name="baseStream">The <see cref="Stream"/> to wrap.</param>
/// <param name="length">The number of bytes readable from this <see cref="Substream"/>.</param>
public Substream(Stream baseStream, long length)
{
_baseStream = baseStream;
Length = length;
}
/// <inheritdoc/>
public override bool CanRead => _baseStream.CanRead;
/// <inheritdoc/>
public override bool CanSeek => false;
/// <inheritdoc/>
public override bool CanWrite => false;
/// <inheritdoc/>
public override long Length { get; }
/// <inheritdoc/>
public override long Position
{
get => _bytesRead;
set => throw new NotSupportedException();
}
/// <inheritdoc/>
public override void Flush() => _baseStream.Flush();
/// <inheritdoc/>
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;
}
/// <inheritdoc/>
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
/// <inheritdoc/>
public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
/// <inheritdoc/>
public override void SetLength(long value) => throw new NotSupportedException();
}
}