Skip to content

Commit

Permalink
Merge pull request #823 from klimatr26/new-7z-filters
Browse files Browse the repository at this point in the history
Add support for 7z ARM64 and RISCV filters
  • Loading branch information
adamhathcock committed Apr 8, 2024
2 parents fb73d8c + 6553e9b commit 3612035
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 0 deletions.
63 changes: 63 additions & 0 deletions src/SharpCompress/Compressors/Filters/BCJFilterARM64.cs
@@ -0,0 +1,63 @@
using System;
using System.Buffers.Binary;
using System.IO;

namespace SharpCompress.Compressors.Filters;

internal class BCJFilterARM64 : Filter
{
private int _pos;

public BCJFilterARM64(bool isEncoder, Stream baseStream)
: base(isEncoder, baseStream, 8) => _pos = 0;

protected override int Transform(byte[] buffer, int offset, int count)
{
var end = offset + count - 4;
int i;

for (i = offset; i <= end; i += 4)
{
uint pc = (uint)(_pos + i - offset);
uint instr = BinaryPrimitives.ReadUInt32LittleEndian(
new ReadOnlySpan<byte>(buffer, i, 4)
);

if ((instr >> 26) == 0x25)
{
uint src = instr;
instr = 0x94000000;

pc >>= 2;
if (!_isEncoder)
pc = 0U - pc;

instr |= (src + pc) & 0x03FFFFFF;
BinaryPrimitives.WriteUInt32LittleEndian(new Span<byte>(buffer, i, 4), instr);
}
else if ((instr & 0x9F000000) == 0x90000000)
{
uint src = ((instr >> 29) & 3) | ((instr >> 3) & 0x001FFFFC);

if (((src + 0x00020000) & 0x001C0000) != 0)
continue;

instr &= 0x9000001F;

pc >>= 12;
if (!_isEncoder)
pc = 0U - pc;

uint dest = src + pc;
instr |= (dest & 3) << 29;
instr |= (dest & 0x0003FFFC) << 3;
instr |= (0U - (dest & 0x00020000)) & 0x00E00000;
BinaryPrimitives.WriteUInt32LittleEndian(new Span<byte>(buffer, i, 4), instr);
}
}

i -= offset;
_pos += i;
return i;
}
}
210 changes: 210 additions & 0 deletions src/SharpCompress/Compressors/Filters/BCJFilterRISCV.cs
@@ -0,0 +1,210 @@
using System;
using System.Buffers.Binary;
using System.IO;

namespace SharpCompress.Compressors.Filters;

