From 46437bbbbc3f8ed8e3af663e8a10c980404103bc Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Tue, 13 Oct 2026 10:21:45 +0100 Subject: [PATCH] Return pooled memory stream blocks when construction fails --- src/SharpCompress/IO/PooledMemoryStream.cs | 22 ++++++++++++++-- .../Streams/PooledMemoryStreamTests.cs | 25 +++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/SharpCompress/IO/PooledMemoryStream.cs b/src/SharpCompress/IO/PooledMemoryStream.cs index 3116386f..58633a7c 100644 --- a/src/SharpCompress/IO/PooledMemoryStream.cs +++ b/src/SharpCompress/IO/PooledMemoryStream.cs @@ -54,7 +54,15 @@ public sealed class PooledMemoryStream : MemoryStream _length = 0; _capacity = capacity; - EnsureSegmentedAllocated(capacity); + try + { + EnsureSegmentedAllocated(capacity); + } + catch + { + ReturnPooledBuffers(); + throw; + } } public override bool CanRead => _isOpen; @@ -565,7 +573,17 @@ public sealed class PooledMemoryStream : MemoryStream while (_blocks.Count < requiredBlocks) { - _blocks.Add(_arrayPool.Rent(_blockSize)); + var block = _arrayPool.Rent(_blockSize); + try + { + _blocks.Add(block); + } + catch + { + // A failed list expansion must not lose the rental either. + _arrayPool.Return(block); + throw; + } } while (_blocks.Count > requiredBlocks) diff --git a/tests/SharpCompress.Test/Streams/PooledMemoryStreamTests.cs b/tests/SharpCompress.Test/Streams/PooledMemoryStreamTests.cs index d2b0996c..0f318947 100644 --- a/tests/SharpCompress.Test/Streams/PooledMemoryStreamTests.cs +++ b/tests/SharpCompress.Test/Streams/PooledMemoryStreamTests.cs @@ -8,6 +8,31 @@ namespace SharpCompress.Test.Streams; public class PooledMemoryStreamTests { + [Fact] + public void FailedConstructorReturnsPreviouslyRentedBlocks() + { + var pool = new FailingArrayPool(); + Assert.Throws(() => new PooledMemoryStream(16, 8, pool)); + Assert.Equal(1, pool.ReturnCount); + } + + private sealed class FailingArrayPool : ArrayPool + { + private int rentCount; + public int ReturnCount { get; private set; } + + public override byte[] Rent(int minimumLength) + { + if (++rentCount == 2) + { + throw new IOException("Injected rental failure"); + } + return new byte[minimumLength]; + } + + public override void Return(byte[] array, bool clearArray = false) => ReturnCount++; + } + [Fact] public void GrowsUsingFixedSizeBlocks() {