You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
106 lines
3.5 KiB
106 lines
3.5 KiB
8 months ago
|
using BestHTTP.Extensions;
|
||
|
using BestHTTP.PlatformSupport.Memory;
|
||
|
using System;
|
||
|
|
||
|
namespace BestHTTP.Decompression
|
||
|
{
|
||
|
public struct DecompressedData
|
||
|
{
|
||
|
public readonly byte[] Data;
|
||
|
public readonly int Length;
|
||
|
|
||
|
internal DecompressedData(byte[] data, int length)
|
||
|
{
|
||
|
this.Data = data;
|
||
|
this.Length = length;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public sealed class GZipDecompressor : IDisposable
|
||
|
{
|
||
|
private BufferPoolMemoryStream decompressorInputStream;
|
||
|
private BufferPoolMemoryStream decompressorOutputStream;
|
||
|
private Zlib.GZipStream decompressorGZipStream;
|
||
|
|
||
|
private int MinLengthToDecompress = 256;
|
||
|
|
||
|
public GZipDecompressor(int minLengthToDecompress)
|
||
|
{
|
||
|
this.MinLengthToDecompress = minLengthToDecompress;
|
||
|
}
|
||
|
|
||
|
private void CloseDecompressors()
|
||
|
{
|
||
|
if (decompressorGZipStream != null)
|
||
|
decompressorGZipStream.Dispose();
|
||
|
decompressorGZipStream = null;
|
||
|
|
||
|
if (decompressorInputStream != null)
|
||
|
decompressorInputStream.Dispose();
|
||
|
decompressorInputStream = null;
|
||
|
|
||
|
if (decompressorOutputStream != null)
|
||
|
decompressorOutputStream.Dispose();
|
||
|
decompressorOutputStream = null;
|
||
|
}
|
||
|
|
||
|
public DecompressedData Decompress(byte[] data, int offset, int count, bool forceDecompress = false, bool dataCanBeLarger = false)
|
||
|
{
|
||
|
if (decompressorInputStream == null)
|
||
|
decompressorInputStream = new BufferPoolMemoryStream(count);
|
||
|
|
||
|
if (data != null)
|
||
|
decompressorInputStream.Write(data, offset, count);
|
||
|
|
||
|
if (!forceDecompress && decompressorInputStream.Length < MinLengthToDecompress)
|
||
|
return new DecompressedData(null, 0);
|
||
|
|
||
|
decompressorInputStream.Position = 0;
|
||
|
|
||
|
if (decompressorGZipStream == null)
|
||
|
{
|
||
|
decompressorGZipStream = new Zlib.GZipStream(decompressorInputStream,
|
||
|
Zlib.CompressionMode.Decompress,
|
||
|
Zlib.CompressionLevel.Default,
|
||
|
true);
|
||
|
decompressorGZipStream.FlushMode = Zlib.FlushType.Sync;
|
||
|
}
|
||
|
|
||
|
if (decompressorOutputStream == null)
|
||
|
decompressorOutputStream = new BufferPoolMemoryStream();
|
||
|
decompressorOutputStream.SetLength(0);
|
||
|
|
||
|
byte[] copyBuffer = BufferPool.Get(1024, true);
|
||
|
|
||
|
int readCount;
|
||
|
int sumReadCount = 0;
|
||
|
while ((readCount = decompressorGZipStream.Read(copyBuffer, 0, copyBuffer.Length)) != 0)
|
||
|
{
|
||
|
decompressorOutputStream.Write(copyBuffer, 0, readCount);
|
||
|
sumReadCount += readCount;
|
||
|
}
|
||
|
|
||
|
BufferPool.Release(copyBuffer);
|
||
|
|
||
|
// If no read is done (returned with any data) don't zero out the input stream, as it would delete any not yet used data.
|
||
|
if (sumReadCount > 0)
|
||
|
decompressorGZipStream.SetLength(0);
|
||
|
|
||
|
byte[] result = decompressorOutputStream.ToArray(dataCanBeLarger);
|
||
|
|
||
|
return new DecompressedData(result, dataCanBeLarger ? (int)decompressorOutputStream.Length : result.Length);
|
||
|
}
|
||
|
|
||
|
~GZipDecompressor()
|
||
|
{
|
||
|
Dispose();
|
||
|
}
|
||
|
|
||
|
public void Dispose()
|
||
|
{
|
||
|
CloseDecompressors();
|
||
|
GC.SuppressFinalize(this);
|
||
|
}
|
||
|
}
|
||
|
}
|