1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 namespace IceInternal
6 {
7 
8     using System;
9     using System.Diagnostics;
10     using System.Runtime.InteropServices;
11 
12     internal static class SafeNativeMethods
13     {
14         [DllImport("bzip2.dll", EntryPoint="BZ2_bzlibVersion", ExactSpelling=true)]
windowsBZ2_bzlibVersion()15         internal static extern IntPtr windowsBZ2_bzlibVersion();
16 
17         [DllImport("bzip2.dll", EntryPoint="BZ2_bzBuffToBuffCompress", ExactSpelling=true)]
windowsBZ2_bzBuffToBuffCompress(byte[] dest, ref int destLen, byte[] source, int sourceLen, int blockSize100k, int verbosity, int workFactor)18         internal static extern int windowsBZ2_bzBuffToBuffCompress(byte[] dest,
19                                                                    ref int destLen,
20                                                                    byte[] source,
21                                                                    int sourceLen,
22                                                                    int blockSize100k,
23                                                                    int verbosity,
24                                                                    int workFactor);
25 
26         [DllImport("bzip2.dll", EntryPoint="BZ2_bzBuffToBuffDecompress", ExactSpelling=true)]
windowsBZ2_bzBuffToBuffDecompress(byte[] dest, ref int destLen, byte[] source, int sourceLen, int small, int verbosity)27         internal static extern int windowsBZ2_bzBuffToBuffDecompress(byte[] dest,
28                                                                      ref int destLen,
29                                                                      byte[] source,
30                                                                      int sourceLen,
31                                                                      int small,
32                                                                      int verbosity);
33 
34         [DllImport("libbz2.so.1", EntryPoint="BZ2_bzlibVersion", ExactSpelling=true)]
unixBZ2_bzlibVersion()35         internal static extern IntPtr unixBZ2_bzlibVersion();
36 
37         [DllImport("libbz2.so.1", EntryPoint="BZ2_bzBuffToBuffCompress", ExactSpelling=true)]
unixBZ2_bzBuffToBuffCompress(byte[] dest, ref int destLen, byte[] source, int sourceLen, int blockSize100k, int verbosity, int workFactor)38         internal static extern int unixBZ2_bzBuffToBuffCompress(byte[] dest,
39                                                                 ref int destLen,
40                                                                 byte[] source,
41                                                                 int sourceLen,
42                                                                 int blockSize100k,
43                                                                 int verbosity,
44                                                                 int workFactor);
45 
46         [DllImport("libbz2.so.1", EntryPoint="BZ2_bzBuffToBuffDecompress", ExactSpelling=true)]
unixBZ2_bzBuffToBuffDecompress(byte[] dest, ref int destLen, byte[] source, int sourceLen, int small, int verbosity)47         internal static extern int unixBZ2_bzBuffToBuffDecompress(byte[] dest,
48                                                                   ref int destLen,
49                                                                   byte[] source,
50                                                                   int sourceLen,
51                                                                   int small,
52                                                                   int verbosity);
53 
54         [DllImport("libbz2.dylib", EntryPoint="BZ2_bzlibVersion", ExactSpelling=true)]
macOSBZ2_bzlibVersion()55         internal static extern IntPtr macOSBZ2_bzlibVersion();
56 
57         [DllImport("libbz2.dylib", EntryPoint="BZ2_bzBuffToBuffCompress", ExactSpelling=true)]
macOSBZ2_bzBuffToBuffCompress(byte[] dest, ref int destLen, byte[] source, int sourceLen, int blockSize100k, int verbosity, int workFactor)58         internal static extern int macOSBZ2_bzBuffToBuffCompress(byte[] dest,
59                                                                  ref int destLen,
60                                                                  byte[] source,
61                                                                  int sourceLen,
62                                                                  int blockSize100k,
63                                                                  int verbosity,
64                                                                  int workFactor);
65 
66         [DllImport("libbz2.dylib", EntryPoint="BZ2_bzBuffToBuffDecompress", ExactSpelling=true)]
macOSBZ2_bzBuffToBuffDecompress(byte[] dest, ref int destLen, byte[] source, int sourceLen, int small, int verbosity)67         internal static extern int macOSBZ2_bzBuffToBuffDecompress(byte[] dest,
68                                                                    ref int destLen,
69                                                                    byte[] source,
70                                                                    int sourceLen,
71                                                                    int small,
72                                                                    int verbosity);
73     }
74 
CompressBuffer(byte[] dest, ref int destLen, byte[] source, int sourceLen, int blockSize100k, int verbosity, int workFactor)75     delegate int CompressBuffer(byte[] dest,
76                                 ref int destLen,
77                                 byte[] source,
78                                 int sourceLen,
79                                 int blockSize100k,
80                                 int verbosity,
81                                 int workFactor);
82 
DecompressBuffer(byte[] dest, ref int destLen, byte[] source, int sourceLen, int small, int verbosity)83     delegate int DecompressBuffer(byte[] dest,
84                                   ref int destLen,
85                                   byte[] source,
86                                   int sourceLen,
87                                   int small,
88                                   int verbosity);
89     public class BZip2
90     {
BZip2()91         static BZip2()
92         {
93             //
94             // Simple trick to find out whether bzip2 is installed: Call the BZ2_bzlibVersion() function in the
95             // library. If we get an exception, the library is not available.
96             //
97             _bzlibInstalled = false;
98 
99             //
100             // We are setting the library name here because, under Mono, we don't know the exact library name.
101             // In addition, the FileName member of the BadImageFormatException is the empty string, even though
102             // it should provide the name of the library.
103             //
104             try
105             {
106                 if(AssemblyUtil.isWindows)
107                 {
108                     SafeNativeMethods.windowsBZ2_bzlibVersion();
109                 }
110                 else if(AssemblyUtil.isMacOS)
111                 {
112                     SafeNativeMethods.macOSBZ2_bzlibVersion();
113                 }
114                 else
115                 {
116                     SafeNativeMethods.unixBZ2_bzlibVersion();
117                 }
118                 _bzlibInstalled = true;
119             }
120             catch(EntryPointNotFoundException)
121             {
122                 string lib = AssemblyUtil.isWindows ? "bzip2.dll" : "libbz2.so.1";
123                 Console.Error.WriteLine("warning: found " + lib + " but entry point BZ2_bzlibVersion is missing.");
124             }
125             catch(TypeLoadException)
126             {
127                 // Expected -- bzip2 lib not installed or not in PATH.
128             }
129             catch(BadImageFormatException ex)
130             {
131                 string lib = AssemblyUtil.isWindows ? "bzip2.dll" : "libbz2.so.1";
132                 if(!String.IsNullOrEmpty(ex.FileName))
133                 {
134                     lib = ex.FileName; // Future-proof: we'll do the right thing if the FileName member is non-empty.
135                 }
136                 Console.Error.Write("warning: " + lib + " could not be loaded (likely due to 32/64-bit mismatch).");
137                 if(IntPtr.Size == 8)
138                 {
139                     Console.Error.Write(" Make sure the directory containing the 64-bit " + lib + " is in your PATH.");
140                 }
141                 Console.Error.WriteLine();
142             }
143 
144             if(AssemblyUtil.isWindows)
145             {
146                 _compressBuffer = (byte[] dest, ref int destLen, byte[] source, int sourceLen, int blockSize100k,
147                                    int verbosity, int workFactor) =>
148                     {
149                         return SafeNativeMethods.windowsBZ2_bzBuffToBuffCompress(dest, ref destLen, source, sourceLen,
150                                                                                  blockSize100k, verbosity, workFactor);
151                     };
152 
153                 _decompressBuffer = (byte[] dest, ref int destLen, byte[] source, int sourceLen, int small,
154                                      int verbosity) =>
155                     {
156                         return SafeNativeMethods.windowsBZ2_bzBuffToBuffDecompress(dest, ref destLen, source, sourceLen,
157                                                                                    small, verbosity);
158                     };
159             }
160             else if(AssemblyUtil.isMacOS)
161             {
162                 _compressBuffer = (byte[] dest, ref int destLen, byte[] source, int sourceLen, int blockSize100k,
163                                    int verbosity, int workFactor) =>
164                     {
165                         return SafeNativeMethods.macOSBZ2_bzBuffToBuffCompress(dest, ref destLen, source, sourceLen,
166                                                                                blockSize100k, verbosity, workFactor);
167                     };
168 
169                 _decompressBuffer = (byte[] dest, ref int destLen, byte[] source, int sourceLen, int small,
170                                      int verbosity) =>
171                     {
172                         return SafeNativeMethods.macOSBZ2_bzBuffToBuffDecompress(dest, ref destLen, source, sourceLen,
173                                                                                  small, verbosity);
174                     };
175             }
176             else
177             {
178                 _compressBuffer = (byte[] dest, ref int destLen, byte[] source, int sourceLen, int blockSize100k,
179                                    int verbosity, int workFactor) =>
180                     {
181                         return SafeNativeMethods.unixBZ2_bzBuffToBuffCompress(dest, ref destLen, source, sourceLen,
182                                                                               blockSize100k, verbosity, workFactor);
183                     };
184 
185                 _decompressBuffer = (byte[] dest, ref int destLen, byte[] source, int sourceLen, int small,
186                                      int verbosity) =>
187                     {
188                         return SafeNativeMethods.unixBZ2_bzBuffToBuffDecompress(dest, ref destLen, source, sourceLen,
189                                                                                    small, verbosity);
190                     };
191             }
192         }
193 
getBZ2Error(int error)194         static string getBZ2Error(int error)
195         {
196             string rc;
197 
198             switch(error)
199             {
200                 case BZ_SEQUENCE_ERROR:
201                 {
202                     rc = "BZ_SEQUENCE_ERROR";
203                     break;
204                 }
205                 case BZ_PARAM_ERROR:
206                 {
207                     rc = "BZ_PARAM_ERROR";
208                     break;
209                 }
210                 case BZ_MEM_ERROR:
211                 {
212                     rc = "BZ_MEM_ERROR";
213                     break;
214                 }
215                 case BZ_DATA_ERROR:
216                 {
217                     rc = "BZ_DATA_ERROR";
218                     break;
219                 }
220                 case BZ_DATA_ERROR_MAGIC:
221                 {
222                     rc = "BZ_DATA_ERROR_MAGIC";
223                     break;
224                 }
225                 case BZ_IO_ERROR:
226                 {
227                     rc = "BZ_IO_ERROR";
228                     break;
229                 }
230                 case BZ_UNEXPECTED_EOF:
231                 {
232                     rc = "BZ_UNEXPECTED_EOF";
233                     break;
234                 }
235                 case BZ_OUTBUFF_FULL:
236                 {
237                     rc = "BZ_OUTBUFF_FULL";
238                     break;
239                 }
240                 case BZ_CONFIG_ERROR:
241                 {
242                     rc = "BZ_CONFIG_ERROR";
243                     break;
244                 }
245                 default:
246                 {
247                     rc = "Unknown bzip2 error: " + error;
248                     break;
249                 }
250             }
251             return rc;
252         }
253 
supported()254         public static bool supported()
255         {
256             return _bzlibInstalled;
257         }
258 
compress(Buffer buf, int headerSize, int compressionLevel)259         public static Buffer compress(Buffer buf, int headerSize, int compressionLevel)
260         {
261             Debug.Assert(supported());
262             //
263             // Compress the message body, but not the header.
264             //
265             int uncompressedLen = buf.size() - headerSize;
266             byte[] data = buf.b.rawBytes(headerSize, uncompressedLen);
267             int compressedLen = (int)(uncompressedLen * 1.01 + 600);
268             byte[] compressed = new byte[compressedLen];
269 
270             int rc = _compressBuffer(compressed, ref compressedLen, data, uncompressedLen, compressionLevel, 0, 0);
271             if(rc == BZ_OUTBUFF_FULL)
272             {
273                 return null;
274             }
275             else if(rc < 0)
276             {
277                 Ice.CompressionException ex = new Ice.CompressionException("BZ2_bzBuffToBuffCompress failed");
278                 ex.reason = getBZ2Error(rc);
279                 throw ex;
280             }
281 
282             //
283             // Don't bother if the compressed data is larger than the
284             // uncompressed data.
285             //
286             if(compressedLen >= uncompressedLen)
287             {
288                 return null;
289             }
290 
291             Buffer r = new Buffer();
292             r.resize(headerSize + 4 + compressedLen, false);
293             r.b.position(0);
294 
295             //
296             // Copy the header from the uncompressed stream to the
297             // compressed one.
298             //
299             r.b.put(buf.b.rawBytes(0, headerSize));
300 
301             //
302             // Add the size of the uncompressed stream before the
303             // message body.
304             //
305             r.b.putInt(buf.size());
306 
307             //
308             // Add the compressed message body.
309             //
310             r.b.put(compressed, 0, compressedLen);
311 
312             return r;
313         }
314 
uncompress(Buffer buf, int headerSize, int messageSizeMax)315         public static Buffer uncompress(Buffer buf, int headerSize, int messageSizeMax)
316         {
317             Debug.Assert(supported());
318 
319             buf.b.position(headerSize);
320             int uncompressedSize = buf.b.getInt();
321             if(uncompressedSize <= headerSize)
322             {
323                 throw new Ice.IllegalMessageSizeException("compressed size <= header size");
324             }
325             if(uncompressedSize > messageSizeMax)
326             {
327                 IceInternal.Ex.throwMemoryLimitException(uncompressedSize, messageSizeMax);
328             }
329 
330             int compressedLen = buf.size() - headerSize - 4;
331             byte[] compressed = buf.b.rawBytes(headerSize + 4, compressedLen);
332             int uncompressedLen = uncompressedSize - headerSize;
333 
334             byte[] uncompressed = new byte[uncompressedLen];
335             int rc = _decompressBuffer(uncompressed, ref uncompressedLen, compressed, compressedLen, 0, 0);
336             if(rc < 0)
337             {
338                 Ice.CompressionException ex = new Ice.CompressionException("BZ2_bzBuffToBuffDecompress failed");
339                 ex.reason = getBZ2Error(rc);
340                 throw ex;
341             }
342 
343             Buffer r = new Buffer();
344             r.resize(uncompressedSize, false);
345 
346             //
347             // Copy the header from the compressed buffer to the uncompressed one.
348             //
349             r.b.position(0);
350             r.b.put(buf.b.rawBytes(), 0, headerSize);
351             r.b.put(uncompressed);
352 
353             return r;
354         }
355 
356         private static bool _bzlibInstalled;
357 
358         private static CompressBuffer _compressBuffer;
359         private static DecompressBuffer _decompressBuffer;
360 
361         const int BZ_SEQUENCE_ERROR = -1;
362         const int BZ_PARAM_ERROR = -2;
363         const int BZ_MEM_ERROR = -3;
364         const int BZ_DATA_ERROR = -4;
365         const int BZ_DATA_ERROR_MAGIC = -5;
366         const int BZ_IO_ERROR = -6;
367         const int BZ_UNEXPECTED_EOF = -7;
368         const int BZ_OUTBUFF_FULL = -8;
369         const int BZ_CONFIG_ERROR = -9;
370     }
371 
372 }
373