mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-24 16:05:27 +00:00
fix async parts of arc
This commit is contained in:
@@ -2,6 +2,8 @@ using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress.Common.Arc
|
||||
{
|
||||
@@ -32,6 +34,23 @@ namespace SharpCompress.Common.Arc
|
||||
return LoadFrom(headerBytes);
|
||||
}
|
||||
|
||||
public async ValueTask<ArcEntryHeader?> ReadHeaderAsync(
|
||||
Stream stream,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
byte[] headerBytes = new byte[29];
|
||||
if (
|
||||
await stream.ReadAsync(headerBytes, 0, headerBytes.Length, cancellationToken)
|
||||
!= headerBytes.Length
|
||||
)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
DataStartPosition = stream.Position;
|
||||
return LoadFrom(headerBytes);
|
||||
}
|
||||
|
||||
public ArcEntryHeader LoadFrom(byte[] headerBytes)
|
||||
{
|
||||
CompressionMethod = GetCompressionType(headerBytes[1]);
|
||||
|
||||
68
src/SharpCompress/Common/Arc/ArcFilePart.Async.cs
Normal file
68
src/SharpCompress/Common/Arc/ArcFilePart.Async.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Compressors.Lzw;
|
||||
using SharpCompress.Compressors.RLE90;
|
||||
using SharpCompress.Compressors.Squeezed;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Arc
|
||||
{
|
||||
public partial class ArcFilePart
|
||||
{
|
||||
internal override async ValueTask<Stream?> GetCompressedStreamAsync(
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
if (_stream != null)
|
||||
{
|
||||
Stream compressedStream;
|
||||
switch (Header.CompressionMethod)
|
||||
{
|
||||
case CompressionType.None:
|
||||
compressedStream = new ReadOnlySubStream(
|
||||
_stream,
|
||||
Header.DataStartPosition,
|
||||
Header.CompressedSize
|
||||
);
|
||||
break;
|
||||
case CompressionType.Packed:
|
||||
compressedStream = new RunLength90Stream(
|
||||
_stream,
|
||||
(int)Header.CompressedSize
|
||||
);
|
||||
break;
|
||||
case CompressionType.Squeezed:
|
||||
compressedStream = await SqueezeStream.CreateAsync(
|
||||
_stream,
|
||||
(int)Header.CompressedSize,
|
||||
cancellationToken
|
||||
);
|
||||
break;
|
||||
case CompressionType.Crunched:
|
||||
if (Header.OriginalSize > 128 * 1024)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"CompressionMethod: "
|
||||
+ Header.CompressionMethod
|
||||
+ " with size > 128KB"
|
||||
);
|
||||
}
|
||||
compressedStream = new ArcLzwStream(
|
||||
_stream,
|
||||
(int)Header.CompressedSize,
|
||||
true
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException(
|
||||
"CompressionMethod: " + Header.CompressionMethod
|
||||
);
|
||||
}
|
||||
return compressedStream;
|
||||
}
|
||||
return _stream;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Arc
|
||||
{
|
||||
public class ArcFilePart : FilePart
|
||||
public partial class ArcFilePart : FilePart
|
||||
{
|
||||
private readonly Stream? _stream;
|
||||
|
||||
@@ -51,7 +51,10 @@ namespace SharpCompress.Common.Arc
|
||||
);
|
||||
break;
|
||||
case CompressionType.Squeezed:
|
||||
compressedStream = new SqueezeStream(_stream, (int)Header.CompressedSize);
|
||||
compressedStream = SqueezeStream.Create(
|
||||
_stream,
|
||||
(int)Header.CompressedSize
|
||||
);
|
||||
break;
|
||||
case CompressionType.Crunched:
|
||||
if (Header.OriginalSize > 128 * 1024)
|
||||
|
||||
33
src/SharpCompress/Compressors/Squeezed/BitReader.Async.cs
Normal file
33
src/SharpCompress/Compressors/Squeezed/BitReader.Async.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress.Compressors.Squeezed
|
||||
{
|
||||
public partial class BitReader
|
||||
{
|
||||
public async ValueTask<bool> ReadBitAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_bitCount == 0)
|
||||
{
|
||||
byte[] buffer = new byte[1];
|
||||
int bytesRead = await _stream
|
||||
.ReadAsync(buffer, 0, 1, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (bytesRead == 0)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
_bitBuffer = buffer[0];
|
||||
_bitCount = 8;
|
||||
}
|
||||
|
||||
bool bit = (_bitBuffer & 1) != 0;
|
||||
_bitBuffer >>= 1;
|
||||
_bitCount--;
|
||||
return bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +1,57 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
public class BitReader
|
||||
namespace SharpCompress.Compressors.Squeezed
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
private int _bitBuffer;
|
||||
private int _bitCount;
|
||||
|
||||
public BitReader(Stream stream)
|
||||
public partial class BitReader
|
||||
{
|
||||
_stream = stream;
|
||||
_bitBuffer = 0;
|
||||
_bitCount = 0;
|
||||
}
|
||||
private readonly Stream _stream;
|
||||
private int _bitBuffer;
|
||||
private int _bitCount;
|
||||
|
||||
public bool ReadBit()
|
||||
{
|
||||
if (_bitCount == 0)
|
||||
public BitReader(Stream stream)
|
||||
{
|
||||
int nextByte = _stream.ReadByte();
|
||||
if (nextByte == -1)
|
||||
_stream = stream;
|
||||
_bitBuffer = 0;
|
||||
_bitCount = 0;
|
||||
}
|
||||
|
||||
public bool ReadBit()
|
||||
{
|
||||
if (_bitCount == 0)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
int nextByte = _stream.ReadByte();
|
||||
if (nextByte == -1)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
_bitBuffer = nextByte;
|
||||
_bitCount = 8;
|
||||
}
|
||||
|
||||
_bitBuffer = nextByte;
|
||||
_bitCount = 8;
|
||||
bool bit = (_bitBuffer & 1) != 0;
|
||||
_bitBuffer >>= 1;
|
||||
_bitCount--;
|
||||
return bit;
|
||||
}
|
||||
|
||||
bool bit = (_bitBuffer & 1) != 0;
|
||||
_bitBuffer >>= 1;
|
||||
_bitCount--;
|
||||
return bit;
|
||||
}
|
||||
|
||||
public int ReadBits(int count)
|
||||
{
|
||||
if (count < 1 || count > 32)
|
||||
public int ReadBits(int count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(count), "Count must be between 1 and 32.");
|
||||
}
|
||||
if (count < 1 || count > 32)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(count),
|
||||
"Count must be between 1 and 32."
|
||||
);
|
||||
}
|
||||
|
||||
int value = 0;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
value = (value << 1) | (ReadBit() ? 1 : 0);
|
||||
int value = 0;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
value = (value << 1) | (ReadBit() ? 1 : 0);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
111
src/SharpCompress/Compressors/Squeezed/SqueezedStream.Async.cs
Normal file
111
src/SharpCompress/Compressors/Squeezed/SqueezedStream.Async.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Compressors.RLE90;
|
||||
|
||||
namespace SharpCompress.Compressors.Squeezed
|
||||
{
|
||||
public partial class SqueezeStream
|
||||
{
|
||||
public static async ValueTask<SqueezeStream> CreateAsync(
|
||||
Stream stream,
|
||||
int compressedSize,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var squeezeStream = new SqueezeStream(stream, compressedSize);
|
||||
squeezeStream._decodedStream = await squeezeStream
|
||||
.BuildDecodedStreamAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
squeezeStream.DebugConstruct(typeof(SqueezeStream));
|
||||
#endif
|
||||
|
||||
return squeezeStream;
|
||||
}
|
||||
|
||||
public override async Task<int> ReadAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
return await _decodedStream
|
||||
.ReadAsync(buffer, offset, count, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
#if !LEGACY_DOTNET
|
||||
public override async ValueTask<int> ReadAsync(
|
||||
Memory<byte> buffer,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
return await _decodedStream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
private async Task<Stream> BuildDecodedStreamAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
byte[] numNodesBytes = new byte[2];
|
||||
int bytesRead = await _stream
|
||||
.ReadAsync(numNodesBytes, 0, 2, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (bytesRead != 2)
|
||||
{
|
||||
return new MemoryStream(Array.Empty<byte>());
|
||||
}
|
||||
|
||||
int numnodes = numNodesBytes[0] | (numNodesBytes[1] << 8);
|
||||
|
||||
if (numnodes >= NUMVALS || numnodes == 0)
|
||||
{
|
||||
return new MemoryStream(Array.Empty<byte>());
|
||||
}
|
||||
|
||||
var dnode = new int[numnodes, 2];
|
||||
for (int j = 0; j < numnodes; j++)
|
||||
{
|
||||
byte[] nodeBytes = new byte[4];
|
||||
bytesRead = await _stream
|
||||
.ReadAsync(nodeBytes, 0, 4, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (bytesRead != 4)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
dnode[j, 0] = (short)(nodeBytes[0] | (nodeBytes[1] << 8));
|
||||
dnode[j, 1] = (short)(nodeBytes[2] | (nodeBytes[3] << 8));
|
||||
}
|
||||
|
||||
var bitReader = new BitReader(_stream);
|
||||
var huffmanDecoded = new MemoryStream();
|
||||
int i = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
bool bit = await bitReader.ReadBitAsync(cancellationToken).ConfigureAwait(false);
|
||||
i = dnode[i, bit ? 1 : 0];
|
||||
if (i < 0)
|
||||
{
|
||||
i = -(i + 1);
|
||||
if (i == SPEOF)
|
||||
{
|
||||
break;
|
||||
}
|
||||
huffmanDecoded.WriteByte((byte)i);
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
huffmanDecoded.Position = 0;
|
||||
return new RunLength90Stream(huffmanDecoded, (int)huffmanDecoded.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,24 +7,31 @@ using SharpCompress.Compressors.RLE90;
|
||||
namespace SharpCompress.Compressors.Squeezed
|
||||
{
|
||||
[CLSCompliant(true)]
|
||||
public class SqueezeStream : Stream
|
||||
public partial class SqueezeStream : Stream
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
private readonly int _compressedSize;
|
||||
private const int NUMVALS = 257;
|
||||
private const int SPEOF = 256;
|
||||
|
||||
private Stream _decodedStream;
|
||||
private Stream _decodedStream = null!;
|
||||
|
||||
public SqueezeStream(Stream stream, int compressedSize)
|
||||
private SqueezeStream(Stream stream, int compressedSize)
|
||||
{
|
||||
_stream = stream ?? throw new ArgumentNullException(nameof(stream));
|
||||
_compressedSize = compressedSize;
|
||||
_decodedStream = BuildDecodedStream();
|
||||
}
|
||||
|
||||
public static SqueezeStream Create(Stream stream, int compressedSize)
|
||||
{
|
||||
var squeezeStream = new SqueezeStream(stream, compressedSize);
|
||||
squeezeStream._decodedStream = squeezeStream.BuildDecodedStream();
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(SqueezeStream));
|
||||
squeezeStream.DebugConstruct(typeof(SqueezeStream));
|
||||
#endif
|
||||
|
||||
return squeezeStream;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
||||
23
src/SharpCompress/Readers/Arc/ArcReader.Async.cs
Normal file
23
src/SharpCompress/Readers/Arc/ArcReader.Async.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using SharpCompress.Common.Arc;
|
||||
|
||||
namespace SharpCompress.Readers.Arc
|
||||
{
|
||||
public partial class ArcReader
|
||||
{
|
||||
protected override async IAsyncEnumerable<ArcEntry> GetEntriesAsync(Stream stream)
|
||||
{
|
||||
ArcEntryHeader headerReader = new ArcEntryHeader(Options.ArchiveEncoding);
|
||||
ArcEntryHeader? header;
|
||||
while (
|
||||
(header = await headerReader.ReadHeaderAsync(stream, CancellationToken.None))
|
||||
!= null
|
||||
)
|
||||
{
|
||||
yield return new ArcEntry(new ArcFilePart(header, stream));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user