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