Skip to content

Commit cd55ed2

Browse files
authored
Merge pull request #1347 from adamhathcock/adam/deflate-allocations
Add pooling for arrays for deflate
2 parents 27cc668 + b4b13b7 commit cd55ed2

5 files changed

Lines changed: 85 additions & 28 deletions

File tree

src/SharpCompress/Compressors/Deflate/CRC32.cs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
// ------------------------------------------------------------------
3434

3535
using System;
36+
using System.Buffers;
3637
using System.IO;
3738

3839
namespace SharpCompress.Compressors.Deflate;
@@ -120,22 +121,29 @@ public uint GetCrc32AndCopy(Stream input, Stream? output)
120121
{
121122
//UInt32 crc32Result;
122123
//crc32Result = 0xFFFFFFFF;
123-
var buffer = new byte[BUFFER_SIZE];
124-
var readSize = BUFFER_SIZE;
125-
126-
TotalBytesRead = 0;
127-
var count = input.Read(buffer, 0, readSize);
128-
output?.Write(buffer, 0, count);
129-
TotalBytesRead += count;
130-
while (count > 0)
124+
var buffer = ArrayPool<byte>.Shared.Rent(BUFFER_SIZE);
125+
try
131126
{
132-
SlurpBlock(buffer, 0, count);
133-
count = input.Read(buffer, 0, readSize);
127+
var readSize = BUFFER_SIZE;
128+
129+
TotalBytesRead = 0;
130+
var count = input.Read(buffer, 0, readSize);
134131
output?.Write(buffer, 0, count);
135132
TotalBytesRead += count;
136-
}
133+
while (count > 0)
134+
{
135+
SlurpBlock(buffer, 0, count);
136+
count = input.Read(buffer, 0, readSize);
137+
output?.Write(buffer, 0, count);
138+
TotalBytesRead += count;
139+
}
137140

138-
return ~runningCrc32Result;
141+
return ~runningCrc32Result;
142+
}
143+
finally
144+
{
145+
ArrayPool<byte>.Shared.Return(buffer, clearArray: true);
146+
}
139147
}
140148
}
141149

src/SharpCompress/Compressors/Deflate/DeflateManager.cs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
// -----------------------------------------------------------------------
7070

7171
using System;
72+
using System.Buffers;
7273
using SharpCompress.Algorithms;
7374
using SharpCompress.Common;
7475

@@ -1726,9 +1727,12 @@ CompressionStrategy strategy
17261727
hash_mask = hash_size - 1;
17271728
hash_shift = ((hash_bits + MIN_MATCH - 1) / MIN_MATCH);
17281729

1729-
window = new byte[w_size * 2];
1730-
prev = new short[w_size];
1731-
head = new short[hash_size];
1730+
window = ArrayPool<byte>.Shared.Rent(w_size * 2);
1731+
prev = ArrayPool<short>.Shared.Rent(w_size);
1732+
head = ArrayPool<short>.Shared.Rent(hash_size);
1733+
Array.Clear(window, 0, w_size * 2);
1734+
Array.Clear(prev, 0, w_size);
1735+
Array.Clear(head, 0, hash_size);
17321736

17331737
// for memLevel==8, this will be 16384, 16k
17341738
lit_bufsize = 1 << (memLevel + 6);
@@ -1737,7 +1741,8 @@ CompressionStrategy strategy
17371741
// the output distance codes, and the output length codes (aka tree).
17381742
// orig comment: This works just fine since the average
17391743
// output size for (length,distance) codes is <= 24 bits.
1740-
pending = new byte[lit_bufsize * 4];
1744+
pending = ArrayPool<byte>.Shared.Rent(lit_bufsize * 4);
1745+
Array.Clear(pending, 0, lit_bufsize * 4);
17411746
_distanceOffset = lit_bufsize;
17421747
_lengthOffset = (1 + 2) * lit_bufsize;
17431748

@@ -1776,20 +1781,46 @@ internal void Reset()
17761781