internal class BCJFilterRISCV : Filter
{
private int _pos;

public BCJFilterRISCV(bool isEncoder, Stream baseStream)
: base(isEncoder, baseStream, 8) => _pos = 0;

private int Decode(byte[] buffer, int offset, int count)
{
if (count < 8)
{
return 0;
}

var end = offset + count - 8;
int i;
for (i = offset; i <= end; i += 2)
{
uint inst = buffer[i];
if (inst == 0xEF)
{
uint b1 = buffer[i + 1];
if ((b1 & 0x0D) != 0)
continue;

uint b2 = buffer[i + 2];
uint b3 = buffer[i + 3];
uint pc = (uint)(_pos + i);

uint addr = ((b1 & 0xF0) << 13) | (b2 << 9) | (b3 << 1);

addr -= pc;

buffer[i + 1] = (byte)((b1 & 0x0F) | ((addr >> 8) & 0xF0));

buffer[i + 2] = (byte)(
((addr >> 16) & 0x0F) | ((addr >> 7) & 0x10) | ((addr << 4) & 0xE0)
);

buffer[i + 3] = (byte)(((addr >> 4) & 0x7F) | ((addr >> 13) & 0x80));

i += 4 - 2;
}
else if ((inst & 0x7F) == 0x17)
{
uint inst2 = 0;
inst |= (uint)buffer[i + 1] << 8;
inst |= (uint)buffer[i + 2] << 16;
inst |= (uint)buffer[i + 3] << 24;

if ((inst & 0xE80) != 0)
{
inst2 = BinaryPrimitives.ReadUInt32LittleEndian(
new ReadOnlySpan<byte>(buffer, i + 4, 4)
);
if (((((inst) << 8) ^ (inst2)) & 0xF8003) != 3)
{
i += 6 - 2;
continue;
}
uint addr = inst & 0xFFFFF000;
addr += inst2 >> 20;

inst = 0x17 | (2 << 7) | (inst2 << 12);
inst2 = addr;
}
else
{
uint inst2_rs1 = inst >> 27;
if ((uint)(((inst) - 0x3117) << 18) >= ((inst2_rs1) & 0x1D))
{
i += 4 - 2;
continue;
}

uint addr = BinaryPrimitives.ReadUInt32BigEndian(
new ReadOnlySpan<byte>(buffer, i + 4, 4)
);

addr -= (uint)(_pos + i);

inst2 = (inst >> 12) | (addr << 20);

inst = 0x17 | (inst2_rs1 << 7) | ((addr + 0x800) & 0xFFFFF000);
}
BinaryPrimitives.WriteUInt32LittleEndian(new Span<byte>(buffer, i, 4), inst);
BinaryPrimitives.WriteUInt32LittleEndian(new Span<byte>(buffer, i + 4, 4), inst2);

i += 8 - 2;
}
}
i -= offset;
_pos += i;
return i;
}

private int Encode(byte[] buffer, int offset, int count)
{
if (count < 8)
{
return 0;
}

var end = offset + count - 8;
int i;
for (i = offset; i <= end; i += 2)
{
uint inst = buffer[i];
if (inst == 0xEF)
{
uint b1 = buffer[i + 1];
if ((b1 & 0x0D) != 0)
continue;

uint b2 = buffer[i + 2];
uint b3 = buffer[i + 3];
uint pc = (uint)(_pos + i);

uint addr =
((b1 & 0xF0) << 8)
| ((b2 & 0x0F) << 16)
| ((b2 & 0x10) << 7)
| ((b2 & 0xE0) >> 4)
| ((b3 & 0x7F) << 4)
| ((b3 & 0x80) << 13);

addr += pc;

buffer[i + 1] = (byte)((b1 & 0x0F) | ((addr >> 13) & 0xF0));

buffer[i + 2] = (byte)(addr >> 9);

buffer[i + 3] = (byte)(addr >> 1);

i += 4 - 2;
}
else if ((inst & 0x7F) == 0x17)
{
inst |= (uint)buffer[i + 1] << 8;
inst |= (uint)buffer[i + 2] << 16;
inst |= (uint)buffer[i + 3] << 24;

if ((inst & 0xE80) != 0)
{
uint inst2 = BinaryPrimitives.ReadUInt32LittleEndian(
new ReadOnlySpan<byte>(buffer, i + 4, 4)
);
if (((((inst) << 8) ^ (inst2)) & 0xF8003) != 3)
{
i += 6 - 2;
continue;
}
uint addr = inst & 0xFFFFF000;
addr += (inst2 >> 20) - ((inst2 >> 19) & 0x1000);

addr += (uint)(_pos + i);
inst = 0x17 | (2 << 7) | (inst2 << 12);

BinaryPrimitives.WriteUInt32LittleEndian(new Span<byte>(buffer, i, 4), inst);
BinaryPrimitives.WriteUInt32BigEndian(new Span<byte>(buffer, i + 4, 4), addr);
}
else
{
uint fake_rs1 = inst >> 27;
if ((uint)(((inst) - 0x3117) << 18) >= ((fake_rs1) & 0x1D))
{
i += 4 - 2;
continue;
}

uint fake_addr = BinaryPrimitives.ReadUInt32LittleEndian(
new ReadOnlySpan<byte>(buffer, i + 4, 4)
);

uint fake_inst2 = (inst >> 12) | (fake_addr << 20);

inst = 0x17 | (fake_rs1 << 7) | (fake_addr & 0xFFFFF000);

BinaryPrimitives.WriteUInt32LittleEndian(new Span<byte>(buffer, i, 4), inst);
BinaryPrimitives.WriteUInt32LittleEndian(
new Span<byte>(buffer, i + 4, 4),
fake_inst2
);
}
i += 8 - 2;
}
}
i -= offset;
_pos += i;
return i;
}

protected override int Transform(byte[] buffer, int offset, int count)
{
if (_isEncoder)
{
return Encode(buffer, offset, count);
}
else
{
return Decode(buffer, offset, count);
}
}
}
6 changes: 6 additions & 0 deletions src/SharpCompress/Compressors/LZMA/Registry.cs
Expand Up @@ -25,6 +25,8 @@ internal static class DecoderRegistry
private const uint K_ARM = 0x03030501;
private const uint K_ARMT = 0x03030701;
private const uint K_SPARC = 0x03030805;
private const uint K_ARM64 = 0x0A;
private const uint K_RISCV = 0x0B;
private const uint K_DEFLATE = 0x040108;
private const uint K_B_ZIP2 = 0x040202;
private const uint K_ZSTD = 0x4F71101;
Expand Down Expand Up @@ -66,6 +68,10 @@ long limit
return new BCJFilterARMT(false, inStreams.Single());
case K_SPARC:
return new BCJFilterSPARC(false, inStreams.Single());
case K_ARM64:
return new BCJFilterARM64(false, inStreams.Single());
case K_RISCV:
return new BCJFilterRISCV(false, inStreams.Single());
case K_B_ZIP2:
return new BZip2Stream(inStreams.Single(), CompressionMode.Decompress, true);
case K_PPMD:
Expand Down
6 changes: 6 additions & 0 deletions tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs
Expand Up @@ -162,6 +162,12 @@ public class SevenZipArchiveTests : ArchiveTests
[Fact]
public void SevenZipArchive_SPARC_FileRead() => ArchiveFileRead("7Zip.SPARC.7z");

[Fact]
public void SevenZipArchive_ARM64_FileRead() => ArchiveFileRead("7Zip.ARM64.7z");

[Fact]
public void SevenZipArchive_RISCV_FileRead() => ArchiveFileRead("7Zip.RISCV.7z");

[Fact]
public void SevenZipArchive_Filters_FileRead() => ArchiveFileRead("7Zip.Filters.7z");

Expand Down
Binary file added tests/TestArchives/Archives/7Zip.ARM64.7z
Binary file not shown.
Binary file modified tests/TestArchives/Archives/7Zip.Filters.7z
Binary file not shown.
Binary file added tests/TestArchives/Archives/7Zip.RISCV.7z
Binary file not shown.

0 comments on commit 3612035

Please sign in to comment.