| 1 | // StreamManipulator.cs
|
|---|
| 2 | //
|
|---|
| 3 | // Copyright (C) 2001 Mike Krueger
|
|---|
| 4 | //
|
|---|
| 5 | // This file was translated from java, it was part of the GNU Classpath
|
|---|
| 6 | // Copyright (C) 2001 Free Software Foundation, Inc.
|
|---|
| 7 | //
|
|---|
| 8 | // This program is free software; you can redistribute it and/or
|
|---|
| 9 | // modify it under the terms of the GNU General Public License
|
|---|
| 10 | // as published by the Free Software Foundation; either version 2
|
|---|
| 11 | // of the License, or (at your option) any later version.
|
|---|
| 12 | //
|
|---|
| 13 | // This program is distributed in the hope that it will be useful,
|
|---|
| 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 16 | // GNU General Public License for more details.
|
|---|
| 17 | //
|
|---|
| 18 | // You should have received a copy of the GNU General Public License
|
|---|
| 19 | // along with this program; if not, write to the Free Software
|
|---|
| 20 | // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|---|
| 21 | //
|
|---|
| 22 | // Linking this library statically or dynamically with other modules is
|
|---|
| 23 | // making a combined work based on this library. Thus, the terms and
|
|---|
| 24 | // conditions of the GNU General Public License cover the whole
|
|---|
| 25 | // combination.
|
|---|
| 26 | //
|
|---|
| 27 | // As a special exception, the copyright holders of this library give you
|
|---|
| 28 | // permission to link this library with independent modules to produce an
|
|---|
| 29 | // executable, regardless of the license terms of these independent
|
|---|
| 30 | // modules, and to copy and distribute the resulting executable under
|
|---|
| 31 | // terms of your choice, provided that you also meet, for each linked
|
|---|
| 32 | // independent module, the terms and conditions of the license of that
|
|---|
| 33 | // module. An independent module is a module which is not derived from
|
|---|
| 34 | // or based on this library. If you modify this library, you may extend
|
|---|
| 35 | // this exception to your version of the library, but you are not
|
|---|
| 36 | // obligated to do so. If you do not wish to do so, delete this
|
|---|
| 37 | // exception statement from your version.
|
|---|
| 38 |
|
|---|
| 39 | using System;
|
|---|
| 40 |
|
|---|
| 41 | namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
|
|---|
| 42 | {
|
|---|
| 43 |
|
|---|
| 44 | /// <summary>
|
|---|
| 45 | /// This class allows us to retrieve a specified number of bits from
|
|---|
| 46 | /// the input buffer, as well as copy big byte blocks.
|
|---|
| 47 | ///
|
|---|
| 48 | /// It uses an int buffer to store up to 31 bits for direct
|
|---|
| 49 | /// manipulation. This guarantees that we can get at least 16 bits,
|
|---|
| 50 | /// but we only need at most 15, so this is all safe.
|
|---|
| 51 | ///
|
|---|
| 52 | /// There are some optimizations in this class, for example, you must
|
|---|
| 53 | /// never peek more than 8 bits more than needed, and you must first
|
|---|
| 54 | /// peek bits before you may drop them. This is not a general purpose
|
|---|
| 55 | /// class but optimized for the behaviour of the Inflater.
|
|---|
| 56 | ///
|
|---|
| 57 | /// authors of the original java version : John Leuner, Jochen Hoenicke
|
|---|
| 58 | /// </summary>
|
|---|
| 59 | public class StreamManipulator
|
|---|
| 60 | {
|
|---|
| 61 | #region Constructors
|
|---|
| 62 | /// <summary>
|
|---|
| 63 | /// Constructs a default StreamManipulator with all buffers empty
|
|---|
| 64 | /// </summary>
|
|---|
| 65 | public StreamManipulator()
|
|---|
| 66 | {
|
|---|
| 67 | }
|
|---|
| 68 | #endregion
|
|---|
| 69 |
|
|---|
| 70 | /// <summary>
|
|---|
| 71 | /// Get the next sequence of bits but don't increase input pointer. bitCount must be
|
|---|
| 72 | /// less or equal 16 and if this call succeeds, you must drop
|
|---|
| 73 | /// at least n - 8 bits in the next call.
|
|---|
| 74 | /// </summary>
|
|---|
| 75 | /// <param name="bitCount">The number of bits to peek.</param>
|
|---|
| 76 | /// <returns>
|
|---|
| 77 | /// the value of the bits, or -1 if not enough bits available. */
|
|---|
| 78 | /// </returns>
|
|---|
| 79 | public int PeekBits(int bitCount)
|
|---|
| 80 | {
|
|---|
| 81 | if (bitsInBuffer_ < bitCount) {
|
|---|
| 82 | if (windowStart_ == windowEnd_) {
|
|---|
| 83 | return -1; // ok
|
|---|
| 84 | }
|
|---|
| 85 | buffer_ |= (uint)((window_[windowStart_++] & 0xff |
|
|---|
| 86 | (window_[windowStart_++] & 0xff) << 8) << bitsInBuffer_);
|
|---|
| 87 | bitsInBuffer_ += 16;
|
|---|
| 88 | }
|
|---|
| 89 | return (int)(buffer_ & ((1 << bitCount) - 1));
|
|---|
| 90 | }
|
|---|
| 91 |
|
|---|
| 92 | /// <summary>
|
|---|
| 93 | /// Drops the next n bits from the input. You should have called PeekBits
|
|---|
| 94 | /// with a bigger or equal n before, to make sure that enough bits are in
|
|---|
| 95 | /// the bit buffer.
|
|---|
| 96 | /// </summary>
|
|---|
| 97 | /// <param name="bitCount">The number of bits to drop.</param>
|
|---|
| 98 | public void DropBits(int bitCount)
|
|---|
| 99 | {
|
|---|
| 100 | buffer_ >>= bitCount;
|
|---|
| 101 | bitsInBuffer_ -= bitCount;
|
|---|
| 102 | }
|
|---|
| 103 |
|
|---|
| 104 | /// <summary>
|
|---|
| 105 | /// Gets the next n bits and increases input pointer. This is equivalent
|
|---|
| 106 | /// to <see cref="PeekBits"/> followed by <see cref="DropBits"/>, except for correct error handling.
|
|---|
| 107 | /// </summary>
|
|---|
| 108 | /// <param name="bitCount">The number of bits to retrieve.</param>
|
|---|
| 109 | /// <returns>
|
|---|
| 110 | /// the value of the bits, or -1 if not enough bits available.
|
|---|
| 111 | /// </returns>
|
|---|
| 112 | public int GetBits(int bitCount)
|
|---|
| 113 | {
|
|---|
| 114 | int bits = PeekBits(bitCount);
|
|---|
| 115 | if (bits >= 0) {
|
|---|
| 116 | DropBits(bitCount);
|
|---|
| 117 | }
|
|---|
| 118 | return bits;
|
|---|
| 119 | }
|
|---|
| 120 |
|
|---|
| 121 | /// <summary>
|
|---|
| 122 | /// Gets the number of bits available in the bit buffer. This must be
|
|---|
| 123 | /// only called when a previous PeekBits() returned -1.
|
|---|
| 124 | /// </summary>
|
|---|
| 125 | /// <returns>
|
|---|
| 126 | /// the number of bits available.
|
|---|
| 127 | /// </returns>
|
|---|
| 128 | public int AvailableBits {
|
|---|
| 129 | get {
|
|---|
| 130 | return bitsInBuffer_;
|
|---|
| 131 | }
|
|---|
| 132 | }
|
|---|
| 133 |
|
|---|
| 134 | /// <summary>
|
|---|
| 135 | /// Gets the number of bytes available.
|
|---|
| 136 | /// </summary>
|
|---|
| 137 | /// <returns>
|
|---|
| 138 | /// The number of bytes available.
|
|---|
| 139 | /// </returns>
|
|---|
| 140 | public int AvailableBytes {
|
|---|
| 141 | get {
|
|---|
| 142 | return windowEnd_ - windowStart_ + (bitsInBuffer_ >> 3);
|
|---|
| 143 | }
|
|---|
| 144 | }
|
|---|
| 145 |
|
|---|
| 146 | /// <summary>
|
|---|
| 147 | /// Skips to the next byte boundary.
|
|---|
| 148 | /// </summary>
|
|---|
| 149 | public void SkipToByteBoundary()
|
|---|
| 150 | {
|
|---|
| 151 | buffer_ >>= (bitsInBuffer_ & 7);
|
|---|
| 152 | bitsInBuffer_ &= ~7;
|
|---|
| 153 | }
|
|---|
| 154 |
|
|---|
| 155 | /// <summary>
|
|---|
| 156 | /// Returns true when SetInput can be called
|
|---|
| 157 | /// </summary>
|
|---|
| 158 | public bool IsNeedingInput {
|
|---|
| 159 | get {
|
|---|
| 160 | return windowStart_ == windowEnd_;
|
|---|
| 161 | }
|
|---|
| 162 | }
|
|---|
| 163 |
|
|---|
| 164 | /// <summary>
|
|---|
| 165 | /// Copies bytes from input buffer to output buffer starting
|
|---|
| 166 | /// at output[offset]. You have to make sure, that the buffer is
|
|---|
| 167 | /// byte aligned. If not enough bytes are available, copies fewer
|
|---|
| 168 | /// bytes.
|
|---|
| 169 | /// </summary>
|
|---|
| 170 | /// <param name="output">
|
|---|
| 171 | /// The buffer to copy bytes to.
|
|---|
| 172 | /// </param>
|
|---|
| 173 | /// <param name="offset">
|
|---|
| 174 | /// The offset in the buffer at which copying starts
|
|---|
| 175 | /// </param>
|
|---|
| 176 | /// <param name="length">
|
|---|
| 177 | /// The length to copy, 0 is allowed.
|
|---|
| 178 | /// </param>
|
|---|
| 179 | /// <returns>
|
|---|
| 180 | /// The number of bytes copied, 0 if no bytes were available.
|
|---|
| 181 | /// </returns>
|
|---|
| 182 | /// <exception cref="ArgumentOutOfRangeException">
|
|---|
| 183 | /// Length is less than zero
|
|---|
| 184 | /// </exception>
|
|---|
| 185 | /// <exception cref="InvalidOperationException">
|
|---|
| 186 | /// Bit buffer isnt byte aligned
|
|---|
| 187 | /// </exception>
|
|---|
| 188 | public int CopyBytes(byte[] output, int offset, int length)
|
|---|
| 189 | {
|
|---|
| 190 | if (length < 0) {
|
|---|
| 191 | throw new ArgumentOutOfRangeException("length");
|
|---|
| 192 | }
|
|---|
| 193 |
|
|---|
| 194 | if ((bitsInBuffer_ & 7) != 0) {
|
|---|
| 195 | // bits_in_buffer may only be 0 or a multiple of 8
|
|---|
| 196 | throw new InvalidOperationException("Bit buffer is not byte aligned!");
|
|---|
| 197 | }
|
|---|
| 198 |
|
|---|
| 199 | int count = 0;
|
|---|
| 200 | while ((bitsInBuffer_ > 0) && (length > 0)) {
|
|---|
| 201 | output[offset++] = (byte) buffer_;
|
|---|
| 202 | buffer_ >>= 8;
|
|---|
| 203 | bitsInBuffer_ -= 8;
|
|---|
| 204 | length--;
|
|---|
| 205 | count++;
|
|---|
| 206 | }
|
|---|
| 207 |
|
|---|
| 208 | if (length == 0) {
|
|---|
| 209 | return count;
|
|---|
| 210 | }
|
|---|
| 211 |
|
|---|
| 212 | int avail = windowEnd_ - windowStart_;
|
|---|
| 213 | if (length > avail) {
|
|---|
| 214 | length = avail;
|
|---|
| 215 | }
|
|---|
| 216 | System.Array.Copy(window_, windowStart_, output, offset, length);
|
|---|
| 217 | windowStart_ += length;
|
|---|
| 218 |
|
|---|
| 219 | if (((windowStart_ - windowEnd_) & 1) != 0) {
|
|---|
| 220 | // We always want an even number of bytes in input, see peekBits
|
|---|
| 221 | buffer_ = (uint)(window_[windowStart_++] & 0xff);
|
|---|
| 222 | bitsInBuffer_ = 8;
|
|---|
| 223 | }
|
|---|
| 224 | return count + length;
|
|---|
| 225 | }
|
|---|
| 226 |
|
|---|
| 227 | /// <summary>
|
|---|
| 228 | /// Resets state and empties internal buffers
|
|---|
| 229 | /// </summary>
|
|---|
| 230 | public void Reset()
|
|---|
| 231 | {
|
|---|
| 232 | buffer_ = 0;
|
|---|
| 233 | windowStart_ = windowEnd_ = bitsInBuffer_ = 0;
|
|---|
| 234 | }
|
|---|
| 235 |
|
|---|
| 236 | /// <summary>
|
|---|
| 237 | /// Add more input for consumption.
|
|---|
| 238 | /// Only call when IsNeedingInput returns true
|
|---|
| 239 | /// </summary>
|
|---|
| 240 | /// <param name="buffer">data to be input</param>
|
|---|
| 241 | /// <param name="offset">offset of first byte of input</param>
|
|---|
| 242 | /// <param name="count">number of bytes of input to add.</param>
|
|---|
| 243 | public void SetInput(byte[] buffer, int offset, int count)
|
|---|
| 244 | {
|
|---|
| 245 | if ( buffer == null ) {
|
|---|
| 246 | throw new ArgumentNullException("buffer");
|
|---|
| 247 | }
|
|---|
| 248 |
|
|---|
| 249 | if ( offset < 0 ) {
|
|---|
| 250 | #if NETCF_1_0
|
|---|
| 251 | throw new ArgumentOutOfRangeException("offset");
|
|---|
| 252 | #else
|
|---|
| 253 | throw new ArgumentOutOfRangeException("offset", "Cannot be negative");
|
|---|
| 254 | #endif
|
|---|
| 255 | }
|
|---|
| 256 |
|
|---|
| 257 | if ( count < 0 ) {
|
|---|
| 258 | #if NETCF_1_0
|
|---|
| 259 | throw new ArgumentOutOfRangeException("count");
|
|---|
| 260 | #else
|
|---|
| 261 | throw new ArgumentOutOfRangeException("count", "Cannot be negative");
|
|---|
| 262 | #endif
|
|---|
| 263 | }
|
|---|
| 264 |
|
|---|
| 265 | if (windowStart_ < windowEnd_) {
|
|---|
| 266 | throw new InvalidOperationException("Old input was not completely processed");
|
|---|
| 267 | }
|
|---|
| 268 |
|
|---|
| 269 | int end = offset + count;
|
|---|
| 270 |
|
|---|
| 271 | // We want to throw an ArrayIndexOutOfBoundsException early.
|
|---|
| 272 | // Note the check also handles integer wrap around.
|
|---|
| 273 | if ((offset > end) || (end > buffer.Length) ) {
|
|---|
| 274 | throw new ArgumentOutOfRangeException("count");
|
|---|
| 275 | }
|
|---|
| 276 |
|
|---|
| 277 | if ((count & 1) != 0) {
|
|---|
| 278 | // We always want an even number of bytes in input, see PeekBits
|
|---|
| 279 | buffer_ |= (uint)((buffer[offset++] & 0xff) << bitsInBuffer_);
|
|---|
| 280 | bitsInBuffer_ += 8;
|
|---|
| 281 | }
|
|---|
| 282 |
|
|---|
| 283 | window_ = buffer;
|
|---|
| 284 | windowStart_ = offset;
|
|---|
| 285 | windowEnd_ = end;
|
|---|
| 286 | }
|
|---|
| 287 |
|
|---|
| 288 | #region Instance Fields
|
|---|
| 289 | private byte[] window_;
|
|---|
| 290 | private int windowStart_;
|
|---|
| 291 | private int windowEnd_;
|
|---|
| 292 |
|
|---|
| 293 | private uint buffer_;
|
|---|
| 294 | private int bitsInBuffer_;
|
|---|
| 295 | #endregion
|
|---|
| 296 | }
|
|---|
| 297 | }
|
|---|