1 using System; 2 3 namespace ICSharpCode.SharpZipLib.Zip.Compression 4 { 5 /// <summary> 6 /// This is the Deflater class. The deflater class compresses input 7 /// with the deflate algorithm described in RFC 1951. It has several 8 /// compression levels and three different strategies described below. 9 /// 10 /// This class is <i>not</i> thread safe. This is inherent in the API, due 11 /// to the split of deflate and setInput. 12 /// 13 /// author of the original java version : Jochen Hoenicke 14 /// </summary> 15 public class Deflater 16 { 17 #region Deflater Documentation 18 /* 19 * The Deflater can do the following state transitions: 20 * 21 * (1) -> INIT_STATE ----> INIT_FINISHING_STATE ---. 22 * / | (2) (5) | 23 * / v (5) | 24 * (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3) 25 * \ | (3) | ,--------' 26 * | | | (3) / 27 * v v (5) v v 28 * (1) -> BUSY_STATE ----> FINISHING_STATE 29 * | (6) 30 * v 31 * FINISHED_STATE 32 * \_____________________________________/ 33 * | (7) 34 * v 35 * CLOSED_STATE 36 * 37 * (1) If we should produce a header we start in INIT_STATE, otherwise 38 * we start in BUSY_STATE. 39 * (2) A dictionary may be set only when we are in INIT_STATE, then 40 * we change the state as indicated. 41 * (3) Whether a dictionary is set or not, on the first call of deflate 42 * we change to BUSY_STATE. 43 * (4) -- intentionally left blank -- :) 44 * (5) FINISHING_STATE is entered, when flush() is called to indicate that 45 * there is no more INPUT. There are also states indicating, that 46 * the header wasn't written yet. 47 * (6) FINISHED_STATE is entered, when everything has been flushed to the 48 * internal pending output buffer. 49 * (7) At any time (7) 50 * 51 */ 52 #endregion 53 #region Public Constants 54 /// <summary> 55 /// The best and slowest compression level. This tries to find very 56 /// long and distant string repetitions. 57 /// </summary> 58 public const int BEST_COMPRESSION = 9; 59 60 /// <summary> 61 /// The worst but fastest compression level. 62 /// </summary> 63 public const int BEST_SPEED = 1; 64 65 /// <summary> 66 /// The default compression level. 67 /// </summary> 68 public const int DEFAULT_COMPRESSION = -1; 69 70 /// <summary> 71 /// This level won't compress at all but output uncompressed blocks. 72 /// </summary> 73 public const int NO_COMPRESSION = 0; 74 75 /// <summary> 76 /// The compression method. This is the only method supported so far. 77 /// There is no need to use this constant at all. 78 /// </summary> 79 public const int DEFLATED = 8; 80 #endregion 81 #region Local Constants 82 private const int IS_SETDICT = 0x01; 83 private const int IS_FLUSHING = 0x04; 84 private const int IS_FINISHING = 0x08; 85 86 private const int INIT_STATE = 0x00; 87 private const int SETDICT_STATE = 0x01; 88 // private static int INIT_FINISHING_STATE = 0x08; 89 // private static int SETDICT_FINISHING_STATE = 0x09; 90 private const int BUSY_STATE = 0x10; 91 private const int FLUSHING_STATE = 0x14; 92 private const int FINISHING_STATE = 0x1c; 93 private const int FINISHED_STATE = 0x1e; 94 private const int CLOSED_STATE = 0x7f; 95 #endregion 96 #region Constructors 97 /// <summary> 98 /// Creates a new deflater with default compression level. 99 /// </summary> Deflater()100 public Deflater() : this(DEFAULT_COMPRESSION, false) 101 { 102 103 } 104 105 /// <summary> 106 /// Creates a new deflater with given compression level. 107 /// </summary> 108 /// <param name="level"> 109 /// the compression level, a value between NO_COMPRESSION 110 /// and BEST_COMPRESSION, or DEFAULT_COMPRESSION. 111 /// </param> 112 /// <exception cref="System.ArgumentOutOfRangeException">if lvl is out of range.</exception> Deflater(int level)113 public Deflater(int level) : this(level, false) 114 { 115 116 } 117 118 /// <summary> 119 /// Creates a new deflater with given compression level. 120 /// </summary> 121 /// <param name="level"> 122 /// the compression level, a value between NO_COMPRESSION 123 /// and BEST_COMPRESSION. 124 /// </param> 125 /// <param name="noZlibHeaderOrFooter"> 126 /// true, if we should suppress the Zlib/RFC1950 header at the 127 /// beginning and the adler checksum at the end of the output. This is 128 /// useful for the GZIP/PKZIP formats. 129 /// </param> 130 /// <exception cref="System.ArgumentOutOfRangeException">if lvl is out of range.</exception> Deflater(int level, bool noZlibHeaderOrFooter)131 public Deflater(int level, bool noZlibHeaderOrFooter) 132 { 133 if (level == DEFAULT_COMPRESSION) { 134 level = 6; 135 } else if (level < NO_COMPRESSION || level > BEST_COMPRESSION) { 136 throw new ArgumentOutOfRangeException(nameof(level)); 137 } 138 139 pending = new DeflaterPending(); 140 engine = new DeflaterEngine(pending); 141 this.noZlibHeaderOrFooter = noZlibHeaderOrFooter; 142 SetStrategy(DeflateStrategy.Default); 143 SetLevel(level); 144 Reset(); 145 } 146 #endregion 147 148 /// <summary> 149 /// Resets the deflater. The deflater acts afterwards as if it was 150 /// just created with the same compression level and strategy as it 151 /// had before. 152 /// </summary> Reset()153 public void Reset() 154 { 155 state = (noZlibHeaderOrFooter ? BUSY_STATE : INIT_STATE); 156 totalOut = 0; 157 pending.Reset(); 158 engine.Reset(); 159 } 160 161 /// <summary> 162 /// Gets the current adler checksum of the data that was processed so far. 163 /// </summary> 164 public int Adler { 165 get { 166 return engine.Adler; 167 } 168 } 169 170 /// <summary> 171 /// Gets the number of input bytes processed so far. 172 /// </summary> 173 public long TotalIn { 174 get { 175 return engine.TotalIn; 176 } 177 } 178 179 /// <summary> 180 /// Gets the number of output bytes so far. 181 /// </summary> 182 public long TotalOut { 183 get { 184 return totalOut; 185 } 186 } 187 188 /// <summary> 189 /// Flushes the current input block. Further calls to deflate() will 190 /// produce enough output to inflate everything in the current input 191 /// block. This is not part of Sun's JDK so I have made it package 192 /// private. It is used by DeflaterOutputStream to implement 193 /// flush(). 194 /// </summary> Flush()195 public void Flush() 196 { 197 state |= IS_FLUSHING; 198 } 199 200 /// <summary> 201 /// Finishes the deflater with the current input block. It is an error 202 /// to give more input after this method was called. This method must 203 /// be called to force all bytes to be flushed. 204 /// </summary> Finish()205 public void Finish() 206 { 207 state |= (IS_FLUSHING | IS_FINISHING); 208 } 209 210 /// <summary> 211 /// Returns true if the stream was finished and no more output bytes 212 /// are available. 213 /// </summary> 214 public bool IsFinished { 215 get { 216 return (state == FINISHED_STATE) && pending.IsFlushed; 217 } 218 } 219 220 /// <summary> 221 /// Returns true, if the input buffer is empty. 222 /// You should then call setInput(). 223 /// NOTE: This method can also return true when the stream 224 /// was finished. 225 /// </summary> 226 public bool IsNeedingInput { 227 get { 228 return engine.NeedsInput(); 229 } 230 } 231 232 /// <summary> 233 /// Sets the data which should be compressed next. This should be only 234 /// called when needsInput indicates that more input is needed. 235 /// If you call setInput when needsInput() returns false, the 236 /// previous input that is still pending will be thrown away. 237 /// The given byte array should not be changed, before needsInput() returns 238 /// true again. 239 /// This call is equivalent to <code>setInput(input, 0, input.length)</code>. 240 /// </summary> 241 /// <param name="input"> 242 /// the buffer containing the input data. 243 /// </param> 244 /// <exception cref="System.InvalidOperationException"> 245 /// if the buffer was finished() or ended(). 246 /// </exception> SetInput(byte[] input)247 public void SetInput(byte[] input) 248 { 249 SetInput(input, 0, input.Length); 250 } 251 252 /// <summary> 253 /// Sets the data which should be compressed next. This should be 254 /// only called when needsInput indicates that more input is needed. 255 /// The given byte array should not be changed, before needsInput() returns 256 /// true again. 257 /// </summary> 258 /// <param name="input"> 259 /// the buffer containing the input data. 260 /// </param> 261 /// <param name="offset"> 262 /// the start of the data. 263 /// </param> 264 /// <param name="count"> 265 /// the number of data bytes of input. 266 /// </param> 267 /// <exception cref="System.InvalidOperationException"> 268 /// if the buffer was Finish()ed or if previous input is still pending. 269 /// </exception> SetInput(byte[] input, int offset, int count)270 public void SetInput(byte[] input, int offset, int count) 271 { 272 if ((state & IS_FINISHING) != 0) { 273 throw new InvalidOperationException("Finish() already called"); 274 } 275 engine.SetInput(input, offset, count); 276 } 277 278 /// <summary> 279 /// Sets the compression level. There is no guarantee of the exact 280 /// position of the change, but if you call this when needsInput is 281 /// true the change of compression level will occur somewhere near 282 /// before the end of the so far given input. 283 /// </summary> 284 /// <param name="level"> 285 /// the new compression level. 286 /// </param> SetLevel(int level)287 public void SetLevel(int level) 288 { 289 if (level == DEFAULT_COMPRESSION) { 290 level = 6; 291 } else if (level < NO_COMPRESSION || level > BEST_COMPRESSION) { 292 throw new ArgumentOutOfRangeException(nameof(level)); 293 } 294 295 if (this.level != level) { 296 this.level = level; 297 engine.SetLevel(level); 298 } 299 } 300 301 /// <summary> 302 /// Get current compression level 303 /// </summary> 304 /// <returns>Returns the current compression level</returns> GetLevel()305 public int GetLevel() 306 { 307 return level; 308 } 309 310 /// <summary> 311 /// Sets the compression strategy. Strategy is one of 312 /// DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED. For the exact 313 /// position where the strategy is changed, the same as for 314 /// SetLevel() applies. 315 /// </summary> 316 /// <param name="strategy"> 317 /// The new compression strategy. 318 /// </param> SetStrategy(DeflateStrategy strategy)319 public void SetStrategy(DeflateStrategy strategy) 320 { 321 engine.Strategy = strategy; 322 } 323 324 /// <summary> 325 /// Deflates the current input block with to the given array. 326 /// </summary> 327 /// <param name="output"> 328 /// The buffer where compressed data is stored 329 /// </param> 330 /// <returns> 331 /// The number of compressed bytes added to the output, or 0 if either 332 /// IsNeedingInput() or IsFinished returns true or length is zero. 333 /// </returns> Deflate(byte[] output)334 public int Deflate(byte[] output) 335 { 336 return Deflate(output, 0, output.Length); 337 } 338 339 /// <summary> 340 /// Deflates the current input block to the given array. 341 /// </summary> 342 /// <param name="output"> 343 /// Buffer to store the compressed data. 344 /// </param> 345 /// <param name="offset"> 346 /// Offset into the output array. 347 /// </param> 348 /// <param name="length"> 349 /// The maximum number of bytes that may be stored. 350 /// </param> 351 /// <returns> 352 /// The number of compressed bytes added to the output, or 0 if either 353 /// needsInput() or finished() returns true or length is zero. 354 /// </returns> 355 /// <exception cref="System.InvalidOperationException"> 356 /// If Finish() was previously called. 357 /// </exception> 358 /// <exception cref="System.ArgumentOutOfRangeException"> 359 /// If offset or length don't match the array length. 360 /// </exception> Deflate(byte[] output, int offset, int length)361 public int Deflate(byte[] output, int offset, int length) 362 { 363 int origLength = length; 364 365 if (state == CLOSED_STATE) { 366 throw new InvalidOperationException("Deflater closed"); 367 } 368 369 if (state < BUSY_STATE) { 370 // output header 371 int header = (DEFLATED + 372 ((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8; 373 int level_flags = (level - 1) >> 1; 374 if (level_flags < 0 || level_flags > 3) { 375 level_flags = 3; 376 } 377 header |= level_flags << 6; 378 if ((state & IS_SETDICT) != 0) { 379 // Dictionary was set 380 header |= DeflaterConstants.PRESET_DICT; 381 } 382 header += 31 - (header % 31); 383 384 pending.WriteShortMSB(header); 385 if ((state & IS_SETDICT) != 0) { 386 int chksum = engine.Adler; 387 engine.ResetAdler(); 388 pending.WriteShortMSB(chksum >> 16); 389 pending.WriteShortMSB(chksum & 0xffff); 390 } 391 392 state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING)); 393 } 394 395 for (;;) { 396 int count = pending.Flush(output, offset, length); 397 offset += count; 398 totalOut += count; 399 length -= count; 400 401 if (length == 0 || state == FINISHED_STATE) { 402 break; 403 } 404 405 if (!engine.Deflate((state & IS_FLUSHING) != 0, (state & IS_FINISHING) != 0)) { 406 switch (state) { 407 case BUSY_STATE: 408 // We need more input now 409 return origLength - length; 410 case FLUSHING_STATE: 411 if (level != NO_COMPRESSION) { 412 /* We have to supply some lookahead. 8 bit lookahead 413 * is needed by the zlib inflater, and we must fill 414 * the next byte, so that all bits are flushed. 415 */ 416 int neededbits = 8 + ((-pending.BitCount) & 7); 417 while (neededbits > 0) { 418 /* write a static tree block consisting solely of 419 * an EOF: 420 */ 421 pending.WriteBits(2, 10); 422 neededbits -= 10; 423 } 424 } 425 state = BUSY_STATE; 426 break; 427 case FINISHING_STATE: 428 pending.AlignToByte(); 429 430 // Compressed data is complete. Write footer information if required. 431 if (!noZlibHeaderOrFooter) { 432 int adler = engine.Adler; 433 pending.WriteShortMSB(adler >> 16); 434 pending.WriteShortMSB(adler & 0xffff); 435 } 436 state = FINISHED_STATE; 437 break; 438 } 439 } 440 } 441 return origLength - length; 442 } 443 444 /// <summary> 445 /// Sets the dictionary which should be used in the deflate process. 446 /// This call is equivalent to <code>setDictionary(dict, 0, dict.Length)</code>. 447 /// </summary> 448 /// <param name="dictionary"> 449 /// the dictionary. 450 /// </param> 451 /// <exception cref="System.InvalidOperationException"> 452 /// if SetInput () or Deflate () were already called or another dictionary was already set. 453 /// </exception> SetDictionary(byte[] dictionary)454 public void SetDictionary(byte[] dictionary) 455 { 456 SetDictionary(dictionary, 0, dictionary.Length); 457 } 458 459 /// <summary> 460 /// Sets the dictionary which should be used in the deflate process. 461 /// The dictionary is a byte array containing strings that are 462 /// likely to occur in the data which should be compressed. The 463 /// dictionary is not stored in the compressed output, only a 464 /// checksum. To decompress the output you need to supply the same 465 /// dictionary again. 466 /// </summary> 467 /// <param name="dictionary"> 468 /// The dictionary data 469 /// </param> 470 /// <param name="index"> 471 /// The index where dictionary information commences. 472 /// </param> 473 /// <param name="count"> 474 /// The number of bytes in the dictionary. 475 /// </param> 476 /// <exception cref="System.InvalidOperationException"> 477 /// If SetInput () or Deflate() were already called or another dictionary was already set. 478 /// </exception> SetDictionary(byte[] dictionary, int index, int count)479 public void SetDictionary(byte[] dictionary, int index, int count) 480 { 481 if (state != INIT_STATE) { 482 throw new InvalidOperationException(); 483 } 484 485 state = SETDICT_STATE; 486 engine.SetDictionary(dictionary, index, count); 487 } 488 489 #region Instance Fields 490 /// <summary> 491 /// Compression level. 492 /// </summary> 493 int level; 494 495 /// <summary> 496 /// If true no Zlib/RFC1950 headers or footers are generated 497 /// </summary> 498 bool noZlibHeaderOrFooter; 499 500 /// <summary> 501 /// The current state. 502 /// </summary> 503 int state; 504 505 /// <summary> 506 /// The total bytes of output written. 507 /// </summary> 508 long totalOut; 509 510 /// <summary> 511 /// The pending output. 512 /// </summary> 513 DeflaterPending pending; 514 515 /// <summary> 516 /// The deflater engine. 517 /// </summary> 518 DeflaterEngine engine; 519 #endregion 520 } 521 } 522