1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 using System.IO;
  8 
  9 namespace System.IO.Compression
 10 {
 11     public enum CompressionMode : int
 12     {
 13         compress = 0decompress = 1
 14     }
 15     
 16     public const int defaultDeflateCompressionLevel = -1;
 17     public const int noDeflateCompression = 0;
 18     public const int fastestDeflateCompression = 1;
 19     public const int optimalDeflateCompression = 9;
 20     public const int Z_NO_FLUSH = 0;
 21     public const int Z_STREAM_END = 1;
 22     public const int Z_FINISH = 4;
 23     public const int Z_STREAM_ERROR = -2;
 24     
 25     public nothrow string ExpandDeflateError(const string& messageint errorCode)
 26     {
 27         string expandedMessage = message;
 28         expandedMessage.Append(" error ").Append(RtRetvalStrZlib(errorCode)).Append(" code ").Append(ToString(errorCode));
 29         return expandedMessage;
 30     }
 31     
 32     public class DeflateException : Exception
 33     {
 34         public nothrow DeflateException(const string& message_int errorCode_) : base(ExpandDeflateError(message_errorCode_))errorCode(errorCode_)
 35         {
 36         }
 37         public inline nothrow int ErrorCode() const
 38         {
 39             return errorCode;
 40         }
 41         private int errorCode;
 42     }
 43     
 44     public class DeflateStream : ByteStream
 45     {
 46         public DeflateStream(const SharedPtr<ByteStream>& underlyingStream_int compressionLevel_) : this(underlyingStream_compressionLevel_16384)
 47         {
 48         }
 49         public DeflateStream(const SharedPtr<ByteStream>& underlyingStream_int compressionLevel_long bufferSize_) : 
 50             underlyingStream(underlyingStream_)mode(CompressionMode.compress)handle(null)inAvail(0u)bufferSize(bufferSize_)
 51             in(bufferSize)outAvail(0u)outPos(0)outHave(0u)endOfInput(false)endOfStream(false)out(bufferSize)
 52         {
 53             int ret = RtInitZlib(modecompressionLevel_&handle);
 54             if (ret < 0)
 55             {
 56                 throw DeflateException("Could not create DeflateStream"ret);
 57             }
 58         }
 59         public DeflateStream(const SharedPtr<ByteStream>& underlyingStream_CompressionMode mode_) : this(underlyingStream_mode_16384)
 60         {
 61         }
 62         public DeflateStream(const SharedPtr<ByteStream>& underlyingStream_CompressionMode mode_long bufferSize_) : 
 63             underlyingStream(underlyingStream_)mode(mode_)handle(null)inAvail(0u)bufferSize(bufferSize_)
 64             in(bufferSize)outAvail(0u)outPos(0)outHave(0u)endOfInput(false)endOfStream(false)out(bufferSize)
 65         {
 66             int ret = RtInitZlib(modedefaultDeflateCompressionLevel&handle);
 67             if (ret < 0)
 68             {
 69                 throw DeflateException("Could not create DeflateStream"ret);
 70             }
 71         }
 72         suppress DeflateStream(DeflateStream&&);
 73         suppress void operator=(DeflateStream&&);
 74         suppress DeflateStream(const DeflateStream&);
 75         suppress void operator=(const DeflateStream&);
 76         public inline nothrow CompressionMode Mode() const
 77         {
 78             return mode;
 79         }
 80         public ~DeflateStream()
 81         {
 82             if (handle != null)
 83             {
 84                 try
 85                 {
 86                     if (mode == CompressionMode.compress)
 87                     {
 88                         Finish();
 89                     }
 90                 }
 91                 catch (const Exception& ex)
 92                 {
 93                     // destructor should not throw
 94                 }
 95                 RtDoneZlib(modehandle);
 96             }
 97         }
 98         public override int ReadByte()
 99         {
100             byte x = 0u;
101             long bytesRead = this->Read(&x1);
102             if (bytesRead == 0)
103             {
104                 return -1;
105             }
106             return x;
107         }
108         public override long Read(byte* buflong count)
109         {
110             if (mode != CompressionMode.decompress)
111             {
112                 throw DeflateException("Cannot read from DeflateStream in CompressionMode.compress"Z_STREAM_ERROR);
113             }
114             long bytesRead = 0;
115             do
116             {
117                 if (inAvail == 0u && !endOfInput)
118                 {
119                     inAvail = cast<uint>(underlyingStream->Read(cast<byte*>(in.Mem())bufferSize));
120                     if (inAvail == 0u)
121                     {
122                         endOfInput = true;
123                     }
124                     RtSetInputZlib(in.Mem()inAvailhandle);
125                 }
126                 do
127                 {
128                     if (outHave == 0u && !endOfStream)
129                     {
130                         int ret = RtInflateZlib(out.Mem()cast<uint>(bufferSize)&outHave&outAvail&inAvailhandle);
131                         if (ret < 0)
132                         {
133                             throw DeflateException("DeflateStream could not decompress"ret);
134                         }
135                         if (ret == Z_STREAM_END)
136                         {
137                             endOfStream = true;
138                         }
139                         outPos = 0;
140                     }
141                     while (count > 0 && outHave > 0u)
142                     {
143                         *buf++ = out[outPos++];
144                         --count;
145                         --outHave;
146                         ++bytesRead;
147                     }
148                 }
149                 while (count > 0 && outAvail == 0u);
150             }
151             while (count > 0 && !endOfStream && !endOfInput);
152             if (endOfInput && !endOfStream)
153             {
154                 throw DeflateException("DeflateStream unexpected end of input"Z_STREAM_ERROR);
155             }
156             return bytesRead;
157         }
158         public override void Write(byte x)
159         {
160             this->Write(&x1);
161         }
162         public override void Write(byte* buflong count)
163         {
164             if (mode != CompressionMode.compress)
165             {
166                 throw DeflateException("Cannot write to DeflateStream in CompressionMode.decompress"Z_STREAM_ERROR);
167             }
168             while (count > 0)
169             {
170                 byte* inP = cast<byte*>(in.Mem());
171                 inAvail = 0u;
172                 while (count > 0 && inAvail < cast<uint>(bufferSize))
173                 {
174                     *inP++ = *buf++;
175                     --count;
176                     ++inAvail;
177                 }
178                 RtSetInputZlib(in.Mem()inAvailhandle);
179                 do
180                 {
181                     uint have = 0u;
182                     int ret = RtDeflateZlib(out.Mem()cast<uint>(bufferSize)&have&outAvailhandleZ_NO_FLUSH);
183                     if (ret < 0)
184                     {
185                         throw DeflateException("DeflateStream could not compress"ret);
186                     }
187                     underlyingStream->Write(cast<byte*>(out.Mem())cast<long>(have));
188                 }
189                 while (outAvail == 0u);
190             }
191         }
192         private void Finish()
193         {
194             do
195             {
196                 uint have = 0u;
197                 int ret = RtDeflateZlib(out.Mem()cast<uint>(bufferSize)&have&outAvailhandleZ_FINISH);
198                 if (ret < 0)
199                 {
200                     throw DeflateException("DeflateStream could not compress"ret);
201                 }
202                 underlyingStream->Write(cast<byte*>(out.Mem())cast<long>(have));
203             }
204             while (outAvail == 0);
205         }
206         private SharedPtr<ByteStream> underlyingStream;
207         private CompressionMode mode;
208         private long bufferSize;
209         private uint inAvail;
210         private IOBuffer in;
211         private uint outAvail;
212         private long outPos;
213         private uint outHave;
214         private bool endOfInput;
215         private bool endOfStream;
216         private IOBuffer out;
217         private void* handle;
218     }
219 }