diff --git a/Directory.Packages.props b/Directory.Packages.props index f0ae33c3a..031dc894f 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -12,7 +12,6 @@ - diff --git a/src/SharpCompress/Archives/ArchiveFactory.cs b/src/SharpCompress/Archives/ArchiveFactory.cs index c979b7000..870092f1a 100644 --- a/src/SharpCompress/Archives/ArchiveFactory.cs +++ b/src/SharpCompress/Archives/ArchiveFactory.cs @@ -45,7 +45,7 @@ public static IWritableArchive Create(ArchiveType type) /// public static IArchive Open(string filePath, ReaderOptions? options = null) { - filePath.CheckNotNullOrEmpty(nameof(filePath)); + filePath.NotNullOrEmpty(nameof(filePath)); return Open(new FileInfo(filePath), options); } @@ -68,7 +68,7 @@ public static IArchive Open(FileInfo fileInfo, ReaderOptions? options = null) /// public static IArchive Open(IEnumerable fileInfos, ReaderOptions? options = null) { - fileInfos.CheckNotNull(nameof(fileInfos)); + fileInfos.NotNull(nameof(fileInfos)); var filesArray = fileInfos.ToArray(); if (filesArray.Length == 0) { @@ -81,7 +81,7 @@ public static IArchive Open(IEnumerable fileInfos, ReaderOptions? opti return Open(fileInfo, options); } - fileInfo.CheckNotNull(nameof(fileInfo)); + fileInfo.NotNull(nameof(fileInfo)); options ??= new ReaderOptions { LeaveStreamOpen = false }; return FindFactory(fileInfo).Open(filesArray, options); @@ -94,7 +94,7 @@ public static IArchive Open(IEnumerable fileInfos, ReaderOptions? opti /// public static IArchive Open(IEnumerable streams, ReaderOptions? options = null) { - streams.CheckNotNull(nameof(streams)); + streams.NotNull(nameof(streams)); var streamsArray = streams.ToArray(); if (streamsArray.Length == 0) { @@ -107,7 +107,7 @@ public static IArchive Open(IEnumerable streams, ReaderOptions? options return Open(firstStream, options); } - firstStream.CheckNotNull(nameof(firstStream)); + firstStream.NotNull(nameof(firstStream)); options ??= new ReaderOptions(); return FindFactory(firstStream).Open(streamsArray, options); @@ -129,7 +129,7 @@ public static void WriteToDirectory( private static T FindFactory(FileInfo finfo) where T : IFactory { - finfo.CheckNotNull(nameof(finfo)); + finfo.NotNull(nameof(finfo)); using Stream stream = finfo.OpenRead(); return FindFactory(stream); } @@ -137,7 +137,7 @@ private static T FindFactory(FileInfo finfo) private static T FindFactory(Stream stream) where T : IFactory { - stream.CheckNotNull(nameof(stream)); + stream.NotNull(nameof(stream)); if (!stream.CanRead || !stream.CanSeek) { throw new ArgumentException("Stream should be readable and seekable"); @@ -172,7 +172,7 @@ public static bool IsArchive( int bufferSize = ReaderOptions.DefaultBufferSize ) { - filePath.CheckNotNullOrEmpty(nameof(filePath)); + filePath.NotNullOrEmpty(nameof(filePath)); using Stream s = File.OpenRead(filePath); return IsArchive(s, out type, bufferSize); } @@ -184,7 +184,7 @@ public static bool IsArchive( ) { type = null; - stream.CheckNotNull(nameof(stream)); + stream.NotNull(nameof(stream)); if (!stream.CanRead || !stream.CanSeek) { @@ -215,7 +215,7 @@ public static bool IsArchive( /// public static IEnumerable GetFileParts(string part1) { - part1.CheckNotNullOrEmpty(nameof(part1)); + part1.NotNullOrEmpty(nameof(part1)); return GetFileParts(new FileInfo(part1)).Select(a => a.FullName); } @@ -226,7 +226,7 @@ public static IEnumerable GetFileParts(string part1) /// public static IEnumerable GetFileParts(FileInfo part1) { - part1.CheckNotNull(nameof(part1)); + part1.NotNull(nameof(part1)); yield return part1; foreach (var factory in Factory.Factories.OfType()) diff --git a/src/SharpCompress/Archives/GZip/GZipArchive.cs b/src/SharpCompress/Archives/GZip/GZipArchive.cs index 4437ff57d..a0345cdf9 100644 --- a/src/SharpCompress/Archives/GZip/GZipArchive.cs +++ b/src/SharpCompress/Archives/GZip/GZipArchive.cs @@ -21,7 +21,7 @@ public class GZipArchive : AbstractWritableArchive /// public static GZipArchive Open(string filePath, ReaderOptions? readerOptions = null) { - filePath.CheckNotNullOrEmpty(nameof(filePath)); + filePath.NotNullOrEmpty(nameof(filePath)); return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions()); } @@ -32,7 +32,7 @@ public static GZipArchive Open(string filePath, ReaderOptions? readerOptions = n /// public static GZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) { - fileInfo.CheckNotNull(nameof(fileInfo)); + fileInfo.NotNull(nameof(fileInfo)); return new GZipArchive( new SourceStream( fileInfo, @@ -52,7 +52,7 @@ public static GZipArchive Open( ReaderOptions? readerOptions = null ) { - fileInfos.CheckNotNull(nameof(fileInfos)); + fileInfos.NotNull(nameof(fileInfos)); var files = fileInfos.ToArray(); return new GZipArchive( new SourceStream( @@ -70,7 +70,7 @@ public static GZipArchive Open( /// public static GZipArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) { - streams.CheckNotNull(nameof(streams)); + streams.NotNull(nameof(streams)); var strms = streams.ToArray(); return new GZipArchive( new SourceStream( @@ -88,7 +88,7 @@ public static GZipArchive Open(IEnumerable streams, ReaderOptions? reade /// public static GZipArchive Open(Stream stream, ReaderOptions? readerOptions = null) { - stream.CheckNotNull(nameof(stream)); + stream.NotNull(nameof(stream)); if (stream is not { CanSeek: true }) { diff --git a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs index 3d1daa1a3..bda1e3580 100644 --- a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs +++ b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs @@ -25,7 +25,7 @@ public static void WriteTo(this IArchiveEntry archiveEntry, Stream streamToWrite using (entryStream) { using Stream s = new ListeningStream(streamListener, entryStream); - s.TransferTo(streamToWriteTo); + s.CopyTo(streamToWriteTo); } streamListener.FireEntryExtractionEnd(archiveEntry); } diff --git a/src/SharpCompress/Archives/Rar/RarArchive.cs b/src/SharpCompress/Archives/Rar/RarArchive.cs index ee4be60e9..b3689a9f5 100644 --- a/src/SharpCompress/Archives/Rar/RarArchive.cs +++ b/src/SharpCompress/Archives/Rar/RarArchive.cs @@ -95,7 +95,7 @@ protected override IReader CreateReaderForSolidExtraction() /// public static RarArchive Open(string filePath, ReaderOptions? options = null) { - filePath.CheckNotNullOrEmpty(nameof(filePath)); + filePath.NotNullOrEmpty(nameof(filePath)); var fileInfo = new FileInfo(filePath); return new RarArchive( new SourceStream( @@ -113,7 +113,7 @@ public static RarArchive Open(string filePath, ReaderOptions? options = null) /// public static RarArchive Open(FileInfo fileInfo, ReaderOptions? options = null) { - fileInfo.CheckNotNull(nameof(fileInfo)); + fileInfo.NotNull(nameof(fileInfo)); return new RarArchive( new SourceStream( fileInfo, @@ -130,7 +130,7 @@ public static RarArchive Open(FileInfo fileInfo, ReaderOptions? options = null) /// public static RarArchive Open(Stream stream, ReaderOptions? options = null) { - stream.CheckNotNull(nameof(stream)); + stream.NotNull(nameof(stream)); if (stream is not { CanSeek: true }) { @@ -150,7 +150,7 @@ public static RarArchive Open( ReaderOptions? readerOptions = null ) { - fileInfos.CheckNotNull(nameof(fileInfos)); + fileInfos.NotNull(nameof(fileInfos)); var files = fileInfos.ToArray(); return new RarArchive( new SourceStream( @@ -168,7 +168,7 @@ public static RarArchive Open( /// public static RarArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) { - streams.CheckNotNull(nameof(streams)); + streams.NotNull(nameof(streams)); var strms = streams.ToArray(); return new RarArchive( new SourceStream( diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs index 323f07ac7..ea763409d 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs @@ -21,7 +21,7 @@ public class SevenZipArchive : AbstractArchive public static SevenZipArchive Open(string filePath, ReaderOptions? readerOptions = null) { - filePath.CheckNotNullOrEmpty("filePath"); + filePath.NotNullOrEmpty("filePath"); return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions()); } @@ -32,7 +32,7 @@ public static SevenZipArchive Open(string filePath, ReaderOptions? readerOptions /// public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) { - fileInfo.CheckNotNull("fileInfo"); + fileInfo.NotNull("fileInfo"); return new SevenZipArchive( new SourceStream( fileInfo, @@ -52,7 +52,7 @@ public static SevenZipArchive Open( ReaderOptions? readerOptions = null ) { - fileInfos.CheckNotNull(nameof(fileInfos)); + fileInfos.NotNull(nameof(fileInfos)); var files = fileInfos.ToArray(); return new SevenZipArchive( new SourceStream( @@ -73,7 +73,7 @@ public static SevenZipArchive Open( ReaderOptions? readerOptions = null ) { - streams.CheckNotNull(nameof(streams)); + streams.NotNull(nameof(streams)); var strms = streams.ToArray(); return new SevenZipArchive( new SourceStream( @@ -91,7 +91,7 @@ public static SevenZipArchive Open( /// public static SevenZipArchive Open(Stream stream, ReaderOptions? readerOptions = null) { - stream.CheckNotNull("stream"); + stream.NotNull("stream"); if (stream is not { CanSeek: true }) { diff --git a/src/SharpCompress/Archives/Tar/TarArchive.cs b/src/SharpCompress/Archives/Tar/TarArchive.cs index 2c9c64c72..39f0fce6a 100644 --- a/src/SharpCompress/Archives/Tar/TarArchive.cs +++ b/src/SharpCompress/Archives/Tar/TarArchive.cs @@ -22,7 +22,7 @@ public class TarArchive : AbstractWritableArchive /// public static TarArchive Open(string filePath, ReaderOptions? readerOptions = null) { - filePath.CheckNotNullOrEmpty(nameof(filePath)); + filePath.NotNullOrEmpty(nameof(filePath)); return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions()); } @@ -33,7 +33,7 @@ public static TarArchive Open(string filePath, ReaderOptions? readerOptions = nu /// public static TarArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) { - fileInfo.CheckNotNull(nameof(fileInfo)); + fileInfo.NotNull(nameof(fileInfo)); return new TarArchive( new SourceStream( fileInfo, @@ -53,7 +53,7 @@ public static TarArchive Open( ReaderOptions? readerOptions = null ) { - fileInfos.CheckNotNull(nameof(fileInfos)); + fileInfos.NotNull(nameof(fileInfos)); var files = fileInfos.ToArray(); return new TarArchive( new SourceStream( @@ -71,7 +71,7 @@ public static TarArchive Open( /// public static TarArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) { - streams.CheckNotNull(nameof(streams)); + streams.NotNull(nameof(streams)); var strms = streams.ToArray(); return new TarArchive( new SourceStream( @@ -89,7 +89,7 @@ public static TarArchive Open(IEnumerable streams, ReaderOptions? reader /// public static TarArchive Open(Stream stream, ReaderOptions? readerOptions = null) { - stream.CheckNotNull(nameof(stream)); + stream.NotNull(nameof(stream)); if (stream is not { CanSeek: true }) { @@ -178,7 +178,7 @@ var header in TarHeaderFactory.ReadHeader( using (var entryStream = entry.OpenEntryStream()) { using var memoryStream = new MemoryStream(); - entryStream.TransferTo(memoryStream); + entryStream.CopyTo(memoryStream); memoryStream.Position = 0; var bytes = memoryStream.ToArray(); diff --git a/src/SharpCompress/Archives/Zip/ZipArchive.cs b/src/SharpCompress/Archives/Zip/ZipArchive.cs index a75b40954..35b8e0cb1 100644 --- a/src/SharpCompress/Archives/Zip/ZipArchive.cs +++ b/src/SharpCompress/Archives/Zip/ZipArchive.cs @@ -43,7 +43,7 @@ internal ZipArchive(SourceStream sourceStream) /// public static ZipArchive Open(string filePath, ReaderOptions? readerOptions = null) { - filePath.CheckNotNullOrEmpty(nameof(filePath)); + filePath.NotNullOrEmpty(nameof(filePath)); return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions()); } @@ -54,7 +54,7 @@ public static ZipArchive Open(string filePath, ReaderOptions? readerOptions = nu /// public static ZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) { - fileInfo.CheckNotNull(nameof(fileInfo)); + fileInfo.NotNull(nameof(fileInfo)); return new ZipArchive( new SourceStream( fileInfo, @@ -74,7 +74,7 @@ public static ZipArchive Open( ReaderOptions? readerOptions = null ) { - fileInfos.CheckNotNull(nameof(fileInfos)); + fileInfos.NotNull(nameof(fileInfos)); var files = fileInfos.ToArray(); return new ZipArchive( new SourceStream( @@ -92,7 +92,7 @@ public static ZipArchive Open( /// public static ZipArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) { - streams.CheckNotNull(nameof(streams)); + streams.NotNull(nameof(streams)); var strms = streams.ToArray(); return new ZipArchive( new SourceStream( @@ -110,7 +110,7 @@ public static ZipArchive Open(IEnumerable streams, ReaderOptions? reader /// public static ZipArchive Open(Stream stream, ReaderOptions? readerOptions = null) { - stream.CheckNotNull(nameof(stream)); + stream.NotNull(nameof(stream)); if (stream is not { CanSeek: true }) { diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs index 2ba0d80cc..106caff9c 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs @@ -413,7 +413,7 @@ private void UnpWriteBuf() else //x memcpy(Mem,Window+BlockStart,BlockLength); { - Utility.Copy(Window, BlockStart, Mem, 0, BlockLength); + Buffer.BlockCopy(Window, (int)BlockStart, Mem, 0, (int)BlockLength); } } else @@ -427,9 +427,21 @@ private void UnpWriteBuf() else { //x memcpy(Mem,Window+BlockStart,FirstPartLength); - Utility.Copy(Window, BlockStart, Mem, 0, FirstPartLength); + Buffer.BlockCopy( + Window, + (int)BlockStart, + Mem, + 0, + (int)FirstPartLength + ); //x memcpy(Mem+FirstPartLength,Window,BlockEnd); - Utility.Copy(Window, 0, Mem, FirstPartLength, BlockEnd); + Buffer.BlockCopy( + Window, + 0, + Mem, + (int)FirstPartLength, + (int)BlockEnd + ); } } diff --git a/src/SharpCompress/Factories/Factory.cs b/src/SharpCompress/Factories/Factory.cs index 1b9e23e96..2462f6148 100644 --- a/src/SharpCompress/Factories/Factory.cs +++ b/src/SharpCompress/Factories/Factory.cs @@ -34,7 +34,7 @@ static Factory() /// must not be null. public static void RegisterFactory(Factory factory) { - factory.CheckNotNull(nameof(factory)); + factory.NotNull(nameof(factory)); _factories.Add(factory); } diff --git a/src/SharpCompress/LazyReadOnlyCollection.cs b/src/SharpCompress/LazyReadOnlyCollection.cs index 9c1b35ebc..cc9cb3fdc 100644 --- a/src/SharpCompress/LazyReadOnlyCollection.cs +++ b/src/SharpCompress/LazyReadOnlyCollection.cs @@ -4,7 +4,7 @@ using System.Collections; using System.Collections.Generic; -namespace SharpCompress.Helpers; +namespace SharpCompress; internal sealed class LazyReadOnlyCollection : ICollection { diff --git a/src/SharpCompress/NotNullExtensions.cs b/src/SharpCompress/NotNullExtensions.cs index 2245612e9..38e6d6d6d 100644 --- a/src/SharpCompress/NotNullExtensions.cs +++ b/src/SharpCompress/NotNullExtensions.cs @@ -4,20 +4,19 @@ using System.Linq; using System.Runtime.CompilerServices; -namespace SharpCompress.Helpers; +namespace SharpCompress; internal static class NotNullExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IEnumerable Empty(this IEnumerable? source) => - source ?? Enumerable.Empty(); + public static IEnumerable Empty(this IEnumerable? source) => source ?? []; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Empty(this T? source) { if (source is null) { - return Enumerable.Empty(); + return []; } return source.AsEnumerable(); } @@ -68,4 +67,14 @@ public static T NotNull( return obj.Value; } #endif + + public static string NotNullOrEmpty(this string obj, string name) + { + obj.NotNull(name); + if (obj.Length == 0) + { + throw new ArgumentException("String is empty.", name); + } + return obj; + } } diff --git a/src/SharpCompress/Readers/Arc/ArcReader.cs b/src/SharpCompress/Readers/Arc/ArcReader.cs index 7d58b8d42..b7cf54675 100644 --- a/src/SharpCompress/Readers/Arc/ArcReader.cs +++ b/src/SharpCompress/Readers/Arc/ArcReader.cs @@ -24,7 +24,7 @@ private ArcReader(Stream stream, ReaderOptions options) /// public static ArcReader Open(Stream stream, ReaderOptions? options = null) { - stream.CheckNotNull(nameof(stream)); + stream.NotNull(nameof(stream)); return new ArcReader(stream, options ?? new ReaderOptions()); } diff --git a/src/SharpCompress/Readers/GZip/GZipReader.cs b/src/SharpCompress/Readers/GZip/GZipReader.cs index 73bc4a9d3..e10d509a6 100644 --- a/src/SharpCompress/Readers/GZip/GZipReader.cs +++ b/src/SharpCompress/Readers/GZip/GZipReader.cs @@ -22,7 +22,7 @@ private GZipReader(Stream stream, ReaderOptions options) /// public static GZipReader Open(Stream stream, ReaderOptions? options = null) { - stream.CheckNotNull(nameof(stream)); + stream.NotNull(nameof(stream)); return new GZipReader(stream, options ?? new ReaderOptions()); } diff --git a/src/SharpCompress/Readers/Rar/RarReader.cs b/src/SharpCompress/Readers/Rar/RarReader.cs index 2a56ee35d..79842e6ab 100644 --- a/src/SharpCompress/Readers/Rar/RarReader.cs +++ b/src/SharpCompress/Readers/Rar/RarReader.cs @@ -42,7 +42,7 @@ public override void Dispose() public static RarReader Open(string filePath, ReaderOptions? options = null) { - filePath.CheckNotNullOrEmpty(nameof(filePath)); + filePath.NotNullOrEmpty(nameof(filePath)); return Open(new FileInfo(filePath), options); } @@ -71,7 +71,7 @@ public static RarReader Open(IEnumerable fileInfos, ReaderOptions? opt /// public static RarReader Open(Stream stream, ReaderOptions? options = null) { - stream.CheckNotNull(nameof(stream)); + stream.NotNull(nameof(stream)); return new SingleVolumeRarReader(stream, options ?? new ReaderOptions()); } @@ -83,7 +83,7 @@ public static RarReader Open(Stream stream, ReaderOptions? options = null) /// public static RarReader Open(IEnumerable streams, ReaderOptions? options = null) { - streams.CheckNotNull(nameof(streams)); + streams.NotNull(nameof(streams)); return new MultiVolumeRarReader(streams, options ?? new ReaderOptions()); } diff --git a/src/SharpCompress/Readers/ReaderFactory.cs b/src/SharpCompress/Readers/ReaderFactory.cs index 9830c8fb5..97846380c 100644 --- a/src/SharpCompress/Readers/ReaderFactory.cs +++ b/src/SharpCompress/Readers/ReaderFactory.cs @@ -11,7 +11,7 @@ public static class ReaderFactory { public static IReader Open(string filePath, ReaderOptions? options = null) { - filePath.CheckNotNullOrEmpty(nameof(filePath)); + filePath.NotNullOrEmpty(nameof(filePath)); return Open(new FileInfo(filePath), options); } @@ -29,7 +29,7 @@ public static IReader Open(FileInfo fileInfo, ReaderOptions? options = null) /// public static IReader Open(Stream stream, ReaderOptions? options = null) { - stream.CheckNotNull(nameof(stream)); + stream.NotNull(nameof(stream)); options ??= new ReaderOptions() { LeaveStreamOpen = false }; var bStream = new SharpCompressStream(stream, bufferSize: options.BufferSize); diff --git a/src/SharpCompress/Readers/Tar/TarReader.cs b/src/SharpCompress/Readers/Tar/TarReader.cs index aaa0005ca..c92188856 100644 --- a/src/SharpCompress/Readers/Tar/TarReader.cs +++ b/src/SharpCompress/Readers/Tar/TarReader.cs @@ -55,7 +55,7 @@ protected override Stream RequestInitialStream() /// public static TarReader Open(Stream stream, ReaderOptions? options = null) { - stream.CheckNotNull(nameof(stream)); + stream.NotNull(nameof(stream)); options = options ?? new ReaderOptions(); var rewindableStream = new SharpCompressStream(stream); diff --git a/src/SharpCompress/Readers/Zip/ZipReader.cs b/src/SharpCompress/Readers/Zip/ZipReader.cs index cda3f3f86..5e82479ce 100644 --- a/src/SharpCompress/Readers/Zip/ZipReader.cs +++ b/src/SharpCompress/Readers/Zip/ZipReader.cs @@ -44,7 +44,7 @@ private ZipReader(Stream stream, ReaderOptions options, IEnumerable en /// public static ZipReader Open(Stream stream, ReaderOptions? options = null) { - stream.CheckNotNull(nameof(stream)); + stream.NotNull(nameof(stream)); return new ZipReader(stream, options ?? new ReaderOptions()); } @@ -54,7 +54,7 @@ public static ZipReader Open( IEnumerable entries ) { - stream.CheckNotNull(nameof(stream)); + stream.NotNull(nameof(stream)); return new ZipReader(stream, options ?? new ReaderOptions(), entries); } diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index 74974850c..19e132d59 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -1,4 +1,3 @@ -global using SharpCompress.Helpers; using System; using System.Buffers; using System.Collections.Generic; @@ -7,10 +6,14 @@ using System.Text; using SharpCompress.Readers; -namespace SharpCompress.Helpers; +namespace SharpCompress; internal static class Utility { + //80kb is a good industry standard temporary buffer size + private const int TEMP_BUFFER_SIZE = 81920; + private static readonly HashSet invalidChars = new(Path.GetInvalidFileNameChars()); + public static ReadOnlyCollection ToReadOnly(this IList items) => new(items); /// @@ -68,60 +71,11 @@ public static void ForEach(this IEnumerable items, Action action) } } - public static void Copy( - Array sourceArray, - long sourceIndex, - Array destinationArray, - long destinationIndex, - long length - ) - { - if (sourceIndex > int.MaxValue || sourceIndex < int.MinValue) - { - throw new ArgumentOutOfRangeException(nameof(sourceIndex)); - } - - if (destinationIndex > int.MaxValue || destinationIndex < int.MinValue) - { - throw new ArgumentOutOfRangeException(nameof(destinationIndex)); - } - - if (length > int.MaxValue || length < int.MinValue) - { - throw new ArgumentOutOfRangeException(nameof(length)); - } - - Array.Copy( - sourceArray, - (int)sourceIndex, - destinationArray, - (int)destinationIndex, - (int)length - ); - } - public static IEnumerable AsEnumerable(this T item) { yield return item; } - public static void CheckNotNull(this object obj, string name) - { - if (obj is null) - { - throw new ArgumentNullException(name); - } - } - - public static void CheckNotNullOrEmpty(this string obj, string name) - { - obj.CheckNotNull(name); - if (obj.Length == 0) - { - throw new ArgumentException("String is empty.", name); - } - } - public static void Skip(this Stream source, long advanceAmount) { if (source.CanSeek) @@ -130,79 +84,23 @@ public static void Skip(this Stream source, long advanceAmount) return; } - var buffer = GetTransferByteArray(); - try + using var buffer = MemoryPool.Shared.Rent(TEMP_BUFFER_SIZE); + while (advanceAmount > 0) { - var read = 0; - var readCount = 0; - do + var toRead = (int)Math.Min(buffer.Memory.Length, advanceAmount); + var read = source.Read(buffer.Memory.Slice(0, toRead).Span); + if (read <= 0) { - readCount = buffer.Length; - if (readCount > advanceAmount) - { - readCount = (int)advanceAmount; - } - read = source.Read(buffer, 0, readCount); - if (read <= 0) - { - break; - } - advanceAmount -= read; - if (advanceAmount == 0) - { - break; - } - } while (true); - } - finally - { - ArrayPool.Shared.Return(buffer); + break; + } + advanceAmount -= read; } } public static void Skip(this Stream source) { - var buffer = GetTransferByteArray(); - try - { - do { } while (source.Read(buffer, 0, buffer.Length) == buffer.Length); - } - finally - { - ArrayPool.Shared.Return(buffer); - } - } - - public static bool Find(this Stream source, byte[] array) - { - var buffer = GetTransferByteArray(); - try - { - var count = 0; - var len = source.Read(buffer, 0, buffer.Length); - - do - { - for (var i = 0; i < len; i++) - { - if (array[count] == buffer[i]) - { - count++; - if (count == array.Length) - { - source.Position = source.Position - len + i - array.Length + 1; - return true; - } - } - } - } while ((len = source.Read(buffer, 0, buffer.Length)) > 0); - } - finally - { - ArrayPool.Shared.Return(buffer); - } - - return false; + using var buffer = MemoryPool.Shared.Rent(TEMP_BUFFER_SIZE); + while (source.Read(buffer.Memory.Span) > 0) { } } public static DateTime DosDateToDateTime(ushort iDate, ushort iTime) @@ -271,31 +169,12 @@ public static DateTime UnixTimeToDateTime(long unixtime) return sTime.AddSeconds(unixtime); } - public static long TransferTo(this Stream source, Stream destination) - { - var array = GetTransferByteArray(); - try - { - long total = 0; - while (ReadTransferBlock(source, array, out var count)) - { - destination.Write(array, 0, count); - total += count; - } - return total; - } - finally - { - ArrayPool.Shared.Return(array); - } - } - public static long TransferTo(this Stream source, Stream destination, long maxLength) { - var array = GetTransferByteArray(); - var maxReadSize = array.Length; + var array = ArrayPool.Shared.Rent(TEMP_BUFFER_SIZE); try { + var maxReadSize = array.Length; long total = 0; var remaining = maxLength; if (remaining < maxReadSize) @@ -331,12 +210,13 @@ public static long TransferTo( IReaderExtractionListener readerExtractionListener ) { - var array = GetTransferByteArray(); + var array = ArrayPool.Shared.Rent(TEMP_BUFFER_SIZE); try { var iterations = 0; long total = 0; - while (ReadTransferBlock(source, array, out var count)) + int count; + while ((count = source.Read(array, 0, array.Length)) != 0) { total += count; destination.Write(array, 0, count); @@ -351,12 +231,10 @@ IReaderExtractionListener readerExtractionListener } } - private static bool ReadTransferBlock(Stream source, byte[] array, out int count) => - (count = source.Read(array, 0, array.Length)) != 0; - - private static bool ReadTransferBlock(Stream source, byte[] array, int size, out int count) + private static bool ReadTransferBlock(Stream source, byte[] array, int maxSize, out int count) { - if (size > array.Length) + var size = maxSize; + if (maxSize > array.Length) { size = array.Length; } @@ -364,8 +242,34 @@ private static bool ReadTransferBlock(Stream source, byte[] array, int size, out return count != 0; } - private static byte[] GetTransferByteArray() => ArrayPool.Shared.Rent(81920); +#if NET60_OR_GREATER + public static bool ReadFully(this Stream stream, byte[] buffer) + { + try + { + stream.ReadExactly(buffer); + return true; + } + catch (EndOfStreamException) + { + return false; + } + } + + public static bool ReadFully(this Stream stream, Span buffer) + { + try + { + stream.ReadExactly(buffer); + return true; + } + catch (EndOfStreamException) + { + return false; + } + } +#else public static bool ReadFully(this Stream stream, byte[] buffer) { var total = 0; @@ -395,6 +299,7 @@ public static bool ReadFully(this Stream stream, Span buffer) } return (total >= buffer.Length); } +#endif public static string TrimNulls(this string source) => source.Replace('\0', ' ').Trim(); @@ -439,7 +344,6 @@ public static void SetBigUInt32(ref byte[] buffer, uint number, long offset) public static string ReplaceInvalidFileNameChars(string fileName) { - var invalidChars = new HashSet(Path.GetInvalidFileNameChars()); var sb = new StringBuilder(fileName.Length); foreach (var c in fileName) { diff --git a/src/SharpCompress/Writers/GZip/GZipWriter.cs b/src/SharpCompress/Writers/GZip/GZipWriter.cs index 4fbf1bb90..7dd4a7de0 100644 --- a/src/SharpCompress/Writers/GZip/GZipWriter.cs +++ b/src/SharpCompress/Writers/GZip/GZipWriter.cs @@ -47,7 +47,7 @@ public override void Write(string filename, Stream source, DateTime? modificatio var stream = (GZipStream)OutputStream; stream.FileName = filename; stream.LastModified = modificationTime; - source.TransferTo(stream); + source.CopyTo(stream); _wroteToStream = true; } } diff --git a/src/SharpCompress/Writers/Zip/ZipWriter.cs b/src/SharpCompress/Writers/Zip/ZipWriter.cs index 55abe4405..f15728230 100644 --- a/src/SharpCompress/Writers/Zip/ZipWriter.cs +++ b/src/SharpCompress/Writers/Zip/ZipWriter.cs @@ -83,7 +83,7 @@ public override void Write(string entryPath, Stream source, DateTime? modificati public void Write(string entryPath, Stream source, ZipWriterEntryOptions zipWriterEntryOptions) { using var output = WriteToStream(entryPath, zipWriterEntryOptions); - source.TransferTo(output); + source.CopyTo(output); } public Stream WriteToStream(string entryPath, ZipWriterEntryOptions options) diff --git a/tests/SharpCompress.Test/Rar/RarReaderTests.cs b/tests/SharpCompress.Test/Rar/RarReaderTests.cs index d7c02d2d7..64d69d545 100644 --- a/tests/SharpCompress.Test/Rar/RarReaderTests.cs +++ b/tests/SharpCompress.Test/Rar/RarReaderTests.cs @@ -223,7 +223,7 @@ private void DoRar_Entry_Stream(string filename) var destinationFileName = Path.Combine(destdir, file); using var fs = File.OpenWrite(destinationFileName); - entryStream.TransferTo(fs); + entryStream.CopyTo(fs); } } } diff --git a/tests/SharpCompress.Test/SharpCompress.Test.csproj b/tests/SharpCompress.Test/SharpCompress.Test.csproj index f5516b915..2afb99380 100644 --- a/tests/SharpCompress.Test/SharpCompress.Test.csproj +++ b/tests/SharpCompress.Test/SharpCompress.Test.csproj @@ -9,6 +9,9 @@ $(DefineConstants);DEBUG_STREAMS + + $(DefineConstants);WINDOWS + @@ -17,7 +20,6 @@ - diff --git a/tests/SharpCompress.Test/Tar/TarReaderTests.cs b/tests/SharpCompress.Test/Tar/TarReaderTests.cs index a676d2dae..88c30cfa9 100644 --- a/tests/SharpCompress.Test/Tar/TarReaderTests.cs +++ b/tests/SharpCompress.Test/Tar/TarReaderTests.cs @@ -85,7 +85,7 @@ public void Tar_BZip2_Entry_Stream() var destinationFileName = Path.Combine(destdir, file.NotNull()); using var fs = File.OpenWrite(destinationFileName); - entryStream.TransferTo(fs); + entryStream.CopyTo(fs); } } } diff --git a/tests/SharpCompress.Test/TestBase.cs b/tests/SharpCompress.Test/TestBase.cs index f9cfb8a3b..184e64420 100644 --- a/tests/SharpCompress.Test/TestBase.cs +++ b/tests/SharpCompress.Test/TestBase.cs @@ -1,4 +1,3 @@ -global using SharpCompress.Helpers; using System; using System.Collections.Generic; using System.IO; diff --git a/tests/SharpCompress.Test/UtilityTests.cs b/tests/SharpCompress.Test/UtilityTests.cs new file mode 100644 index 000000000..cbd573042 --- /dev/null +++ b/tests/SharpCompress.Test/UtilityTests.cs @@ -0,0 +1,754 @@ +using System; +using System.Buffers; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Xunit; + +namespace SharpCompress.Test; + +public class UtilityTests +{ + #region URShift Tests + + [Fact] + public void URShift_Int_PositiveNumber_ShiftsCorrectly() + { + var result = Utility.URShift(16, 2); + Assert.Equal(4, result); + } + + [Fact] + public void URShift_Int_NegativeNumber_PerformsUnsignedShift() + { + // -1 in binary is all 1s (0xFFFFFFFF), shifted right by 1 should be 0x7FFFFFFF + var result = Utility.URShift(-1, 1); + Assert.Equal(int.MaxValue, result); + } + + [Fact] + public void URShift_Int_Zero_ReturnsZero() + { + var result = Utility.URShift(0, 5); + Assert.Equal(0, result); + } + + [Fact] + public void URShift_Long_PositiveNumber_ShiftsCorrectly() + { + var result = Utility.URShift(32L, 3); + Assert.Equal(4L, result); + } + + [Fact] + public void URShift_Long_NegativeNumber_PerformsUnsignedShift() + { + var result = Utility.URShift(-1L, 1); + Assert.Equal(long.MaxValue, result); + } + + [Fact] + public void URShift_Long_Zero_ReturnsZero() + { + var result = Utility.URShift(0L, 10); + Assert.Equal(0L, result); + } + + #endregion + + #region ReadFully Tests + + [Fact] + public void ReadFully_ByteArray_ReadsExactlyRequiredBytes() + { + var data = new byte[] { 1, 2, 3, 4, 5 }; + using var stream = new MemoryStream(data); + var buffer = new byte[5]; + + var result = stream.ReadFully(buffer); + + Assert.True(result); + Assert.Equal(data, buffer); + } + + [Fact] + public void ReadFully_ByteArray_ReturnsFalseWhenNotEnoughData() + { + var data = new byte[] { 1, 2, 3 }; + using var stream = new MemoryStream(data); + var buffer = new byte[5]; + + var result = stream.ReadFully(buffer); + + Assert.False(result); + } + + [Fact] + public void ReadFully_ByteArray_EmptyStream_ReturnsFalse() + { + using var stream = new MemoryStream(); + var buffer = new byte[5]; + + var result = stream.ReadFully(buffer); + + Assert.False(result); + } + + [Fact] + public void ReadFully_ByteArray_EmptyBuffer_ReturnsTrue() + { + var data = new byte[] { 1, 2, 3 }; + using var stream = new MemoryStream(data); + var buffer = new byte[0]; + + var result = stream.ReadFully(buffer); + + Assert.True(result); + } + + [Fact] + public void ReadFully_Span_ReadsExactlyRequiredBytes() + { + var data = new byte[] { 1, 2, 3, 4, 5 }; + using var stream = new MemoryStream(data); + Span buffer = new byte[5]; + + var result = stream.ReadFully(buffer); + + Assert.True(result); + Assert.Equal(data, buffer.ToArray()); + } + + [Fact] + public void ReadFully_Span_ReturnsFalseWhenNotEnoughData() + { + var data = new byte[] { 1, 2, 3 }; + using var stream = new MemoryStream(data); + Span buffer = new byte[5]; + + var result = stream.ReadFully(buffer); + + Assert.False(result); + } + + [Fact] + public void ReadFully_Span_EmptyStream_ReturnsFalse() + { + using var stream = new MemoryStream(); + Span buffer = new byte[5]; + + var result = stream.ReadFully(buffer); + + Assert.False(result); + } + + [Fact] + public void ReadFully_Span_EmptyBuffer_ReturnsTrue() + { + var data = new byte[] { 1, 2, 3 }; + using var stream = new MemoryStream(data); + Span buffer = new byte[0]; + + var result = stream.ReadFully(buffer); + + Assert.True(result); + } + + #endregion + + #region Skip Tests + + [Fact] + public void Skip_SeekableStream_UsesSeek() + { + var data = new byte[] { 1, 2, 3, 4, 5 }; + using var stream = new MemoryStream(data); + + stream.Skip(3); + + Assert.Equal(3, stream.Position); + } + + [Fact] + public void Skip_SeekableStream_SkipsCorrectAmount() + { + var data = new byte[] { 1, 2, 3, 4, 5 }; + using var stream = new MemoryStream(data); + + stream.Skip(2); + var buffer = new byte[2]; + stream.Read(buffer); + + Assert.Equal(new byte[] { 3, 4 }, buffer); + } + + [Fact] + public void Skip_NonSeekableStream_SkipsCorrectAmount() + { + var data = new byte[] { 1, 2, 3, 4, 5 }; + using var seekableStream = new MemoryStream(data); + using var nonSeekableStream = new NonSeekableStream(seekableStream); + + nonSeekableStream.Skip(2); + var buffer = new byte[2]; + nonSeekableStream.Read(buffer); + + Assert.Equal(new byte[] { 3, 4 }, buffer); + } + + [Fact] + public void Skip_NonSeekableStream_SkipsBeyondStreamEnd() + { + var data = new byte[] { 1, 2, 3 }; + using var seekableStream = new MemoryStream(data); + using var nonSeekableStream = new NonSeekableStream(seekableStream); + + // Should not throw, just skip what's available + nonSeekableStream.Skip(10); + + Assert.Equal(-1, nonSeekableStream.ReadByte()); + } + + [Fact] + public void Skip_Parameterless_SkipsEntireStream() + { + var data = new byte[] { 1, 2, 3, 4, 5 }; + using var stream = new MemoryStream(data); + + stream.Skip(); + + Assert.Equal(-1, stream.ReadByte()); + } + + [Fact] + public void Skip_Zero_DoesNotMove() + { + var data = new byte[] { 1, 2, 3, 4, 5 }; + using var stream = new MemoryStream(data); + stream.Position = 2; + + stream.Skip(0); + + Assert.Equal(2, stream.Position); + } + + #endregion + + #region SetSize Tests + + [Fact] + public void SetSize_GrowsList_AddsZeroBytes() + { + var list = new List { 1, 2, 3 }; + + Utility.SetSize(list, 5); + + Assert.Equal(5, list.Count); + Assert.Equal(new byte[] { 1, 2, 3, 0, 0 }, list); + } + + [Fact] + public void SetSize_ShrinksListByOne() + { + var list = new List { 1, 2, 3, 4, 5 }; + + Utility.SetSize(list, 3); + + Assert.Equal(3, list.Count); + Assert.Equal(new byte[] { 1, 2, 3 }, list); + } + + [Fact] + public void SetSize_ToZero_ClearsAllItems() + { + var list = new List { 1, 2, 3 }; + + Utility.SetSize(list, 0); + + Assert.Empty(list); + } + + [Fact] + public void SetSize_SameSize_NoChange() + { + var list = new List { 1, 2, 3 }; + + Utility.SetSize(list, 3); + + Assert.Equal(3, list.Count); + Assert.Equal(new byte[] { 1, 2, 3 }, list); + } + + #endregion + + #region ForEach Tests + + [Fact] + public void ForEach_ExecutesActionForEachItem() + { + var items = new[] { 1, 2, 3, 4, 5 }; + var results = new List(); + + items.ForEach(x => results.Add(x)); + + Assert.Equal(items, results); + } + + [Fact] + public void ForEach_EmptyCollection_NoExecutions() + { + var items = Array.Empty(); + var count = 0; + + items.ForEach(x => count++); + + Assert.Equal(0, count); + } + + #endregion + + #region AsEnumerable Tests + + [Fact] + public void AsEnumerable_SingleItem_YieldsItem() + { + var item = 42; + + var result = item.AsEnumerable().ToList(); + + Assert.Single(result); + Assert.Equal(42, result[0]); + } + + [Fact] + public void AsEnumerable_String_YieldsString() + { + var item = "test"; + + var result = item.AsEnumerable().ToList(); + + Assert.Single(result); + Assert.Equal("test", result[0]); + } + + #endregion + + #region DosDateToDateTime Tests + + [Fact] + public void DosDateToDateTime_ValidDate_ConvertsCorrectly() + { + // DOS date format: year (7 bits) | month (4 bits) | day (5 bits) + // DOS time format: hour (5 bits) | minute (6 bits) | second (5 bits, in 2-second increments) + // This represents: 2020-01-15 10:30:20 (approximately) + ushort dosDate = (ushort)(((2020 - 1980) << 9) | (1 << 5) | 15); // 2020-01-15 + ushort dosTime = (ushort)((10 << 11) | (30 << 5) | 10); // 10:30:20 + + var result = Utility.DosDateToDateTime(dosDate, dosTime); + + Assert.Equal(2020, result.Year); + Assert.Equal(1, result.Month); + Assert.Equal(15, result.Day); + Assert.Equal(10, result.Hour); + Assert.Equal(30, result.Minute); + Assert.Equal(20, result.Second); + } + + [Fact] + public void DosDateToDateTime_InvalidDate_DefaultsTo1980_01_01() + { + ushort dosDate = ushort.MaxValue; + ushort dosTime = (ushort)((10 << 11) | (30 << 5) | 10); + + var result = Utility.DosDateToDateTime(dosDate, dosTime); + + Assert.Equal(1980, result.Year); + Assert.Equal(1, result.Month); + Assert.Equal(1, result.Day); + } + + [Fact] + public void DosDateToDateTime_InvalidTime_DefaultsToMidnight() + { + ushort dosDate = (ushort)(((2020 - 1980) << 9) | (1 << 5) | 15); + ushort dosTime = ushort.MaxValue; + + var result = Utility.DosDateToDateTime(dosDate, dosTime); + + Assert.Equal(0, result.Hour); + Assert.Equal(0, result.Minute); + Assert.Equal(0, result.Second); + } + + [Fact] + public void DosDateToDateTime_FromUint_ConvertsCorrectly() + { + ushort dosDate = (ushort)(((2020 - 1980) << 9) | (6 << 5) | 20); // 2020-06-20 + ushort dosTime = (ushort)((14 << 11) | (45 << 5) | 15); // 14:45:30 + uint combined = (uint)(dosDate << 16) | dosTime; + + var result = Utility.DosDateToDateTime(combined); + + Assert.Equal(2020, result.Year); + Assert.Equal(6, result.Month); + Assert.Equal(20, result.Day); + Assert.Equal(14, result.Hour); + Assert.Equal(45, result.Minute); + } + + #endregion + + #region DateTimeToDosTime Tests + + [Fact] + public void DateTimeToDosTime_ValidDateTime_ConvertsCorrectly() + { + //always do local time + var dt = new DateTime(2020, 6, 15, 14, 30, 20, DateTimeKind.Local); + + var result = Utility.DateTimeToDosTime(dt); + + // Verify we can convert back + var reversed = Utility.DosDateToDateTime(result); + Assert.Equal(2020, reversed.Year); + Assert.Equal(6, reversed.Month); + Assert.Equal(15, reversed.Day); + Assert.Equal(14, reversed.Hour); + Assert.Equal(30, reversed.Minute); + // Seconds are rounded down to nearest even number in DOS format + Assert.True(reversed.Second == 20 || reversed.Second == 18); + } + + [Fact] + public void DateTimeToDosTime_NullDateTime_ReturnsZero() + { + DateTime? dt = null; + + var result = Utility.DateTimeToDosTime(dt); + + Assert.Equal(0u, result); + } + + #endregion + + #region UnixTimeToDateTime Tests + + [Fact] + public void UnixTimeToDateTime_Zero_Returns1970_01_01() + { + var result = Utility.UnixTimeToDateTime(0); + + Assert.Equal(1970, result.Year); + Assert.Equal(1, result.Month); + Assert.Equal(1, result.Day); + Assert.Equal(0, result.Hour); + Assert.Equal(0, result.Minute); + Assert.Equal(0, result.Second); + } + + [Fact] + public void UnixTimeToDateTime_ValidTimestamp_ConvertsCorrectly() + { + // January 1, 2000 00:00:00 UTC is 946684800 seconds after epoch + var result = Utility.UnixTimeToDateTime(946684800); + + Assert.Equal(2000, result.Year); + Assert.Equal(1, result.Month); + Assert.Equal(1, result.Day); + } + + [Fact] + public void UnixTimeToDateTime_NegativeTimestamp_ReturnsBeforeEpoch() + { + // -86400 is one day before epoch + var result = Utility.UnixTimeToDateTime(-86400); + + Assert.Equal(1969, result.Year); + Assert.Equal(12, result.Month); + Assert.Equal(31, result.Day); + } + + #endregion + + #region TransferTo Tests + + [Fact] + public void TransferTo_WithMaxLength_TransfersCorrectAmount() + { + var sourceData = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + using var source = new MemoryStream(sourceData); + using var destination = new MemoryStream(); + + var transferred = source.TransferTo(destination, 5); + + Assert.Equal(5, transferred); + Assert.Equal(new byte[] { 1, 2, 3, 4, 5 }, destination.ToArray()); + } + + [Fact] + public void TransferTo_SourceSmallerThanMax_TransfersAll() + { + var sourceData = new byte[] { 1, 2, 3 }; + using var source = new MemoryStream(sourceData); + using var destination = new MemoryStream(); + + var transferred = source.TransferTo(destination, 100); + + Assert.Equal(3, transferred); + Assert.Equal(sourceData, destination.ToArray()); + } + + [Fact] + public void TransferTo_EmptySource_TransfersNothing() + { + using var source = new MemoryStream(); + using var destination = new MemoryStream(); + + var transferred = source.TransferTo(destination, 100); + + Assert.Equal(0, transferred); + Assert.Empty(destination.ToArray()); + } + + #endregion + + #region SwapUINT32 Tests + + [Fact] + public void SwapUINT32_SimpleValue_SwapsEndianness() + { + uint value = 0x12345678; + + var result = Utility.SwapUINT32(value); + + Assert.Equal(0x78563412u, result); + } + + [Fact] + public void SwapUINT32_Zero_ReturnsZero() + { + var result = Utility.SwapUINT32(0); + + Assert.Equal(0u, result); + } + + [Fact] + public void SwapUINT32_MaxValue_SwapsCorrectly() + { + var result = Utility.SwapUINT32(uint.MaxValue); + + Assert.Equal(uint.MaxValue, result); + } + + [Fact] + public void SwapUINT32_Involution_SwappingTwiceReturnsOriginal() + { + uint value = 0x12345678; + + var result = Utility.SwapUINT32(Utility.SwapUINT32(value)); + + Assert.Equal(value, result); + } + + #endregion + + #region SetLittleUInt32 Tests + + [Fact] + public void SetLittleUInt32_InsertsValueCorrectly() + { + byte[] buffer = new byte[10]; + uint value = 0x12345678; + + Utility.SetLittleUInt32(ref buffer, value, 2); + + Assert.Equal(0x78, buffer[2]); + Assert.Equal(0x56, buffer[3]); + Assert.Equal(0x34, buffer[4]); + Assert.Equal(0x12, buffer[5]); + } + + [Fact] + public void SetLittleUInt32_AtOffset_InsertsBehindOffset() + { + byte[] buffer = new byte[10]; + uint value = 0xDEADBEEF; + + Utility.SetLittleUInt32(ref buffer, value, 5); + + Assert.Equal(0xEF, buffer[5]); + Assert.Equal(0xBE, buffer[6]); + Assert.Equal(0xAD, buffer[7]); + Assert.Equal(0xDE, buffer[8]); + } + + #endregion + + #region SetBigUInt32 Tests + + [Fact] + public void SetBigUInt32_InsertsValueCorrectly() + { + byte[] buffer = new byte[10]; + uint value = 0x12345678; + + Utility.SetBigUInt32(ref buffer, value, 2); + + Assert.Equal(0x12, buffer[2]); + Assert.Equal(0x34, buffer[3]); + Assert.Equal(0x56, buffer[4]); + Assert.Equal(0x78, buffer[5]); + } + + [Fact] + public void SetBigUInt32_AtOffset_InsertsBehindOffset() + { + byte[] buffer = new byte[10]; + uint value = 0xDEADBEEF; + + Utility.SetBigUInt32(ref buffer, value, 5); + + Assert.Equal(0xDE, buffer[5]); + Assert.Equal(0xAD, buffer[6]); + Assert.Equal(0xBE, buffer[7]); + Assert.Equal(0xEF, buffer[8]); + } + + #endregion + + #region ReplaceInvalidFileNameChars Tests + +#if WINDOWS + [Theory] + [InlineData("valid_filename.txt", "valid_filename.txt")] + [InlineData("filetest.txt", "file_name_test.txt")] + [InlineData("<>:\"|?*", "_______")] + public void ReplaceInvalidFileNameChars_Windows(string fileName, string expected) + { + var result = Utility.ReplaceInvalidFileNameChars(fileName); + + Assert.Equal(expected, result); + } + +#else + [Theory] + [InlineData("valid_filename.txt", "valid_filename.txt")] + [InlineData("filetest.txt", "filetest.txt")] + [InlineData("<>:\"|?*", "<>:\"|?*")] + public void ReplaceInvalidFileNameChars_NonWindows(string fileName, string expected) + { + var result = Utility.ReplaceInvalidFileNameChars(fileName); + + Assert.Equal(expected, result); + } +#endif + + #endregion + + #region ToReadOnly Tests + + [Fact] + public void ToReadOnly_IList_ReturnsReadOnlyCollection() + { + var list = new List { 1, 2, 3, 4, 5 }; + + var result = list.ToReadOnly(); + + Assert.Equal(5, result.Count); + Assert.Equal(1, result[0]); + Assert.Equal(5, result[4]); + } + + [Fact] + public void ToReadOnly_EmptyList_ReturnsEmptyReadOnlyCollection() + { + var list = new List(); + + var result = list.ToReadOnly(); + + Assert.Empty(result); + } + + #endregion + + #region TrimNulls Tests + + [Fact] + public void TrimNulls_StringWithNulls_ReplacesAndTrims() + { + var input = " hello\0world\0 "; + + var result = Utility.TrimNulls(input); + + Assert.Equal("hello world", result); + } + + [Fact] + public void TrimNulls_StringWithoutNulls_TrimsWhitespace() + { + var input = " hello world "; + + var result = Utility.TrimNulls(input); + + Assert.Equal("hello world", result); + } + + [Fact] + public void TrimNulls_OnlyNulls_ReturnsEmpty() + { + var input = "\0\0\0"; + + var result = Utility.TrimNulls(input); + + Assert.Empty(result); + } + + #endregion +} + +/// +/// Helper class for testing non-seekable streams +/// +internal class NonSeekableStream : Stream +{ + private readonly Stream _inner; + + public NonSeekableStream(Stream inner) + { + _inner = inner; + } + + public override bool CanRead => _inner.CanRead; + public override bool CanSeek => false; // Force non-seekable + public override bool CanWrite => _inner.CanWrite; + public override long Length => _inner.Length; + public override long Position + { + get => _inner.Position; + set => throw new NotSupportedException("Stream is not seekable"); + } + + public override void Flush() => _inner.Flush(); + + public override int Read(byte[] buffer, int offset, int count) => + _inner.Read(buffer, offset, count); + + public override long Seek(long offset, SeekOrigin origin) => + throw new NotSupportedException("Stream is not seekable"); + + public override void SetLength(long value) => + throw new NotSupportedException("Stream is not seekable"); + + public override void Write(byte[] buffer, int offset, int count) => + _inner.Write(buffer, offset, count); + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _inner.Dispose(); + } + base.Dispose(disposing); + } +} diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs index 53849e468..a2a1c0cca 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs @@ -589,12 +589,10 @@ public void Zip_Deflate_PKWear_Multipy_Entry_Access() } } - [SkippableFact] - public void Zip_Evil_Throws_Exception() +#if WINDOWS + [Fact] + public void Zip_Evil_Throws_Exception_Windows() { - //windows only because of the paths - Skip.IfNot(Environment.OSVersion.Platform == PlatformID.Win32NT); - var zipFile = Path.Combine(TEST_ARCHIVES_PATH, "Zip.Evil.zip"); Assert.ThrowsAny(() => @@ -609,6 +607,7 @@ public void Zip_Evil_Throws_Exception() } }); } +#endif private class NonSeekableMemoryStream : MemoryStream { diff --git a/tests/SharpCompress.Test/packages.lock.json b/tests/SharpCompress.Test/packages.lock.json index b789ce764..01daed881 100644 --- a/tests/SharpCompress.Test/packages.lock.json +++ b/tests/SharpCompress.Test/packages.lock.json @@ -49,16 +49,6 @@ "Microsoft.TestPlatform.ObjectModel": "17.13.0" } }, - "Xunit.SkippableFact": { - "type": "Direct", - "requested": "[1.5.23, )", - "resolved": "1.5.23", - "contentHash": "JlKobLTlsGcuJ8OtoodxL63bUagHSVBnF+oQ2GgnkwNqK+XYjeYyhQasULi5Ebx1MNDGNbOMplQYr89mR+nItQ==", - "dependencies": { - "Validation": "2.5.51", - "xunit.extensibility.execution": "2.4.0" - } - }, "Microsoft.CodeCoverage": { "type": "Transitive", "resolved": "17.13.0", @@ -108,11 +98,6 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.3" } }, - "Validation": { - "type": "Transitive", - "resolved": "2.5.51", - "contentHash": "g/Aug7PVWaenlJ0QUyt/mEetngkQNsMCuNeRVXbcJED1nZS7JcK+GTU4kz3jcQ7bFuKfi8PF4ExXH7XSFNuSLQ==" - }, "xunit.abstractions": { "type": "Transitive", "resolved": "2.0.3", @@ -247,16 +232,6 @@ "resolved": "3.1.5", "contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA==" }, - "Xunit.SkippableFact": { - "type": "Direct", - "requested": "[1.5.23, )", - "resolved": "1.5.23", - "contentHash": "JlKobLTlsGcuJ8OtoodxL63bUagHSVBnF+oQ2GgnkwNqK+XYjeYyhQasULi5Ebx1MNDGNbOMplQYr89mR+nItQ==", - "dependencies": { - "Validation": "2.5.51", - "xunit.extensibility.execution": "2.4.0" - } - }, "Microsoft.CodeCoverage": { "type": "Transitive", "resolved": "17.13.0", @@ -294,11 +269,6 @@ "resolved": "1.6.0", "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" }, - "Validation": { - "type": "Transitive", - "resolved": "2.5.51", - "contentHash": "g/Aug7PVWaenlJ0QUyt/mEetngkQNsMCuNeRVXbcJED1nZS7JcK+GTU4kz3jcQ7bFuKfi8PF4ExXH7XSFNuSLQ==" - }, "xunit.abstractions": { "type": "Transitive", "resolved": "2.0.3",