diff --git a/Src/Workshell.PE/ImageReader.cs b/Src/Workshell.PE/ImageReader.cs index 9e5aa2d..edf8d75 100644 --- a/Src/Workshell.PE/ImageReader.cs +++ b/Src/Workshell.PE/ImageReader.cs @@ -6,8 +6,6 @@ using System.Text; using System.Threading.Tasks; -using Microsoft.IO; - using Workshell.PE.Native; namespace Workshell.PE @@ -16,7 +14,7 @@ namespace Workshell.PE public class ImageReader : IDisposable, ISupportsBytes { - class PreloadedInformation + private class PreloadedInformation { public IMAGE_DOS_HEADER DOSHeader; @@ -35,12 +33,11 @@ class PreloadedInformation } - static RecyclableMemoryStreamManager memory_manager; - private bool _disposed; private Stream _stream; private bool _own_stream; private LocationCalculator _calc; + private IMemoryStreamProvider _memory_stream_provider; private DOSHeader _dos_header; private DOSStub _dos_stub; @@ -53,17 +50,13 @@ class PreloadedInformation private bool _is_clr; private bool _is_signed; - static ImageReader() - { - memory_manager = new RecyclableMemoryStreamManager(); - } - private ImageReader(Stream sourceStream, bool ownStream) { _disposed = false; _stream = sourceStream; _own_stream = ownStream; _calc = null; + _memory_stream_provider = new DefaultMemoryStreamProvider(); _dos_header = null; _dos_stub = null; @@ -348,7 +341,7 @@ public Stream GetStream() public byte[] GetBytes() { - using (var mem = memory_manager.GetStream()) + using (var mem = _memory_stream_provider.GetStream()) { _stream.Seek(0,SeekOrigin.Begin); _stream.CopyTo(mem,4096); @@ -410,19 +403,22 @@ private void Load() #endregion - #region Static Properties + #region Properties - internal static RecyclableMemoryStreamManager MemoryManager + public IMemoryStreamProvider MemoryStreamProvider { get { - return memory_manager; + return _memory_stream_provider; } - } - - #endregion + set + { + _memory_stream_provider = value; - #region Properties + if (_memory_stream_provider == null) + _memory_stream_provider = new DefaultMemoryStreamProvider(); + } + } public bool Is32Bit { diff --git a/Src/Workshell.PE/MemoryStreamProvider.cs b/Src/Workshell.PE/MemoryStreamProvider.cs new file mode 100644 index 0000000..d38aed2 --- /dev/null +++ b/Src/Workshell.PE/MemoryStreamProvider.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Workshell.PE +{ + + public interface IMemoryStreamProvider + { + + MemoryStream GetStream(); + MemoryStream GetStream(byte[] buffer); + + } + + public sealed class DefaultMemoryStreamProvider : IMemoryStreamProvider + { + + public MemoryStream GetStream() + { + return new MemoryStream(); + } + + public MemoryStream GetStream(byte[] buffer) + { + return new MemoryStream(buffer); + } + + } + +} diff --git a/Src/Workshell.PE/Microsoft/Events.cs b/Src/Workshell.PE/Microsoft/Events.cs deleted file mode 100644 index 9e948e1..0000000 --- a/Src/Workshell.PE/Microsoft/Events.cs +++ /dev/null @@ -1,153 +0,0 @@ -// --------------------------------------------------------------------- -// Copyright (c) 2015 Microsoft -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// --------------------------------------------------------------------- - -namespace Microsoft.IO -{ - using System; - using System.Diagnostics.Tracing; - - partial class RecyclableMemoryStreamManager - { - - [EventSource(Name = "Microsoft-IO-RecyclableMemoryStream", Guid = "{B80CD4E4-890E-468D-9CBA-90EB7C82DFC7}")] - public sealed class Events : EventSource - { - - public static Events Write = new Events(); - - public enum MemoryStreamBufferType - { - Small, - Large - } - - public enum MemoryStreamDiscardReason - { - TooLarge, - EnoughFree - } - - [Event(1, Level = EventLevel.Verbose)] - public void MemoryStreamCreated(Guid guid, string tag, int requestedSize) - { - if (this.IsEnabled(EventLevel.Verbose, EventKeywords.None)) - { - WriteEvent(1, guid, tag ?? string.Empty, requestedSize); - } - } - - [Event(2, Level = EventLevel.Verbose)] - public void MemoryStreamDisposed(Guid guid, string tag) - { - if (this.IsEnabled(EventLevel.Verbose, EventKeywords.None)) - { - WriteEvent(2, guid, tag ?? string.Empty); - } - } - - [Event(3, Level = EventLevel.Critical)] - public void MemoryStreamDoubleDispose(Guid guid, string tag, string allocationStack, string disposeStack1, string disposeStack2) - { - if (this.IsEnabled()) - { - this.WriteEvent(3, guid, tag ?? string.Empty, allocationStack ?? string.Empty, - disposeStack1 ?? string.Empty, disposeStack2 ?? string.Empty); - } - } - - [Event(4, Level = EventLevel.Error)] - public void MemoryStreamFinalized(Guid guid, string tag, string allocationStack) - { - if (this.IsEnabled()) - { - WriteEvent(4, guid, tag ?? string.Empty, allocationStack ?? string.Empty); - } - } - - [Event(5, Level = EventLevel.Verbose)] - public void MemoryStreamToArray(Guid guid, string tag, string stack, int size) - { - if (this.IsEnabled(EventLevel.Verbose, EventKeywords.None)) - { - WriteEvent(5, guid, tag ?? string.Empty, stack ?? string.Empty, size); - } - } - - [Event(6, Level = EventLevel.Informational)] - public void MemoryStreamManagerInitialized(int blockSize, int largeBufferMultiple, int maximumBufferSize) - { - if (this.IsEnabled()) - { - WriteEvent(6, blockSize, largeBufferMultiple, maximumBufferSize); - } - } - - [Event(7, Level = EventLevel.Verbose)] - public void MemoryStreamNewBlockCreated(long smallPoolInUseBytes) - { - if (this.IsEnabled(EventLevel.Verbose, EventKeywords.None)) - { - WriteEvent(7, smallPoolInUseBytes); - } - } - - [Event(8, Level = EventLevel.Verbose)] - public void MemoryStreamNewLargeBufferCreated(int requiredSize, long largePoolInUseBytes) - { - if (this.IsEnabled(EventLevel.Verbose, EventKeywords.None)) - { - WriteEvent(8, requiredSize, largePoolInUseBytes); - } - } - - [Event(9, Level = EventLevel.Verbose)] - public void MemoryStreamNonPooledLargeBufferCreated(int requiredSize, string tag, string allocationStack) - { - if (this.IsEnabled(EventLevel.Verbose, EventKeywords.None)) - { - WriteEvent(9, requiredSize, tag ?? string.Empty, allocationStack ?? string.Empty); - } - } - - [Event(10, Level = EventLevel.Warning)] - public void MemoryStreamDiscardBuffer(MemoryStreamBufferType bufferType, string tag, MemoryStreamDiscardReason reason) - { - if (this.IsEnabled()) - { - WriteEvent(10, bufferType, tag ?? string.Empty, reason); - } - } - - [Event(11, Level = EventLevel.Error)] - public void MemoryStreamOverCapacity(int requestedCapacity, long maxCapacity, string tag, string allocationStack) - { - if (this.IsEnabled()) - { - WriteEvent(11, requestedCapacity, maxCapacity, tag ?? string.Empty, allocationStack ?? string.Empty); - } - } - - } - - } - -} \ No newline at end of file diff --git a/Src/Workshell.PE/Microsoft/RecyclableMemoryStream.cs b/Src/Workshell.PE/Microsoft/RecyclableMemoryStream.cs deleted file mode 100644 index 217c027..0000000 --- a/Src/Workshell.PE/Microsoft/RecyclableMemoryStream.cs +++ /dev/null @@ -1,873 +0,0 @@ -// --------------------------------------------------------------------- -// Copyright (c) 2015 Microsoft -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// --------------------------------------------------------------------- - -namespace Microsoft.IO -{ - using System; - using System.IO; - using System.Collections.Generic; - - using Events = RecyclableMemoryStreamManager.Events; - - /// - /// MemoryStream implementation that deals with pooling and managing memory streams which use potentially large - /// buffers. - /// - /// - /// This class works in tandem with the RecylableMemoryStreamManager to supply MemoryStream - /// objects to callers, while avoiding these specific problems: - /// 1. LOH allocations - since all large buffers are pooled, they will never incur a Gen2 GC - /// 2. Memory waste - A standard memory stream doubles its size when it runs out of room. This - /// leads to continual memory growth as each stream approaches the maximum allowed size. - /// 3. Memory copying - Each time a MemoryStream grows, all the bytes are copied into new buffers. - /// This implementation only copies the bytes when GetBuffer is called. - /// 4. Memory fragmentation - By using homogeneous buffer sizes, it ensures that blocks of memory - /// can be easily reused. - /// - /// The stream is implemented on top of a series of uniformly-sized blocks. As the stream's length grows, - /// additional blocks are retrieved from the memory manager. It is these blocks that are pooled, not the stream - /// object itself. - /// - /// The biggest wrinkle in this implementation is when GetBuffer() is called. This requires a single - /// contiguous buffer. If only a single block is in use, then that block is returned. If multiple blocks - /// are in use, we retrieve a larger buffer from the memory manager. These large buffers are also pooled, - /// split by size--they are multiples of a chunk size (1 MB by default). - /// - /// Once a large buffer is assigned to the stream the blocks are NEVER again used for this stream. All operations take place on the - /// large buffer. The large buffer can be replaced by a larger buffer from the pool as needed. All blocks and large buffers - /// are maintained in the stream until the stream is disposed (unless AggressiveBufferReturn is enabled in the stream manager). - /// - /// - internal sealed class RecyclableMemoryStream : MemoryStream - { - private const long MaxStreamLength = Int32.MaxValue; - - private static readonly byte[] emptyArray = new byte[0]; - - /// - /// All of these blocks must be the same size - /// - private readonly List blocks = new List(1); - - /// - /// This is only set by GetBuffer() if the necessary buffer is larger than a single block size, or on - /// construction if the caller immediately requests a single large buffer. - /// - /// If this field is non-null, it contains the concatenation of the bytes found in the individual - /// blocks. Once it is created, this (or a larger) largeBuffer will be used for the life of the stream. - /// - private byte[] largeBuffer; - - /// - /// This list is used to store buffers once they're replaced by something larger. - /// This is for the cases where you have users of this class that may hold onto the buffers longer - /// than they should and you want to prevent race conditions which could corrupt the data. - /// - private List dirtyBuffers; - - private readonly Guid id; - /// - /// Unique identifier for this stream across it's entire lifetime - /// - /// Object has been disposed - internal Guid Id { get { this.CheckDisposed(); return this.id; } } - - private readonly string tag; - /// - /// A temporary identifier for the current usage of this stream. - /// - /// Object has been disposed - internal string Tag { get { this.CheckDisposed(); return this.tag; } } - - private readonly RecyclableMemoryStreamManager memoryManager; - - /// - /// Gets the memory manager being used by this stream. - /// - /// Object has been disposed - internal RecyclableMemoryStreamManager MemoryManager - { - get - { - this.CheckDisposed(); - return this.memoryManager; - } - } - - private bool disposed; - - private readonly string allocationStack; - private string disposeStack; - - /// - /// Callstack of the constructor. It is only set if MemoryManager.GenerateCallStacks is true, - /// which should only be in debugging situations. - /// - internal string AllocationStack { get { return this.allocationStack; } } - - /// - /// Callstack of the Dispose call. It is only set if MemoryManager.GenerateCallStacks is true, - /// which should only be in debugging situations. - /// - internal string DisposeStack { get { return this.disposeStack; } } - - /// - /// This buffer exists so that WriteByte can forward all of its calls to Write - /// without creating a new byte[] buffer on every call. - /// - private readonly byte[] byteBuffer = new byte[1]; - - #region Constructors - /// - /// Allocate a new RecyclableMemoryStream object. - /// - /// The memory manager - public RecyclableMemoryStream(RecyclableMemoryStreamManager memoryManager) - : this(memoryManager, null, 0, null) - { - } - - /// - /// Allocate a new RecyclableMemoryStream object - /// - /// The memory manager - /// A string identifying this stream for logging and debugging purposes - public RecyclableMemoryStream(RecyclableMemoryStreamManager memoryManager, string tag) - : this(memoryManager, tag, 0, null) - { - } - - /// - /// Allocate a new RecyclableMemoryStream object - /// - /// The memory manager - /// A string identifying this stream for logging and debugging purposes - /// The initial requested size to prevent future allocations - public RecyclableMemoryStream(RecyclableMemoryStreamManager memoryManager, string tag, int requestedSize) - : this(memoryManager, tag, requestedSize, null) - { - } - - /// - /// Allocate a new RecyclableMemoryStream object - /// - /// The memory manager - /// A string identifying this stream for logging and debugging purposes - /// The initial requested size to prevent future allocations - /// An initial buffer to use. This buffer will be owned by the stream and returned to the memory manager upon Dispose. - internal RecyclableMemoryStream(RecyclableMemoryStreamManager memoryManager, string tag, int requestedSize, - byte[] initialLargeBuffer) - : base(emptyArray) - { - this.memoryManager = memoryManager; - this.id = Guid.NewGuid(); - this.tag = tag; - - if (requestedSize < memoryManager.BlockSize) - { - requestedSize = memoryManager.BlockSize; - } - - if (initialLargeBuffer == null) - { - this.EnsureCapacity(requestedSize); - } - else - { - this.largeBuffer = initialLargeBuffer; - } - - this.disposed = false; - - if (this.memoryManager.GenerateCallStacks) - { - this.allocationStack = Environment.StackTrace; - } - - Events.Write.MemoryStreamCreated(this.id, this.tag, requestedSize); - this.memoryManager.ReportStreamCreated(); - } - #endregion - - #region Dispose and Finalize - ~RecyclableMemoryStream() - { - this.Dispose(false); - } - - /// - /// Returns the memory used by this stream back to the pool. - /// - /// Whether we're disposing (true), or being called by the finalizer (false) - /// This method is not thread safe and it may not be called more than once. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1816:CallGCSuppressFinalizeCorrectly", Justification = "We have different disposal semantics, so SuppressFinalize is in a different spot.")] - protected override void Dispose(bool disposing) - { - if (this.disposed) - { - string doubleDisposeStack = null; - if (this.memoryManager.GenerateCallStacks) - { - doubleDisposeStack = Environment.StackTrace; - } - - Events.Write.MemoryStreamDoubleDispose(this.id, this.tag, this.allocationStack, this.disposeStack, doubleDisposeStack); - return; - } - - Events.Write.MemoryStreamDisposed(this.id, this.tag); - - if (this.memoryManager.GenerateCallStacks) - { - this.disposeStack = Environment.StackTrace; - } - - if (disposing) - { - // Once this flag is set, we can't access any properties -- use fields directly - this.disposed = true; - - this.memoryManager.ReportStreamDisposed(); - - GC.SuppressFinalize(this); - } - else - { - // We're being finalized. - - Events.Write.MemoryStreamFinalized(this.id, this.tag, this.allocationStack); - - if (AppDomain.CurrentDomain.IsFinalizingForUnload()) - { - // If we're being finalized because of a shutdown, don't go any further. - // We have no idea what's already been cleaned up. Triggering events may cause - // a crash. - base.Dispose(disposing); - return; - } - - this.memoryManager.ReportStreamFinalized(); - } - - this.memoryManager.ReportStreamLength(this.length); - - if (this.largeBuffer != null) - { - this.memoryManager.ReturnLargeBuffer(this.largeBuffer, this.tag); - } - - if (this.dirtyBuffers != null) - { - foreach (var buffer in this.dirtyBuffers) - { - this.memoryManager.ReturnLargeBuffer(buffer, this.tag); - } - } - - this.memoryManager.ReturnBlocks(this.blocks, this.tag); - - base.Dispose(disposing); - } - - /// - /// Equivalent to Dispose - /// - public override void Close() - { - this.Dispose(true); - } - #endregion - - #region MemoryStream overrides - /// - /// Gets or sets the capacity - /// - /// Capacity is always in multiples of the memory manager's block size, unless - /// the large buffer is in use. Capacity never decreases during a stream's lifetime. - /// Explicitly setting the capacity to a lower value than the current value will have no effect. - /// This is because the buffers are all pooled by chunks and there's little reason to - /// allow stream truncation. - /// - /// Object has been disposed - public override int Capacity - { - get - { - this.CheckDisposed(); - if (this.largeBuffer != null) - { - return this.largeBuffer.Length; - } - - if (this.blocks.Count > 0) - { - return this.blocks.Count * this.memoryManager.BlockSize; - } - return 0; - } - set - { - this.CheckDisposed(); - this.EnsureCapacity(value); - } - } - - private int length; - - /// - /// Gets the number of bytes written to this stream. - /// - /// Object has been disposed - public override long Length - { - get - { - this.CheckDisposed(); - return this.length; - } - } - - private int position; - - /// - /// Gets the current position in the stream - /// - /// Object has been disposed - public override long Position - { - get - { - this.CheckDisposed(); - return this.position; - } - set - { - this.CheckDisposed(); - if (value < 0) - { - throw new ArgumentOutOfRangeException("value", "value must be non-negative"); - } - - if (value > MaxStreamLength) - { - throw new ArgumentOutOfRangeException("value", "value cannot be more than " + MaxStreamLength); - } - - this.position = (int)value; - } - } - - /// - /// Whether the stream can currently read - /// - public override bool CanRead - { - get { return !this.disposed; } - } - - /// - /// Whether the stream can currently seek - /// - public override bool CanSeek - { - get { return !this.disposed; } - } - - /// - /// Always false - /// - public override bool CanTimeout - { - get { return false; } - } - - /// - /// Whether the stream can currently write - /// - public override bool CanWrite - { - get { return !this.disposed; } - } - - /// - /// Returns a single buffer containing the contents of the stream. - /// The buffer may be longer than the stream length. - /// - /// A byte[] buffer - /// IMPORTANT: Doing a Write() after calling GetBuffer() invalidates the buffer. The old buffer is held onto - /// until Dispose is called, but the next time GetBuffer() is called, a new buffer from the pool will be required. - /// Object has been disposed - public override byte[] GetBuffer() - { - this.CheckDisposed(); - - if (this.largeBuffer != null) - { - return this.largeBuffer; - } - - if (this.blocks.Count == 1) - { - return this.blocks[0]; - } - - // Buffer needs to reflect the capacity, not the length, because - // it's possible that people will manipulate the buffer directly - // and set the length afterward. Capacity sets the expectation - // for the size of the buffer. - var newBuffer = this.memoryManager.GetLargeBuffer(this.Capacity, this.tag); - - // InternalRead will check for existence of largeBuffer, so make sure we - // don't set it until after we've copied the data. - this.InternalRead(newBuffer, 0, this.length, 0); - this.largeBuffer = newBuffer; - - if (this.blocks.Count > 0 && this.memoryManager.AggressiveBufferReturn) - { - this.memoryManager.ReturnBlocks(this.blocks, this.tag); - this.blocks.Clear(); - } - - return this.largeBuffer; - } - - /// - /// Returns a new array with a copy of the buffer's contents. You should almost certainly be using GetBuffer combined with the Length to - /// access the bytes in this stream. Calling ToArray will destroy the benefits of pooled buffers, but it is included - /// for the sake of completeness. - /// - /// Object has been disposed - [Obsolete("This method has degraded performance vs. GetBuffer and should be avoided.")] - public override byte[] ToArray() - { - this.CheckDisposed(); - var newBuffer = new byte[this.Length]; - - this.InternalRead(newBuffer, 0, this.length, 0); - string stack = this.memoryManager.GenerateCallStacks ? Environment.StackTrace : null; - Events.Write.MemoryStreamToArray(this.id, this.tag, stack, 0); - this.memoryManager.ReportStreamToArray(); - - return newBuffer; - } - - /// - /// Reads from the current position into the provided buffer - /// - /// Destination buffer - /// Offset into buffer at which to start placing the read bytes. - /// Number of bytes to read. - /// The number of bytes read - /// buffer is null - /// offset or count is less than 0 - /// offset subtracted from the buffer length is less than count - /// Object has been disposed - public override int Read(byte[] buffer, int offset, int count) - { - return this.SafeRead(buffer, offset, count, ref this.position); - } - - /// - /// Reads from the specified position into the provided buffer - /// - /// Destination buffer - /// Offset into buffer at which to start placing the read bytes. - /// Number of bytes to read. - /// Position in the stream to start reading from - /// The number of bytes read - /// buffer is null - /// offset or count is less than 0 - /// offset subtracted from the buffer length is less than count - /// Object has been disposed - public int SafeRead(byte[] buffer, int offset, int count, ref int streamPosition) - { - this.CheckDisposed(); - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - - if (offset < 0) - { - throw new ArgumentOutOfRangeException("offset", "offset cannot be negative"); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException("count", "count cannot be negative"); - } - - if (offset + count > buffer.Length) - { - throw new ArgumentException("buffer length must be at least offset + count"); - } - - int amountRead = this.InternalRead(buffer, offset, count, streamPosition); - streamPosition += amountRead; - return amountRead; - } - - /// - /// Writes the buffer to the stream - /// - /// Source buffer - /// Start position - /// Number of bytes to write - /// buffer is null - /// offset or count is negative - /// buffer.Length - offset is not less than count - /// Object has been disposed - public override void Write(byte[] buffer, int offset, int count) - { - this.CheckDisposed(); - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - - if (offset < 0) - { - throw new ArgumentOutOfRangeException("offset", offset, "Offset must be in the range of 0 - buffer.Length-1"); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException("count", count, "count must be non-negative"); - } - - if (count + offset > buffer.Length) - { - throw new ArgumentException("count must be greater than buffer.Length - offset"); - } - - int blockSize = this.memoryManager.BlockSize; - long end = (long)this.position + count; - // Check for overflow - if (end > MaxStreamLength) - { - throw new IOException("Maximum capacity exceeded"); - } - - long requiredBuffers = (end + blockSize - 1) / blockSize; - - if (requiredBuffers * blockSize > MaxStreamLength) - { - throw new IOException("Maximum capacity exceeded"); - } - - this.EnsureCapacity((int)end); - - if (this.largeBuffer == null) - { - int bytesRemaining = count; - int bytesWritten = 0; - var blockAndOffset = this.GetBlockAndRelativeOffset(this.position); - - while (bytesRemaining > 0) - { - byte[] currentBlock = this.blocks[blockAndOffset.Block]; - int remainingInBlock = blockSize - blockAndOffset.Offset; - int amountToWriteInBlock = Math.Min(remainingInBlock, bytesRemaining); - - Buffer.BlockCopy(buffer, offset + bytesWritten, currentBlock, blockAndOffset.Offset, amountToWriteInBlock); - - bytesRemaining -= amountToWriteInBlock; - bytesWritten += amountToWriteInBlock; - - ++blockAndOffset.Block; - blockAndOffset.Offset = 0; - } - } - else - { - Buffer.BlockCopy(buffer, offset, this.largeBuffer, this.position, count); - } - this.position = (int)end; - this.length = Math.Max(this.position, this.length); - } - - /// - /// Returns a useful string for debugging. This should not normally be called in actual production code. - /// - public override string ToString() - { - return string.Format("Id = {0}, Tag = {1}, Length = {2:N0} bytes", this.Id, this.Tag, this.Length); - } - - /// - /// Writes a single byte to the current position in the stream. - /// - /// byte value to write - /// Object has been disposed - public override void WriteByte(byte value) - { - this.CheckDisposed(); - this.byteBuffer[0] = value; - this.Write(this.byteBuffer, 0, 1); - } - - /// - /// Reads a single byte from the current position in the stream. - /// - /// The byte at the current position, or -1 if the position is at the end of the stream. - /// Object has been disposed - public override int ReadByte() - { - return this.SafeReadByte(ref this.position); - } - - /// - /// Reads a single byte from the specified position in the stream. - /// - /// The position in the stream to read from - /// The byte at the current position, or -1 if the position is at the end of the stream. - /// Object has been disposed - public int SafeReadByte(ref int streamPosition) - { - this.CheckDisposed(); - if (streamPosition == this.length) - { - return -1; - } - byte value = 0; - if (this.largeBuffer == null) - { - var blockAndOffset = this.GetBlockAndRelativeOffset(streamPosition); - value = this.blocks[blockAndOffset.Block][blockAndOffset.Offset]; - } - else - { - value = this.largeBuffer[streamPosition]; - } - streamPosition++; - return value; - } - - /// - /// Sets the length of the stream - /// - /// value is negative or larger than MaxStreamLength - /// Object has been disposed - public override void SetLength(long value) - { - this.CheckDisposed(); - if (value < 0 || value > MaxStreamLength) - { - throw new ArgumentOutOfRangeException("value", "value must be non-negative and at most " + MaxStreamLength); - } - - this.EnsureCapacity((int)value); - - this.length = (int)value; - if (this.position > value) - { - this.position = (int)value; - } - } - - /// - /// Sets the position to the offset from the seek location - /// - /// How many bytes to move - /// From where - /// The new position - /// Object has been disposed - /// offset is larger than MaxStreamLength - /// Invalid seek origin - /// Attempt to set negative position - public override long Seek(long offset, SeekOrigin loc) - { - this.CheckDisposed(); - if (offset > MaxStreamLength) - { - throw new ArgumentOutOfRangeException("offset", "offset cannot be larger than " + MaxStreamLength); - } - - int newPosition; - switch (loc) - { - case SeekOrigin.Begin: - newPosition = (int)offset; - break; - case SeekOrigin.Current: - newPosition = (int)offset + this.position; - break; - case SeekOrigin.End: - newPosition = (int)offset + this.length; - break; - default: - throw new ArgumentException("Invalid seek origin", "loc"); - } - if (newPosition < 0) - { - throw new IOException("Seek before beginning"); - } - this.position = newPosition; - return this.position; - } - - /// - /// Synchronously writes this stream's bytes to the parameter stream. - /// - /// Destination stream - /// Important: This does a synchronous write, which may not be desired in some situations - public override void WriteTo(Stream stream) - { - this.CheckDisposed(); - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - if (this.largeBuffer == null) - { - int currentBlock = 0; - int bytesRemaining = this.length; - - while (bytesRemaining > 0) - { - int amountToCopy = Math.Min(this.blocks[currentBlock].Length, bytesRemaining); - stream.Write(this.blocks[currentBlock], 0, amountToCopy); - - bytesRemaining -= amountToCopy; - - ++currentBlock; - } - } - else - { - stream.Write(this.largeBuffer, 0, this.length); - } - } - #endregion - - #region Helper Methods - private void CheckDisposed() - { - if (this.disposed) - { - throw new ObjectDisposedException(string.Format("The stream with Id {0} and Tag {1} is disposed.", this.id, this.tag)); - } - } - - private int InternalRead(byte[] buffer, int offset, int count, int fromPosition) - { - if (this.length - fromPosition <= 0) - { - return 0; - } - if (this.largeBuffer == null) - { - var blockAndOffset = this.GetBlockAndRelativeOffset(fromPosition); - int bytesWritten = 0; - int bytesRemaining = Math.Min(count, this.length - fromPosition); - - while (bytesRemaining > 0) - { - int amountToCopy = Math.Min(this.blocks[blockAndOffset.Block].Length - blockAndOffset.Offset, bytesRemaining); - Buffer.BlockCopy(this.blocks[blockAndOffset.Block], blockAndOffset.Offset, buffer, bytesWritten + offset, amountToCopy); - - bytesWritten += amountToCopy; - bytesRemaining -= amountToCopy; - - ++blockAndOffset.Block; - blockAndOffset.Offset = 0; - } - return bytesWritten; - } - else - { - int amountToCopy = Math.Min(count, this.length - fromPosition); - Buffer.BlockCopy(this.largeBuffer, fromPosition, buffer, offset, amountToCopy); - return amountToCopy; - } - } - - private struct BlockAndOffset - { - public int Block; - public int Offset; - - public BlockAndOffset(int block, int offset) - { - this.Block = block; - this.Offset = offset; - } - } - - private BlockAndOffset GetBlockAndRelativeOffset(int offset) - { - var blockSize = this.memoryManager.BlockSize; - return new BlockAndOffset(offset / blockSize, offset % blockSize); - } - - private void EnsureCapacity(int newCapacity) - { - if (newCapacity > this.memoryManager.MaximumStreamCapacity && this.memoryManager.MaximumStreamCapacity > 0) - { - Events.Write.MemoryStreamOverCapacity(newCapacity, this.memoryManager.MaximumStreamCapacity, this.tag, this.allocationStack); - throw new InvalidOperationException("Requested capacity is too large: " + newCapacity + ". Limit is " + this.memoryManager.MaximumStreamCapacity); - } - - if (this.largeBuffer != null) - { - if (newCapacity > this.largeBuffer.Length) - { - var newBuffer = this.memoryManager.GetLargeBuffer(newCapacity, this.tag); - this.InternalRead(newBuffer, 0, this.length, 0); - this.ReleaseLargeBuffer(); - this.largeBuffer = newBuffer; - } - } - else - { - while (this.Capacity < newCapacity) - { - blocks.Add((this.memoryManager.GetBlock())); - } - } - } - - /// - /// Release the large buffer (either stores it for eventual release or returns it immediately). - /// - private void ReleaseLargeBuffer() - { - if (this.memoryManager.AggressiveBufferReturn) - { - this.memoryManager.ReturnLargeBuffer(this.largeBuffer, this.tag); - } - else - { - if (this.dirtyBuffers == null) - { - // We most likely will only ever need space for one - this.dirtyBuffers = new List(1); - } - this.dirtyBuffers.Add(this.largeBuffer); - } - - this.largeBuffer = null; - } - #endregion - } -} \ No newline at end of file diff --git a/Src/Workshell.PE/Microsoft/RecyclableMemoryStreamManager.cs b/Src/Workshell.PE/Microsoft/RecyclableMemoryStreamManager.cs deleted file mode 100644 index 8b5d20b..0000000 --- a/Src/Workshell.PE/Microsoft/RecyclableMemoryStreamManager.cs +++ /dev/null @@ -1,672 +0,0 @@ -// --------------------------------------------------------------------- -// Copyright (c) 2015 Microsoft -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// --------------------------------------------------------------------- - -namespace Microsoft.IO -{ - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.IO; - using System.Linq; - using System.Threading; - - /// - /// Manages pools of RecyclableMemoryStream objects. - /// - /// - /// There are two pools managed in here. The small pool contains same-sized buffers that are handed to streams - /// as they write more data. - /// - /// For scenarios that need to call GetBuffer(), the large pool contains buffers of various sizes, all - /// multiples of LargeBufferMultiple (1 MB by default). They are split by size to avoid overly-wasteful buffer - /// usage. There should be far fewer 8 MB buffers than 1 MB buffers, for example. - /// - internal sealed partial class RecyclableMemoryStreamManager - { - /// - /// Generic delegate for handling events without any arguments. - /// - public delegate void EventHandler(); - - /// - /// Delegate for handling large buffer discard reports. - /// - /// Reason the buffer was discarded. - public delegate void LargeBufferDiscardedEventHandler(Events.MemoryStreamDiscardReason reason); - - /// - /// Delegate for handling reports of stream size when streams are allocated - /// - /// Bytes allocated. - public delegate void StreamLengthReportHandler(long bytes); - - /// - /// Delegate for handling periodic reporting of memory use statistics. - /// - /// Bytes currently in use in the small pool. - /// Bytes currently free in the small pool. - /// Bytes currently in use in the large pool. - /// Bytes currently free in the large pool. - public delegate void UsageReportEventHandler( - long smallPoolInUseBytes, long smallPoolFreeBytes, long largePoolInUseBytes, long largePoolFreeBytes); - - public const int DefaultBlockSize = 128 * 1024; - public const int DefaultLargeBufferMultiple = 1024 * 1024; - public const int DefaultMaximumBufferSize = 128 * 1024 * 1024; - - private readonly int blockSize; - private readonly long[] largeBufferFreeSize; - private readonly long[] largeBufferInUseSize; - - private readonly int largeBufferMultiple; - - /// - /// pools[0] = 1x largeBufferMultiple buffers - /// pools[1] = 2x largeBufferMultiple buffers - /// etc., up to maximumBufferSize - /// - private readonly ConcurrentStack[] largePools; - - private readonly int maximumBufferSize; - private readonly ConcurrentStack smallPool; - - private long smallPoolFreeSize; - private long smallPoolInUseSize; - - /// - /// Initializes the memory manager with the default block/buffer specifications. - /// - public RecyclableMemoryStreamManager() - : this(DefaultBlockSize, DefaultLargeBufferMultiple, DefaultMaximumBufferSize) { } - - /// - /// Initializes the memory manager with the given block requiredSize. - /// - /// Size of each block that is pooled. Must be > 0. - /// Each large buffer will be a multiple of this value. - /// Buffers larger than this are not pooled - /// blockSize is not a positive number, or largeBufferMultiple is not a positive number, or maximumBufferSize is less than blockSize. - /// maximumBufferSize is not a multiple of largeBufferMultiple - public RecyclableMemoryStreamManager(int blockSize, int largeBufferMultiple, int maximumBufferSize) - { - if (blockSize <= 0) - { - throw new ArgumentOutOfRangeException("blockSize", blockSize, "blockSize must be a positive number"); - } - - if (largeBufferMultiple <= 0) - { - throw new ArgumentOutOfRangeException("largeBufferMultiple", - "largeBufferMultiple must be a positive number"); - } - - if (maximumBufferSize < blockSize) - { - throw new ArgumentOutOfRangeException("maximumBufferSize", - "maximumBufferSize must be at least blockSize"); - } - - this.blockSize = blockSize; - this.largeBufferMultiple = largeBufferMultiple; - this.maximumBufferSize = maximumBufferSize; - - if (!this.IsLargeBufferMultiple(maximumBufferSize)) - { - throw new ArgumentException("maximumBufferSize is not a multiple of largeBufferMultiple", - "maximumBufferSize"); - } - - this.smallPool = new ConcurrentStack(); - var numLargePools = maximumBufferSize / largeBufferMultiple; - - // +1 to store size of bytes in use that are too large to be pooled - this.largeBufferInUseSize = new long[numLargePools + 1]; - this.largeBufferFreeSize = new long[numLargePools]; - - this.largePools = new ConcurrentStack[numLargePools]; - - for (var i = 0; i < this.largePools.Length; ++i) - { - this.largePools[i] = new ConcurrentStack(); - } - - Events.Write.MemoryStreamManagerInitialized(blockSize, largeBufferMultiple, maximumBufferSize); - } - - /// - /// The size of each block. It must be set at creation and cannot be changed. - /// - public int BlockSize - { - get { return this.blockSize; } - } - - /// - /// All buffers are multiples of this number. It must be set at creation and cannot be changed. - /// - public int LargeBufferMultiple - { - get { return this.largeBufferMultiple; } - } - - /// - /// Gets or sets the maximum buffer size. - /// - /// Any buffer that is returned to the pool that is larger than this will be - /// discarded and garbage collected. - public int MaximumBufferSize - { - get { return this.maximumBufferSize; } - } - - /// - /// Number of bytes in small pool not currently in use - /// - public long SmallPoolFreeSize - { - get { return this.smallPoolFreeSize; } - } - - /// - /// Number of bytes currently in use by stream from the small pool - /// - public long SmallPoolInUseSize - { - get { return this.smallPoolInUseSize; } - } - - /// - /// Number of bytes in large pool not currently in use - /// - public long LargePoolFreeSize - { - get { return this.largeBufferFreeSize.Sum(); } - } - - /// - /// Number of bytes currently in use by streams from the large pool - /// - public long LargePoolInUseSize - { - get { return this.largeBufferInUseSize.Sum(); } - } - - /// - /// How many blocks are in the small pool - /// - public long SmallBlocksFree - { - get { return this.smallPool.Count; } - } - - /// - /// How many buffers are in the large pool - /// - public long LargeBuffersFree - { - get - { - long free = 0; - foreach (var pool in this.largePools) - { - free += pool.Count; - } - return free; - } - } - - /// - /// How many bytes of small free blocks to allow before we start dropping - /// those returned to us. - /// - public long MaximumFreeSmallPoolBytes { get; set; } - - /// - /// How many bytes of large free buffers to allow before we start dropping - /// those returned to us. - /// - public long MaximumFreeLargePoolBytes { get; set; } - - /// - /// Maximum stream capacity in bytes. Attempts to set a larger capacity will - /// result in an exception. - /// - /// A value of 0 indicates no limit. - public long MaximumStreamCapacity { get; set; } - - /// - /// Whether to save callstacks for stream allocations. This can help in debugging. - /// It should NEVER be turned on generally in production. - /// - public bool GenerateCallStacks { get; set; } - - /// - /// Whether dirty buffers can be immediately returned to the buffer pool. E.g. when GetBuffer() is called on - /// a stream and creates a single large buffer, if this setting is enabled, the other blocks will be returned - /// to the buffer pool immediately. - /// Note when enabling this setting that the user is responsible for ensuring that any buffer previously - /// retrieved from a stream which is subsequently modified is not used after modification (as it may no longer - /// be valid). - /// - public bool AggressiveBufferReturn { get; set; } - - /// - /// Removes and returns a single block from the pool. - /// - /// A byte[] array - internal byte[] GetBlock() - { - byte[] block; - if (!this.smallPool.TryPop(out block)) - { - // We'll add this back to the pool when the stream is disposed - // (unless our free pool is too large) - block = new byte[this.BlockSize]; - Events.Write.MemoryStreamNewBlockCreated(this.smallPoolInUseSize); - ReportBlockCreated(); - } - else - { - Interlocked.Add(ref this.smallPoolFreeSize, -this.BlockSize); - } - - Interlocked.Add(ref this.smallPoolInUseSize, this.BlockSize); - return block; - } - - /// - /// Returns a buffer of arbitrary size from the large buffer pool. This buffer - /// will be at least the requiredSize and always be a multiple of largeBufferMultiple. - /// - /// The minimum length of the buffer - /// The tag of the stream returning this buffer, for logging if necessary. - /// A buffer of at least the required size. - internal byte[] GetLargeBuffer(int requiredSize, string tag) - { - requiredSize = this.RoundToLargeBufferMultiple(requiredSize); - - var poolIndex = requiredSize / this.largeBufferMultiple - 1; - - byte[] buffer; - if (poolIndex < this.largePools.Length) - { - if (!this.largePools[poolIndex].TryPop(out buffer)) - { - buffer = new byte[requiredSize]; - - Events.Write.MemoryStreamNewLargeBufferCreated(requiredSize, this.LargePoolInUseSize); - ReportLargeBufferCreated(); - } - else - { - Interlocked.Add(ref this.largeBufferFreeSize[poolIndex], -buffer.Length); - } - } - else - { - // Buffer is too large to pool. They get a new buffer. - - // We still want to track the size, though, and we've reserved a slot - // in the end of the inuse array for nonpooled bytes in use. - poolIndex = this.largeBufferInUseSize.Length - 1; - - // We still want to round up to reduce heap fragmentation. - buffer = new byte[requiredSize]; - string callStack = null; - if (this.GenerateCallStacks) - { - // Grab the stack -- we want to know who requires such large buffers - callStack = Environment.StackTrace; - } - Events.Write.MemoryStreamNonPooledLargeBufferCreated(requiredSize, tag, callStack); - ReportLargeBufferCreated(); - } - - Interlocked.Add(ref this.largeBufferInUseSize[poolIndex], buffer.Length); - - return buffer; - } - - private int RoundToLargeBufferMultiple(int requiredSize) - { - return ((requiredSize + this.LargeBufferMultiple - 1) / this.LargeBufferMultiple) * this.LargeBufferMultiple; - } - - private bool IsLargeBufferMultiple(int value) - { - return (value != 0) && (value % this.LargeBufferMultiple) == 0; - } - - /// - /// Returns the buffer to the large pool - /// - /// The buffer to return. - /// The tag of the stream returning this buffer, for logging if necessary. - /// buffer is null - /// buffer.Length is not a multiple of LargeBufferMultiple (it did not originate from this pool) - internal void ReturnLargeBuffer(byte[] buffer, string tag) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - - if (!this.IsLargeBufferMultiple(buffer.Length)) - { - throw new ArgumentException( - "buffer did not originate from this memory manager. The size is not a multiple of " + - this.LargeBufferMultiple); - } - - var poolIndex = buffer.Length / this.largeBufferMultiple - 1; - - if (poolIndex < this.largePools.Length) - { - if ((this.largePools[poolIndex].Count + 1) * buffer.Length <= this.MaximumFreeLargePoolBytes || - this.MaximumFreeLargePoolBytes == 0) - { - this.largePools[poolIndex].Push(buffer); - Interlocked.Add(ref this.largeBufferFreeSize[poolIndex], buffer.Length); - } - else - { - Events.Write.MemoryStreamDiscardBuffer(Events.MemoryStreamBufferType.Large, tag, - Events.MemoryStreamDiscardReason.EnoughFree); - ReportLargeBufferDiscarded(Events.MemoryStreamDiscardReason.EnoughFree); - } - } - else - { - // This is a non-poolable buffer, but we still want to track its size for inuse - // analysis. We have space in the inuse array for this. - poolIndex = this.largeBufferInUseSize.Length - 1; - - Events.Write.MemoryStreamDiscardBuffer(Events.MemoryStreamBufferType.Large, tag, - Events.MemoryStreamDiscardReason.TooLarge); - ReportLargeBufferDiscarded(Events.MemoryStreamDiscardReason.TooLarge); - } - - Interlocked.Add(ref this.largeBufferInUseSize[poolIndex], -buffer.Length); - - ReportUsageReport(this.smallPoolInUseSize, this.smallPoolFreeSize, this.LargePoolInUseSize, - this.LargePoolFreeSize); - } - - /// - /// Returns the blocks to the pool - /// - /// Collection of blocks to return to the pool - /// The tag of the stream returning these blocks, for logging if necessary. - /// blocks is null - /// blocks contains buffers that are the wrong size (or null) for this memory manager - internal void ReturnBlocks(ICollection blocks, string tag) - { - if (blocks == null) - { - throw new ArgumentNullException("blocks"); - } - - var bytesToReturn = blocks.Count * this.BlockSize; - Interlocked.Add(ref this.smallPoolInUseSize, -bytesToReturn); - - foreach (var block in blocks) - { - if (block == null || block.Length != this.BlockSize) - { - throw new ArgumentException("blocks contains buffers that are not BlockSize in length"); - } - } - - foreach (var block in blocks) - { - if (this.MaximumFreeSmallPoolBytes == 0 || this.SmallPoolFreeSize < this.MaximumFreeSmallPoolBytes) - { - Interlocked.Add(ref this.smallPoolFreeSize, this.BlockSize); - this.smallPool.Push(block); - } - else - { - Events.Write.MemoryStreamDiscardBuffer(Events.MemoryStreamBufferType.Small, tag, - Events.MemoryStreamDiscardReason.EnoughFree); - ReportBlockDiscarded(); - break; - } - } - - ReportUsageReport(this.smallPoolInUseSize, this.smallPoolFreeSize, this.LargePoolInUseSize, - this.LargePoolFreeSize); - } - - internal void ReportBlockCreated() - { - var blockCreated = Interlocked.CompareExchange(ref this.BlockCreated, null, null); - if (blockCreated != null) - { - blockCreated(); - } - } - - internal void ReportBlockDiscarded() - { - var blockDiscarded = Interlocked.CompareExchange(ref this.BlockDiscarded, null, null); - if (blockDiscarded != null) - { - blockDiscarded(); - } - } - - internal void ReportLargeBufferCreated() - { - var largeBufferCreated = Interlocked.CompareExchange(ref this.LargeBufferCreated, null, null); - if (largeBufferCreated != null) - { - largeBufferCreated(); - } - } - - internal void ReportLargeBufferDiscarded(Events.MemoryStreamDiscardReason reason) - { - var largeBufferDiscarded = Interlocked.CompareExchange(ref this.LargeBufferDiscarded, null, null); - if (largeBufferDiscarded != null) - { - largeBufferDiscarded(reason); - } - } - - internal void ReportStreamCreated() - { - var streamCreated = Interlocked.CompareExchange(ref this.StreamCreated, null, null); - if (streamCreated != null) - { - streamCreated(); - } - } - - internal void ReportStreamDisposed() - { - var streamDisposed = Interlocked.CompareExchange(ref this.StreamDisposed, null, null); - if (streamDisposed != null) - { - streamDisposed(); - } - } - - internal void ReportStreamFinalized() - { - var streamFinalized = Interlocked.CompareExchange(ref this.StreamFinalized, null, null); - if (streamFinalized != null) - { - streamFinalized(); - } - } - - internal void ReportStreamLength(long bytes) - { - var streamLength = Interlocked.CompareExchange(ref this.StreamLength, null, null); - if (streamLength != null) - { - streamLength(bytes); - } - } - - internal void ReportStreamToArray() - { - var streamConvertedToArray = Interlocked.CompareExchange(ref this.StreamConvertedToArray, null, null); - if (streamConvertedToArray != null) - { - streamConvertedToArray(); - } - } - - internal void ReportUsageReport( - long smallPoolInUseBytes, long smallPoolFreeBytes, long largePoolInUseBytes, long largePoolFreeBytes) - { - var usageReport = Interlocked.CompareExchange(ref this.UsageReport, null, null); - if (usageReport != null) - { - usageReport(smallPoolInUseBytes, smallPoolFreeBytes, largePoolInUseBytes, largePoolFreeBytes); - } - } - - /// - /// Retrieve a new MemoryStream object with no tag and a default initial capacity. - /// - /// A MemoryStream. - public MemoryStream GetStream() - { - return new RecyclableMemoryStream(this); - } - - /// - /// Retrieve a new MemoryStream object with the given tag and a default initial capacity. - /// - /// A tag which can be used to track the source of the stream. - /// A MemoryStream. - public MemoryStream GetStream(string tag) - { - return new RecyclableMemoryStream(this, tag); - } - - /// - /// Retrieve a new MemoryStream object with the given tag and at least the given capacity. - /// - /// A tag which can be used to track the source of the stream. - /// The minimum desired capacity for the stream. - /// A MemoryStream. - public MemoryStream GetStream(string tag, int requiredSize) - { - return new RecyclableMemoryStream(this, tag, requiredSize); - } - - /// - /// Retrieve a new MemoryStream object with the given tag and at least the given capacity, possibly using - /// a single continugous underlying buffer. - /// - /// Retrieving a MemoryStream which provides a single contiguous buffer can be useful in situations - /// where the initial size is known and it is desirable to avoid copying data between the smaller underlying - /// buffers to a single large one. This is most helpful when you know that you will always call GetBuffer - /// on the underlying stream. - /// A tag which can be used to track the source of the stream. - /// The minimum desired capacity for the stream. - /// Whether to attempt to use a single contiguous buffer. - /// A MemoryStream. - public MemoryStream GetStream(string tag, int requiredSize, bool asContiguousBuffer) - { - if (!asContiguousBuffer || requiredSize <= this.BlockSize) - { - return this.GetStream(tag, requiredSize); - } - - return new RecyclableMemoryStream(this, tag, requiredSize, this.GetLargeBuffer(requiredSize, tag)); - } - - /// - /// Retrieve a new MemoryStream object with the given tag and with contents copied from the provided - /// buffer. The provided buffer is not wrapped or used after construction. - /// - /// The new stream's position is set to the beginning of the stream when returned. - /// A tag which can be used to track the source of the stream. - /// The byte buffer to copy data from. - /// The offset from the start of the buffer to copy from. - /// The number of bytes to copy from the buffer. - /// A MemoryStream. - [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] - public MemoryStream GetStream(string tag, byte[] buffer, int offset, int count) - { - var stream = new RecyclableMemoryStream(this, tag, count); - stream.Write(buffer, offset, count); - stream.Position = 0; - return stream; - } - - /// - /// Triggered when a new block is created. - /// - public event EventHandler BlockCreated; - - /// - /// Triggered when a new block is created. - /// - public event EventHandler BlockDiscarded; - - /// - /// Triggered when a new large buffer is created. - /// - public event EventHandler LargeBufferCreated; - - /// - /// Triggered when a new stream is created. - /// - public event EventHandler StreamCreated; - - /// - /// Triggered when a stream is disposed. - /// - public event EventHandler StreamDisposed; - - /// - /// Triggered when a stream is finalized. - /// - public event EventHandler StreamFinalized; - - /// - /// Triggered when a stream is finalized. - /// - public event StreamLengthReportHandler StreamLength; - - /// - /// Triggered when a user converts a stream to array. - /// - public event EventHandler StreamConvertedToArray; - - /// - /// Triggered when a large buffer is discarded, along with the reason for the discard. - /// - public event LargeBufferDiscardedEventHandler LargeBufferDiscarded; - - /// - /// Periodically triggered to report usage statistics. - /// - public event UsageReportEventHandler UsageReport; - } -} \ No newline at end of file diff --git a/Src/Workshell.PE/Workshell.PE.csproj b/Src/Workshell.PE/Workshell.PE.csproj index 6f2a20e..8a771cb 100644 --- a/Src/Workshell.PE/Workshell.PE.csproj +++ b/Src/Workshell.PE/Workshell.PE.csproj @@ -88,9 +88,7 @@ - - - +