17771782
internal int End()
17781783
{
1784+
var result = ZlibConstants.Z_OK;
17791785
if (status != INIT_STATE && status != BUSY_STATE && status != FINISH_STATE)
17801786
{
1781-
return ZlibConstants.Z_STREAM_ERROR;
1787+
result = ZlibConstants.Z_STREAM_ERROR;
1788+
}
1789+
else if (status == BUSY_STATE)
1790+
{
1791+
result = ZlibConstants.Z_DATA_ERROR;
17821792
}
17831793

17841794
// Deallocate in reverse order of allocations:
1785-
pending = null;
1786-
head = null;
1787-
prev = null;
1788-
window = null;
1795+
ReturnBuffers();
17891796

17901797
// free
17911798
// dstate=null;
1792-
return status == BUSY_STATE ? ZlibConstants.Z_DATA_ERROR : ZlibConstants.Z_OK;
1799+
return result;
1800+
}
1801+
1802+
private void ReturnBuffers()
1803+
{
1804+
if (pending is not null)
1805+
{
1806+
ArrayPool<byte>.Shared.Return(pending, clearArray: true);
1807+
pending = null;
1808+
}
1809+
if (head is not null)
1810+
{
1811+
ArrayPool<short>.Shared.Return(head, clearArray: true);
1812+
head = null;
1813+
}
1814+
if (prev is not null)
1815+
{
1816+
ArrayPool<short>.Shared.Return(prev, clearArray: true);
1817+
prev = null;
1818+
}
1819+
if (window is not null)
1820+
{
1821+
ArrayPool<byte>.Shared.Return(window, clearArray: true);
1822+
window = null;
1823+
}
17931824
}
17941825

17951826
private void SetDeflater() =>

src/SharpCompress/Compressors/Deflate/Inflate.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ internal sealed class InflateBlocks
124124
internal InflateBlocks(ZlibCodec codec, object checkfn, int w)
125125
{
126126
_codec = codec;
127-
hufts = new int[MANY * 3];
127+
hufts = ArrayPool<int>.Shared.Rent(MANY * 3);
128+
Array.Clear(hufts, 0, MANY * 3);
128129
window = MemoryPool<byte>.Shared.Rent(w);
129130
end = w;
130131
this.checkfn = checkfn;
@@ -719,7 +720,11 @@ internal void Free()
719720
Reset();
720721
window?.Dispose();
721722
window = null;
722-
hufts = null;
723+
if (hufts is not null)
724+
{
725+
ArrayPool<int>.Shared.Return(hufts, clearArray: true);
726+
hufts = null;
727+
}
723728
}
724729

725730
internal void SetDictionary(byte[] d, int start, int n)

src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
// ------------------------------------------------------------------
2828

2929
using System;
30+
using System.Buffers;
3031
using System.Buffers.Binary;
3132
using System.Collections.Generic;
3233
using System.IO;
@@ -136,7 +137,7 @@ private ZlibCodec z
136137
}
137138
}
138139

139-
private byte[] workingBuffer => _workingBuffer ??= new byte[_bufferSize];
140+
private byte[] workingBuffer => _workingBuffer ??= ArrayPool<byte>.Shared.Rent(_bufferSize);
140141

141142
public override void Write(byte[] buffer, int offset, int count)
142143
{
@@ -541,6 +542,7 @@ protected override void Dispose(bool disposing)
541542
finally
542543
{
543544
end();
545+
ReturnWorkingBuffer();
544546
if (!_leaveOpen)
545547
{
546548
_stream?.Dispose();
@@ -575,6 +577,7 @@ public async ValueTask DisposeAsync()
575577
finally
576578
{
577579
end();
580+
ReturnWorkingBuffer();
578581
if (_stream != null)
579582
{
580583
if (!_leaveOpen)
@@ -593,6 +596,17 @@ public async ValueTask DisposeAsync()
593596
}
594597
}
595598

599+
private void ReturnWorkingBuffer()
600+
{
601+
if (_workingBuffer is null)
602+
{
603+
return;
604+
}
605+
606+
ArrayPool<byte>.Shared.Return(_workingBuffer, clearArray: true);
607+
_workingBuffer = null;
608+
}
609+
596610
public override void Flush()
597611
{
598612
// Only flush the underlying stream when in write mode

src/SharpCompress/Compressors/Deflate/ZlibCodec.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -612,10 +612,9 @@ public int EndDeflate()
612612
throw new ZlibException("No Deflate State!");
613613
}
614614

615-
// TODO: dinoch Tue, 03 Nov 2009 15:39 (test this)
616-
//int ret = dstate.End();
615+
_ = dstate.End();
617616
dstate = null;
618-
return ZlibConstants.Z_OK; //ret;
617+
return ZlibConstants.Z_OK;
619618
}
620619

621620
/// <summary>

0 commit comments

Comments
 (0)