InflaterDynHeader.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
  2. using System;
  3. using System.Collections.Generic;
  4. namespace ICSharpCode.SharpZipLib.Zip.Compression
  5. {
  6. internal class InflaterDynHeader
  7. {
  8. #region Constants
  9. // maximum number of literal/length codes
  10. private const int LITLEN_MAX = 286;
  11. // maximum number of distance codes
  12. private const int DIST_MAX = 30;
  13. // maximum data code lengths to read
  14. private const int CODELEN_MAX = LITLEN_MAX + DIST_MAX;
  15. // maximum meta code length codes to read
  16. private const int META_MAX = 19;
  17. private static readonly int[] MetaCodeLengthIndex =
  18. { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
  19. #endregion Constants
  20. /// <summary>
  21. /// Continue decoding header from <see cref="input"/> until more bits are needed or decoding has been completed
  22. /// </summary>
  23. /// <returns>Returns whether decoding could be completed</returns>
  24. public bool AttemptRead()
  25. => !state.MoveNext() || state.Current;
  26. public InflaterDynHeader(StreamManipulator input)
  27. {
  28. this.input = input;
  29. stateMachine = CreateStateMachine();
  30. state = stateMachine.GetEnumerator();
  31. }
  32. private IEnumerable<bool> CreateStateMachine()
  33. {
  34. // Read initial code length counts from header
  35. while (!input.TryGetBits(5, ref litLenCodeCount, 257)) yield return false;
  36. while (!input.TryGetBits(5, ref distanceCodeCount, 1)) yield return false;
  37. while (!input.TryGetBits(4, ref metaCodeCount, 4)) yield return false;
  38. var dataCodeCount = litLenCodeCount + distanceCodeCount;
  39. if (litLenCodeCount > LITLEN_MAX) throw new ValueOutOfRangeException(nameof(litLenCodeCount));
  40. if (distanceCodeCount > DIST_MAX) throw new ValueOutOfRangeException(nameof(distanceCodeCount));
  41. if (metaCodeCount > META_MAX) throw new ValueOutOfRangeException(nameof(metaCodeCount));
  42. // Load code lengths for the meta tree from the header bits
  43. for (int i = 0; i < metaCodeCount; i++)
  44. {
  45. while (!input.TryGetBits(3, ref codeLengths, MetaCodeLengthIndex[i])) yield return false;
  46. }
  47. var metaCodeTree = new InflaterHuffmanTree(codeLengths);
  48. // Decompress the meta tree symbols into the data table code lengths
  49. int index = 0;
  50. while (index < dataCodeCount)
  51. {
  52. byte codeLength;
  53. int symbol;
  54. while ((symbol = metaCodeTree.GetSymbol(input)) < 0) yield return false;
  55. if (symbol < 16)
  56. {
  57. // append literal code length
  58. codeLengths[index++] = (byte)symbol;
  59. }
  60. else
  61. {
  62. int repeatCount = 0;
  63. if (symbol == 16) // Repeat last code length 3..6 times
  64. {
  65. if (index == 0)
  66. throw new StreamDecodingException("Cannot repeat previous code length when no other code length has been read");
  67. codeLength = codeLengths[index - 1];
  68. // 2 bits + 3, [3..6]
  69. while (!input.TryGetBits(2, ref repeatCount, 3)) yield return false;
  70. }
  71. else if (symbol == 17) // Repeat zero 3..10 times
  72. {
  73. codeLength = 0;
  74. // 3 bits + 3, [3..10]
  75. while (!input.TryGetBits(3, ref repeatCount, 3)) yield return false;
  76. }
  77. else // (symbol == 18), Repeat zero 11..138 times
  78. {
  79. codeLength = 0;
  80. // 7 bits + 11, [11..138]
  81. while (!input.TryGetBits(7, ref repeatCount, 11)) yield return false;
  82. }
  83. if (index + repeatCount > dataCodeCount)
  84. throw new StreamDecodingException("Cannot repeat code lengths past total number of data code lengths");
  85. while (repeatCount-- > 0)
  86. codeLengths[index++] = codeLength;
  87. }
  88. }
  89. if (codeLengths[256] == 0)
  90. throw new StreamDecodingException("Inflater dynamic header end-of-block code missing");
  91. litLenTree = new InflaterHuffmanTree(new ArraySegment<byte>(codeLengths, 0, litLenCodeCount));
  92. distTree = new InflaterHuffmanTree(new ArraySegment<byte>(codeLengths, litLenCodeCount, distanceCodeCount));
  93. yield return true;
  94. }
  95. /// <summary>
  96. /// Get literal/length huffman tree, must not be used before <see cref="AttemptRead"/> has returned true
  97. /// </summary>
  98. /// <exception cref="StreamDecodingException">If hader has not been successfully read by the state machine</exception>
  99. public InflaterHuffmanTree LiteralLengthTree
  100. => litLenTree ?? throw new StreamDecodingException("Header properties were accessed before header had been successfully read");
  101. /// <summary>
  102. /// Get distance huffman tree, must not be used before <see cref="AttemptRead"/> has returned true
  103. /// </summary>
  104. /// <exception cref="StreamDecodingException">If hader has not been successfully read by the state machine</exception>
  105. public InflaterHuffmanTree DistanceTree
  106. => distTree ?? throw new StreamDecodingException("Header properties were accessed before header had been successfully read");
  107. #region Instance Fields
  108. private readonly StreamManipulator input;
  109. private readonly IEnumerator<bool> state;
  110. private readonly IEnumerable<bool> stateMachine;
  111. private byte[] codeLengths = new byte[CODELEN_MAX];
  112. private InflaterHuffmanTree litLenTree;
  113. private InflaterHuffmanTree distTree;
  114. private int litLenCodeCount, distanceCodeCount, metaCodeCount;
  115. #endregion Instance Fields
  116. }
  117. }