// Originally copyright (c) 2009 Dino Chiesa and Microsoft Corporation. // All rights reserved. // See license.txt, section Ionic.Zlib license #if __MonoCS__ using System; using System.IO; using ClassicalSharp; namespace Ionic.Zlib { internal class DeflateStream : ReadOnlyStream { ZlibCodec z; bool _leaveOpen; byte[] workBuffer; Stream _stream; public DeflateStream(Stream stream, bool leaveOpen) { _stream = stream; _leaveOpen = leaveOpen; workBuffer = new byte[16384]; // TODO: 1024 bytes? z = new ZlibCodec(); } public override void Close() { z.EndInflate(); z = null; if (!_leaveOpen) _stream.Dispose(); _stream = null; } public override void Flush() { } public override int Read(byte[] buffer, int offset, int count) { // According to MS documentation, any implementation of the IO.Stream.Read function must: // (a) throw an exception if offset & count reference an invalid part of the buffer, // or if count < 0, or if buffer is null // (b) return 0 only upon EOF, or if count = 0 // (c) if not EOF, then return at least 1 byte, up to bytes if (count == 0) return 0; int rc = 0; // set up the output of the deflate/inflate codec: z.OutputBuffer = buffer; z.NextOut = offset; z.AvailableBytesOut = count; z.InputBuffer = workBuffer; bool endOfInput = false; do { // need data in _workingBuffer in order to deflate/inflate. Here, we check if we have any. if (z.AvailableBytesIn == 0 && !endOfInput) { // No data available, so try to Read data from the captive stream. z.NextIn = 0; z.AvailableBytesIn = _stream.Read(workBuffer, 0, workBuffer.Length); if (z.AvailableBytesIn == 0) endOfInput = true; } rc = z.Inflate(); if (endOfInput && rc == RCode.BufferError) return 0; if (rc != RCode.Okay && rc != RCode.StreamEnd) throw new InvalidDataException("inflating: rc=" + rc); if ((endOfInput || rc == RCode.StreamEnd) && z.AvailableBytesOut == count) break; // nothing more to read } while (z.AvailableBytesOut > 0 && !endOfInput && rc == RCode.Okay); return count - z.AvailableBytesOut; } } } #endif