mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-07-08 18:16:30 +00:00
Fixed XZ block CRC64 computation and added notes on XZ format specifics
This commit is contained in:
@@ -48,9 +48,15 @@
|
||||
2. Skip through the decompressed data to reach each file's starting position
|
||||
|
||||
**Recommendation**: For best performance with 7Zip archives, use synchronous extraction methods (`MoveToNextEntry()` and `WriteEntryToDirectory()`) when possible. Use async methods only when you need to avoid blocking the thread (e.g., in UI applications or async-only contexts).
|
||||
|
||||
|
||||
**Technical Details**: 7Zip archives group files into "folders" (compression units), where all files in a folder share one continuous LZMA-compressed stream. The LZMA decoder maintains internal state (dictionary window, decoder positions) that assumes sequential, non-interruptible processing. Async operations can yield control during awaits, which would corrupt this shared state. To avoid this, async extraction creates a fresh decoder stream for each file.
|
||||
|
||||
### XZ Format Notes
|
||||
|
||||
- XZ is a container format around LZMA2-compressed blocks, not just raw LZMA/LZMA2 data.
|
||||
- XZ streams can include per-block integrity checks selected by the stream header: CRC32, CRC64/XZ, SHA-256, or none. SharpCompress validates these checks while reading XZ blocks.
|
||||
- Raw LZMA/LZMA2 decoding does not provide the same container-level CRC validation; it only validates what the decoder format itself can detect, such as malformed compressed data or invalid end markers.
|
||||
|
||||
## Compression Streams
|
||||
|
||||
For those who want to directly compress/decompress bits. The single file formats are represented here as well. However, BZip2, LZip and XZ have no metadata (GZip has a little) so using them without something like a Tar file makes little sense.
|
||||
|
||||
@@ -6,10 +6,13 @@ namespace SharpCompress.Compressors.Xz;
|
||||
public static class Crc64
|
||||
{
|
||||
public const ulong DefaultSeed = 0x0;
|
||||
internal const ulong XZ_SEED = 0xffffffffffffffff;
|
||||
|
||||
internal static ulong[]? Table;
|
||||
private static ulong[]? _xzTable;
|
||||
|
||||
public const ulong Iso3309Polynomial = 0xD800000000000000;
|
||||
private const ulong XZ_POLYNOMIAL = 0xC96C5795D7870F42;
|
||||
|
||||
public static ulong Compute(byte[] buffer) => Compute(DefaultSeed, buffer);
|
||||
|
||||
@@ -20,6 +23,15 @@ public static class Crc64
|
||||
return CalculateHash(seed, Table, buffer);
|
||||
}
|
||||
|
||||
public static ulong ComputeXz(byte[] buffer) => ~UpdateXz(XZ_SEED, buffer);
|
||||
|
||||
public static ulong UpdateXz(ulong seed, ReadOnlySpan<byte> buffer)
|
||||
{
|
||||
_xzTable ??= CreateTable(XZ_POLYNOMIAL);
|
||||
|
||||
return CalculateHash(seed, _xzTable, buffer);
|
||||
}
|
||||
|
||||
public static ulong CalculateHash(ulong seed, ulong[] table, ReadOnlySpan<byte> buffer)
|
||||
{
|
||||
var crc = seed;
|
||||
|
||||
@@ -21,7 +21,7 @@ public sealed partial class XZBlock : XZReadOnlyStream
|
||||
private readonly CheckType _checkType;
|
||||
private readonly int _checkSize;
|
||||
private uint _crc32 = Crc32.DefaultSeed;
|
||||
private ulong _crc64 = Crc64.DefaultSeed;
|
||||
private ulong _crc64 = Crc64.XZ_SEED;
|
||||
private readonly SHA256? _sha256;
|
||||
private bool _streamConnected;
|
||||
private int _numFilters;
|
||||
@@ -125,8 +125,7 @@ public sealed partial class XZBlock : XZReadOnlyStream
|
||||
_crc32 = Crc32.Update(_crc32, bytes);
|
||||
break;
|
||||
case CheckType.CRC64:
|
||||
Crc64.Table ??= Crc64.CreateTable(Crc64.Iso3309Polynomial);
|
||||
_crc64 = Crc64.CalculateHash(_crc64, Crc64.Table, bytes);
|
||||
_crc64 = Crc64.UpdateXz(_crc64, bytes);
|
||||
break;
|
||||
case CheckType.SHA256:
|
||||
_sha256.NotNull().TransformBlock(buffer, offset, count, null, 0);
|
||||
|
||||
@@ -27,4 +27,12 @@ public class Crc64Tests
|
||||
|
||||
Assert.Equal((ulong)0x416B4150508661EE, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void XzCheckString()
|
||||
{
|
||||
var actual = Crc64.ComputeXz(Encoding.ASCII.GetBytes("123456789"));
|
||||
|
||||
Assert.Equal(0x995DC9BBDF1939FAUL, actual);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ public class XzBlockAsyncTests : XzTestsBase
|
||||
{
|
||||
using var xzBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8);
|
||||
using var sr = new StreamReader(xzBlock);
|
||||
Assert.Equal(await sr.ReadToEndAsync().ConfigureAwait(false), Original);
|
||||
Assert.Equal(Original, await sr.ReadToEndAsync().ConfigureAwait(false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -101,8 +101,8 @@ public class XzBlockAsyncTests : XzTestsBase
|
||||
[Fact]
|
||||
public async ValueTask SkipsPaddingWhenPresentAsync()
|
||||
{
|
||||
// CompressedIndexedStream's first block has 1-byte padding.
|
||||
using var xzBlock = new XZBlock(CompressedIndexedStream, CheckType.CRC64, 8);
|
||||
// CompressedIndexedStream uses CRC32 checks.
|
||||
using var xzBlock = new XZBlock(CompressedIndexedStream, CheckType.CRC32, 4);
|
||||
using var sr = new StreamReader(xzBlock);
|
||||
await sr.ReadToEndAsync().ConfigureAwait(false);
|
||||
Assert.Equal(0L, CompressedIndexedStream.Position % 4L);
|
||||
|
||||
@@ -72,7 +72,7 @@ public class XzBlockTests : XzTestsBase
|
||||
{
|
||||
var xzBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8);
|
||||
using var sr = new StreamReader(xzBlock);
|
||||
Assert.Equal(sr.ReadToEnd(), Original);
|
||||
Assert.Equal(Original, sr.ReadToEnd());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -88,8 +88,8 @@ public class XzBlockTests : XzTestsBase
|
||||
[Fact]
|
||||
public void SkipsPaddingWhenPresent()
|
||||
{
|
||||
// CompressedIndexedStream's first block has 1-byte padding.
|
||||
using var xzBlock = new XZBlock(CompressedIndexedStream, CheckType.CRC64, 8);
|
||||
// CompressedIndexedStream uses CRC32 checks.
|
||||
using var xzBlock = new XZBlock(CompressedIndexedStream, CheckType.CRC32, 4);
|
||||
using var sr = new StreamReader(xzBlock);
|
||||
sr.ReadToEnd();
|
||||
Assert.Equal(0L, CompressedIndexedStream.Position % 4L);
|
||||
|
||||
Reference in New Issue
Block a user