This commit is contained in:
Adam Hathcock
2026-01-20 15:37:15 +00:00
parent 0a9c5bfe15
commit cc47fde57f

View File

@@ -267,6 +267,39 @@ internal class CBZip2InputStream : Stream, IStreamStack
return retChar;
}
public async Task<int> ReadByteAsync(CancellationToken cancellationToken)
{
if (streamEnd)
{
return -1;
}
var retChar = currentChar;
switch (currentState)
{
case START_BLOCK_STATE:
break;
case RAND_PART_A_STATE:
break;
case RAND_PART_B_STATE:
SetupRandPartB();
break;
case RAND_PART_C_STATE:
SetupRandPartC();
break;
case NO_RAND_PART_A_STATE:
break;
case NO_RAND_PART_B_STATE:
SetupNoRandPartB();
break;
case NO_RAND_PART_C_STATE:
SetupNoRandPartC();
break;
default:
break;
}
return retChar;
}
private bool Initialize(bool isFirstStream)
{
var magic0 = bsStream.ReadByte();
@@ -294,6 +327,49 @@ internal class CBZip2InputStream : Stream, IStreamStack
return true;
}
private async Task<bool> InitializeAsync(
bool isFirstStream,
CancellationToken cancellationToken
)
{
var singleByte = new byte[1];
var read0 = await bsStream
.ReadAsync(singleByte, 0, 1, cancellationToken)
.ConfigureAwait(false);
var magic0 = read0 == 0 ? -1 : singleByte[0];
var read1 = await bsStream
.ReadAsync(singleByte, 0, 1, cancellationToken)
.ConfigureAwait(false);
var magic1 = read1 == 0 ? -1 : singleByte[0];
var read2 = await bsStream
.ReadAsync(singleByte, 0, 1, cancellationToken)
.ConfigureAwait(false);
var magic2 = read2 == 0 ? -1 : singleByte[0];
if (magic0 == -1 && !isFirstStream)
{
return false;
}
if (magic0 != 'B' || magic1 != 'Z' || magic2 != 'h')
{
throw new IOException("Not a BZIP2 marked stream");
}
var read3 = await bsStream
.ReadAsync(singleByte, 0, 1, cancellationToken)
.ConfigureAwait(false);
var magic3 = read3 == 0 ? -1 : singleByte[0];
if (magic3 < '1' || magic3 > '9')
{
BsFinishedWithStream();
streamEnd = true;
return false;
}
SetDecompressStructureSizes(magic3 - '0');
bsLive = 0;
computedCombinedCRC = 0;
return true;
}
private void InitBlock()
{
char magic1,
@@ -361,6 +437,73 @@ internal class CBZip2InputStream : Stream, IStreamStack
currentState = START_BLOCK_STATE;
}
private async Task InitBlockAsync(CancellationToken cancellationToken)
{
char magic1,
magic2,
magic3,
magic4;
char magic5,
magic6;
while (true)
{
magic1 = await BsGetUCharAsync(cancellationToken).ConfigureAwait(false);
magic2 = await BsGetUCharAsync(cancellationToken).ConfigureAwait(false);
magic3 = await BsGetUCharAsync(cancellationToken).ConfigureAwait(false);
magic4 = await BsGetUCharAsync(cancellationToken).ConfigureAwait(false);
magic5 = await BsGetUCharAsync(cancellationToken).ConfigureAwait(false);
magic6 = await BsGetUCharAsync(cancellationToken).ConfigureAwait(false);
if (
magic1 != 0x17
|| magic2 != 0x72
|| magic3 != 0x45
|| magic4 != 0x38
|| magic5 != 0x50
|| magic6 != 0x90
)
{
break;
}
if (await CompleteAsync(cancellationToken).ConfigureAwait(false))
{
return;
}
}
if (
magic1 != 0x31
|| magic2 != 0x41
|| magic3 != 0x59
|| magic4 != 0x26
|| magic5 != 0x53
|| magic6 != 0x59
)
{
BadBlockHeader();
streamEnd = true;
return;
}
storedBlockCRC = await BsGetInt32Async(cancellationToken).ConfigureAwait(false);
if (await BsRAsync(1, cancellationToken).ConfigureAwait(false) == 1)
{
blockRandomised = true;
}
else
{
blockRandomised = false;
}
// currBlockNo++;
await GetAndMoveToFrontDecodeAsync(cancellationToken).ConfigureAwait(false);
mCrc.InitialiseCRC();
currentState = START_BLOCK_STATE;
}
private void EndBlock()
{
computedBlockCRC = mCrc.GetFinalCRC();
@@ -394,6 +537,28 @@ internal class CBZip2InputStream : Stream, IStreamStack
return complete;
}
private async Task<bool> CompleteAsync(CancellationToken cancellationToken)
{
storedCombinedCRC = await BsGetInt32Async(cancellationToken).ConfigureAwait(false);
if (storedCombinedCRC != computedCombinedCRC)
{
CrcError();
}
var complete =
!decompressConcatenated
|| !(await InitializeAsync(false, cancellationToken).ConfigureAwait(false));
if (complete)
{
BsFinishedWithStream();
streamEnd = true;
}
// Look for the next .bz2 stream if decompressing
// concatenated files.
return complete;
}
private static void BlockOverrun() => Cadvise();
private static void BadBlockHeader() => Cadvise();
@@ -442,8 +607,44 @@ internal class CBZip2InputStream : Stream, IStreamStack
return v;
}
private async Task<int> BsRAsync(int n, CancellationToken cancellationToken)
{
var singleByte = new byte[1];
int v;
while (bsLive < n)
{
int zzi;
int thech = '\0';
try
{
var readCount = await bsStream
.ReadAsync(singleByte, 0, 1, cancellationToken)
.ConfigureAwait(false);
thech = readCount == 0 ? '\uffff' : singleByte[0];
}
catch (IOException)
{
CompressedStreamEOF();
}
if (thech == '\uffff')
{
CompressedStreamEOF();
}
zzi = thech;
bsBuff = (bsBuff << 8) | (zzi & 0xff);
bsLive += 8;
}
v = (bsBuff >> (bsLive - n)) & ((1 << n) - 1);
bsLive -= n;
return v;
}
private char BsGetUChar() => (char)BsR(8);
private async Task<char> BsGetUCharAsync(CancellationToken cancellationToken) =>
(char)(await BsRAsync(8, cancellationToken).ConfigureAwait(false));
private int BsGetint()
{
var u = 0;
@@ -454,10 +655,26 @@ internal class CBZip2InputStream : Stream, IStreamStack
return u;
}
private async Task<int> BsGetintAsync(CancellationToken cancellationToken)
{
var u = 0;
u = (u << 8) | (await BsRAsync(8, cancellationToken).ConfigureAwait(false));
u = (u << 8) | (await BsRAsync(8, cancellationToken).ConfigureAwait(false));
u = (u << 8) | (await BsRAsync(8, cancellationToken).ConfigureAwait(false));
u = (u << 8) | (await BsRAsync(8, cancellationToken).ConfigureAwait(false));
return u;
}
private int BsGetIntVS(int numBits) => BsR(numBits);
private Task<int> BsGetIntVSAsync(int numBits, CancellationToken cancellationToken) =>
BsRAsync(numBits, cancellationToken);
private int BsGetInt32() => BsGetint();
private Task<int> BsGetInt32Async(CancellationToken cancellationToken) =>
BsGetintAsync(cancellationToken);
private void HbCreateDecodeTables(
int[] limit,
int[] basev,
@@ -650,6 +867,138 @@ internal class CBZip2InputStream : Stream, IStreamStack
}
}
private async Task RecvDecodingTablesAsync(CancellationToken cancellationToken)
{
var len = InitCharArray(BZip2Constants.N_GROUPS, BZip2Constants.MAX_ALPHA_SIZE);
int i,
j,
t,
nGroups,
nSelectors,
alphaSize;
int minLen,
maxLen;
var inUse16 = new bool[16];
/* Receive the mapping table */
for (i = 0; i < 16; i++)
{
if (await BsRAsync(1, cancellationToken).ConfigureAwait(false) == 1)
{
inUse16[i] = true;
}
else
{
inUse16[i] = false;
}
}
for (i = 0; i < 256; i++)
{
inUse[i] = false;
}
for (i = 0; i < 16; i++)
{
if (inUse16[i])
{
for (j = 0; j < 16; j++)
{
if (await BsRAsync(1, cancellationToken).ConfigureAwait(false) == 1)
{
inUse[(i * 16) + j] = true;
}
}
}
}
MakeMaps();
alphaSize = nInUse + 2;
/* Now the selectors */
nGroups = await BsRAsync(3, cancellationToken).ConfigureAwait(false);
nSelectors = await BsRAsync(15, cancellationToken).ConfigureAwait(false);
for (i = 0; i < nSelectors; i++)
{
j = 0;
while (await BsRAsync(1, cancellationToken).ConfigureAwait(false) == 1)
{
j++;
}
if (i < BZip2Constants.MAX_SELECTORS)
{
selectorMtf[i] = (char)j;
}
}
nSelectors = Math.Min(nSelectors, BZip2Constants.MAX_SELECTORS);
/* Undo the MTF values for the selectors. */
{
var pos = new char[BZip2Constants.N_GROUPS];
char tmp,
v;
for (v = '\0'; v < nGroups; v++)
{
pos[v] = v;
}
for (i = 0; i < nSelectors; i++)
{
v = selectorMtf[i];
tmp = pos[v];
while (v > 0)
{
pos[v] = pos[v - 1];
v--;
}
pos[0] = tmp;
selector[i] = tmp;
}
}
/* Now the coding tables */
for (t = 0; t < nGroups; t++)
{
var curr = await BsRAsync(5, cancellationToken).ConfigureAwait(false);
for (i = 0; i < alphaSize; i++)
{
while (await BsRAsync(1, cancellationToken).ConfigureAwait(false) == 1)
{
if (await BsRAsync(1, cancellationToken).ConfigureAwait(false) == 0)
{
curr++;
}
else
{
curr--;
}
}
len[t][i] = (char)curr;
}
}
/* Create the Huffman decoding tables */
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];
}
}
HbCreateDecodeTables(limit[t], basev[t], perm[t], len[t], minLen, maxLen, alphaSize);
minLens[t] = minLen;
}
}
private void GetAndMoveToFrontDecode()
{
var yy = new char[256];
@@ -904,6 +1253,270 @@ internal class CBZip2InputStream : Stream, IStreamStack
}
}
private async Task GetAndMoveToFrontDecodeAsync(CancellationToken cancellationToken)
{
var yy = new char[256];
int i,
j,
nextSym,
limitLast;
int EOB,
groupNo,
groupPos;
var singleByte = new byte[1];
limitLast = BZip2Constants.baseBlockSize * blockSize100k;
origPtr = await BsGetIntVSAsync(24, cancellationToken).ConfigureAwait(false);
await RecvDecodingTablesAsync(cancellationToken).ConfigureAwait(false);
EOB = nInUse + 1;
groupNo = -1;
groupPos = 0;
/*
Setting up the unzftab entries here is not strictly
necessary, but it does save having to do it later
in a separate pass, and so saves a block's worth of
cache misses.
*/
for (i = 0; i <= 255; i++)
{
unzftab[i] = 0;
}
for (i = 0; i <= 255; i++)
{
yy[i] = (char)i;
}
last = -1;
{
int zt,
zn,
zvec,
zj;
if (groupPos == 0)
{
groupNo++;
groupPos = BZip2Constants.G_SIZE;
}
groupPos--;
zt = selector[groupNo];
zn = minLens[zt];
zvec = await BsRAsync(zn, cancellationToken).ConfigureAwait(false);
while (zvec > limit[zt][zn])
{
zn++;
{
{
while (bsLive < 1)
{
int zzi;
int thech = '\0';
try
{
var readCount = await bsStream
.ReadAsync(singleByte, 0, 1, cancellationToken)
.ConfigureAwait(false);
thech = readCount == 0 ? '\uffff' : singleByte[0];
}
catch (IOException)
{
CompressedStreamEOF();
}
if (thech == '\uffff')
{
CompressedStreamEOF();
}
zzi = thech;
bsBuff = (bsBuff << 8) | (zzi & 0xff);
bsLive += 8;
}
}
zj = (bsBuff >> (bsLive - 1)) & 1;
bsLive--;
}
zvec = (zvec << 1) | zj;
}
nextSym = perm[zt][zvec - basev[zt][zn]];
}
while (true)
{
if (nextSym == EOB)
{
break;
}
if (nextSym == BZip2Constants.RUNA || nextSym == BZip2Constants.RUNB)
{
char ch;
var s = -1;
var N = 1;
do
{
if (nextSym == BZip2Constants.RUNA)
{
s += (0 + 1) * N;
}
else if (nextSym == BZip2Constants.RUNB)
{
s += (1 + 1) * N;
}
N *= 2;
{
int zt,
zn,
zvec,
zj;
if (groupPos == 0)
{
groupNo++;
groupPos = BZip2Constants.G_SIZE;
}
groupPos--;
zt = selector[groupNo];
zn = minLens[zt];
zvec = await BsRAsync(zn, cancellationToken).ConfigureAwait(false);
while (zvec > limit[zt][zn])
{
zn++;
{
{
while (bsLive < 1)
{
int zzi;
int thech = '\0';
try
{
var readCount = await bsStream
.ReadAsync(singleByte, 0, 1, cancellationToken)
.ConfigureAwait(false);
thech = readCount == 0 ? '\uffff' : singleByte[0];
}
catch (IOException)
{
CompressedStreamEOF();
}
if (thech == '\uffff')
{
CompressedStreamEOF();
}
zzi = thech;
bsBuff = (bsBuff << 8) | (zzi & 0xff);
bsLive += 8;
}
}
zj = (bsBuff >> (bsLive - 1)) & 1;
bsLive--;
}
zvec = (zvec << 1) | zj;
}
nextSym = perm[zt][zvec - basev[zt][zn]];
}
} while (nextSym == BZip2Constants.RUNA || nextSym == BZip2Constants.RUNB);
s++;
ch = seqToUnseq[yy[0]];
unzftab[ch] += s;
while (s > 0)
{
last++;
ll8[last] = ch;
s--;
}
if (last >= limitLast)
{
BlockOverrun();
}
}
else
{
char tmp;
last++;
if (last >= limitLast)
{
BlockOverrun();
}
tmp = yy[nextSym - 1];
unzftab[seqToUnseq[tmp]]++;
ll8[last] = seqToUnseq[tmp];
/*
This loop is hammered during decompression,
hence the unrolling.
for (j = nextSym-1; j > 0; j--) yy[j] = yy[j-1];
*/
j = nextSym - 1;
for (; j > 3; j -= 4)
{
yy[j] = yy[j - 1];
yy[j - 1] = yy[j - 2];
yy[j - 2] = yy[j - 3];
yy[j - 3] = yy[j - 4];
}
for (; j > 0; j--)
{
yy[j] = yy[j - 1];
}
yy[0] = tmp;
{
int zt,
zn,
zvec,
zj;
if (groupPos == 0)
{
groupNo++;
groupPos = BZip2Constants.G_SIZE;
}
groupPos--;
zt = selector[groupNo];
zn = minLens[zt];
zvec = await BsRAsync(zn, cancellationToken).ConfigureAwait(false);
while (zvec > limit[zt][zn])
{
zn++;
{
{
while (bsLive < 1)
{
int zzi;
int thech = '\0';
try
{
var readCount = await bsStream
.ReadAsync(singleByte, 0, 1, cancellationToken)
.ConfigureAwait(false);
thech = readCount == 0 ? '\uffff' : singleByte[0];
}
catch (IOException)
{
CompressedStreamEOF();
}
zzi = thech;
bsBuff = (bsBuff << 8) | (zzi & 0xff);
bsLive += 8;
}
}
zj = (bsBuff >> (bsLive - 1)) & 1;
bsLive--;
}
zvec = (zvec << 1) | zj;
}
nextSym = perm[zt][zvec - basev[zt][zn]];
}
}
}
}
private void SetupBlock()
{
Span<int> cftab = stackalloc int[257];
@@ -1133,7 +1746,7 @@ internal class CBZip2InputStream : Stream, IStreamStack
return k;
}
public override Task<int> ReadAsync(
public override async Task<int> ReadAsync(
byte[] buffer,
int offset,
int count,
@@ -1145,14 +1758,14 @@ internal class CBZip2InputStream : Stream, IStreamStack
for (k = 0; k < count; ++k)
{
cancellationToken.ThrowIfCancellationRequested();
c = ReadByte();
c = await ReadByteAsync(cancellationToken).ConfigureAwait(false);
if (c == -1)
{
break;
}
buffer[k + offset] = (byte)c;
}
return Task.FromResult(k);
return k;
}
public override long Seek(long offset, SeekOrigin origin) => 0;