OutputWindow.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. using System;
  2. namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
  3. {
  4. /// <summary>
  5. /// Contains the output from the Inflation process.
  6. /// We need to have a window so that we can refer backwards into the output stream
  7. /// to repeat stuff.<br/>
  8. /// Author of the original java version : John Leuner
  9. /// </summary>
  10. public class OutputWindow
  11. {
  12. #region Constants
  13. const int WindowSize = 1 << 15;
  14. const int WindowMask = WindowSize - 1;
  15. #endregion
  16. #region Instance Fields
  17. byte[] window = new byte[WindowSize]; //The window is 2^15 bytes
  18. int windowEnd;
  19. int windowFilled;
  20. #endregion
  21. /// <summary>
  22. /// Write a byte to this output window
  23. /// </summary>
  24. /// <param name="value">value to write</param>
  25. /// <exception cref="InvalidOperationException">
  26. /// if window is full
  27. /// </exception>
  28. public void Write(int value)
  29. {
  30. if (windowFilled++ == WindowSize) {
  31. throw new InvalidOperationException("Window full");
  32. }
  33. window[windowEnd++] = (byte)value;
  34. windowEnd &= WindowMask;
  35. }
  36. private void SlowRepeat(int repStart, int length, int distance)
  37. {
  38. while (length-- > 0) {
  39. window[windowEnd++] = window[repStart++];
  40. windowEnd &= WindowMask;
  41. repStart &= WindowMask;
  42. }
  43. }
  44. /// <summary>
  45. /// Append a byte pattern already in the window itself
  46. /// </summary>
  47. /// <param name="length">length of pattern to copy</param>
  48. /// <param name="distance">distance from end of window pattern occurs</param>
  49. /// <exception cref="InvalidOperationException">
  50. /// If the repeated data overflows the window
  51. /// </exception>
  52. public void Repeat(int length, int distance)
  53. {
  54. if ((windowFilled += length) > WindowSize) {
  55. throw new InvalidOperationException("Window full");
  56. }
  57. int repStart = (windowEnd - distance) & WindowMask;
  58. int border = WindowSize - length;
  59. if ((repStart <= border) && (windowEnd < border)) {
  60. if (length <= distance) {
  61. System.Array.Copy(window, repStart, window, windowEnd, length);
  62. windowEnd += length;
  63. } else {
  64. // We have to copy manually, since the repeat pattern overlaps.
  65. while (length-- > 0) {
  66. window[windowEnd++] = window[repStart++];
  67. }
  68. }
  69. } else {
  70. SlowRepeat(repStart, length, distance);
  71. }
  72. }
  73. /// <summary>
  74. /// Copy from input manipulator to internal window
  75. /// </summary>
  76. /// <param name="input">source of data</param>
  77. /// <param name="length">length of data to copy</param>
  78. /// <returns>the number of bytes copied</returns>
  79. public int CopyStored(StreamManipulator input, int length)
  80. {
  81. length = Math.Min(Math.Min(length, WindowSize - windowFilled), input.AvailableBytes);
  82. int copied;
  83. int tailLen = WindowSize - windowEnd;
  84. if (length > tailLen) {
  85. copied = input.CopyBytes(window, windowEnd, tailLen);
  86. if (copied == tailLen) {
  87. copied += input.CopyBytes(window, 0, length - tailLen);
  88. }
  89. } else {
  90. copied = input.CopyBytes(window, windowEnd, length);
  91. }
  92. windowEnd = (windowEnd + copied) & WindowMask;
  93. windowFilled += copied;
  94. return copied;
  95. }
  96. /// <summary>
  97. /// Copy dictionary to window
  98. /// </summary>
  99. /// <param name="dictionary">source dictionary</param>
  100. /// <param name="offset">offset of start in source dictionary</param>
  101. /// <param name="length">length of dictionary</param>
  102. /// <exception cref="InvalidOperationException">
  103. /// If window isnt empty
  104. /// </exception>
  105. public void CopyDict(byte[] dictionary, int offset, int length)
  106. {
  107. if (dictionary == null) {
  108. throw new ArgumentNullException("nameof(dictionary)");
  109. }
  110. if (windowFilled > 0) {
  111. throw new InvalidOperationException();
  112. }
  113. if (length > WindowSize) {
  114. offset += length - WindowSize;
  115. length = WindowSize;
  116. }
  117. System.Array.Copy(dictionary, offset, window, 0, length);
  118. windowEnd = length & WindowMask;
  119. }
  120. /// <summary>
  121. /// Get remaining unfilled space in window
  122. /// </summary>
  123. /// <returns>Number of bytes left in window</returns>
  124. public int GetFreeSpace()
  125. {
  126. return WindowSize - windowFilled;
  127. }
  128. /// <summary>
  129. /// Get bytes available for output in window
  130. /// </summary>
  131. /// <returns>Number of bytes filled</returns>
  132. public int GetAvailable()
  133. {
  134. return windowFilled;
  135. }
  136. /// <summary>
  137. /// Copy contents of window to output
  138. /// </summary>
  139. /// <param name="output">buffer to copy to</param>
  140. /// <param name="offset">offset to start at</param>
  141. /// <param name="len">number of bytes to count</param>
  142. /// <returns>The number of bytes copied</returns>
  143. /// <exception cref="InvalidOperationException">
  144. /// If a window underflow occurs
  145. /// </exception>
  146. public int CopyOutput(byte[] output, int offset, int len)
  147. {
  148. int copyEnd = windowEnd;
  149. if (len > windowFilled) {
  150. len = windowFilled;
  151. } else {
  152. copyEnd = (windowEnd - windowFilled + len) & WindowMask;
  153. }
  154. int copied = len;
  155. int tailLen = len - copyEnd;
  156. if (tailLen > 0) {
  157. System.Array.Copy(window, WindowSize - tailLen, output, offset, tailLen);
  158. offset += tailLen;
  159. len = copyEnd;
  160. }
  161. System.Array.Copy(window, copyEnd - len, output, offset, len);
  162. windowFilled -= copied;
  163. if (windowFilled < 0) {
  164. throw new InvalidOperationException();
  165. }
  166. return copied;
  167. }
  168. /// <summary>
  169. /// Reset by clearing window so <see cref="GetAvailable">GetAvailable</see> returns 0
  170. /// </summary>
  171. public void Reset()
  172. {
  173. windowFilled = windowEnd = 0;
  174. }
  175. }
  176. }