You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@@ -0,0 +1,280 @@
|
||||
///----------- ----------- ----------- ----------- ----------- ----------- -----------
|
||||
/// <copyright file="DeflaterZLib.cs" company="Microsoft">
|
||||
/// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
/// </copyright>
|
||||
///
|
||||
/// <owner>gpaperin</owner>
|
||||
///----------- ----------- ----------- ----------- ----------- ----------- -----------
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
using ZErrorCode = System.IO.Compression.ZLibNative.ErrorCode;
|
||||
using ZFlushCode = System.IO.Compression.ZLibNative.FlushCode;
|
||||
|
||||
|
||||
namespace System.IO.Compression {
|
||||
|
||||
#if FEATURE_NETCORE
|
||||
[SecuritySafeCritical]
|
||||
#endif
|
||||
internal class DeflaterZLib : IDeflater {
|
||||
|
||||
private ZLibNative.ZLibStreamHandle _zlibStream;
|
||||
private GCHandle? _inputBufferHandle;
|
||||
private bool _isDisposed;
|
||||
|
||||
// Note, DeflateStream or the deflater do not try to be thread safe.
|
||||
// The lock is just used to make writing to unmanaged structures atomic to make sure
|
||||
// that they do not get inconsistent fields that may lead to an unmanaged memory violation.
|
||||
// To prevent *managed* buffer corruption or other weird behaviour users need to synchronise
|
||||
// on the stream explicitly.
|
||||
private readonly Object syncLock = new Object();
|
||||
|
||||
#region exposed members
|
||||
|
||||
internal DeflaterZLib()
|
||||
|
||||
: this(CompressionLevel.Optimal) {
|
||||
}
|
||||
|
||||
internal DeflaterZLib(CompressionLevel compressionLevel) {
|
||||
|
||||
ZLibNative.CompressionLevel zlibCompressionLevel;
|
||||
int windowBits;
|
||||
int memLevel;
|
||||
ZLibNative.CompressionStrategy strategy;
|
||||
|
||||
switch (compressionLevel) {
|
||||
|
||||
// Note that ZLib currently exactly correspond to the optimal values.
|
||||
// However, we have determined the optimal values by intependent measurements across
|
||||
// a range of all possible ZLib parameters and over a set of different data.
|
||||
// We stress that by using explicitly the values obtained by the measurements rather than
|
||||
// ZLib defaults even if they happened to be the same.
|
||||
// For ZLib 1.2.3 we have (copied from ZLibNative.cs):
|
||||
// ZLibNative.CompressionLevel.DefaultCompression = 6;
|
||||
// ZLibNative.Deflate_DefaultWindowBits = -15;
|
||||
// ZLibNative.Deflate_DefaultMemLevel = 8;
|
||||
|
||||
|
||||
case CompressionLevel.Optimal:
|
||||
zlibCompressionLevel = (ZLibNative.CompressionLevel) 6;
|
||||
windowBits = -15;
|
||||
memLevel = 8;
|
||||
strategy = ZLibNative.CompressionStrategy.DefaultStrategy;
|
||||
break;
|
||||
|
||||
case CompressionLevel.Fastest:
|
||||
zlibCompressionLevel = (ZLibNative.CompressionLevel) 1;
|
||||
windowBits = -15;
|
||||
memLevel = 8;
|
||||
strategy = ZLibNative.CompressionStrategy.DefaultStrategy;
|
||||
break;
|
||||
|
||||
case CompressionLevel.NoCompression:
|
||||
zlibCompressionLevel = (ZLibNative.CompressionLevel) 0;
|
||||
windowBits = -15;
|
||||
memLevel = 7;
|
||||
strategy = ZLibNative.CompressionStrategy.DefaultStrategy;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("compressionLevel");
|
||||
}
|
||||
|
||||
_isDisposed = false;
|
||||
DeflateInit(zlibCompressionLevel, windowBits, memLevel, strategy);
|
||||
}
|
||||
|
||||
void IDisposable.Dispose() {
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
protected virtual void Dispose(bool disposing) {
|
||||
|
||||
if (disposing && !_isDisposed) {
|
||||
|
||||
if (_inputBufferHandle.HasValue)
|
||||
DeallocateInputBufferHandle();
|
||||
|
||||
_zlibStream.Dispose();
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool NeedsInput() {
|
||||
// Convenience method to call NeedsInput privately without a cast.
|
||||
return ((IDeflater) this).NeedsInput();
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
bool IDeflater.NeedsInput() {
|
||||
return 0 == _zlibStream.AvailIn;
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
void IDeflater.SetInput(byte[] inputBuffer, int startIndex, int count) {
|
||||
|
||||
Contract.Assert(NeedsInput(), "We have something left in previous input!");
|
||||
Contract.Assert(null != inputBuffer);
|
||||
Contract.Assert(startIndex >= 0 && count >= 0 && count + startIndex <= inputBuffer.Length);
|
||||
Contract.Assert(!_inputBufferHandle.HasValue);
|
||||
|
||||
if (0 == count)
|
||||
return;
|
||||
|
||||
lock (syncLock) {
|
||||
|
||||
_inputBufferHandle = GCHandle.Alloc(inputBuffer, GCHandleType.Pinned);
|
||||
|
||||
_zlibStream.NextIn = _inputBufferHandle.Value.AddrOfPinnedObject() + startIndex;
|
||||
_zlibStream.AvailIn = (uint) count;
|
||||
}
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
int IDeflater.GetDeflateOutput(byte[] outputBuffer) {
|
||||
|
||||
Contract.Ensures(Contract.Result<int>() >= 0 && Contract.Result<int>() <= outputBuffer.Length);
|
||||
|
||||
Contract.Assert(null != outputBuffer, "Can't pass in a null output buffer!");
|
||||
Contract.Assert(!NeedsInput(), "GetDeflateOutput should only be called after providing input");
|
||||
Contract.Assert(_inputBufferHandle.HasValue);
|
||||
Contract.Assert(_inputBufferHandle.Value.IsAllocated);
|
||||
|
||||
try {
|
||||
int bytesRead;
|
||||
ReadDeflateOutput(outputBuffer, ZFlushCode.NoFlush, out bytesRead);
|
||||
return bytesRead;
|
||||
|
||||
} finally {
|
||||
// Before returning, make sure to release input buffer if necesary:
|
||||
if (0 == _zlibStream.AvailIn && _inputBufferHandle.HasValue)
|
||||
DeallocateInputBufferHandle();
|
||||
}
|
||||
}
|
||||
|
||||
private ZErrorCode ReadDeflateOutput(byte[] outputBuffer, ZFlushCode flushCode, out int bytesRead) {
|
||||
|
||||
lock (syncLock) {
|
||||
|
||||
GCHandle outputBufferHndl = GCHandle.Alloc(outputBuffer, GCHandleType.Pinned);
|
||||
|
||||
try {
|
||||
|
||||
|
||||
_zlibStream.NextOut = outputBufferHndl.AddrOfPinnedObject();
|
||||
_zlibStream.AvailOut = (uint) outputBuffer.Length;
|
||||
|
||||
ZErrorCode errC = Deflate(flushCode);
|
||||
bytesRead = outputBuffer.Length - (int) _zlibStream.AvailOut;
|
||||
|
||||
return errC;
|
||||
|
||||
} finally {
|
||||
outputBufferHndl.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IDeflater.Finish(byte[] outputBuffer, out int bytesRead) {
|
||||
|
||||
Contract.Assert(null != outputBuffer, "Can't pass in a null output buffer!");
|
||||
Contract.Assert(NeedsInput(), "We have something left in previous input!");
|
||||
Contract.Assert(!_inputBufferHandle.HasValue);
|
||||
|
||||
// Note: we require that NeedsInput() == true, i.e. that 0 == _zlibStream.AvailIn.
|
||||
// If there is still input left we should never be getting here; instead we
|
||||
// should be calling GetDeflateOutput.
|
||||
|
||||
ZErrorCode errC = ReadDeflateOutput(outputBuffer, ZFlushCode.Finish, out bytesRead);
|
||||
return errC == ZErrorCode.StreamEnd;
|
||||
}
|
||||
|
||||
#endregion // exposed functions
|
||||
|
||||
|
||||
#region helpers & native call wrappers
|
||||
|
||||
private void DeallocateInputBufferHandle() {
|
||||
|
||||
Contract.Assert(_inputBufferHandle.HasValue);
|
||||
Contract.Assert(_inputBufferHandle.Value.IsAllocated);
|
||||
|
||||
lock(syncLock) {
|
||||
_zlibStream.AvailIn = 0;
|
||||
_zlibStream.NextIn = ZLibNative.ZNullPtr;
|
||||
_inputBufferHandle.Value.Free();
|
||||
_inputBufferHandle = null;
|
||||
}
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
private void DeflateInit(ZLibNative.CompressionLevel compressionLevel, int windowBits, int memLevel,
|
||||
ZLibNative.CompressionStrategy strategy) {
|
||||
|
||||
ZErrorCode errC;
|
||||
try {
|
||||
errC = ZLibNative.CreateZLibStreamForDeflate(out _zlibStream, compressionLevel,
|
||||
windowBits, memLevel, strategy);
|
||||
} catch (Exception cause) {
|
||||
throw new ZLibException(SR.GetString(SR.ZLibErrorDLLLoadError), cause);
|
||||
}
|
||||
|
||||
switch (errC) {
|
||||
|
||||
case ZErrorCode.Ok:
|
||||
return;
|
||||
|
||||
case ZErrorCode.MemError:
|
||||
throw new ZLibException(SR.GetString(SR.ZLibErrorNotEnoughMemory), "deflateInit2_", (int) errC, _zlibStream.GetErrorMessage());
|
||||
|
||||
case ZErrorCode.VersionError:
|
||||
throw new ZLibException(SR.GetString(SR.ZLibErrorVersionMismatch), "deflateInit2_", (int) errC, _zlibStream.GetErrorMessage());
|
||||
|
||||
case ZErrorCode.StreamError:
|
||||
throw new ZLibException(SR.GetString(SR.ZLibErrorIncorrectInitParameters), "deflateInit2_", (int) errC, _zlibStream.GetErrorMessage());
|
||||
|
||||
default:
|
||||
throw new ZLibException(SR.GetString(SR.ZLibErrorUnexpected), "deflateInit2_", (int) errC, _zlibStream.GetErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[SecuritySafeCritical]
|
||||
private ZErrorCode Deflate(ZFlushCode flushCode) {
|
||||
|
||||
ZErrorCode errC;
|
||||
try {
|
||||
errC = _zlibStream.Deflate(flushCode);
|
||||
} catch (Exception cause) {
|
||||
throw new ZLibException(SR.GetString(SR.ZLibErrorDLLLoadError), cause);
|
||||
}
|
||||
|
||||
switch (errC) {
|
||||
|
||||
case ZErrorCode.Ok:
|
||||
case ZErrorCode.StreamEnd:
|
||||
return errC;
|
||||
|
||||
case ZErrorCode.BufError:
|
||||
return errC; // This is a recoverable error
|
||||
|
||||
case ZErrorCode.StreamError:
|
||||
throw new ZLibException(SR.GetString(SR.ZLibErrorInconsistentStream), "deflate", (int) errC, _zlibStream.GetErrorMessage());
|
||||
|
||||
default:
|
||||
throw new ZLibException(SR.GetString(SR.ZLibErrorUnexpected), "deflate", (int) errC, _zlibStream.GetErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#endregion // helpers & native call wrappers
|
||||
|
||||
} // internal class DeflaterZLib
|
||||
} // namespace System.IO.Compression
|
||||
|
||||
// file DeflaterZLib.cs
|
||||
Reference in New Issue
Block a user