Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit ed18842

Browse files
committed
Use zlib in System.IO.Compression on Unix
Updates the Unix implementation of System.IO.Compression to use zlib. For the most part, the Windows implementation "just works"; the primary differences are the name of the library on Unix and the size of a data type being used in the z_stream data structure. Additionally, on Windows we're planning to distribute the native library with the managed library, but on Unix we're currently planning on relying on the platform's package management to provide the native zlib library. As such, I've added a fallback mechanism, such that if zlib isn't available, we use the managed implementation that'll work everywhere.
1 parent d98e0a4 commit ed18842

File tree

6 files changed

+124
-57
lines changed

6 files changed

+124
-57
lines changed

src/Common/src/Interop/Unix/Interop.Libraries.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ private static partial class Libraries
88
internal const string Libc = "libc"; // C library
99
internal const string LibCoreClr= "libcoreclr"; // CoreCLR runtime
1010
internal const string LibCrypto = "libcrypto"; // OpenSSL crypto library
11+
internal const string Zlib = "libz"; // zlib compression library
1112
}
1213
}

src/System.IO.Compression/src/System.IO.Compression.csproj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,17 @@
5252
<Compile Include="System\IO\Compression\ZipVersionNeededValues.cs" />
5353
<Compile Include="System\IO\Compression\ZLibException.cs" />
5454
<Compile Include="System\IO\Compression\ZLibNative.cs" />
55-
</ItemGroup>
56-
<ItemGroup>
5755
<Compile Include="Interop\Interop.zlib.cs" />
58-
<Compile Include="$(CommonPath)\Interop\Windows\Interop.Libraries.cs" />
5956
</ItemGroup>
6057
<ItemGroup Condition=" '$(TargetsWindows)' == 'true' ">
6158
<Compile Include="System\IO\Compression\DeflateStream.Windows.cs" />
59+
<Compile Include="System\IO\Compression\ZLibNative.Windows.cs" />
60+
<Compile Include="$(CommonPath)\Interop\Windows\Interop.Libraries.cs" />
6261
</ItemGroup>
6362
<ItemGroup Condition=" '$(TargetsUnix)' == 'true' ">
6463
<Compile Include="System\IO\Compression\DeflateStream.Unix.cs" />
64+
<Compile Include="System\IO\Compression\ZLibNative.Unix.cs" />
65+
<Compile Include="$(CommonPath)\Interop\Unix\Interop.Libraries.cs" />
6566
</ItemGroup>
6667
<ItemGroup>
6768
<None Include="project.json" />
Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
11
// Copyright (c) Microsoft. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
using System.Diagnostics;
5+
46
namespace System.IO.Compression
57
{
68
public partial class DeflateStream : Stream
79
{
810
private static WorkerType GetDeflaterType()
911
{
10-
return WorkerType.Managed; // TODO: Switch to ZLib once interop is worked out
12+
try
13+
{
14+
// Make any P/Invoke into zlib to ensure we're able to find and use it.
15+
// If we are, then use zlib.
16+
Interop.zlib.zlibCompileFlags();
17+
return WorkerType.ZLib;
18+
}
19+
catch
20+
{
21+
// Otherwise, fallback to managed implementation if zlib isn't available
22+
Debug.Fail("zlib unavailable");
23+
return WorkerType.Managed;
24+
}
1125
}
1226
}
1327
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.Runtime.InteropServices;
5+
6+
namespace System.IO.Compression
7+
{
8+
internal static partial class ZLibNative
9+
{
10+
/// <summary>
11+
/// ZLib stream descriptor data structure
12+
/// Do not construct instances of <code>ZStream</code> explicitly.
13+
/// Always use <code>ZLibNative.DeflateInit2_</code> or <code>ZLibNative.InflateInit2_</code> instead.
14+
/// Those methods will wrap this structure into a <code>SafeHandle</code> and thus make sure that it is always disposed correctly.
15+
/// </summary>
16+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
17+
internal struct ZStream
18+
{
19+
internal IntPtr nextIn; //Bytef *next_in; /* next input byte */
20+
internal UIntPtr availIn; //uInt avail_in; /* number of bytes available at next_in */
21+
internal UIntPtr totalIn; //uLong total_in; /* total nb of input bytes read so far */
22+
23+
internal IntPtr nextOut; //Bytef *next_out; /* next output byte should be put there */
24+
internal UIntPtr availOut; //uInt avail_out; /* remaining free space at next_out */
25+
internal UIntPtr totalOut; //uLong total_out; /* total nb of bytes output so far */
26+
27+
internal IntPtr msg; //char *msg; /* last error message, NULL if no error */
28+
29+
internal IntPtr state; //struct internal_state FAR *state; /* not visible by applications */
30+
31+
internal IntPtr zalloc; //alloc_func zalloc; /* used to allocate the internal state */
32+
internal IntPtr zfree; //free_func zfree; /* used to free the internal state */
33+
internal IntPtr opaque; //voidpf opaque; /* private data object passed to zalloc and zfree */
34+
35+
internal int dataType; //int data_type; /* best guess about the data type: binary or text */
36+
internal UIntPtr adler; //uLong adler; /* adler32 value of the uncompressed data */
37+
internal UIntPtr reserved; //uLong reserved; /* reserved for future use */
38+
}
39+
40+
/// <summary>Casts a uint to a native zlib uLong.</summary>
41+
private static UIntPtr CastUInt32ToNativeuLong(uint value)
42+
{
43+
// Several members of ZStream are defined by zlib as uLong, which is 32-bits on
44+
// Windows, but 32-bits on 32-bit Unix and 64-bits on 64-bit Unix. The consuming code
45+
// works in terms of a 32-bit value, so we need to cast back and forth in
46+
// a platform-agnostic way, hence we have a PAL helper on Unix and one on Windows.
47+
return (UIntPtr)value;
48+
}
49+
}
50+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.Runtime.InteropServices;
5+
6+
namespace System.IO.Compression
7+
{
8+
internal static partial class ZLibNative
9+
{
10+
/// <summary>
11+
/// ZLib stream descriptor data structure
12+
/// Do not construct instances of <code>ZStream</code> explicitly.
13+
/// Always use <code>ZLibNative.DeflateInit2_</code> or <code>ZLibNative.InflateInit2_</code> instead.
14+
/// Those methods will wrap this structure into a <code>SafeHandle</code> and thus make sure that it is always disposed correctly.
15+
/// </summary>
16+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
17+
internal struct ZStream
18+
{
19+
internal IntPtr nextIn; //Bytef *next_in; /* next input byte */
20+
internal uint availIn; //uInt avail_in; /* number of bytes available at next_in */
21+
internal uint totalIn; //uLong total_in; /* total nb of input bytes read so far */
22+
23+
internal IntPtr nextOut; //Bytef *next_out; /* next output byte should be put there */
24+
internal uint availOut; //uInt avail_out; /* remaining free space at next_out */
25+
internal uint totalOut; //uLong total_out; /* total nb of bytes output so far */
26+
27+
internal IntPtr msg; //char *msg; /* last error message, NULL if no error */
28+
29+
internal IntPtr state; //struct internal_state FAR *state; /* not visible by applications */
30+
31+
internal IntPtr zalloc; //alloc_func zalloc; /* used to allocate the internal state */
32+
internal IntPtr zfree; //free_func zfree; /* used to free the internal state */
33+
internal IntPtr opaque; //voidpf opaque; /* private data object passed to zalloc and zfree */
34+
35+
internal int dataType; //int data_type; /* best guess about the data type: binary or text */
36+
internal uint adler; //uLong adler; /* adler32 value of the uncompressed data */
37+
internal uint reserved; //uLong reserved; /* reserved for future use */
38+
}
39+
40+
/// <summary>Casts a uint to a native zlib uLong.</summary>
41+
private static uint CastUInt32ToNativeuLong(uint value)
42+
{
43+
return value;
44+
}
45+
46+
}
47+
}

src/System.IO.Compression/src/System/IO/Compression/ZLibNative.cs

Lines changed: 7 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
// Copyright (c) Microsoft. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4-
using System.Text;
5-
using System.Runtime.InteropServices;
6-
using System.Runtime.Versioning;
7-
using System.Runtime.CompilerServices;
8-
using Microsoft.Win32.SafeHandles;
94
using System.Diagnostics.Contracts;
5+
using System.Runtime.InteropServices;
106
using System.Security;
7+
using System.Text;
118

129
namespace System.IO.Compression
1310
{
@@ -17,7 +14,7 @@ namespace System.IO.Compression
1714
///
1815
/// See also: How to choose a compression level (in comments to <code>CompressionLevel</code>.
1916
/// </summary>
20-
internal static class ZLibNative
17+
internal static partial class ZLibNative
2118
{
2219
#region Constants defined in zlib.h
2320

@@ -178,41 +175,6 @@ public enum CompressionMethod : int
178175
// More is faster and better compression with more memory usage.
179176
#endregion // Defaults for ZLib parameters
180177

181-
182-
#region ZLib stream descriptor data structure
183-
184-
/// <summary>
185-
/// Do not construct instances of <code>ZStream</code> explicitly.
186-
/// Always use <code>ZLibNative.DeflateInit2_</code> or <code>ZLibNative.InflateInit2_</code> instead.
187-
/// Those methods will wrap this structure into a <code>SafeHandle</code> and thus make sure that it is always disposed correctly.
188-
/// </summary>
189-
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
190-
internal struct ZStream
191-
{
192-
internal IntPtr nextIn; //Bytef *next_in; /* next input byte */
193-
internal UInt32 availIn; //uInt avail_in; /* number of bytes available at next_in */
194-
internal UInt32 totalIn; //uLong total_in; /* total nb of input bytes read so far */
195-
196-
internal IntPtr nextOut; //Bytef *next_out; /* next output byte should be put there */
197-
internal UInt32 availOut; //uInt avail_out; /* remaining free space at next_out */
198-
internal UInt32 totalOut; //uLong total_out; /* total nb of bytes output so far */
199-
200-
internal IntPtr msg; //char *msg; /* last error message, NULL if no error */
201-
202-
internal IntPtr state; //struct internal_state FAR *state; /* not visible by applications */
203-
204-
internal IntPtr zalloc; //alloc_func zalloc; /* used to allocate the internal state */
205-
internal IntPtr zfree; //free_func zfree; /* used to free the internal state */
206-
internal IntPtr opaque; //voidpf opaque; /* private data object passed to zalloc and zfree */
207-
208-
internal Int32 dataType; //int data_type; /* best guess about the data type: binary or text */
209-
internal UInt32 adler; //uLong adler; /* adler32 value of the uncompressed data */
210-
internal UInt32 reserved; //uLong reserved; /* reserved for future use */
211-
}
212-
213-
#endregion // ZLib stream descriptor data structure
214-
215-
216178
/**
217179
* Do not remove the nested typing of types inside of <code>System.IO.Compression.ZLibNative</code>.
218180
* This was done on purpose to:
@@ -314,12 +276,10 @@ public IntPtr NextIn
314276

315277
public UInt32 AvailIn
316278
{
317-
[SecurityCritical] get { return _zStream.availIn; }
318-
[SecurityCritical] set { _zStream.availIn = value; }
279+
[SecurityCritical] get { return (uint)_zStream.availIn; }
280+
[SecurityCritical] set { _zStream.availIn = CastUInt32ToNativeuLong(value); }
319281
}
320282

321-
public UInt32 TotalIn {[SecurityCritical] get { return _zStream.totalIn; } }
322-
323283
public IntPtr NextOut
324284
{
325285
[SecurityCritical] get { return _zStream.nextOut; }
@@ -328,16 +288,10 @@ public IntPtr NextOut
328288

329289
public UInt32 AvailOut
330290
{
331-
[SecurityCritical] get { return _zStream.availOut; }
332-
[SecurityCritical] set { _zStream.availOut = value; }
291+
[SecurityCritical] get { return (uint)_zStream.availOut; }
292+
[SecurityCritical] set { _zStream.availOut = CastUInt32ToNativeuLong(value); }
333293
}
334294

335-
public UInt32 TotalOut {[SecurityCritical] get { return _zStream.totalOut; } }
336-
337-
public Int32 DataType {[SecurityCritical] get { return _zStream.dataType; } }
338-
339-
public UInt32 Adler {[SecurityCritical] get { return _zStream.adler; } }
340-
341295
#endregion // Expose fields on ZStream for use by user / Fx code (add more as required)
342296

343297

0 commit comments

Comments
 (0)