mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-25 16:34:45 +00:00
Add SOZip detection in ZipEntry and additional tests
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
This commit is contained in:
@@ -37,11 +37,7 @@ internal sealed class SOZipDeflateStream : Stream
|
||||
// Record the first offset (start of compressed data)
|
||||
_compressedOffsets.Add(0);
|
||||
|
||||
_deflateStream = new DeflateStream(
|
||||
baseStream,
|
||||
CompressionMode.Compress,
|
||||
compressionLevel
|
||||
);
|
||||
_deflateStream = new DeflateStream(baseStream, CompressionMode.Compress, compressionLevel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -150,9 +150,7 @@ public sealed class SOZipIndex
|
||||
Span<byte> buf8 = stackalloc byte[8];
|
||||
if (stream.Read(buf8) != 8)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
"Invalid SOZip index: unable to read uncompressed size"
|
||||
);
|
||||
throw new InvalidDataException("Invalid SOZip index: unable to read uncompressed size");
|
||||
}
|
||||
index.UncompressedSize = BinaryPrimitives.ReadUInt64LittleEndian(buf8);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
using SharpCompress.Common.Zip.SOZip;
|
||||
|
||||
namespace SharpCompress.Common.Zip;
|
||||
|
||||
@@ -11,7 +12,7 @@ public class ZipEntry : Entry
|
||||
|
||||
internal ZipEntry(ZipFilePart? filePart)
|
||||
{
|
||||
if (filePart == null)
|
||||
if (filePart is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -88,4 +89,24 @@ public class ZipEntry : Entry
|
||||
public override int? Attrib => (int?)_filePart?.Header.ExternalFileAttributes;
|
||||
|
||||
public string? Comment => _filePart?.Header.Comment;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this entry has SOZip (Seek-Optimized ZIP) support.
|
||||
/// A SOZip entry has an associated index file that enables random access within
|
||||
/// the compressed data.
|
||||
/// </summary>
|
||||
public bool IsSozip => _filePart?.Header.Extra.Any(e => e.Type == ExtraDataType.SOZip) ?? false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this entry is a SOZip index file.
|
||||
/// Index files are hidden files with a .sozip.idx extension that contain
|
||||
/// offsets into the main compressed file.
|
||||
/// </summary>
|
||||
public bool IsSozipIndexFile => Key is not null && SOZipIndex.IsIndexFile(Key);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SOZip extra field data, if present.
|
||||
/// </summary>
|
||||
internal SOZipExtraField? SOZipExtra =>
|
||||
_filePart?.Header.Extra.OfType<SOZipExtraField>().FirstOrDefault();
|
||||
}
|
||||
|
||||
@@ -26,12 +26,19 @@ public class ZipWriter : AbstractWriter
|
||||
private long streamPosition;
|
||||
private PpmdProperties? ppmdProps;
|
||||
private readonly bool isZip64;
|
||||
private readonly bool enableSOZip;
|
||||
private readonly int sozipChunkSize;
|
||||
private readonly long sozipMinFileSize;
|
||||
|
||||
public ZipWriter(Stream destination, ZipWriterOptions zipWriterOptions)
|
||||
: base(ArchiveType.Zip, zipWriterOptions)
|
||||
{
|
||||
zipComment = zipWriterOptions.ArchiveComment ?? string.Empty;
|
||||
isZip64 = zipWriterOptions.UseZip64;
|
||||
enableSOZip = zipWriterOptions.EnableSOZip;
|
||||
sozipChunkSize = zipWriterOptions.SOZipChunkSize;
|
||||
sozipMinFileSize = zipWriterOptions.SOZipMinFileSize;
|
||||
|
||||
if (destination.CanSeek)
|
||||
{
|
||||
streamPosition = destination.Position;
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Zip.SOZip;
|
||||
using SharpCompress.Writers;
|
||||
using SharpCompress.Writers.Zip;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.Zip;
|
||||
@@ -39,9 +45,7 @@ public class SOZipTests : TestBase
|
||||
{
|
||||
var invalidData = new byte[] { 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
var exception = Assert.Throws<InvalidDataException>(
|
||||
() => SOZipIndex.Read(invalidData)
|
||||
);
|
||||
var exception = Assert.Throws<InvalidDataException>(() => SOZipIndex.Read(invalidData));
|
||||
|
||||
Assert.Contains("magic number mismatch", exception.Message);
|
||||
}
|
||||
@@ -132,4 +136,64 @@ public class SOZipTests : TestBase
|
||||
Assert.Null(SOZipIndex.GetMainFileName("file.txt"));
|
||||
Assert.Null(SOZipIndex.GetMainFileName(""));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZipEntry_IsSozipIndexFile_Detection()
|
||||
{
|
||||
// Create a zip with a file that has a SOZip index file name pattern
|
||||
using var memoryStream = new MemoryStream();
|
||||
|
||||
using (
|
||||
var writer = WriterFactory.Open(
|
||||
memoryStream,
|
||||
ArchiveType.Zip,
|
||||
new ZipWriterOptions(CompressionType.Deflate) { LeaveStreamOpen = true }
|
||||
)
|
||||
)
|
||||
{
|
||||
// Write a regular file
|
||||
writer.Write("test.txt", new MemoryStream(Encoding.UTF8.GetBytes("Hello World")));
|
||||
|
||||
// Write a file with SOZip index name pattern
|
||||
var indexData = new SOZipIndex(
|
||||
chunkSize: 32768,
|
||||
uncompressedSize: 100,
|
||||
compressedSize: 50,
|
||||
compressedOffsets: new ulong[] { 0 }
|
||||
);
|
||||
writer.Write(".test.txt.sozip.idx", new MemoryStream(indexData.ToByteArray()));
|
||||
}
|
||||
|
||||
memoryStream.Position = 0;
|
||||
|
||||
using var archive = ZipArchive.Open(memoryStream);
|
||||
var entries = archive.Entries.ToList();
|
||||
|
||||
Assert.Equal(2, entries.Count);
|
||||
|
||||
var regularEntry = entries.First(e => e.Key == "test.txt");
|
||||
Assert.False(regularEntry.IsSozipIndexFile);
|
||||
Assert.False(regularEntry.IsSozip); // No SOZip extra field
|
||||
|
||||
var indexEntry = entries.First(e => e.Key == ".test.txt.sozip.idx");
|
||||
Assert.True(indexEntry.IsSozipIndexFile);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZipWriterOptions_SOZipDefaults()
|
||||
{
|
||||
var options = new ZipWriterOptions(CompressionType.Deflate);
|
||||
|
||||
Assert.False(options.EnableSOZip);
|
||||
Assert.Equal((int)SOZipIndex.DEFAULT_CHUNK_SIZE, options.SOZipChunkSize);
|
||||
Assert.Equal(1048576L, options.SOZipMinFileSize); // 1MB
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZipWriterEntryOptions_SOZipDefaults()
|
||||
{
|
||||
var options = new ZipWriterEntryOptions();
|
||||
|
||||
Assert.Null(options.EnableSOZip);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user