mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-07-09 02:26:47 +00:00
BZip2 now is async
This commit is contained in:
706
src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.Async.cs
Normal file
706
src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.Async.cs
Normal file
@@ -0,0 +1,706 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SharpCompress.Compressors.BZip2;
|
||||
|
||||
internal sealed partial class CBZip2OutputStream
|
||||
{
|
||||
private readonly byte[] bsAsyncWriteBuffer = new byte[1];
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the BZip2 stream header ('B', 'Z', 'h', blocksize) has been written
|
||||
/// asynchronously before the first compressed byte is written.
|
||||
/// </summary>
|
||||
private async Task EnsureStreamHeaderWrittenAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (!_streamHeaderWritten)
|
||||
{
|
||||
_streamHeaderWritten = true;
|
||||
// Write 'B', 'Z', 'h' async, then set up bit buffer as Initialize() would.
|
||||
// Initialize() calls BsPutUChar('h') then BsPutUChar('0'+N):
|
||||
// - First call buffers 'h' (bsLive=8)
|
||||
// - Second call flushes 'h' to stream, then buffers '0'+N (bsLive=8)
|
||||
// So after Initialize(), stream has 'h' and bit buffer has '0'+N with bsLive=8.
|
||||
var header = new byte[] { (byte)'B', (byte)'Z', (byte)'h' };
|
||||
await bsStream
|
||||
.WriteAsync(header, 0, header.Length, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
// Replicate the bit buffer state that Initialize() leaves:
|
||||
bytesOut = 1; // 'h' was written via BsW (Initialize increments bytesOut via BsW)
|
||||
nBlocksRandomised = 0;
|
||||
combinedCRC = 0;
|
||||
bsBuff = (blockSize100k + '0') << 24;
|
||||
bsLive = 8;
|
||||
InitBlock();
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask WriteByteAsync(byte bv, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await EnsureStreamHeaderWrittenAsync(cancellationToken).ConfigureAwait(false);
|
||||
var b = (256 + bv) % 256;
|
||||
if (currentChar != -1)
|
||||
{
|
||||
if (currentChar == b)
|
||||
{
|
||||
runLength++;
|
||||
if (runLength > 254)
|
||||
{
|
||||
await WriteRunAsync(cancellationToken).ConfigureAwait(false);
|
||||
currentChar = -1;
|
||||
runLength = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await WriteRunAsync(cancellationToken).ConfigureAwait(false);
|
||||
runLength = 1;
|
||||
currentChar = b;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
currentChar = b;
|
||||
runLength++;
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask WriteRunAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (last < allowableBlockSize)
|
||||
{
|
||||
inUse[currentChar] = true;
|
||||
for (var i = 0; i < runLength; i++)
|
||||
{
|
||||
mCrc.UpdateCRC((char)currentChar);
|
||||
}
|
||||
switch (runLength)
|
||||
{
|
||||
case 1:
|
||||
last++;
|
||||
block[last + 1] = (char)currentChar;
|
||||
break;
|
||||
case 2:
|
||||
last++;
|
||||
block[last + 1] = (char)currentChar;
|
||||
last++;
|
||||
block[last + 1] = (char)currentChar;
|
||||
break;
|
||||
case 3:
|
||||
last++;
|
||||
block[last + 1] = (char)currentChar;
|
||||
last++;
|
||||
block[last + 1] = (char)currentChar;
|
||||
last++;
|
||||
block[last + 1] = (char)currentChar;
|
||||
break;
|
||||
default:
|
||||
inUse[runLength - 4] = true;
|
||||
last++;
|
||||
block[last + 1] = (char)currentChar;
|
||||
last++;
|
||||
block[last + 1] = (char)currentChar;
|
||||
last++;
|
||||
block[last + 1] = (char)currentChar;
|
||||
last++;
|
||||
block[last + 1] = (char)currentChar;
|
||||
last++;
|
||||
block[last + 1] = (char)(runLength - 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await EndBlockAsync(cancellationToken).ConfigureAwait(false);
|
||||
InitBlock();
|
||||
await WriteRunAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask EndBlockAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// Skip block processing for empty input (no data written)
|
||||
if (last < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
blockCRC = mCrc.GetFinalCRC();
|
||||
combinedCRC = (combinedCRC << 1) | (int)(((uint)combinedCRC) >> 31);
|
||||
combinedCRC ^= blockCRC;
|
||||
|
||||
/* sort the block and establish posn of original string */
|
||||
DoReversibleTransformation();
|
||||
|
||||
await BsPutUCharAsync(0x31, cancellationToken).ConfigureAwait(false);
|
||||
await BsPutUCharAsync(0x41, cancellationToken).ConfigureAwait(false);
|
||||
await BsPutUCharAsync(0x59, cancellationToken).ConfigureAwait(false);
|
||||
await BsPutUCharAsync(0x26, cancellationToken).ConfigureAwait(false);
|
||||
await BsPutUCharAsync(0x53, cancellationToken).ConfigureAwait(false);
|
||||
await BsPutUCharAsync(0x59, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await BsPutintAsync(blockCRC, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (blockRandomised)
|
||||
{
|
||||
await BsWAsync(1, 1, cancellationToken).ConfigureAwait(false);
|
||||
nBlocksRandomised++;
|
||||
}
|
||||
else
|
||||
{
|
||||
await BsWAsync(1, 0, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await MoveToFrontCodeAndSendAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async ValueTask EndCompressionAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await BsPutUCharAsync(0x17, cancellationToken).ConfigureAwait(false);
|
||||
await BsPutUCharAsync(0x72, cancellationToken).ConfigureAwait(false);
|
||||
await BsPutUCharAsync(0x45, cancellationToken).ConfigureAwait(false);
|
||||
await BsPutUCharAsync(0x38, cancellationToken).ConfigureAwait(false);
|
||||
await BsPutUCharAsync(0x50, cancellationToken).ConfigureAwait(false);
|
||||
await BsPutUCharAsync(0x90, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await BsPutintAsync(combinedCRC, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await BsFinishedWithStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async ValueTask BsFinishedWithStreamAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
while (bsLive > 0)
|
||||
{
|
||||
var ch = bsBuff >> 24;
|
||||
bsAsyncWriteBuffer[0] = (byte)ch;
|
||||
await bsStream
|
||||
.WriteAsync(bsAsyncWriteBuffer, 0, 1, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
bsBuff <<= 8;
|
||||
bsLive -= 8;
|
||||
bytesOut++;
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask BsWAsync(int n, int v, CancellationToken cancellationToken)
|
||||
{
|
||||
while (bsLive >= 8)
|
||||
{
|
||||
var ch = bsBuff >> 24;
|
||||
bsAsyncWriteBuffer[0] = (byte)ch;
|
||||
await bsStream
|
||||
.WriteAsync(bsAsyncWriteBuffer, 0, 1, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
bsBuff <<= 8;
|
||||
bsLive -= 8;
|
||||
bytesOut++;
|
||||
}
|
||||
bsBuff |= v << (32 - bsLive - n);
|
||||
bsLive += n;
|
||||
}
|
||||
|
||||
private ValueTask BsPutUCharAsync(int c, CancellationToken cancellationToken) =>
|
||||
BsWAsync(8, c, cancellationToken);
|
||||
|
||||
private async ValueTask BsPutintAsync(int u, CancellationToken cancellationToken)
|
||||
{
|
||||
await BsWAsync(8, (u >> 24) & 0xff, cancellationToken).ConfigureAwait(false);
|
||||
await BsWAsync(8, (u >> 16) & 0xff, cancellationToken).ConfigureAwait(false);
|
||||
await BsWAsync(8, (u >> 8) & 0xff, cancellationToken).ConfigureAwait(false);
|
||||
await BsWAsync(8, u & 0xff, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private ValueTask BsPutIntVSAsync(int numBits, int c, CancellationToken cancellationToken) =>
|
||||
BsWAsync(numBits, c, cancellationToken);
|
||||
|
||||
private async ValueTask SendMTFValuesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var len = CBZip2InputStream.InitCharArray(
|
||||
BZip2Constants.N_GROUPS,
|
||||
BZip2Constants.MAX_ALPHA_SIZE
|
||||
);
|
||||
|
||||
int v,
|
||||
t,
|
||||
i,
|
||||
j,
|
||||
gs,
|
||||
ge,
|
||||
totc,
|
||||
bt,
|
||||
bc,
|
||||
iter;
|
||||
int nSelectors = 0,
|
||||
alphaSize,
|
||||
minLen,
|
||||
maxLen,
|
||||
selCtr;
|
||||
int nGroups; //, nBytes;
|
||||
|
||||
alphaSize = nInUse + 2;
|
||||
for (t = 0; t < BZip2Constants.N_GROUPS; t++)
|
||||
{
|
||||
for (v = 0; v < alphaSize; v++)
|
||||
{
|
||||
len[t][v] = (char)GREATER_ICOST;
|
||||
}
|
||||
}
|
||||
|
||||
if (nMTF <= 0)
|
||||
{
|
||||
Panic();
|
||||
}
|
||||
|
||||
if (nMTF < 200)
|
||||
{
|
||||
nGroups = 2;
|
||||
}
|
||||
else if (nMTF < 600)
|
||||
{
|
||||
nGroups = 3;
|
||||
}
|
||||
else if (nMTF < 1200)
|
||||
{
|
||||
nGroups = 4;
|
||||
}
|
||||
else if (nMTF < 2400)
|
||||
{
|
||||
nGroups = 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
nGroups = 6;
|
||||
}
|
||||
|
||||
{
|
||||
int nPart,
|
||||
remF,
|
||||
tFreq,
|
||||
aFreq;
|
||||
|
||||
nPart = nGroups;
|
||||
remF = nMTF;
|
||||
gs = 0;
|
||||
while (nPart > 0)
|
||||
{
|
||||
tFreq = remF / nPart;
|
||||
ge = gs - 1;
|
||||
aFreq = 0;
|
||||
while (aFreq < tFreq && ge < alphaSize - 1)
|
||||
{
|
||||
ge++;
|
||||
aFreq += mtfFreq[ge];
|
||||
}
|
||||
|
||||
if (ge > gs && nPart != nGroups && nPart != 1 && ((nGroups - nPart) % 2 == 1))
|
||||
{
|
||||
aFreq -= mtfFreq[ge];
|
||||
ge--;
|
||||
}
|
||||
|
||||
for (v = 0; v < alphaSize; v++)
|
||||
{
|
||||
if (v >= gs && v <= ge)
|
||||
{
|
||||
len[nPart - 1][v] = (char)LESSER_ICOST;
|
||||
}
|
||||
else
|
||||
{
|
||||
len[nPart - 1][v] = (char)GREATER_ICOST;
|
||||
}
|
||||
}
|
||||
|
||||
nPart--;
|
||||
gs = ge + 1;
|
||||
remF -= aFreq;
|
||||
}
|
||||
}
|
||||
|
||||
var rfreq = CBZip2InputStream.InitIntArray(
|
||||
BZip2Constants.N_GROUPS,
|
||||
BZip2Constants.MAX_ALPHA_SIZE
|
||||
);
|
||||
var fave = new int[BZip2Constants.N_GROUPS];
|
||||
var cost = new short[BZip2Constants.N_GROUPS];
|
||||
for (iter = 0; iter < BZip2Constants.N_ITERS; iter++)
|
||||
{
|
||||
for (t = 0; t < nGroups; t++)
|
||||
{
|
||||
fave[t] = 0;
|
||||
}
|
||||
|
||||
for (t = 0; t < nGroups; t++)
|
||||
{
|
||||
for (v = 0; v < alphaSize; v++)
|
||||
{
|
||||
rfreq[t][v] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
nSelectors = 0;
|
||||
totc = 0;
|
||||
gs = 0;
|
||||
while (true)
|
||||
{
|
||||
if (gs >= nMTF)
|
||||
{
|
||||
break;
|
||||
}
|
||||
ge = gs + BZip2Constants.G_SIZE - 1;
|
||||
if (ge >= nMTF)
|
||||
{
|
||||
ge = nMTF - 1;
|
||||
}
|
||||
|
||||
for (t = 0; t < nGroups; t++)
|
||||
{
|
||||
cost[t] = 0;
|
||||
}
|
||||
|
||||
if (nGroups == 6)
|
||||
{
|
||||
short cost0,
|
||||
cost1,
|
||||
cost2,
|
||||
cost3,
|
||||
cost4,
|
||||
cost5;
|
||||
cost0 = cost1 = cost2 = cost3 = cost4 = cost5 = 0;
|
||||
for (i = gs; i <= ge; i++)
|
||||
{
|
||||
var icv = szptr[i];
|
||||
cost0 += (short)len[0][icv];
|
||||
cost1 += (short)len[1][icv];
|
||||
cost2 += (short)len[2][icv];
|
||||
cost3 += (short)len[3][icv];
|
||||
cost4 += (short)len[4][icv];
|
||||
cost5 += (short)len[5][icv];
|
||||
}
|
||||
cost[0] = cost0;
|
||||
cost[1] = cost1;
|
||||
cost[2] = cost2;
|
||||
cost[3] = cost3;
|
||||
cost[4] = cost4;
|
||||
cost[5] = cost5;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = gs; i <= ge; i++)
|
||||
{
|
||||
var icv = szptr[i];
|
||||
for (t = 0; t < nGroups; t++)
|
||||
{
|
||||
cost[t] += (short)len[t][icv];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bc = 999999999;
|
||||
bt = -1;
|
||||
for (t = 0; t < nGroups; t++)
|
||||
{
|
||||
if (cost[t] < bc)
|
||||
{
|
||||
bc = cost[t];
|
||||
bt = t;
|
||||
}
|
||||
}
|
||||
;
|
||||
totc += bc;
|
||||
fave[bt]++;
|
||||
selector[nSelectors] = (char)bt;
|
||||
nSelectors++;
|
||||
|
||||
for (i = gs; i <= ge; i++)
|
||||
{
|
||||
rfreq[bt][szptr[i]]++;
|
||||
}
|
||||
|
||||
gs = ge + 1;
|
||||
}
|
||||
|
||||
for (t = 0; t < nGroups; t++)
|
||||
{
|
||||
HbMakeCodeLengths(len[t], rfreq[t], alphaSize, 20);
|
||||
}
|
||||
}
|
||||
|
||||
rfreq = null;
|
||||
fave = null;
|
||||
cost = null;
|
||||
|
||||
if (!(nGroups < 8))
|
||||
{
|
||||
Panic();
|
||||
}
|
||||
if (!(nSelectors < 32768 && nSelectors <= (2 + (900000 / BZip2Constants.G_SIZE))))
|
||||
{
|
||||
Panic();
|
||||
}
|
||||
|
||||
{
|
||||
var pos = new char[BZip2Constants.N_GROUPS];
|
||||
char ll_i,
|
||||
tmp2,
|
||||
tmp;
|
||||
for (i = 0; i < nGroups; i++)
|
||||
{
|
||||
pos[i] = (char)i;
|
||||
}
|
||||
for (i = 0; i < nSelectors; i++)
|
||||
{
|
||||
ll_i = selector[i];
|
||||
j = 0;
|
||||
tmp = pos[j];
|
||||
while (ll_i != tmp)
|
||||
{
|
||||
j++;
|
||||
tmp2 = tmp;
|
||||
tmp = pos[j];
|
||||
pos[j] = tmp2;
|
||||
}
|
||||
pos[0] = tmp;
|
||||
selectorMtf[i] = (char)j;
|
||||
}
|
||||
}
|
||||
|
||||
var code = CBZip2InputStream.InitIntArray(
|
||||
BZip2Constants.N_GROUPS,
|
||||
BZip2Constants.MAX_ALPHA_SIZE
|
||||
);
|
||||
|
||||
for (t = 0; t < nGroups; t++)
|
||||
{
|
||||
minLen = 32;
|
||||
maxLen = 0;
|
||||
for (i = 0; i < alphaSize; i++)
|
||||
{
|
||||
if (len[t][i] > maxLen)
|
||||
{
|
||||
maxLen = len[t][i];
|
||||
}
|
||||
if (len[t][i] < minLen)
|
||||
{
|
||||
minLen = len[t][i];
|
||||
}
|
||||
}
|
||||
if (maxLen > 20)
|
||||
{
|
||||
Panic();
|
||||
}
|
||||
if (minLen < 1)
|
||||
{
|
||||
Panic();
|
||||
}
|
||||
HbAssignCodes(code[t], len[t], minLen, maxLen, alphaSize);
|
||||
}
|
||||
|
||||
{
|
||||
var inUse16 = new bool[16];
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
inUse16[i] = false;
|
||||
for (j = 0; j < 16; j++)
|
||||
{
|
||||
if (inUse[(i * 16) + j])
|
||||
{
|
||||
inUse16[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
if (inUse16[i])
|
||||
{
|
||||
await BsWAsync(1, 1, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await BsWAsync(1, 0, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
if (inUse16[i])
|
||||
{
|
||||
for (j = 0; j < 16; j++)
|
||||
{
|
||||
if (inUse[(i * 16) + j])
|
||||
{
|
||||
await BsWAsync(1, 1, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await BsWAsync(1, 0, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await BsWAsync(3, nGroups, cancellationToken).ConfigureAwait(false);
|
||||
await BsWAsync(15, nSelectors, cancellationToken).ConfigureAwait(false);
|
||||
for (i = 0; i < nSelectors; i++)
|
||||
{
|
||||
for (j = 0; j < selectorMtf[i]; j++)
|
||||
{
|
||||
await BsWAsync(1, 1, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
await BsWAsync(1, 0, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
for (t = 0; t < nGroups; t++)
|
||||
{
|
||||
int curr = len[t][0];
|
||||
await BsWAsync(5, curr, cancellationToken).ConfigureAwait(false);
|
||||
for (i = 0; i < alphaSize; i++)
|
||||
{
|
||||
while (curr < len[t][i])
|
||||
{
|
||||
await BsWAsync(2, 2, cancellationToken).ConfigureAwait(false);
|
||||
curr++;
|
||||
}
|
||||
while (curr > len[t][i])
|
||||
{
|
||||
await BsWAsync(2, 3, cancellationToken).ConfigureAwait(false);
|
||||
curr--;
|
||||
}
|
||||
await BsWAsync(1, 0, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
selCtr = 0;
|
||||
gs = 0;
|
||||
while (true)
|
||||
{
|
||||
if (gs >= nMTF)
|
||||
{
|
||||
break;
|
||||
}
|
||||
ge = gs + BZip2Constants.G_SIZE - 1;
|
||||
if (ge >= nMTF)
|
||||
{
|
||||
ge = nMTF - 1;
|
||||
}
|
||||
for (i = gs; i <= ge; i++)
|
||||
{
|
||||
await BsWAsync(
|
||||
len[selector[selCtr]][szptr[i]],
|
||||
code[selector[selCtr]][szptr[i]],
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
gs = ge + 1;
|
||||
selCtr++;
|
||||
}
|
||||
if (!(selCtr == nSelectors))
|
||||
{
|
||||
Panic();
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask MoveToFrontCodeAndSendAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await BsPutIntVSAsync(24, origPtr, cancellationToken).ConfigureAwait(false);
|
||||
GenerateMTFValues();
|
||||
await SendMTFValuesAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public override async Task WriteAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
await EnsureStreamHeaderWrittenAsync(cancellationToken).ConfigureAwait(false);
|
||||
for (var k = 0; k < count; ++k)
|
||||
{
|
||||
await WriteByteAsync(buffer[k + offset], cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
#if !LEGACY_DOTNET
|
||||
public override async ValueTask WriteAsync(
|
||||
ReadOnlyMemory<byte> buffer,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
await EnsureStreamHeaderWrittenAsync(cancellationToken).ConfigureAwait(false);
|
||||
for (var k = 0; k < buffer.Length; ++k)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var value = buffer.Span[k];
|
||||
await WriteByteAsync(value, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously finalizes the BZip2 compressed stream, flushing all pending data.
|
||||
/// Writes the remaining compressed data to the underlying stream using async I/O.
|
||||
/// </summary>
|
||||
public async ValueTask FinishAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (finished)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await EnsureStreamHeaderWrittenAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (runLength > 0)
|
||||
{
|
||||
await WriteRunAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
currentChar = -1;
|
||||
await EndBlockAsync(cancellationToken).ConfigureAwait(false);
|
||||
await EndCompressionAsync(cancellationToken).ConfigureAwait(false);
|
||||
finished = true;
|
||||
await bsStream.FlushAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
#if !LEGACY_DOTNET || NETSTANDARD2_1
|
||||
public override async ValueTask DisposeAsync()
|
||||
#else
|
||||
public async ValueTask DisposeAsync()
|
||||
#endif
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await FinishAsync().ConfigureAwait(false);
|
||||
disposed = true;
|
||||
if (!leaveOpen && bsStream is not null)
|
||||
{
|
||||
if (bsStream is IAsyncDisposable asyncDisposable)
|
||||
{
|
||||
await asyncDisposable.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
bsStream.Dispose();
|
||||
}
|
||||
}
|
||||
bsStream = null;
|
||||
|
||||
#if !LEGACY_DOTNET || NETSTANDARD2_1
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
#else
|
||||
await Task.CompletedTask.ConfigureAwait(false);
|
||||
#endif
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
/*
|
||||
* Copyright 2001,2004-2005 The Apache Software Foundation
|
||||
*
|
||||
@@ -39,7 +37,7 @@ namespace SharpCompress.Compressors.BZip2;
|
||||
* start of the BZIP2 stream to make it compatible with other PGP programs.
|
||||
*/
|
||||
|
||||
internal sealed class CBZip2OutputStream : Stream
|
||||
internal sealed partial class CBZip2OutputStream : Stream
|
||||
{
|
||||
private const int SETMASK = (1 << 21);
|
||||
private const int CLEARMASK = (~SETMASK);
|
||||
@@ -373,34 +371,6 @@ internal sealed class CBZip2OutputStream : Stream
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the BZip2 stream header ('B', 'Z', 'h', blocksize) has been written
|
||||
/// asynchronously before the first compressed byte is written.
|
||||
/// </summary>
|
||||
private async Task EnsureStreamHeaderWrittenAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (!_streamHeaderWritten)
|
||||
{
|
||||
_streamHeaderWritten = true;
|
||||
// Write 'B', 'Z', 'h' async, then set up bit buffer as Initialize() would.
|
||||
// Initialize() calls BsPutUChar('h') then BsPutUChar('0'+N):
|
||||
// - First call buffers 'h' (bsLive=8)
|
||||
// - Second call flushes 'h' to stream, then buffers '0'+N (bsLive=8)
|
||||
// So after Initialize(), stream has 'h' and bit buffer has '0'+N with bsLive=8.
|
||||
var header = new byte[] { (byte)'B', (byte)'Z', (byte)'h' };
|
||||
await bsStream
|
||||
.WriteAsync(header, 0, header.Length, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
// Replicate the bit buffer state that Initialize() leaves:
|
||||
bytesOut = 1; // 'h' was written via BsW (Initialize increments bytesOut via BsW)
|
||||
nBlocksRandomised = 0;
|
||||
combinedCRC = 0;
|
||||
bsBuff = (blockSize100k + '0') << 24;
|
||||
bsLive = 8;
|
||||
InitBlock();
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteByte(byte bv)
|
||||
{
|
||||
EnsureStreamHeaderWritten();
|
||||
@@ -2054,111 +2024,6 @@ internal sealed class CBZip2OutputStream : Stream
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task WriteAsync(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
await EnsureStreamHeaderWrittenAsync(cancellationToken).ConfigureAwait(false);
|
||||
for (var k = 0; k < count; ++k)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
WriteByte(buffer[k + offset]);
|
||||
}
|
||||
}
|
||||
|
||||
#if !LEGACY_DOTNET
|
||||
public override async ValueTask WriteAsync(
|
||||
ReadOnlyMemory<byte> buffer,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
await EnsureStreamHeaderWrittenAsync(cancellationToken).ConfigureAwait(false);
|
||||
for (var k = 0; k < buffer.Length; ++k)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
WriteByte(buffer.Span[k]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously finalizes the BZip2 compressed stream, flushing all pending data.
|
||||
/// Writes the remaining compressed data to the underlying stream using async I/O.
|
||||
/// </summary>
|
||||
public async ValueTask FinishAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (finished)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await EnsureStreamHeaderWrittenAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// Run the CPU-bound finalization synchronously into a temporary MemoryStream
|
||||
// to avoid synchronous writes to the potentially async-only underlying stream.
|
||||
using var tempStream = new MemoryStream();
|
||||
var realStream = bsStream;
|
||||
bsStream = tempStream;
|
||||
|
||||
try
|
||||
{
|
||||
if (runLength > 0)
|
||||
{
|
||||
WriteRun();
|
||||
}
|
||||
currentChar = -1;
|
||||
EndBlock();
|
||||
EndCompression();
|
||||
finished = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
bsStream = realStream;
|
||||
}
|
||||
|
||||
// Async-copy the buffered compressed data to the real stream
|
||||
tempStream.Position = 0;
|
||||
await tempStream.CopyToAsync(realStream, 81920, cancellationToken).ConfigureAwait(false);
|
||||
await realStream.FlushAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
#if !LEGACY_DOTNET || NETSTANDARD2_1
|
||||
public override async ValueTask DisposeAsync()
|
||||
#else
|
||||
public async ValueTask DisposeAsync()
|
||||
#endif
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await FinishAsync().ConfigureAwait(false);
|
||||
disposed = true;
|
||||
if (!leaveOpen && bsStream is not null)
|
||||
{
|
||||
if (bsStream is IAsyncDisposable asyncDisposable)
|
||||
{
|
||||
await asyncDisposable.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
bsStream.Dispose();
|
||||
}
|
||||
}
|
||||
bsStream = null;
|
||||
|
||||
#if !LEGACY_DOTNET || NETSTANDARD2_1
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
#else
|
||||
await Task.CompletedTask.ConfigureAwait(false);
|
||||
#endif
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override bool CanRead => false;
|
||||
|
||||
public override bool CanSeek => false;
|
||||
|
||||
@@ -22,6 +22,18 @@ public sealed class BZip2CompressionProvider : CompressionProviderBase
|
||||
return BZip2Stream.Create(destination, CompressionMode.Compress, false);
|
||||
}
|
||||
|
||||
public override async ValueTask<Stream> CreateCompressStreamAsync(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
// BZip2 doesn't use compressionLevel parameter in this implementation
|
||||
return await BZip2Stream
|
||||
.CreateAsync(destination, CompressionMode.Compress, false, false, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public override Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
return BZip2Stream.Create(source, CompressionMode.Decompress, false);
|
||||
|
||||
Reference in New Issue
Block a user