diff --git a/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs b/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs index 7932caadf..dbc1d1702 100644 --- a/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs +++ b/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs @@ -2,6 +2,7 @@ #if !NO_FILE using System.Collections.Generic; using System.IO; +using System.Linq; using SharpCompress.Common.Rar; using SharpCompress.Common.Rar.Headers; using SharpCompress.IO; @@ -18,7 +19,7 @@ internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options) : base(StreamingMode.Seekable, fileInfo.OpenRead(), FixOptions(options)) { FileInfo = fileInfo; - FileParts = GetVolumeFileParts().ToReadOnly(); + FileParts = GetVolumeFileParts().ToArray().ToReadOnly(); } private static ReaderOptions FixOptions(ReaderOptions options) @@ -43,4 +44,4 @@ internal override IEnumerable ReadFileParts() } } } -#endif \ No newline at end of file +#endif diff --git a/src/SharpCompress/Buffers/ArrayPool.cs b/src/SharpCompress/Buffers/ArrayPool.cs deleted file mode 100644 index d81ed4c07..000000000 --- a/src/SharpCompress/Buffers/ArrayPool.cs +++ /dev/null @@ -1,119 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -#if NETCORE -using System.Runtime.CompilerServices; -using System.Threading; - -namespace SharpCompress.Buffers -{ - /// - /// Provides a resource pool that enables reusing instances of type . - /// - /// - /// - /// Renting and returning buffers with an can increase performance - /// in situations where arrays are created and destroyed frequently, resulting in significant - /// memory pressure on the garbage collector. - /// - /// - /// This class is thread-safe. All members may be used by multiple threads concurrently. - /// - /// - internal abstract class ArrayPool - { - /// The lazily-initialized shared pool instance. - private static ArrayPool s_sharedInstance = null; - - /// - /// Retrieves a shared instance. - /// - /// - /// The shared pool provides a default implementation of - /// that's intended for general applicability. It maintains arrays of multiple sizes, and - /// may hand back a larger array than was actually requested, but will never hand back a smaller - /// array than was requested. Renting a buffer from it with will result in an - /// existing buffer being taken from the pool if an appropriate buffer is available or in a new - /// buffer being allocated if one is not available. - /// - public static ArrayPool Shared - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get { return Volatile.Read(ref s_sharedInstance) ?? EnsureSharedCreated(); } - } - - /// Ensures that has been initialized to a pool and returns it. - [MethodImpl(MethodImplOptions.NoInlining)] - private static ArrayPool EnsureSharedCreated() - { - Interlocked.CompareExchange(ref s_sharedInstance, Create(), null); - return s_sharedInstance; - } - - /// - /// Creates a new instance using default configuration options. - /// - /// A new instance. - public static ArrayPool Create() - { - return new DefaultArrayPool(); - } - - /// - /// Creates a new instance using custom configuration options. - /// - /// The maximum length of array instances that may be stored in the pool. - /// - /// The maximum number of array instances that may be stored in each bucket in the pool. The pool - /// groups arrays of similar lengths into buckets for faster access. - /// - /// A new instance with the specified configuration options. - /// - /// The created pool will group arrays into buckets, with no more than - /// in each bucket and with those arrays not exceeding in length. - /// - public static ArrayPool Create(int maxArrayLength, int maxArraysPerBucket) - { - return new DefaultArrayPool(maxArrayLength, maxArraysPerBucket); - } - - /// - /// Retrieves a buffer that is at least the requested length. - /// - /// The minimum length of the array needed. - /// - /// An that is at least in length. - /// - /// - /// This buffer is loaned to the caller and should be returned to the same pool via - /// so that it may be reused in subsequent usage of . - /// It is not a fatal error to not return a rented buffer, but failure to do so may lead to - /// decreased application performance, as the pool may need to create a new buffer to replace - /// the one lost. - /// - public abstract T[] Rent(int minimumLength); - - /// - /// Returns to the pool an array that was previously obtained via on the same - /// instance. - /// - /// - /// The buffer previously obtained from to return to the pool. - /// - /// - /// If true and if the pool will store the buffer to enable subsequent reuse, - /// will clear of its contents so that a subsequent consumer via - /// will not see the previous consumer's content. If false or if the pool will release the buffer, - /// the array's contents are left unchanged. - /// - /// - /// Once a buffer has been returned to the pool, the caller gives up all ownership of the buffer - /// and must not use it. The reference returned from a given call to must only be - /// returned via once. The default - /// may hold onto the returned buffer in order to rent it again, or it may release the returned buffer - /// if it's determined that the pool already has enough buffers stored. - /// - public abstract void Return(T[] array, bool clearArray = false); - } -} -#endif \ No newline at end of file diff --git a/src/SharpCompress/Buffers/DefaultArrayPool.cs b/src/SharpCompress/Buffers/DefaultArrayPool.cs deleted file mode 100644 index 43cd2c37e..000000000 --- a/src/SharpCompress/Buffers/DefaultArrayPool.cs +++ /dev/null @@ -1,144 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -#if NETCORE -using System; - -namespace SharpCompress.Buffers -{ - internal sealed partial class DefaultArrayPool : ArrayPool - { - /// The default maximum length of each array in the pool (2^20). - private const int DefaultMaxArrayLength = 1024 * 1024; - /// The default maximum number of arrays per bucket that are available for rent. - private const int DefaultMaxNumberOfArraysPerBucket = 50; - /// Lazily-allocated empty array used when arrays of length 0 are requested. - private static T[] s_emptyArray; // we support contracts earlier than those with Array.Empty() - - private readonly Bucket[] _buckets; - - internal DefaultArrayPool() : this(DefaultMaxArrayLength, DefaultMaxNumberOfArraysPerBucket) - { - } - - internal DefaultArrayPool(int maxArrayLength, int maxArraysPerBucket) - { - if (maxArrayLength <= 0) - { - throw new ArgumentOutOfRangeException(nameof(maxArrayLength)); - } - if (maxArraysPerBucket <= 0) - { - throw new ArgumentOutOfRangeException(nameof(maxArraysPerBucket)); - } - - // Our bucketing algorithm has a min length of 2^4 and a max length of 2^30. - // Constrain the actual max used to those values. - const int MinimumArrayLength = 0x10, MaximumArrayLength = 0x40000000; - if (maxArrayLength > MaximumArrayLength) - { - maxArrayLength = MaximumArrayLength; - } - else if (maxArrayLength < MinimumArrayLength) - { - maxArrayLength = MinimumArrayLength; - } - - // Create the buckets. - int poolId = Id; - int maxBuckets = Utilities.SelectBucketIndex(maxArrayLength); - var buckets = new Bucket[maxBuckets + 1]; - for (int i = 0; i < buckets.Length; i++) - { - buckets[i] = new Bucket(Utilities.GetMaxSizeForBucket(i), maxArraysPerBucket, poolId); - } - _buckets = buckets; - } - - /// Gets an ID for the pool to use with events. - private int Id => GetHashCode(); - - public override T[] Rent(int minimumLength) - { - // Arrays can't be smaller than zero. We allow requesting zero-length arrays (even though - // pooling such an array isn't valuable) as it's a valid length array, and we want the pool - // to be usable in general instead of using `new`, even for computed lengths. - if (minimumLength < 0) - { - throw new ArgumentOutOfRangeException(nameof(minimumLength)); - } - else if (minimumLength == 0) - { - // No need for events with the empty array. Our pool is effectively infinite - // and we'll never allocate for rents and never store for returns. - return s_emptyArray ?? (s_emptyArray = new T[0]); - } - - T[] buffer = null; - - int index = Utilities.SelectBucketIndex(minimumLength); - if (index < _buckets.Length) - { - // Search for an array starting at the 'index' bucket. If the bucket is empty, bump up to the - // next higher bucket and try that one, but only try at most a few buckets. - const int MaxBucketsToTry = 2; - int i = index; - do - { - // Attempt to rent from the bucket. If we get a buffer from it, return it. - buffer = _buckets[i].Rent(); - if (buffer != null) - { - return buffer; - } - } - while (++i < _buckets.Length && i != index + MaxBucketsToTry); - - // The pool was exhausted for this buffer size. Allocate a new buffer with a size corresponding - // to the appropriate bucket. - buffer = new T[_buckets[index]._bufferLength]; - } - else - { - // The request was for a size too large for the pool. Allocate an array of exactly the requested length. - // When it's returned to the pool, we'll simply throw it away. - buffer = new T[minimumLength]; - } - - return buffer; - } - - public override void Return(T[] array, bool clearArray = false) - { - if (array == null) - { - throw new ArgumentNullException(nameof(array)); - } - else if (array.Length == 0) - { - // Ignore empty arrays. When a zero-length array is rented, we return a singleton - // rather than actually taking a buffer out of the lowest bucket. - return; - } - - // Determine with what bucket this array length is associated - int bucket = Utilities.SelectBucketIndex(array.Length); - - // If we can tell that the buffer was allocated, drop it. Otherwise, check if we have space in the pool - if (bucket < _buckets.Length) - { - // Clear the array if the user requests - if (clearArray) - { - Array.Clear(array, 0, array.Length); - } - - // Return the buffer to its bucket. In the future, we might consider having Return return false - // instead of dropping a bucket, in which case we could try to return to a lower-sized bucket, - // just as how in Rent we allow renting from a higher-sized bucket. - _buckets[bucket].Return(array); - } - } - } -} -#endif \ No newline at end of file diff --git a/src/SharpCompress/Buffers/DefaultArrayPoolBucket.cs b/src/SharpCompress/Buffers/DefaultArrayPoolBucket.cs deleted file mode 100644 index 301248891..000000000 --- a/src/SharpCompress/Buffers/DefaultArrayPoolBucket.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if NETCORE -using System; -using System.Diagnostics; -using System.Threading; - -namespace SharpCompress.Buffers -{ - internal sealed partial class DefaultArrayPool : ArrayPool - { - /// Provides a thread-safe bucket containing buffers that can be Rent'd and Return'd. - private sealed class Bucket - { - internal readonly int _bufferLength; - private readonly T[][] _buffers; - private readonly int _poolId; - - private SpinLock _lock; // do not make this readonly; it's a mutable struct - private int _index; - - /// - /// Creates the pool with numberOfBuffers arrays where each buffer is of bufferLength length. - /// - internal Bucket(int bufferLength, int numberOfBuffers, int poolId) - { - _lock = new SpinLock(Debugger.IsAttached); // only enable thread tracking if debugger is attached; it adds non-trivial overheads to Enter/Exit - _buffers = new T[numberOfBuffers][]; - _bufferLength = bufferLength; - _poolId = poolId; - } - - /// Gets an ID for the bucket to use with events. - internal int Id => GetHashCode(); - - /// Takes an array from the bucket. If the bucket is empty, returns null. - internal T[] Rent() - { - T[][] buffers = _buffers; - T[] buffer = null; - - // While holding the lock, grab whatever is at the next available index and - // update the index. We do as little work as possible while holding the spin - // lock to minimize contention with other threads. The try/finally is - // necessary to properly handle thread aborts on platforms which have them. - bool lockTaken = false, allocateBuffer = false; - try - { - _lock.Enter(ref lockTaken); - - if (_index < buffers.Length) - { - buffer = buffers[_index]; - buffers[_index++] = null; - allocateBuffer = buffer == null; - } - } - finally - { - if (lockTaken) _lock.Exit(false); - } - - // While we were holding the lock, we grabbed whatever was at the next available index, if - // there was one. If we tried and if we got back null, that means we hadn't yet allocated - // for that slot, in which case we should do so now. - if (allocateBuffer) - { - buffer = new T[_bufferLength]; - } - - return buffer; - } - - /// - /// Attempts to return the buffer to the bucket. If successful, the buffer will be stored - /// in the bucket and true will be returned; otherwise, the buffer won't be stored, and false - /// will be returned. - /// - internal void Return(T[] array) - { - // Check to see if the buffer is the correct size for this bucket - if (array.Length != _bufferLength) - { - throw new ArgumentException("Buffer not from pool", nameof(array)); - } - - // While holding the spin lock, if there's room available in the bucket, - // put the buffer into the next available slot. Otherwise, we just drop it. - // The try/finally is necessary to properly handle thread aborts on platforms - // which have them. - bool lockTaken = false; - try - { - _lock.Enter(ref lockTaken); - - if (_index != 0) - { - _buffers[--_index] = array; - } - } - finally - { - if (lockTaken) _lock.Exit(false); - } - } - } - } -} -#endif \ No newline at end of file diff --git a/src/SharpCompress/Buffers/Utilities.cs b/src/SharpCompress/Buffers/Utilities.cs deleted file mode 100644 index f4100e37d..000000000 --- a/src/SharpCompress/Buffers/Utilities.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -#if NETCORE -using System.Diagnostics; -using System.Runtime.CompilerServices; - -namespace SharpCompress.Buffers -{ - internal static class Utilities - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static int SelectBucketIndex(int bufferSize) - { - Debug.Assert(bufferSize > 0); - - uint bitsRemaining = ((uint)bufferSize - 1) >> 4; - - int poolIndex = 0; - if (bitsRemaining > 0xFFFF) { bitsRemaining >>= 16; poolIndex = 16; } - if (bitsRemaining > 0xFF) { bitsRemaining >>= 8; poolIndex += 8; } - if (bitsRemaining > 0xF) { bitsRemaining >>= 4; poolIndex += 4; } - if (bitsRemaining > 0x3) { bitsRemaining >>= 2; poolIndex += 2; } - if (bitsRemaining > 0x1) { bitsRemaining >>= 1; poolIndex += 1; } - - return poolIndex + (int)bitsRemaining; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static int GetMaxSizeForBucket(int binIndex) - { - int maxSize = 16 << binIndex; - Debug.Assert(maxSize >= 0); - return maxSize; - } - } -} -#endif \ No newline at end of file diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index dfc597e2a..ce617455f 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -29,11 +29,13 @@ + $(DefineConstants);NETCORE + - \ No newline at end of file + diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index e97d093db..19939bc23 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -1,29 +1,30 @@ using System; +#if NETCORE +using System.Buffers; +#endif using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.InteropServices; -#if NETCORE -using SharpCompress.Buffers; -#endif + using SharpCompress.Readers; namespace SharpCompress { internal static class Utility { - public static ReadOnlyCollection ToReadOnly(this IEnumerable items) + public static ReadOnlyCollection ToReadOnly(this ICollection items) { - return new ReadOnlyCollection(items.ToList()); + return new ReadOnlyCollection(items); } /// /// Performs an unsigned bitwise right shift with the specified number /// /// Number to operate on - /// Ammount of bits to shift + /// Amount of bits to shift /// The resulting number from the shift operation public static int URShift(int number, int bits) { @@ -38,7 +39,7 @@ public static int URShift(int number, int bits) /// Performs an unsigned bitwise right shift with the specified number /// /// Number to operate on - /// Ammount of bits to shift + /// Amount of bits to shift /// The resulting number from the shift operation public static long URShift(long number, int bits) { @@ -81,7 +82,8 @@ public static void Fill(T[] array, int fromindex, int toindex, T val) where T // see https://stackoverflow.com/questions/1897555/what-is-the-equivalent-of-memset-in-c private static readonly Action MemsetDelegate = CreateMemsetDelegate(); - private static Action CreateMemsetDelegate() { + private static Action CreateMemsetDelegate() + { var dynamicMethod = new DynamicMethod( "Memset", MethodAttributes.Public | MethodAttributes.Static, @@ -108,7 +110,7 @@ public static void Memset(byte[] array, byte what, int length) #else public static void Memset(byte[] array, byte what, int length) { - for(var i = 0; i < length; i++) + for (var i = 0; i < length; i++) { array[i] = what; } @@ -117,7 +119,7 @@ public static void Memset(byte[] array, byte what, int length) public static void Memset(T[] array, T what, int length) { - for(var i = 0; i < length; i++) + for (var i = 0; i < length; i++) { array[i] = what; } @@ -184,7 +186,7 @@ public static void ForEach(this IEnumerable items, Action action) action(item); } } - + public static void Copy(Array sourceArray, long sourceIndex, Array destinationArray, long destinationIndex, long length) { if (sourceIndex > Int32.MaxValue || sourceIndex < Int32.MinValue) @@ -440,4 +442,4 @@ public static bool BinaryEquals(this byte[] source, byte[] target) return true; } } -} \ No newline at end of file +} diff --git a/tests/SharpCompress.Test/SharpCompress.Test.csproj b/tests/SharpCompress.Test/SharpCompress.Test.csproj index 813fcee44..6f7985839 100644 --- a/tests/SharpCompress.Test/SharpCompress.Test.csproj +++ b/tests/SharpCompress.Test/SharpCompress.Test.csproj @@ -18,4 +18,4 @@ - \ No newline at end of file +